/** * PyStorage.cpp -- * * This file contains the storage defintion. * * Authors: Mike Krimerman. * hemkond@yahoo.com * * Copyright (c) 2003, Mike Krimerman. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE, EVEN IF * MIKE KRIMERMAN IS MADE AWARE OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "e4py.h" #include /** Helpers */ /** PyStorage_New. * A helper function for object creation from C. */ PyObject* PyStorage_New() { PyStorage* result = PyObject_NEW(PyStorage, &PyStorageType); if (result) new(&result->storage) e4_Storage(); //Placement new return (PyObject*)result; } /** PyStorage_FromStorage. * A helper function for object creation from C. */ PyObject* PyStorage_FromStorage(e4_Storage storage) { PyStorage* result = PyObject_NEW(PyStorage, &PyStorageType); if (result) new(&result->storage) e4_Storage(storage); //Placement new return (PyObject*)result; } /** PyStorage_AsStorage. * */ e4_Storage& PyStorage_AsStorage(PyObject* self) { return ((PyStorage*)self)->storage; } /** PyStorage_Callback. * */ void PyStorage_Callback(void *clientData, const e4_RefCount &ref, void *callsiteData) { PyObject *callable = (PyObject*)clientData; PyObject *obj = NULL; switch (ref.Kind()) { case E4_RKSTORAGE: obj = PyStorage_FromStorage(ref); break; case E4_RKNODE: obj = PyNode_FromNode(ref); break; case E4_RKVERTEX: obj = PyVertex_FromVertex(ref); break; default: obj = Py_None; Py_INCREF(obj); break; } PyObject *result = PyObject_CallFunctionObjArgs(callable, obj, NULL); Py_DECREF(obj); Py_DECREF(result); } /** Methods */ /** PyStorage_Commit. * */ static PyObject* PyStorage_Commit(PyStorage* self) { if (!self->storage.Commit()) { PyErr_SetString(e4pyExc_APIFailure, "Commit: Operation failed"); return 0; } Py_INCREF(Py_None); return Py_None; } /** PyStorage_CopyTo. * */ static PyObject* PyStorage_CopyTo(PyStorage* self, PyObject* args) { int force_commit = 0; PyStorage *py_storage; if (!PyArg_ParseTuple(args, "O!|i", &PyStorageType, &py_storage, &force_commit)) return 0; if (!self->storage.CopyTo(py_storage->storage, force_commit != 0)) { PyErr_SetString(e4pyExc_APIFailure, "CopyTo: Operation failed"); return 0; } Py_INCREF(Py_None); return Py_None; } /** PyStorage_Delete. * */ static PyObject* PyStorage_Delete(PyStorage* self) { if (self->storage.Delete()) { PyErr_SetString(e4pyExc_APIFailure, "Delete: Cannot delete storage"); return 0; } Py_INCREF(Py_None); return Py_None; } /** PyStorage_MarkUnstable. * */ static PyObject* PyStorage_MarkUnstable(PyStorage* self) { self->storage.MarkUnstable(); Py_INCREF(Py_None); return Py_None; } /** PyStorage_DoGC. * */ static PyObject* PyStorage_DoGC(PyStorage* self) { self->storage.DoGC(); Py_INCREF(Py_None); return Py_None; } /** PyStorage_NeedsGC. * */ static PyObject* PyStorage_NeedsGC(PyStorage* self) { return PyInt_FromLong(long(self->storage.NeedsGC())); } /** PyStorage_CreateDetachedNode. * */ static PyObject* PyStorage_CreateDetachedNode(PyStorage* self) { e4_Node node; if (!self->storage.CreateDetachedNode(node)) { PyErr_SetString(e4pyExc_APIFailure, "CreateDetachedNode: Creation failed"); return 0; } return PyNode_FromNode(node); } /** PyStorage_CreateDetachedVertex. * */ static PyObject* PyStorage_CreateDetachedVertex(PyStorage* self, PyObject* args) { const char* name; PyObject *value; if (!PyArg_ParseTuple(args, "sO", &name, &value)) return 0; e4_Vertex vertex; bool success = false; if (PyInt_CheckExact(value)) { success = self->storage.CreateDetachedVertex(name, (int)PyInt_AsLong(value), vertex); } else if (PyFloat_CheckExact(value)) { success = self->storage.CreateDetachedVertex(name, PyFloat_AsDouble(value), vertex); } else if (PyString_CheckExact(value)) { success = self->storage.CreateDetachedVertex(name, PyString_AsString(value), vertex); } else if (PyNode_Check(value)) { success = self->storage.CreateDetachedVertex(name, PyNode_AsNode(value), vertex); } else { //binary PyObject *data = PickleTo(value); if (data && PyString_Check(data)) { success = self->storage.CreateDetachedVertex(name, (void*)PyString_AsString(data), PyString_Size(data), vertex); Py_DECREF(data); } else { if (data) Py_DECREF(data); return 0; } } if (!success) { PyErr_SetString(e4pyExc_APIFailure, "CreateDetachedVertex: Creation failed"); return 0; } return PyVertex_FromVertex(vertex); } /** PyStorage_GetNodeFromID. * */ static PyObject* PyStorage_GetNodeFromID(PyStorage* self, PyObject* args) { int id = 0, sp = 0; if (!PyArg_ParseTuple(args, "(ii)", &id, &sp)) return 0; e4_Node node; if (!self->storage.GetNodeFromID(e4_NodeUniqueID(id, sp), node)) { PyErr_SetString(e4pyExc_APIFailure, "NodeFromId: Unknown id"); return 0; } return PyNode_FromNode(node); } /** PyStorage_GetVertexFromID. * */ static PyObject* PyStorage_GetVertexFromID(PyStorage* self, PyObject* args) { int id = 0, sp = 0; if (!PyArg_ParseTuple(args, "(ii)", &id, &sp)) return 0; e4_Vertex vertex; if (!self->storage.GetVertexFromID(e4_VertexUniqueID(id, sp), vertex)) { PyErr_SetString(e4pyExc_APIFailure, "VertexFromId: Unknown id"); return 0; } return PyVertex_FromVertex(vertex); } /** PyStorage_GetStatistic. * */ static PyObject* PyStorage_GetStatistic(PyStorage* self, PyObject* args) { int sp = 0, st = 0; if (!PyArg_ParseTuple(args, "ii", &sp, &st)) return 0; int v; if (!self->storage.GetStatistic(e4_Space(sp), e4_SpaceStat(st), v)) { PyErr_SetString(e4pyExc_APIFailure, "GetStatistic: Operation failed"); return 0; } return PyInt_FromLong(v); } /** PyStorage_DeclareCallback. * bool DeclareCallback(int eventCode, e4_CallbackFunction fn, void *clientData); */ static PyObject* PyStorage_DeclareCallback(PyStorage* self, PyObject* args) { int code; PyObject *callback; if (!PyArg_ParseTuple(args, "iO", &code, &callback)) return 0; if (!PyCallable_Check(callback)) { PyErr_SetString(PyExc_TypeError, "Second argument must be a callable"); return 0; } Py_INCREF(callback); if (!self->storage.DeclareCallback(code, PyStorage_Callback, callback)) { PyErr_SetString(e4pyExc_APIFailure, "DeclareCallback: Operation failed"); return 0; } Py_INCREF(Py_None); return Py_None; } /** PyStorage_DeleteCallback. * bool DeleteCallback(int eventCode, e4_CallbackFunction fn, void *clientData); */ static PyObject* PyStorage_DeleteCallback(PyStorage* self, PyObject* args) { int code; PyObject *callback; if (!PyArg_ParseTuple(args, "iO", &code, &callback)) return 0; if (!PyCallable_Check(callback)) { PyErr_SetString(PyExc_TypeError, "Second argument must be a callable"); return 0; } if (!self->storage.DeleteCallback(code, PyStorage_Callback, callback)) { PyErr_SetString(e4pyExc_APIFailure, "DeclareCallback: Operation failed"); return 0; } Py_DECREF(callback); Py_INCREF(Py_None); return Py_None; } /** PyStorage_CauseEvent. * bool CauseEvent(int eventCode, const e4_RefCount &r, void *csdata); */ static PyObject* PyStorage_CauseEvent(PyStorage* self, PyObject* args) { int code; PyObject *obj; if (!PyArg_ParseTuple(args, "iO", &code, &obj)) return 0; bool success = false; if (PyStorage_Check(obj)) success = self->storage.CauseEvent(code, PyStorage_AsStorage(obj), NULL); else if (PyNode_Check(obj)) success = self->storage.CauseEvent(code, PyNode_AsNode(obj), NULL); else if (PyVertex_Check(obj)) success = self->storage.CauseEvent(code, PyVertex_AsVertex(obj), NULL); else { PyErr_SetString(PyExc_TypeError, "Second argument must be one of storage/vertex/node"); return 0; } if (!success) { PyErr_SetString(e4pyExc_APIFailure, "Failed: CauseEvent"); return 0; } Py_INCREF(Py_None); return Py_None; } /** Method doc */ static char doc_Commit[] = "\ Commit()\n\ Commits any changes to the storage at this time."; static char doc_CopyTo[] = "\ CopyTo(other-storage, force-commit)\n\ Copies the contents of this storage to other-storage.\n\ If force-commit is true, then other-storage is committed after the copy is done."; static char doc_Delete[] = "\ Delete()\n\ Deletes the underlying storage"; static char doc_MarkUnstable[] = "\ MarkUnstable()\n\ Marks the storage as unstable, i.e. it has been modified and not yet committed."; static char doc_DoGC[] = "\ DoGC()\n\ Causes a garbage collection to be executed in this storage"; static char doc_NeedsGC[] = "\ NeedsGC()\n\ Returns true if this storage contains unreclaimed unreachable entities"; static char doc_CreateDetachedNode[] = "\ CreateDetachedNode() -> node\n\ Returns a new node in storage."; static char doc_CreateDetachedVertex[] = "\ CreateDetachedVertex(name, value) -> vertex\n\ Returns a new detached vertex created within this storage that has value."; static char doc_GetNodeFromID[] = "\ GetNodeFromID(id) -> node\n\ Returns a node corresponding to id in storage"; static char doc_GetVertexFromID[] = "\ GetVertexFromID(id) -> vertex\n\ Returns a vertex corresponding to id in storage"; static char doc_GetStatistic[] = "\ GetStatistic(space, space-stat) -> int\n\ Retrieves statistics about the use of various allocation spaces within a storage.\n\ space is one of (E4_SPNODE, E4_SPVERTEX, E4_SPNAME, E4_SPSTRING, E4_SPINT, E4_SPDOUBLE, E4_SPBINARY)\n\ space-stat is one of (E4_SSUSED, E4_SSAVAIL, E4_SSFREED, E4_SSALLOC)"; static char doc_DeclareCallback[] = "\ DeclareCallback(event-code, callable)\n\ Declares that the callable will be called whenever the event defined by event-code occurs in this storage."; static char doc_DeleteCallback[] = "\ DeleteCallback(event-code, callable)\n\ Deletes a callback function previously declared with DeclareCallback."; static char doc_CauseEvent[] = "\ CauseEvent(event-code, storage|vertex|node)\n\ Causes the event defined by eventCode to be fired for this storage on the second argument."; /** Method list */ static PyMethodDef PyStorageMethods[] = { {"Commit", (PyCFunction)PyStorage_Commit, METH_NOARGS, doc_Commit}, {"CopyTo", (PyCFunction)PyStorage_CopyTo, METH_VARARGS, doc_CopyTo}, {"Delete", (PyCFunction)PyStorage_Delete, METH_NOARGS, doc_Delete}, {"MarkUnstable", (PyCFunction)PyStorage_MarkUnstable, METH_NOARGS, doc_MarkUnstable}, {"DoGC", (PyCFunction)PyStorage_DoGC, METH_NOARGS, doc_DoGC}, {"NeedsGC", (PyCFunction)PyStorage_NeedsGC, METH_NOARGS, doc_NeedsGC}, {"CreateDetachedNode", (PyCFunction)PyStorage_CreateDetachedNode, METH_NOARGS, doc_CreateDetachedNode}, {"CreateDetachedVertex", (PyCFunction)PyStorage_CreateDetachedVertex, METH_VARARGS, doc_CreateDetachedVertex}, {"GetNodeFromID", (PyCFunction)PyStorage_GetNodeFromID, METH_VARARGS, doc_GetNodeFromID}, {"GetVertexFromID", (PyCFunction)PyStorage_GetVertexFromID, METH_VARARGS, doc_GetVertexFromID}, {"GetStatistic", (PyCFunction)PyStorage_GetStatistic, METH_VARARGS, doc_GetStatistic}, {"DeclareCallback", (PyCFunction)PyStorage_DeclareCallback, METH_VARARGS, doc_DeclareCallback}, {"DeleteCallback", (PyCFunction)PyStorage_DeleteCallback, METH_VARARGS, doc_DeleteCallback}, {"CauseEvent", (PyCFunction)PyStorage_CauseEvent, METH_VARARGS, doc_CauseEvent}, {0, 0, 0, 0} }; /** Attributes */ /** PyStorage_get_name. * */ static PyObject* PyStorage_get_name(PyStorage* self, void *) { const char* name = self->storage.GetName(); if (name == NULL) { PyErr_SetString(PyExc_TypeError, ErrInvalidStorage); return 0; } return PyString_FromString(name); } /** PyStorage_get_driver. * */ static PyObject* PyStorage_get_driver(PyStorage* self, void *) { const char* driver = self->storage.GetDriver(); if (driver == NULL) { PyErr_SetString(PyExc_TypeError, ErrInvalidStorage); return 0; } return PyString_FromString(driver); } /** PyStorage_get_state. * */ static PyObject* PyStorage_get_state(PyStorage* self, void *) { return PyInt_FromLong(self->storage.GetState()); } /** PyStorage_set_state. * */ static int PyStorage_set_state(PyStorage* self, PyObject* value, void *) { if (!PyInt_Check(value)) { PyErr_SetString(PyExc_TypeError, ErrInvalidArgs); return -1; } if (!self->storage.SetState(int(PyInt_AsLong(value)))) { PyErr_SetString(e4pyExc_APIFailure, "state: Setting state failed"); return -1; } return 0; } /** PyStorage_get_stable. * */ static PyObject* PyStorage_get_stable(PyStorage* self, void *) { return PyInt_FromLong(self->storage.IsStable()); } /** PyStorage_get_root. * */ static PyObject* PyStorage_get_root(PyStorage* self, void *) { e4_Node node; if (!self->storage.GetRootNode(node)) { PyErr_SetString(e4pyExc_APIFailure, "Root: Root node not set"); return 0; } return PyNode_FromNode(node); } /** PyStorage_set_root. * */ static int PyStorage_set_root(PyStorage* self, PyObject* value, void *) { if (!PyNode_Check(value)) { PyErr_SetString(PyExc_TypeError, ErrInvalidArgs); return 0; } if (!self->storage.SetRootNode(((PyNode*)value)->node)) { PyErr_SetString(e4pyExc_APIFailure, "Root: Setting node failed"); return -1; } return 0; } /** PyStorage_get_kind. * */ static PyObject* PyStorage_get_kind(PyStorage* self, void *) { return PyInt_FromLong(self->storage.Kind()); } /** PyStorage_get_valid. * */ static PyObject* PyStorage_get_valid(PyStorage* self, void *) { return PyInt_FromLong(self->storage.IsValid()); } /** PyStorage_get_tempUID. * */ static PyObject* PyStorage_get_tempUID(PyStorage* self, void *) { return PyInt_FromLong(self->storage.GetTemporaryUID()); } /** PyStorage_get_transientUserData. * */ static PyObject* PyStorage_get_transientUserData(PyStorage* self, void *) { PyObject* result = (PyObject*)self->storage.GetTransientUserData(); if (result == NULL) PyErr_SetString(e4pyExc_APIFailure, "transientUserData: Canot set value"); else Py_INCREF(result); return result; } /** PyStorage_set_transientUserData. * */ static int PyStorage_set_transientUserData(PyStorage* self, PyObject* value, void *) { Py_INCREF(value); self->storage.SetTransientUserData(value); return 0; } /** Attribute doc */ static char doc_name[] = "\ Returns the name of the storage."; static char doc_driver[] = "\ Returns the storage-kind used to open this storage."; static char doc_state[] = "\ Get/Set a bitmask with bits turned on for every mode that is currently turned on for this storage."; static char doc_stable[] = "\ Returns false when the storage has been modified and not yet committed. Returns true otherwise."; static char doc_root[] = "\ Get/Set storage root node."; static char doc_kind[] = "\ Returns E4_RKSTORAGE"; static char doc_valid[] = "\ Returns true if the storage is valid, false otherwise. A storage is valid if it designates an open persistent storage."; static char doc_tempUID[] = "\ Returns temporary unique id for storage."; static char doc_transientUserData[] = "\ Get/Set Transient user data for storage."; /** Attribute list */ static PyGetSetDef PyStorageGetsets[] = { {"name", (getter)PyStorage_get_name, (setter)NULL, doc_name}, {"driver", (getter)PyStorage_get_driver, (setter)NULL, doc_driver}, {"state", (getter)PyStorage_get_state, (setter)PyStorage_set_state, doc_state}, {"stable", (getter)PyStorage_get_stable, (setter)NULL, doc_stable}, {"root", (getter)PyStorage_get_root, (setter)PyStorage_set_root, doc_root}, {"kind", (getter)PyStorage_get_kind, (setter)NULL, doc_kind}, {"valid", (getter)PyStorage_get_valid, (setter)NULL, doc_valid}, {"tempUID", (getter)PyStorage_get_tempUID, (setter)NULL, doc_tempUID}, {"transientUserData", (getter)PyStorage_get_transientUserData, (setter)PyStorage_set_transientUserData, doc_transientUserData}, {NULL} }; /** Storage access */ /** PyStorage_new. * @todo simplify */ /*static*/ PyObject* PyStorage_new(PyObject* o, PyObject* args) { PyStorage* result = NULL; switch (PyTuple_Size(args)) { case 0: result = (PyStorage*)PyStorage_New(); break; case 1: if (PyString_Check(PyTuple_GetItem(args, 0))) { //check if first item is a string result = (PyStorage*)PyStorage_New(); result->storage = e4_Storage( PyString_AsString(PyTuple_GetItem(args, 0)), E4_METAKIT); } else if (PyStorage_Check(PyTuple_GetItem(args, 0))) { //is it a storage object? result = (PyStorage*)PyStorage_New(); result->storage = e4_Storage( ((PyStorage*)PyTuple_GetItem(args, 0))->storage); } else PyErr_SetString(PyExc_TypeError, ErrInvalidArgs); break; case 2: //check if first, second items are strings if (PyString_Check(PyTuple_GetItem(args, 0)) && PyString_Check(PyTuple_GetItem(args, 1))) { result = (PyStorage*)PyStorage_New(); result->storage = e4_Storage( PyString_AsString(PyTuple_GetItem(args, 0)), PyString_AsString(PyTuple_GetItem(args, 1))); } else PyErr_SetString(PyExc_TypeError, ErrInvalidArgs); break; case 3: //check if first, second items are strings if (PyString_Check(PyTuple_GetItem(args, 0)) && PyString_Check(PyTuple_GetItem(args, 1)) && PyInt_Check(PyTuple_GetItem(args, 2))) { result = (PyStorage*)PyStorage_New(); result->storage = e4_Storage( PyString_AsString(PyTuple_GetItem(args, 0)), PyString_AsString(PyTuple_GetItem(args, 1)), PyInt_AsLong(PyTuple_GetItem(args, 2))); } else PyErr_SetString(PyExc_TypeError, ErrInvalidArgs); break; default: PyErr_SetString(PyExc_TypeError, ErrInvalidArgs); break; } // Not good solution: e4_Storage temp; memcpy(&(result->storage), &temp, sizeof(e4_Storage)); return (PyObject*)result; } /** PyStorage_dealloc. * */ static void PyStorage_dealloc(PyStorage *self) { //remove any previous transient data PyObject* td = (PyObject*)self->storage.GetTransientUserData(); if (td != NULL) Py_DECREF(td); self->storage.~e4_Storage(); //"placement" dtor PyObject_DEL(self); } /** PyStorage_compare. * */ static int PyStorage_compare(PyStorage *self, PyObject *rhs) { if (!PyStorage_Check(rhs)) return -1; return self->storage == PyStorage_AsStorage(rhs) ? 0 : 1; } /** PyStorage_iter. * */ static PyObject *PyStorage_iter(PyStorage *self) { e4_VertexVisitor visitor(self->storage); return PyVertexVisitor_FromVertexVisitor(visitor); } /** Type doc */ static char doc_PyStorage[] = "\ Storage class provides persistent storage of nodes and vertices."; /** Storage type */ PyTypeObject PyStorageType = { PyObject_HEAD_INIT(&PyType_Type) 0, "Storage", sizeof(PyStorage), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)PyStorage_dealloc, /* tp_dealloc */ 0,//(printfunc)PyStorage_print, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ (cmpfunc)PyStorage_compare, /* tp_compare */ (reprfunc)0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ PyObject_GenericSetAttr, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ doc_PyStorage, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)PyStorage_iter, /* tp_iter */ 0, /* tp_iternext */ PyStorageMethods, /* tp_methods */ 0, /* tp_members */ PyStorageGetsets, /* tp_getset */ };