Design/StorageImplementationNotes

From Dyna

Jump to: navigation, search
  A term declaration must contain:
  
     name = "foo", // name of the term on c++ side
     storage = direct, // or indirect_nointern, or indirect_intern
  
  It may contain:
  
     args = [ "atype", "anothertype" ], % list of C++ side dyna terms
     sval_type = "afurthertype", % stored value type of the term
     sval_default = "afurthertype( afurthertypearg )" % constructor of sval_type to use; default: no_default
     
     primitive = "string" % this is akin to args = [ "string" ] and sval_type = name, 
           % with special glue code for correct behavior of primitive types 
           % (namely, sval() returns *this). Must be disjoint from args/sval declarations
  
  
  
  Note on "sval_default = no_default" :
  
  This option neccesiates, in some cases, the keeping of a valid bit. It must be kept when:
  1) The term is interned
  2) The term is *not* interned, but is symbiotic with another term.
  It may not be kept when:
  1) sval is the only field in it's struct
  
  In such a case we simply check for its presence.


   // Example 1:
   
   paramaters_to_storage_layer = [
   sterm(
       name = "edge",
       args = ["vertexID", "vertexID"],
       sval_type = "int_t",
       sval_default = "int_t(INT_MAX)",
       storage = direct,
   ),
   ]
   
   // Output of the storage layer:
   
   struct struct1 {
       vertexID field1;
       vertexID field2;
   
       hash_val hash() const; // some combination of field1.hash() & field2.hash()
   
       // field1 == rhs.field1 && field2 == rhs.field2
       bool operator == ( const struct1 & ) const;
       
       // field1 < rhs.field1 || field1 == rhs.field1 && field2 < rhs.field2
       bool operator < ( const struct1 & ) const;
   }
   
   struct struct2 {
       int_t field1;
   }
   
   hash_map< struct1, struct2 > edge_hash; // < key, value >
   
   class edge {
   
       // user exposed constructor
       edge( const vertexID & , const vertexID & ); // assign to struct1
   
       struct1 mem1;
   
       inline const vertexID & arg0() const;
       inline const vertexID & arg1() const;
   
       hash_val hash() const; // mem1.hash();
   
       const int_t & get_sval() const; // edge_hash.lookup( mem1 ).field1;
       const int_t & set_sval( const int_t & );
       
       bool operator == ( const edge & ) const; // arg0() == rhs.arg0() && arg1() == rhs.arg1()
       bool operator < ( const edge & ) const; // arg0() < rhs.arg0() || arg0() == rhs.arg0() && arg1() < rhs.arg1()
   
   }
   
   /* Point1: arg0, arg1, and sval are additionally implicit access methods, 
   * upon which others can be built. Further access methods should
   * have no need to visit struct1 and struct2 directly, correct?
   * They can rely upon the edge interface, or a pointer reinterpretation */
   
   /* Point2: I'm not sure there's any distinction between a shallow and deep
   * equality method from a structure's point of view, as a struct never contains
   * a term pointer, just a term itself. It's up to the term whether it does a
   * pointer check (interned case) or a recursive (deep) equality test. */
   
   /* Point3: Structs, terms, and primitives should also expose less-than
     * operators. This allows for sorted containers and space efficient intern
     * strategies like treemap/treeset */


   // Example 2:
   
   paramaters_to_storage_layer = [
   sterm(
       name = "foo",
       args = ["vertexID", "int_t", "string_t", ],
       sval_type = "int_t",
       sval_default = "int_t(INT_MAX)",
       storage = indirect_nointern,
   ),
   ]
   
   // Output of the storage layer:
   
   struct struct3 {
       vertexID field1;
       int_t field2;
       string_t field3;
   
       hash_val hash() const; // combine field(1-3).hash()
       bool operator == ( const struct3 & ) const;
       bool operator < ( const struct3 & ) const; // field1 < rhs.field1 || field1 == rhs.field1 && (field2 < rhs.field2 || field2 == rhs.field2 && field3 < rhs.field3 
   }
   
   struct struct4 {
       int_t field1;
   }
   
   hash_map< struct3, struct4 > foo_hash; // < key, value >
   
   class foo {
   
       foo( vertexID, int_t, string_t ); // maybe allocate a struct3 and assign
   
       struct3 * mem1;
   
       inline const vertexID & arg0() const;
       inline const dyna_int & arg1() const;
       inline const dyna_string & arg2() const;
   
       hash_val hash() const; // mem1->hash();
   
       const int_t & get_sval() const; // foo_hash.lookup( *mem1 ).field1;
       const int_t & set_sval( const int_t & );
       
       bool operator == ( const foo & ) const; // arg0() == rhs.arg0 && arg1() == rhs.arg1() && arg2 == rhs.arg2()
       
       bool operator < ( const foo & ) const; // arg0() < rhs.arg0() || arg0() == rhs.arg0() && (arg1() < rhs.arg1() || arg1() == rhs.arg1() && arg2() < rhs.arg2() 
   }


   // Example 3:
   
   paramaters_to_storage_layer = [
   sterm(
       name = "bar",
       args = ["baz", "int_t", ],
       sval_type = "edge",
       sval_default = no_default,
       storage = indirect_intern,
   ),
   ]
   
   
   // Output of the storage layer:
   
   struct struct5 {
       baz field1;
       int_t field2;
       edge field3;
       
       char bits; // extra bit collection
       
       hash_val hash() const; // combine field1.hash() & field2.hash() ONLY
       bool operator == ( const struct5 & ) const; // field1 == rhs.field1 && field2 == rhs.field2 ONLY
       bool operator < ( const struct5 & ) const; // field1 < rhs.field1 || field1 == rhs.field1 && field2 < rhs.field2 ONLY
   }
   
   hash_set< struct5 > bar_hash; // < key_value >
   
   
   class bar {
   
       // user exposed constructor
       bar( baz, dyna_int ); // allocate & assign struct 5 to var1; mem1 = bar_hash.find_or_insert( *var1 );
   
       struct5 * mem1;
       
       inline const baz & arg0() const;
       inline const dyna_int & arg1() const;
        
       hash_val hash() const; // mem1->hash();
          
       bool has_sval(); // mask test of mem1->bits
         
       const edge & get_sval() const; // checked mem1->field3
       const edge & set_sval( const edge & );
       
       bool operator == ( const edge & ) const; // arg0() == rhs.arg0() && arg1() == rhs.arg1()
        
       bool operator < ( const bar & ) const; // arg0() < rhs.arg0() || arg0() == rhs.arg0() && arg1() < rhs.arg1()
   }


  // Example 4:
  
  paramaters_to_storage_layer = [
  sterm(
      name = "string_t",
      primitive = "string",
      storage = indirect_intern,
  ),
  ]
  
  
  // Output of the storage layer:
  
  struct struct6 {
      string field1; // string is c++ wrapper around char*
     
      int hash() const; // field1.hash()
      bool operator == ( const struct6 & ) const; // field1 == rhs.field1
      bool operator < ( const struct6 & ) const; // field1 < rhs.field1
  }
  
  hash_set< struct6 > string_t_hash; // < key_value >
  
  
  class string_t {
  
      // user exposed constructor
      string_t( string ); // allocate & assign struct 6 to var1; mem1 = string_t_hash.find_or_insert( *var1 );
  
      struct6 * mem1;
  
      inline const string & arg0() const; // mem1->field1;
  
      int hash() const; // mem1->hash();
  
      string_t & sval(); // *this <- since this is a primitive type
      bool operator == ( const string_t & ) const; // arg0() == rhs.arg0()
      
      bool operator < ( const string_t & ) const; // arg0() < rhs.arg0()
  }
Personal tools