The e4_Storage class provides persistent storage of nodes and vertices. The representation of the physical storage is hidden in a driver, selected at construction time. Drivers for various databases can be easily constructed. The e4Graph package comes with a driver for MetaKit based storage.
A user application may have any number of storages open at a given time, limited only by the available machine resources. Each storage can use a different driver, storing its data in a variety of representations such as flat files or relational databases. The e4_Storage class provides methods to retrieve the name and driver identifier that were used to open the storage.
The storage representation is reference counted and is closed automatically when the last reference is discarded. The e4Graph package strongly encourages a programming style that uses stack allocated instances and so-called dot-based method invocation. Programming with pointers to instances allocated on the heap is possible but cumbersome and, unless care is taken, may lead to reference counting errors and memory leaks.
The e4_Storage class provides assignment and comparison operators, as shown in the following code snippet:
If auto-commit is turned on, any changes are committed to the storage at the time it is closed. Auto-commit is turned on by default. Changes can also be committed at any time under the control of the user program. A storage is considered dirty if any node or vertex in it has been modified and the change has not yet been committed. In the following example, the storage is automatically closed and committed when the instance s goes out of scope:e4_Storage s("mystorage", E4_METAKIT); ... if (s.IsValid()) { printf("The storage \"%s\" is valid\n", s.GetName()); } ... e4_Storage another = s; ... if (s == another) { printf("Yes, they are one and the same!\n"); }
The global variable invalidStorage refers to a constant instance of e4_Storage that is guaranteed to be invalid. You can assign this instance to a local e4_Storage variable to discard the reference to another storage it contains, as shown in the following example:{ e4_Storage s("mystorage", E4_METAKIT); ... }
In the above example, the assignment to s causes the number of references to the storage named mystorage to drop to zero, and it is automatically closed and committed if needed. When using some C++ compilers, you may need to assign invalidStorage to instance variables of e4_Storage embedded within heap allocated structures before these structures are freed, to ensure that the reference count of any storages referenced by the embedded instances is correct. When a storage is closed, any e4_Node and e4_Vertex instances held by the user program that refer to elements within that storage also become invalid.e4_Storage s("mystorage", E4_METAKIT); ... s = invalidStorage; if (s.IsValid()) { printf("Something fishy here!\n"); }
Each storage has a root node. The root node can be retrieved using the GetRootNode method and assigned using the SetRootNode method.
While a storage is open, it may also contain detached nodes and vertices. A vertex is detached if it is not contained within a node, and a node is detached if it is the value of only detached vertices. All detached entities within a storage are recycled when the storage is closed.
Memory space in a storage is recycled by a built in garbage collection mechanism. The garbage collector uses reachability to determine which memory space is in use and which can be recycled. A node is reachable if it is the current root node, if it is referenced by the user program, or if it can be visited from a reachable node by traversing one or more vertices. Similarly, a vertex is reachable if it is contained within a reachable node. All nodes and vertices that are not reachable are recycled when the garbage collector runs.
The e4_Storage class provides methods to control the behavior of the garbage collector. Garbage collection normally runs whenever e4Graph determines that a node or vertex became unreachable, for example, when the last reference held by the user program to a detached node is discarded. User programs instruct e4Graph to run the garbage collector by calling the DoGC method. It is possible also to defer garbage collecion by using the SetGCDeferral method, and user programs can query whether garbage collection is currently deferred by calling IsGCDeferred.
The cost of garbage collection is roughly proportional to the number of reachable entities within a storage, because the garbage collector must traverse the entire reachable graph to determine reachability. Therefore, for efficiency reasons, it is sometimes important to defer garbage collection. This is especially important for programs that operate on very large storages or that detach many nodes or vertices and then drop the last reference. In those cases, unless garbage collection is deferred, many garbage collections will be caused, and each collection will be able to recycle only the node or vertex that just became unreachable. If garbage collection is instead deferred until a later time in the execution and then invoked explicitly by calling DoGC, all garbage collections except the last one are avoided, and the unreachable elements are recycled while avoiding the cost of all but the last garbage collection.
Whenever an application makes significant changes to a storage, such as adding a node or vertex, or when a node or vertex becomes detached, e4Graph fires an event that can be intercepted by a callback function registered by the application. For example, when a node becomes, e4Graph fires a node detach event which can be intercepted by a callback registered with that storage by the application. The callback receives the node that just became detached as an argument; what the callback does is determined by the application.
Events are fired when the application explicitly requests the changes through the appropriate operation on a storage, node or vertex, or when the application implicitly causes the change, such as when a node becomes detached because it is referenced by the user program but not otherwise reachable. Note that when a node or vertex becomes detached because of some indirect operation on another part of the storage, delivering the node detach or vertex detach event may be deferred until a garbage collection occurs.
The following events can be intercepted by an application using e4Graph,
by registering callbacks for each specific event:
Event Name | When Fired: | Callback Function Type: |
Node Addition | After a new node is added to a storage. | typedef void (*e4_NodeAddCallbackFunction)(void *clientData, e4_Node n) |
Node Detach | After a node is determined to be detached. The callback may be delayed until a garbage collection is caused. | typedef void (*e4_NodeDetCallbackFunction)(void *clientData, e4_Node n) |
Vertex Addition | After a new vertex is added to a storage. | typedef void (*e4_VertexAddCallbackFunction)(void *clientData, e4_Vertex v) |
Vertex Detach | After a vertex is determined to be detached. The callback may be delayed until a garbage collection is caused. | typedef void (*e4_VertexDetCallbackFunction)(void *clientData, e4_Vertex v) |
Vertex Modification | Right after a vertex value is modified. | typedef void (*e4_VertexModCallbackFunction)(void *clientData, e4_Vertex v) |
The application can register, through interfaces described below, any number of callback functions for each event for a given storage. At callback registration time the application supplies the address of the function to call as well as as an arbitrary client data argument accessed through a void * pointer. The client data is passed as the first argument to the callback function when the event is fired, and the e4Graph storage element (node or vertex, respectively) on which the event occurs is passed as the second argument. Your application can also register the same callback function several times, with different client data pointers; in that case, the callback function will be invoked several times for each event, each time with the appropriate client data and the e4Graph storage element on which the event occurs. When several callback functions are registered for an event, the order in which they are called is undetermined; therefore applications should not rely on the order in which callback functions are invoked for a specific event. Similarly, when an event applies to several entities (e.g. detaching several vertices), the order in which callback functions are called is undetermined.
A single detach event, e.g. a vertex detach, may cause implicit detaching of a large number of other entities because they become from other entities in the storage but are still referenced by the use program. As explained above, detach events may be delayed, if the storage has deferred garbage collection, until a garbage collection is caused.
All detach callback functions are called after the current garbage collection has finished. Detach callback functions are free to do anything, including reattaching the node or vertex, or dropping the last reference to the entity. However, callback functions should still be implemented carefully, to avoid actions that may cause infinite loops. For example, a vertex modification callback function should not modify the value of the same or another vertex, otherwise an infinite chain of callback events is created.
To cancel a specific callback function for an event, your application can use the appropriate interface described below, supplying the same arguments as used when the callback function was registered. After the callback function is unregistered, that callback function will no longer be called with that client data when the event is fired; other callback functions registered for that event continue to be called when the event is fired. All registered callback functions are automatically removed when the storage on which they are registered is closed.
Methods and Constructors of e4_Storage
The following methods are defined for the e4_Storage class:
e4_Storage() | Default constructor. Returns a storage that is not connected to a persistent representation and is invalid. |
e4_Storage(const e4_Storage &ref) | Constructs a storage by copying the state of ref. The new storage and ref refer to the same underlying persistent representation. |
e4_Storage(const char *name, const char * storageKind) | Constructor. Returns a storage with the given name, using the storage driver identified by the storageKind argument. |
~e4_Storage() | Destructor. The underlying representation is reference counted and closed automatically when the last reference to it is discarded. If auto-commit is turned on, then changes to the storage are committed when the last reference is discarded. |
bool operator==(const e4_Storage &comp) const | Returns true if comp refers to the same storage instance as this or if both are invalid, false otherwise. |
bool operator!=(const e4_Storage &comp) const | Returns true if comp does not refer to the same storage instance as this, false if they are the same or if both are invalid. |
e4_Storage & operator=(const e4_Storage &ref) | Copies the state of ref to this e4_Storage instance and returns this. |
void AutoCommit(bool on) const | Turns auto-commit on or off. When on is true, the e4_Tree package automatically commits changes to the storage when it is closed. |
bool AutoCommit() const | Returns true when auto-commit is true, false otherwise. |
bool Commit() const | Commits any changes to the storage at this time. Returns true if the commit succeeded, false otherwise. |
bool Delete() | Deletes the underlying storage. No events are fired because of the deletion of storage elements. If the operation succeeds, this returns true. |
bool CopyTo(e4_Graph otherStorage, bool forceCommit) const | Copies the e4Graph contents of this storage to otherStorage. The previous contents of otherStorage is deleted, all references held by the user program to entities in otherStorage become invalid, and no events are fired because of the deletion. If forceCommit is true, then otherStorage is committed after the copy is done. After this operation, the e4Graph contents of this storage and otherStorage are identical. Changes made to one storage after the copy are not reflected in the other storage. Callbacks registered for otherStorage stay in effect and may be called when events fire after the copy if changes are made to otherStorage. |
bool IsDirty() const | Returns true when the storage has been modified and not yet committed. |
void MarkDirty() const | Marks the storage as dirty, i.e. it has been modified and not yet committed. |
bool GetRootdNode(e4_Node &n) const | Retrieves the current root node. |
bool SetRootNode(e4_Node n) const | Makes the node denoted by n be the root node. That node must be within this storage and be valid. |
bool CreateDetachedNode(e4_Node &n) const | Assigns to n a new detached node created within this storage. |
bool CreateDetachedVertex(const char *m, e4_Node n, e4_Vertex &v) const | Assigns to v a new detached vertex created within this storage that has the node n as its value. |
bool CreateDetachedVertex(const char *nm, int i, e4_Vertex &v) const | Assigns to v a new detached vertex created within this storage that has the integer i as its value. |
bool CreateDetachedVertex(const char *nm, double d, e4_Vertex &v) const | Assigns to v a new detached vertex created within this storage that has the double d as its value. |
bool CreateDetachedVertex(const char *nm, const char *s, e4_Vertex &v) const | Assigns to v a new detached vertex created within this storage that has the NULL terminated string s as its value. |
bool CreateDetachedVertex(const char *nm, const void *b, int nb, e4_Vertex &v) const | Assigns to v a new detached vertex created within this storage that has the uninterpreted binary value constructed from b and nb as it value. |
bool CreateDetachedVertex(const char *nm, e4_Value &vv, e4_Value &v) const | Assigns to v a new detached vertex created within this storage that has the value and type of vv as its value. |
bool GetNodeFromID(e4_NodeUniqueID id, e4_Node &n) const | Given a valid unique ID obtained from a node using GetUniqueID, retrieves the corresponding node. |
bool GetVertexFromID(e4_VertexUniqueID id, e4_Vertex &v) const | Given a valid unique ID obtained from a vertex using GetUniqueID, retrieves the corresponding vertex. |
bool IsValid() const | Returns true if the storage is valid, false otherwise. A storage is valid if it designates an open persistent storage. |
bool IsDirty() const | Returns true if the storage contains modifications to trees, nodes or fields that have not yet been committed to persistent storage. |
void DoGC() const | Causes a garbage collection to be executed in this storage. This call returns when the garbage collection has finished and after all detach events have fired. |
bool NeedsGC() const | Returns true if this storage contains unreclaimed unreachable entities. |
bool IsGCDeferred() const | Returns true if garbage collection is currently deferred in this storage. |
bool SetGCDeferral(bool newval) const | Sets garbage collection deferral for this storage to the value of newval and returns the value of the setting previously in effect. |
const char *GetName() const | Returns the name of the storage. Returns NULL if the storage is invalid. Note that the memory occupied by the returned string is owned by e4Graph and may be reused by the next e4Graph method invocation. |
const char *GetDriver() const | Returns the StorageKind used to open this storage. Returns NULL if the storage is invalid. Note that the memory occupied by the returned string is owned by e4Graph and may be reused by the next e4Graph method invocation. |
bool GetStatistic(e4_Space sp, e4_SpaceStat st, int &v) const | Retrieves statistics about the use of various allocation spaces within a storage. If the operation succeeds, returns true and v contains the statistic measure selected by sp and st. |
bool DeclareNodeAddCallback(e4_NodeAddCallbackFunction fn, void *clientData) | Registers the supplied function fn to be called with the given clientData when a node addition event is fired by e4Graph. Returns true if the registration succeeds. |
bool DeleteNodeAddCallback(e4_NodeAddCallbackFunction fn, void *clientData) | Deletes the registration of a previously registered function fn that was being called with the supplied clientData when a node addition event is fired by e4Graph. Returns true if the fn and clientData were previously registered for this event and the registration is removed successfully. |
bool DeclareNodeDetCallback(e4_NodeDetCallbackFunction fn, void *clientData) | Registers the supplied function fn to be called with the given clientData when a node detach event is fired by e4Graph. Returns true if the registration succeeds. |
bool DeleteNodeDetCallback(e4_NodeDetCallbackFunction fn, void *clientData) | Deletes the registration of a previously registered function fn that was being called with the supplied clientData when a node detach event is fired by e4Graph. Returns true if the fn and clientData were previously registered for this event and the registration is removed successfully. |
bool DeclareVertexAddCallback(e4_VertexAddCallbackFunction fn, void *clientData) | Registers the supplied function fn to be called with the given clientData when a vertex addition event is fired by e4Graph. Returns true if the registration succeeds. |
bool DeleteVertexAddCallback(e4_VertexAddCallbackFunction fn, void *clientData) | Deletes the registration of a previously registered function fn that was being called with the supplied clientData when a vertex addition event is fired by e4Graph. Returns true if the fn and clientData were previously registered for this event and the registration is removed successfully. |
bool DeclareVertexDetCallback(e4_VertexDetCallbackFunction fn, void *clientData) | Registers the supplied function fn to be called with the given clientData when a vertex detach event is fired by e4Graph. Returns true if the registration succeeds. |
bool DeleteVertexDetCallback(e4_VertexDetCallbackFunction fn, void *clientData) | Deletes the registration of a previously registered function fn that was being called with the supplied clientData when a vertex detach event is fired by e4Graph. Returns true if the fn and clientData were previously registered for this event and the registration is removed successfully. |
e4_RefKind Kind() const | Returns E4_RKSTORAGE, the e4_RefKind identifier for the e4_Storage type. |