/**
 * 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 <new>

/** 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 */
};
