Commit 351c5750 authored by Jim Fulton's avatar Jim Fulton

*** empty log message ***

parent 3765adf4
...@@ -48,12 +48,12 @@ ...@@ -48,12 +48,12 @@
__doc__='''Python implementation of persistent base types __doc__='''Python implementation of persistent base types
$Id: PersistentMapping.py,v 1.3 1998/11/11 02:00:56 jim Exp $''' $Id: PersistentMapping.py,v 1.4 1999/05/07 01:03:03 jim Exp $'''
__version__='$Revision: 1.3 $'[11:-2] __version__='$Revision: 1.4 $'[11:-2]
import Persistence import Persistence
class PM(Persistence.Persistent): class PersistentMapping(Persistence.Persistent):
"""A persistent wrapper for mapping objects. """A persistent wrapper for mapping objects.
This class allows wrapping of mapping objects so that This class allows wrapping of mapping objects so that
...@@ -113,4 +113,3 @@ class PM(Persistence.Persistent): ...@@ -113,4 +113,3 @@ class PM(Persistence.Persistent):
def values(self): def values(self):
return map(lambda k, d=self: d[k], self.keys()) return map(lambda k, d=self: d[k], self.keys())
PersistentMapping=PM
/*********************************************************************** /***********************************************************************
$Id: cPersistence.c,v 1.25 1998/11/11 02:00:56 jim Exp $ $Id: cPersistence.c,v 1.26 1999/05/07 01:03:03 jim Exp $
C Persistence Module C Persistence Module
...@@ -12,16 +12,19 @@ ...@@ -12,16 +12,19 @@
*****************************************************************************/ *****************************************************************************/
static char *what_string = "$Id: cPersistence.c,v 1.25 1998/11/11 02:00:56 jim Exp $"; static char *what_string = "$Id: cPersistence.c,v 1.26 1999/05/07 01:03:03 jim Exp $";
#include <time.h> #include <string.h>
#include "cPersistence.h" #include "cPersistence.h"
#define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;} #define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;}
#define UNLESS(E) if(!(E)) #define UNLESS(E) if(!(E))
#define UNLESS_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V) #define UNLESS_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V)
#define OBJECT(V) ((PyObject*)(V))
static PyObject *py_keys, *py_setstate, *py___dict__; static PyObject *py_keys, *py_setstate, *py___dict__, *py_timeTime;
static PyObject *TimeStamp;
#ifdef DEBUG_LOG #ifdef DEBUG_LOG
static PyObject *debug_log=0; static PyObject *debug_log=0;
...@@ -35,8 +38,8 @@ call_debug(char *event, cPersistentObject *self) ...@@ -35,8 +38,8 @@ call_debug(char *event, cPersistentObject *self)
/* /*
printf("%s %p\n",event,self->ob_type->tp_name); printf("%s %p\n",event,self->ob_type->tp_name);
*/ */
r=PyObject_CallFunction(debug_log,"s(ss#i)",event, r=PyObject_CallFunction(debug_log,"s(sOi)",event,
self->ob_type->tp_name, self->oid, 8, self->ob_type->tp_name, self->oid,
self->state); self->state);
Py_XDECREF(r); Py_XDECREF(r);
} }
...@@ -48,6 +51,7 @@ init_strings() ...@@ -48,6 +51,7 @@ init_strings()
#define INIT_STRING(S) py_ ## S = PyString_FromString(#S) #define INIT_STRING(S) py_ ## S = PyString_FromString(#S)
INIT_STRING(keys); INIT_STRING(keys);
INIT_STRING(setstate); INIT_STRING(setstate);
INIT_STRING(timeTime);
INIT_STRING(__dict__); INIT_STRING(__dict__);
#undef INIT_STRING #undef INIT_STRING
} }
...@@ -201,14 +205,16 @@ static PyObject * ...@@ -201,14 +205,16 @@ static PyObject *
Per___changed__(cPersistentObject *self, PyObject *args) Per___changed__(cPersistentObject *self, PyObject *args)
{ {
PyObject *v=0; PyObject *v=0;
if (args && ! PyArg_ParseTuple(args, "|O",&v)) return NULL; if (args && ! PyArg_ParseTuple(args, "|O",&v)) return NULL;
if (v && ! PyObject_IsTrue(v)) if (! v) return PyObject_GetAttrString(OBJECT(self), "_p_changed");
if (PyObject_IsTrue(v))
{ {
PyErr_SetString(PyExc_TypeError, if (changed(self) < 0) return NULL;
"Only true arguments are allowed.");
return NULL;
} }
if (changed(self) < 0) return NULL; else if (self->state >= 0) self->state=cPersistent_UPTODATE_STATE;
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
...@@ -368,35 +374,30 @@ Per_dealloc(self) ...@@ -368,35 +374,30 @@ Per_dealloc(self)
PyMem_DEL(self); PyMem_DEL(self);
} }
static PyObject *
orNothing(PyObject *v)
{
if (! v) v=Py_None;
Py_INCREF(v);
return v;
}
static PyObject * static PyObject *
Per_getattr(cPersistentObject *self, PyObject *oname, char *name, Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
PyObject *(*getattrf)(PyObject *, PyObject*)) PyObject *(*getattrf)(PyObject *, PyObject*))
{ {
char *n=name; char *n=name;
if(*n++=='_') if(n && *n++=='_')
if(*n++=='p' && *n++=='_') if(*n++=='p' && *n++=='_')
{ {
switch(*n++) switch(*n++)
{ {
case 'o': case 'o':
if(*n++=='i' && *n++=='d' && ! *n) if(*n++=='i' && *n++=='d' && ! *n) return orNothing(self->oid);
return PyString_FromStringAndSize(self->oid, 8);
break; break;
case 'j': case 'j':
if(*n++=='a' && *n++=='r' && ! *n) if(*n++=='a' && *n++=='r' && ! *n) return orNothing(self->jar);
{
if(self->jar)
{
Py_INCREF(self->jar);
return self->jar;
}
else
{
Py_INCREF(Py_None);
return Py_None;
}
}
break; break;
case 'c': case 'c':
if(strcmp(n,"hanged")==0) if(strcmp(n,"hanged")==0)
...@@ -410,11 +411,32 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name, ...@@ -410,11 +411,32 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
cPersistent_CHANGED_STATE); cPersistent_CHANGED_STATE);
} }
break; break;
case 's':
if(strcmp(n,"erial")==0)
return PyString_FromStringAndSize(self->serial, 8);
break;
case 'm':
if(strcmp(n,"time")==0)
{
UPDATE_STATE_IF_NECESSARY(self, NULL);
self->atime=((long)(time(NULL)/3))%65536;
oname=PyString_FromStringAndSize(self->serial, 8);
if (! oname) return oname;
ASSIGN(oname, PyObject_CallFunction(TimeStamp, "O", oname));
if (! oname) return oname;
ASSIGN(oname, PyObject_GetAttr(oname, py_timeTime));
if (! oname) return oname;
ASSIGN(oname, PyObject_CallObject(oname, NULL));
return oname;
}
break;
} }
return getattrf((PyObject *)self, oname); return getattrf((PyObject *)self, oname);
} }
if(! (*name++=='_' && *name++=='_' && if(! (name && *name++=='_' && *name++=='_' &&
(strcmp(name,"dict__")==0 || strcmp(name,"class__")==0 (strcmp(name,"dict__")==0 || strcmp(name,"class__")==0
|| strcmp(name, "of__")==0))) || strcmp(name, "of__")==0)))
{ {
...@@ -429,10 +451,11 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name, ...@@ -429,10 +451,11 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
static PyObject* static PyObject*
Per_getattro(cPersistentObject *self, PyObject *name) Per_getattro(cPersistentObject *self, PyObject *name)
{ {
char *s; char *s=NULL;
UNLESS(s=PyString_AsString(name)) return NULL; if (PyString_Check(name))
return Per_getattr(self,name,s, PyExtensionClassCAPI->getattro); UNLESS(s=PyString_AsString(name)) return NULL;
return Per_getattr(self, name, s, PyExtensionClassCAPI->getattro);
} }
static int static int
...@@ -456,27 +479,36 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v, ...@@ -456,27 +479,36 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v,
{ {
if(name[3]=='o' && name[4]=='i' && name[5]=='d' && ! name[6]) if(name[3]=='o' && name[4]=='i' && name[5]=='d' && ! name[6])
{ {
if (! v) return bad_delattr(); Py_XINCREF(v);
if (PyString_Check(v) && PyString_GET_SIZE(v)==8) ASSIGN(self->oid, v);
memcpy(self->oid, PyString_AS_STRING(v), 8);
else
{
PyErr_SetString(PyExc_AttributeError,
"_p_oid must be an 8-character string");
return -1;
}
return 0; return 0;
} }
if(name[3]=='j' && name[4]=='a' && name[5]=='r' && ! name[6]) if(name[3]=='j' && name[4]=='a' && name[5]=='r' && ! name[6])
{ {
Py_XINCREF(v);
ASSIGN(self->jar, v); ASSIGN(self->jar, v);
Py_XINCREF(self->jar);
return 0; return 0;
} }
if(strcmp(name+3,"changed")==0) if(name[3]=='s' && strcmp(name+4,"erial")==0)
{ {
if (! v) return bad_delattr(); if (v)
if (v==Py_None) {
if (PyString_Check(v) && PyString_Size(v)==8)
memcpy(self->serial, PyString_AS_STRING(v), 8);
else
{
PyErr_SetString(PyExc_ValueError,
"_p_serial must be an 8-character string");
return -1;
}
}
else
memset(self->serial, 0, 8);
return 0;
}
if(name[3]=='c' && strcmp(name+4,"hanged")==0)
{
if (! v || v==Py_None)
{ {
if (Per__p_deactivate(self, NULL)) Py_DECREF(Py_None); if (Per__p_deactivate(self, NULL)) Py_DECREF(Py_None);
return 0; return 0;
...@@ -585,8 +617,15 @@ void ...@@ -585,8 +617,15 @@ void
initcPersistence() initcPersistence()
{ {
PyObject *m, *d; PyObject *m, *d;
char *rev="$Revision: 1.25 $"; char *rev="$Revision: 1.26 $";
TimeStamp=PyString_FromString("TimeStamp");
if (! TimeStamp) return;
ASSIGN(TimeStamp, PyImport_Import(TimeStamp));
if (! TimeStamp) return;
ASSIGN(TimeStamp, PyObject_GetAttrString(TimeStamp, "TimeStamp"));
if (! TimeStamp) return;
m = Py_InitModule4("cPersistence", cP_methods, m = Py_InitModule4("cPersistence", cP_methods,
"", "",
(PyObject*)NULL,PYTHON_API_VERSION); (PyObject*)NULL,PYTHON_API_VERSION);
......
/* /*
$Id: cPersistence.h,v 1.10 1998/11/11 02:00:56 jim Exp $ $Id: cPersistence.h,v 1.11 1999/05/07 01:03:03 jim Exp $
Definitions to facilitate making cPersistent subclasses in C. Definitions to facilitate making cPersistent subclasses in C.
...@@ -15,9 +15,11 @@ ...@@ -15,9 +15,11 @@
#define cPersistent_HEAD PyObject_HEAD \ #define cPersistent_HEAD PyObject_HEAD \
PyObject *jar; \ PyObject *jar; \
char oid[8]; \ PyObject *oid; \
char serial[8]; \
unsigned short atime; \ unsigned short atime; \
signed char state; \ signed char state; \
unsigned char reserved; \
#define cPersistent_GHOST_STATE -1 #define cPersistent_GHOST_STATE -1
#define cPersistent_UPTODATE_STATE 0 #define cPersistent_UPTODATE_STATE 0
...@@ -43,6 +45,8 @@ typedef struct { ...@@ -43,6 +45,8 @@ typedef struct {
static cPersistenceCAPIstruct *cPersistenceCAPI; static cPersistenceCAPIstruct *cPersistenceCAPI;
#define cPersistanceModuleName "cPersistence"
#define PER_USE_OR_RETURN(O,R) { \ #define PER_USE_OR_RETURN(O,R) { \
if ((O)->state==cPersistent_GHOST_STATE && \ if ((O)->state==cPersistent_GHOST_STATE && \
cPersistenceCAPI->setstate((PyObject*)(O)) < 0) \ cPersistenceCAPI->setstate((PyObject*)(O)) < 0) \
...@@ -57,6 +61,10 @@ static cPersistenceCAPIstruct *cPersistenceCAPI; ...@@ -57,6 +61,10 @@ static cPersistenceCAPIstruct *cPersistenceCAPI;
((O)->state==cPersistent_STICKY_STATE && \ ((O)->state==cPersistent_STICKY_STATE && \
((O)->state=cPersistent_UPTODATE_STATE)) ((O)->state=cPersistent_UPTODATE_STATE))
#define PER_PREVENT_DEACTIVATION(O) \
((O)->state==cPersistent_UPTODATE_STATE && \
((O)->state=cPersistent_STICKY_STATE))
#define PER_DEL(O) Py_XDECREF((O)->jar) #define PER_DEL(O) Py_XDECREF((O)->jar)
#endif #endif
......
static char *what_string = "$Id: cPickleCache.c,v 1.16 1998/11/11 02:00:56 jim Exp $"; static char *what_string = "$Id: cPickleCache.c,v 1.17 1999/05/07 01:03:03 jim Exp $";
#define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;} #define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;}
#define UNLESS(E) if(!(E)) #define UNLESS(E) if(!(E))
...@@ -12,12 +12,10 @@ static char *what_string = "$Id: cPickleCache.c,v 1.16 1998/11/11 02:00:56 jim E ...@@ -12,12 +12,10 @@ static char *what_string = "$Id: cPickleCache.c,v 1.16 1998/11/11 02:00:56 jim E
static PyObject *py_reload, *py__p_jar, *py__p_deactivate; static PyObject *py_reload, *py__p_jar, *py__p_deactivate;
/* Declarations for objects of type cCache */
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
PyObject *data; PyObject *data;
PyObject *jar;
int position; int position;
int cache_size; int cache_size;
int cache_age; int cache_age;
...@@ -65,8 +63,11 @@ gc_item(ccobject *self, PyObject *key, PyObject *v, long now, int dt) ...@@ -65,8 +63,11 @@ gc_item(ccobject *self, PyObject *key, PyObject *v, long now, int dt)
return PyDict_DelItem(self->data, key); return PyDict_DelItem(self->data, key);
} }
if (dt && v->ob_type->tp_basicsize >= sizeof(cPersistentObject) && if (dt &&
((cPersistentObject*)v)->state==cPersistent_UPTODATE_STATE) (! PyExtensionClass_Check(v)) &&
((cPersistentObject*)v)->jar==self->jar /* I'm paranoid */ &&
((cPersistentObject*)v)->state==cPersistent_UPTODATE_STATE
)
{ {
now -= ((cPersistentObject*)v)->atime; now -= ((cPersistentObject*)v)->atime;
if (now < 0) now += 65536; if (now < 0) now += 65536;
...@@ -281,13 +282,15 @@ static struct PyMethodDef cc_methods[] = { ...@@ -281,13 +282,15 @@ static struct PyMethodDef cc_methods[] = {
}; };
static ccobject * static ccobject *
newccobject(int cache_size, int cache_age) newccobject(PyObject *jar, int cache_size, int cache_age)
{ {
ccobject *self; ccobject *self;
UNLESS(self = PyObject_NEW(ccobject, &Cctype)) return NULL; UNLESS(self = PyObject_NEW(ccobject, &Cctype)) return NULL;
if(self->data=PyDict_New()) if(self->data=PyDict_New())
{ {
self->jar=jar;
Py_INCREF(jar);
self->position=0; self->position=0;
self->cache_size=cache_size; self->cache_size=cache_size;
self->cache_age=cache_age < 1 ? 1 : cache_age; self->cache_age=cache_age < 1 ? 1 : cache_age;
...@@ -312,6 +315,7 @@ static void ...@@ -312,6 +315,7 @@ static void
cc_dealloc(ccobject *self) cc_dealloc(ccobject *self)
{ {
Py_XDECREF(self->data); Py_XDECREF(self->data);
Py_XDECREF(self->jar);
PyMem_DEL(self); PyMem_DEL(self);
} }
...@@ -404,10 +408,24 @@ cc_subscript(ccobject *self, PyObject *key) ...@@ -404,10 +408,24 @@ cc_subscript(ccobject *self, PyObject *key)
return r; return r;
} }
static PyExtensionClass *Persistent=0;
static int static int
cc_ass_sub(ccobject *self, PyObject *key, PyObject *v) cc_ass_sub(ccobject *self, PyObject *key, PyObject *v)
{ {
if(v) return PyDict_SetItem(self->data, key, v); if(v)
{
if (PyExtensionClass_Check(v) ||
(PyExtensionInstance_Check(v) &&
ExtensionClassSubclassInstance_Check(v, Persistent)
)
)
return PyDict_SetItem(self->data, key, v);
PyErr_SetString(PyExc_ValueError,
"Cache values must be persistent objects or classes.");
return -1;
}
return PyDict_DelItem(self->data, key); return PyDict_DelItem(self->data, key);
} }
...@@ -446,8 +464,10 @@ static PyObject * ...@@ -446,8 +464,10 @@ static PyObject *
cCM_new(PyObject *self, PyObject *args) cCM_new(PyObject *self, PyObject *args)
{ {
int cache_size=100, cache_age=1000; int cache_size=100, cache_age=1000;
UNLESS(PyArg_ParseTuple(args, "|ii", &cache_size, &cache_age)) return NULL; PyObject *jar;
return (PyObject*)newccobject(cache_size,cache_age);
UNLESS(PyArg_ParseTuple(args, "O|ii", &jar, &cache_size, &cache_age)) return NULL;
return (PyObject*)newccobject(jar, cache_size,cache_age);
} }
static struct PyMethodDef cCM_methods[] = { static struct PyMethodDef cCM_methods[] = {
...@@ -459,10 +479,20 @@ void ...@@ -459,10 +479,20 @@ void
initcPickleCache() initcPickleCache()
{ {
PyObject *m, *d; PyObject *m, *d;
char *rev="$Revision: 1.16 $"; char *rev="$Revision: 1.17 $";
Cctype.ob_type=&PyType_Type; Cctype.ob_type=&PyType_Type;
UNLESS(ExtensionClassImported) return;
/* Get the Persistent base class */
UNLESS(m=PyString_FromString(cPersistanceModuleName)) return;
ASSIGN(m, PyImport_Import(m));
UNLESS(m) return;
ASSIGN(m, PyObject_GetAttrString(m, "Persistent"));
UNLESS(m) return;
Persistent=(PyExtensionClass *)m;
m = Py_InitModule4("cPickleCache", cCM_methods, "", m = Py_InitModule4("cPickleCache", cCM_methods, "",
(PyObject*)NULL,PYTHON_API_VERSION); (PyObject*)NULL,PYTHON_API_VERSION);
......
/***********************************************************
Copyright
Copyright 1997 Digital Creations, L.L.C., 910 Princess Anne
Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
rights reserved.
******************************************************************/
static char BTree_module_documentation[] =
""
"\n$Id: BTree.c,v 1.17 1998/11/11 02:00:55 jim Exp $"
;
#define PERSISTENT
#ifdef PERSISTENT
#include "cPersistence.h"
#else
#include "ExtensionClass.h"
#define PER_USE_OR_RETURN(self, NULL)
#define PER_ALLOW_DEACTIVATION(self)
#define PER_DEL(self)
#endif
static void PyVar_Assign(PyObject **v, PyObject *e) { Py_XDECREF(*v); *v=e;}
#define ASSIGN(V,E) PyVar_Assign(&(V),(E))
#define UNLESS(E) if(!(E))
#define UNLESS_ASSIGN(V,E) ASSIGN(V,E); UNLESS(V)
#define RETURN_NONE Py_INCREF(Py_None); return Py_None
#define LIST(O) ((PyListObject*)(O))
#define OBJECT(O) ((PyObject*)(O))
#define MIN_BUCKET_ALLOC 8
#define MAX_SIZE(N) 32
#ifdef INTKEY
#define KEY_TYPE INTKEY
#define KEY_PARSE "i"
#define TEST_KEY(k) ((k)-ikey)
#define DECREF_KEY(k)
#define ASSIGN_KEY(k,e) (k=e)
#else
#define KEY_TYPE PyObject *
#define KEY_PARSE "O"
#define TEST_KEY(k) PyObject_Compare(k,key)
#define DECREF_KEY(k) Py_DECREF(k)
#define ASSIGN_KEY(k,e) ASSIGN(k,e)
#endif
#ifdef INTVAL
#define VALUE_TYPE INTVAL
#define VALUE_PARSE "i"
#define DECREF_VALUE(k)
#define ASSIGN_VALUE(k,e) (k=e)
#else
#define VALUE_TYPE PyObject *
#define VALUE_PARSE "O"
#define DECREF_VALUE(k) Py_DECREF(k)
#define ASSIGN_VALUE(k,e) ASSIGN(k,e)
#endif
typedef struct ItemStruct {
KEY_TYPE key;
VALUE_TYPE value;
} Item;
typedef struct BTreeItemStruct {
KEY_TYPE key;
PyObject *value;
int count;
} BTreeItem;
typedef struct {
cPersistent_HEAD
int size, len;
Item *data;
} Bucket;
staticforward PyExtensionClass BucketType;
#define BUCKET(O) ((Bucket*)(O))
#define Bucket_Check(O) ((O)->ob_type==(PyTypeObject*)&BucketType)
typedef struct {
cPersistent_HEAD
int size, len;
BTreeItem *data;
int count;
} BTree;
staticforward PyExtensionClass BucketType;
#define BTREE(O) ((BTree*)(O))
#define BTree_Check(O) ((O)->ob_type==(PyTypeObject*)&BTreeType)
/************************************************************************
BTreeItems
*/
typedef struct {
PyObject_HEAD
BTree *data;
int first, len;
char kind;
} BTreeItems;
staticforward PyTypeObject BTreeItemsType;
static PyObject *
newBTreeItems(BTree *data, char kind, int first, int last)
{
BTreeItems *self;
UNLESS(self = PyObject_NEW(BTreeItems, &BTreeItemsType)) return NULL;
Py_INCREF(data);
self->data=data;
self->kind=kind;
self->first=first;
self->len=last-first;
return OBJECT(self);
}
static void
BTreeItems_dealloc(BTreeItems *self)
{
Py_DECREF(self->data);
PyMem_DEL(self);
}
static int
BTreeItems_length( BTreeItems *self)
{
return self->len;
}
static PyObject *
BTreeItems_concat( BTreeItems *self, PyObject *bb)
{
PyErr_SetString(PyExc_TypeError,
"BTreeItems objects do not support concatenation");
return NULL;
}
static PyObject *
BTreeItems_repeat(BTreeItems *self, int n)
{
PyErr_SetString(PyExc_TypeError,
"BTreeItems objects do not support repetition");
return NULL;
}
static PyObject *
BTreeItems_item_BTree(char kind, int i, BTree *btree)
{
int l;
BTreeItem *d;
PyObject *r;
PER_USE_OR_RETURN(btree, NULL);
for(d=btree->data, l=btree->len;
--l >= 0 && i >= d->count;
i -= d->count, d++);
PER_ALLOW_DEACTIVATION(btree);
if(Bucket_Check(d->value))
{
PER_USE_OR_RETURN((Bucket*)(d->value), NULL);
switch(kind)
{
case 'k':
#ifdef INTKEY
r=PyInt_FromLong((BUCKET(d->value)->data[i].key));
#else
r=(BUCKET(d->value)->data[i].key);
Py_INCREF(r);
#endif
break;
case 'v':
#ifdef INTVAL
r=PyInt_FromLong((BUCKET(d->value)->data[i].value));
#else
r=(BUCKET(d->value)->data[i].value);
Py_INCREF(r);
#endif
break;
default:
r=Py_BuildValue(KEY_PARSE VALUE_PARSE,
BUCKET(d->value)->data[i].key,
BUCKET(d->value)->data[i].value);
}
PER_ALLOW_DEACTIVATION(BUCKET(d->value));
return r;
}
return BTreeItems_item_BTree(kind, i, BTREE(d->value));
}
static PyObject *
BTreeItems_item(BTreeItems *self, int i)
{
int j, l;
j=i;
l=self->len;
if(j < 0) j += l;
if(j < 0 || j >= l)
{
PyObject *v;
v=PyInt_FromLong(i);
UNLESS(v)
{
v=Py_None;
Py_INCREF(v);
}
PyErr_SetObject(PyExc_IndexError, v);
Py_DECREF(v);
return NULL;
}
i=j+self->first;
return BTreeItems_item_BTree(self->kind, i, self->data);
}
static PyObject *
BTreeItems_slice(BTreeItems *self, int ilow, int ihigh)
{
if(ihigh > self->len) ihigh=self->len;
ilow += self->first;
ihigh += self->first;
return newBTreeItems(self->data, self->kind, ilow, ihigh);
}
static int
BTreeItems_ass_item(BTreeItems *self, int i, PyObject *v)
{
PyErr_SetString(PyExc_TypeError,
"BTreeItems objects do not support item assignment");
return -1;
}
static int
BTreeItems_ass_slice(PyListObject *self, int ilow, int ihigh, PyObject *v)
{
PyErr_SetString(PyExc_TypeError,
"BTreeItems objects do not support slice assignment");
return -1;
}
static PySequenceMethods BTreeItems_as_sequence = {
(inquiry)BTreeItems_length, /*sq_length*/
(binaryfunc)BTreeItems_concat, /*sq_concat*/
(intargfunc)BTreeItems_repeat, /*sq_repeat*/
(intargfunc)BTreeItems_item, /*sq_item*/
(intintargfunc)BTreeItems_slice, /*sq_slice*/
(intobjargproc)BTreeItems_ass_item, /*sq_ass_item*/
(intintobjargproc)BTreeItems_ass_slice, /*sq_ass_slice*/
};
/* -------------------------------------------------------------- */
static PyTypeObject BTreeItemsType = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"BTreeItems", /*tp_name*/
sizeof(BTreeItems), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)BTreeItems_dealloc, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)0, /*obsolete tp_getattr*/
(setattrfunc)0, /*obsolete tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/
&BTreeItems_as_sequence, /*tp_as_sequence*/
0, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/
(ternaryfunc)0, /*tp_call*/
(reprfunc)0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
/* Space for future expansion */
0L,0L,
"Sequence type used to iterate over BTree items." /* Documentation string */
};
/************************************************************************/
static void *
PyMalloc(size_t sz)
{
void *r;
if(r=malloc(sz)) return r;
PyErr_NoMemory();
return NULL;
}
static void *
PyRealloc(void *p, size_t sz)
{
void *r;
if(r=realloc(p,sz)) return r;
PyErr_NoMemory();
return NULL;
}
static PyObject *
Twople(PyObject *i1, PyObject *i2)
{
PyObject *t;
if(t=PyTuple_New(2))
{
Py_INCREF(i1);
PyTuple_SET_ITEM(t,0,i1);
Py_INCREF(i2);
PyTuple_SET_ITEM(t,1,i2);
}
return t;
}
static int
BTree_ini(BTree *self)
{
PyObject *b;
UNLESS(b=PyObject_CallObject(OBJECT(&BucketType), NULL)) return -1;
#ifndef INTKEY
Py_INCREF(Py_None);
self->data->key=Py_None;
#endif
self->data->value=b;
self->data->count=0;
self->len=1;
self->count=0;
return 0;
}
static int
BTree_init(BTree *self)
{
UNLESS(self->data=PyMalloc(sizeof(BTreeItem)*2)) return -1;
self->size=2;
return BTree_ini(self);
}
static int
bucket_index(Bucket *self, PyObject *key, int less)
{
/*
If less, return the index of the largest key that is less than or
equall to key. Otherwise return the index of the smallest key
that is greater than or equal to key.
*/
int min, max, i, l, cmp;
#ifdef INTKEY
int ikey;
UNLESS(PyInt_Check(key))
{
PyErr_SetString(PyExc_TypeError,
"Bucket __getitem__ expected integer key");
return -9;
}
ikey=PyInt_AsLong(key);
#endif
PER_USE_OR_RETURN(self, -1);
for(min=0, max=self->len, i=max/2, l=max; i != l; l=i, i=(min+max)/2)
{
cmp=TEST_KEY(self->data[i].key);
if(cmp < 0) min=i;
else if(cmp == 0)
{
PER_ALLOW_DEACTIVATION(self);
return i;
}
else max=i;
}
PER_ALLOW_DEACTIVATION(self);
if(less) return max-1;
if(max==min) return min;
return min+1;
}
static PyObject *
_bucket_get(Bucket *self, PyObject *key, int has_key)
{
int min, max, i, l, cmp;
PyObject *r;
#ifdef INTKEY
int ikey;
UNLESS(PyInt_Check(key))
{
PyErr_SetString(PyExc_TypeError,
"Bucket __getitem__ expected integer key");
return NULL;
}
ikey=PyInt_AsLong(key);
#endif
PER_USE_OR_RETURN(self, NULL);
for(min=0, max=self->len, i=max/2, l=max; i != l; l=i, i=(min+max)/2)
{
cmp=TEST_KEY(self->data[i].key);
if(cmp < 0) min=i;
else if(cmp == 0)
{
if(has_key) r=PyInt_FromLong(1);
else
{
#ifdef INTVAL
r=PyInt_FromLong(self->data[i].value);
#else
r=self->data[i].value;
Py_INCREF(r);
#endif
}
PER_ALLOW_DEACTIVATION(self);
return r;
}
else max=i;
}
PER_ALLOW_DEACTIVATION(self);
if(has_key) return PyInt_FromLong(0);
PyErr_SetObject(PyExc_KeyError, key);
return NULL;
}
static PyObject *
bucket_get(Bucket *self, PyObject *key)
{
return _bucket_get(self, key, 0);
}
static PyObject *
bucket_map(Bucket *self, PyObject *args)
{
PyObject *keys, *key, *r;
int l, i, a;
UNLESS(PyArg_ParseTuple(args,"O", &keys)) return NULL;
if((l=PyObject_Length(keys)) < 0) return NULL;
UNLESS(r=PyList_New(0)) return NULL;
for(i=0; i < l; i++)
{
UNLESS(key=PySequence_GetItem(keys,i)) goto err;
ASSIGN(key, _bucket_get(self, key, 0));
if(key)
{
a=PyList_Append(r,key);
Py_DECREF(key);
if(a<0) goto err;
}
else PyErr_Clear();
}
return r;
err:
Py_DECREF(r);
return NULL;
}
static int
BTree_index(BTree *self, PyObject *key, int less)
{
/*
If less, return the index of the largest key that is less than or
equall to key. Otherwise return the index of the smallest key
that is greater than or equal to key.
*/
int min, max, i, cmp;
#ifdef INTKEY
int ikey;
UNLESS(PyInt_Check(key))
{
PyErr_SetString(PyExc_TypeError,
"Bucket __getitem__ expected integer key");
return -9;
}
ikey=PyInt_AsLong(key);
#endif
PER_USE_OR_RETURN(self, -1);
UNLESS(self->data) if(BTree_init(self) < 0) goto err;
for(min=0, max=self->len, i=max/2; max-min > 1; i=(min+max)/2)
{
cmp=TEST_KEY(self->data[i].key);
if(cmp < 0) min=i;
else if(cmp == 0)
{
min=i;
break;
}
else max=i;
}
if(Bucket_Check(self->data[min].value))
i=bucket_index(BUCKET(self->data[min].value), key, less);
else
i= BTree_index( BTREE(self->data[min].value), key, less);
if(i==-9) goto err;
while(--min >= 0) i+=self->data[min].count;
PER_ALLOW_DEACTIVATION(self);
return i;
err:
PER_ALLOW_DEACTIVATION(self);
return -9;
}
static PyObject *
_BTree_get(BTree *self, PyObject *key, int has_key)
{
int min, max, i, cmp;
PyObject *r;
#ifdef INTKEY
int ikey;
UNLESS(PyInt_Check(key))
{
PyErr_SetString(PyExc_TypeError,
"Bucket __getitem__ expected integer key");
return NULL;
}
ikey=PyInt_AsLong(key);
#endif
PER_USE_OR_RETURN(self, NULL);
UNLESS(self->data) if(BTree_init(self) < 0) goto err;
for(min=0, max=self->len, i=max/2; max-min > 1; i=(min+max)/2)
{
cmp=TEST_KEY(self->data[i].key);
if(cmp < 0) min=i;
else if(cmp == 0)
{
min=i;
break;
}
else max=i;
}
if(Bucket_Check(self->data[min].value))
r=_bucket_get(BUCKET(self->data[min].value), key, has_key);
else
r=_BTree_get( BTREE(self->data[min].value), key, has_key);
PER_ALLOW_DEACTIVATION(self);
return r;
err:
PER_ALLOW_DEACTIVATION(self);
return NULL;
}
static PyObject *
BTree_get(BTree *self, PyObject *key)
{
return _BTree_get(self, key, 0);
}
static PyObject *
BTree_map(BTree *self, PyObject *args)
{
PyObject *keys, *key, *r;
int l, i, a;
UNLESS(PyArg_ParseTuple(args,"O", &keys)) return NULL;
if((l=PyObject_Length(keys)) < 0) return NULL;
UNLESS(r=PyList_New(0)) return NULL;
for(i=0; i < l; i++)
{
UNLESS(key=PySequence_GetItem(keys,i)) goto err;
ASSIGN(key, _BTree_get(self, key, 0));
if(key)
{
a=PyList_Append(r,key);
Py_DECREF(key);
if(a<0) goto err;
}
else PyErr_Clear();
}
return r;
err:
Py_DECREF(r);
return NULL;
}
static int
_bucket_set(Bucket *self, PyObject *key, PyObject *v)
{
int min, max, i, l, cmp;
Item *d;
#ifdef INTKEY
int ikey;
#endif
#ifdef INTVAL
int iv;
#endif
#ifdef INTKEY
UNLESS(PyInt_Check(key))
{
PyErr_SetString(PyExc_TypeError,
"Bucket __setitem__ expected integer value");
return -1;
}
ikey=PyInt_AsLong(key);
#endif
#ifdef INTVAL
if(v)
{
UNLESS(PyInt_Check(v))
{
PyErr_SetString(PyExc_TypeError,
"Bucket __getitem__ expected integer key");
return -1;
}
iv=PyInt_AsLong(v);
}
#endif
PER_USE_OR_RETURN(self, -1);
for(min=0, max=l=self->len, i=max/2; i != l; l=i, i=(min+max)/2)
{
if((cmp=TEST_KEY(self->data[i].key)) < 0) min=i;
else if(cmp==0)
{
if(v)
{
#ifdef INTVAL
self->data[i].value=iv;
#else
Py_INCREF(v);
ASSIGN(self->data[i].value, v);
#endif
if(PER_CHANGED(self) < 0) goto err;
PER_ALLOW_DEACTIVATION(self);
return 0;
}
else
{
self->len--;
d=self->data+i;
DECREF_KEY(d->key);
DECREF_VALUE(d->value);
if(i < self->len) memmove(d,d+1,sizeof(Item)*(self->len-i));
else if(! self->len)
{
self->size=0;
free(self->data);
self->data=NULL;
}
if(PER_CHANGED(self) < 0) goto err;
PER_ALLOW_DEACTIVATION(self);
return 1;
}
}
else max=i;
}
if(!v)
{
PyErr_SetObject(PyExc_KeyError, key);
goto err;
}
if(self->len==self->size)
{
if(self->data)
{
UNLESS(d=PyRealloc(self->data, sizeof(Item)*self->size*2)) goto err;
self->data=d;
self->size*=2;
}
else
{
UNLESS(self->data=PyMalloc(sizeof(Item)*MIN_BUCKET_ALLOC)) goto err;
self->size=MIN_BUCKET_ALLOC;
}
}
if(max != i) i++;
d=self->data+i;
if(self->len > i) memmove(d+1,d,sizeof(Item)*(self->len-i));
#ifdef INTKEY
d->key=ikey;
#else
d->key=key;
Py_INCREF(key);
#endif
#ifdef INTVAL
d->value=iv;
#else
d->value=v;
Py_INCREF(v);
#endif
self->len++;
if(PER_CHANGED(self) < 0) goto err;
PER_ALLOW_DEACTIVATION(self);
return 1;
err:
PER_ALLOW_DEACTIVATION(self);
return -1;
}
static int
bucket_setitem(Bucket *self, PyObject *key, PyObject *v)
{
if(_bucket_set(self,key,v) < 0) return -1;
return 0;
}
static int
bucket_split(Bucket *self, int index, Bucket *next)
{
if(index < 0 || index >= self->len) index=self->len/2;
UNLESS(next->data=PyMalloc(sizeof(Item)*(self->len-index))) return -1;
next->len=self->len-index;
next->size=next->len;
memcpy(next->data, self->data+index, sizeof(Item)*next->size);
self->len=index;
return 0;
}
static int
BTree_count(BTree *self)
{
int i, c=0;
BTreeItem *d;
for(i=self->len, d=self->data; --i >= 0; d++)
c += d->count;
return c;
}
static int
BTree_split(BTree *self, int index, BTree *next)
{
if(index < 0 || index >= self->len) index=self->len/2;
UNLESS(next->data=PyMalloc(sizeof(BTreeItem)*(self->len-index)))
return -1;
next->len=self->len-index;
next->size=next->len;
memcpy(next->data, self->data+index, sizeof(BTreeItem)*next->size);
if((next->count=BTree_count(next)) < 0) return -1;
self->len = index;
self->count -= next->count;
return 0;
}
static int
BTree_clone(BTree *self)
{
/* We've grown really big without anybody splitting us.
We should split ourselves.
*/
BTree *n1=0, *n2=0;
BTreeItem *d=0;
int count;
/* Create two BTrees to hold ourselves after split */
UNLESS(n1=BTREE(PyObject_CallObject(OBJECT(self->ob_type), NULL))) return -1;
UNLESS(n2=BTREE(PyObject_CallObject(OBJECT(self->ob_type), NULL))) goto err;
/* Create a new data buffer to hold two BTrees */
UNLESS(d=PyMalloc(sizeof(BTreeItem)*2)) goto err;
count=self->count;
/* Split ourself */
if(BTree_split(self,-1,n2) < 0) goto err;
/* Move our data to new BTree */
n1->size=self->size;
n1->len=self->len;
n1->count=self->count;
n1->data=self->data;
/* Initialize our data to hold split data */
self->data=d;
Py_INCREF(Py_None);
#ifndef INTKEY
self->data->key=Py_None;
#endif
self->len=2;
self->size=2;
self->data->value=OBJECT(n1);
self->data->count=n1->count;
#ifndef INTKEY
Py_INCREF(n2->data->key);
#endif
self->data[1].key=n2->data->key;
self->data[1].value=OBJECT(n2);
self->data[1].count=n2->count;
self->count=count;
return 0;
err:
Py_XDECREF(n1);
Py_XDECREF(n2);
free(d);
return -1;
}
static int
BTree_grow(BTree *self, int index)
{
int i;
PyObject *v, *e=0;
BTreeItem *d;
if(self->len == self->size)
{
UNLESS(d=PyRealloc(self->data, sizeof(BTreeItem)*self->size*2))
return -1;
self->data=d;
self->size *= 2;
}
d=self->data+index;
v=d->value;
UNLESS(e=PyObject_CallObject(OBJECT(v->ob_type), NULL)) return -1;
PER_USE_OR_RETURN((Bucket*)v, -1);
if(Bucket_Check(v))
{
i=bucket_split(BUCKET(v), -1, BUCKET(e));
d->count=BUCKET(v)->len;
}
else
{
i=BTree_split( BTREE(v), -1, BTREE(e));
d->count=BTREE(v)->count;
}
PER_ALLOW_DEACTIVATION(BUCKET(v));
if(i < 0)
{
Py_DECREF(e);
return -1;
}
index++;
d++;
if(self->len > index)
memmove(d+1, d, sizeof(BTreeItem)*(self->len-index));
if(Bucket_Check(v))
{
d->key=BUCKET(e)->data->key;
d->count=BUCKET(e)->len;
}
else
{
d->key=BTREE(e)->data->key;
d->count=BTREE(e)->count;
}
#ifndef INTKEY
Py_INCREF(d->key);
#endif
d->value=e;
self->len++;
if(self->len >= MAX_SIZE(self) * 2) return BTree_clone(self);
return 0;
}
static int
_BTree_set(BTree *self, PyObject *key, PyObject *value)
{
int i, min, max, cmp, grew;
BTreeItem *d;
#ifdef INTKEY
int ikey;
#endif
#ifdef INTKEY
UNLESS(PyInt_Check(key))
{
PyErr_SetString(PyExc_TypeError,
"Bucket __setitem__ expected integer value");
return -1;
}
ikey=PyInt_AsLong(key);
#endif
PER_USE_OR_RETURN(self, -1);
UNLESS(self->data) if(BTree_init(self) < 0) goto err;
for(min=0, max=self->len, i=max/2; max-min > 1; i=(max+min)/2)
{
d=self->data+i;
cmp=TEST_KEY(d->key);
if(cmp < 0) min=i;
else if(cmp==0)
{
min=i;
break;
}
else max=i;
}
d=self->data+min;
if(Bucket_Check(d->value))
grew=_bucket_set(BUCKET(d->value), key, value);
else
grew= _BTree_set( BTREE(d->value), key, value);
if(grew < 0) goto err;
if(grew)
{
if(value) /* got bigger */
{
d->count++;
self->count++;
if(BUCKET(d->value)->len > MAX_SIZE(self) &&
BTree_grow(self,min) < 0) goto err;
}
else /* got smaller */
{
d->count--;
self->count--;
if(! d->count && self->len > 1)
{
self->len--;
Py_DECREF(d->value);
DECREF_KEY(d->key);
if(min < self->len)
memmove(d, d+1, (self->len-min)*sizeof(BTreeItem));
}
}
if(PER_CHANGED(self) < 0) goto err;
}
PER_ALLOW_DEACTIVATION(self);
return grew;
err:
PER_ALLOW_DEACTIVATION(self);
return -1;
}
static int
BTree_setitem(BTree *self, PyObject *key, PyObject *v)
{
if(_BTree_set(self,key,v) < 0) return -1;
return 0;
}
static PyObject *
bucket_keys(Bucket *self, PyObject *args)
{
PyObject *r=0, *key;
int i;
PER_USE_OR_RETURN(self, NULL);
UNLESS(r=PyList_New(self->len)) goto err;
for(i=self->len; --i >= 0; )
{
#ifdef INTKEY
UNLESS(key=PyInt_FromLong(self->data[i].key)) goto err;
#else
key=self->data[i].key;
Py_INCREF(key);
#endif
if(PyList_SetItem(r, i, key) < 0) goto err;
}
PER_ALLOW_DEACTIVATION(self);
return r;
err:
PER_ALLOW_DEACTIVATION(self);
Py_DECREF(r);
return NULL;
}
static PyObject *
bucket_values(Bucket *self, PyObject *args)
{
PyObject *r=0, *v;
int i;
PER_USE_OR_RETURN(self, NULL);
UNLESS(r=PyList_New(self->len)) goto err;
for(i=self->len; --i >= 0; )
{
#ifdef INTVAL
UNLESS(v=PyInt_FromLong(self->data[i].value)) goto err;
#else
v=self->data[i].value;
Py_INCREF(v);
#endif
if(PyList_SetItem(r, i, v) < 0) goto err;
}
PER_ALLOW_DEACTIVATION(self);
return r;
err:
PER_ALLOW_DEACTIVATION(self);
Py_DECREF(r);
return NULL;
}
static PyObject *
bucket_items(Bucket *self, PyObject *args)
{
PyObject *r, *item;
int i;
PER_USE_OR_RETURN(self, NULL);
UNLESS(r=PyList_New(self->len)) goto err;
for(i=self->len; --i >= 0; )
{
UNLESS(item=Py_BuildValue(KEY_PARSE VALUE_PARSE,
self->data[i].key,self->data[i].value))
goto err;
if(PyList_SetItem(r, i, item) < 0) goto err;
}
PER_ALLOW_DEACTIVATION(self);
return r;
err:
PER_ALLOW_DEACTIVATION(self);
Py_DECREF(r);
return NULL;
}
#ifdef PERSISTENT
static PyObject *
bucket__p___reinit__(Bucket *self, PyObject *args)
{
if(self->state==cPersistent_UPTODATE_STATE)
{
int i;
PyObject *dict;
for(i=self->len; --i >= 0; )
{
DECREF_KEY(self->data[i].key);
DECREF_VALUE(self->data[i].value);
}
if(HasInstDict(self) && (dict=INSTANCE_DICT(self))) PyDict_Clear(dict);
self->len=0;
self->state=cPersistent_GHOST_STATE;
}
Py_INCREF(Py_None);
return Py_None;
}
#endif
static PyObject *
bucket_clear(Bucket *self, PyObject *args)
{
int i;
PER_USE_OR_RETURN(self, NULL);
for(i=self->len; --i >= 0; )
{
DECREF_KEY(self->data[i].key);
DECREF_VALUE(self->data[i].value);
}
self->len=0;
if(PER_CHANGED(self) < 0) goto err;
PER_ALLOW_DEACTIVATION(self);
RETURN_NONE;
err:
PER_ALLOW_DEACTIVATION(self);
return NULL;
}
static int
_BTree_clear(BTree *self)
{
int i;
UNLESS(self->data) return 0;
for(i=self->len; --i >= 0; )
{
DECREF_KEY(self->data[i].key);
Py_DECREF(self->data[i].value);
}
i=BTree_ini(self);
return i;
}
#ifdef PERSISTENT
static PyObject *
BTree__p___reinit__(BTree *self, PyObject *args)
{
if(self->state==cPersistent_UPTODATE_STATE)
{
PyObject *dict;
if(_BTree_clear(self) < 0) return NULL;
if(HasInstDict(self) && (dict=INSTANCE_DICT(self))) PyDict_Clear(dict);
self->state=cPersistent_GHOST_STATE;
}
Py_INCREF(Py_None);
return Py_None;
}
#endif
static PyObject *
BTree_clear(BTree *self, PyObject *args)
{
PER_USE_OR_RETURN(self, NULL);
if(_BTree_clear(self) < 0) goto err;
if(PER_CHANGED(self) < 0) goto err;
PER_ALLOW_DEACTIVATION(self);
RETURN_NONE;
err:
PER_ALLOW_DEACTIVATION(self);
return NULL;
}
static PyObject *
bucket_getstate(Bucket *self, PyObject *args)
{
PyObject *r, *keys=0, *values=0;
int i, l;
#ifdef INTKEY
int v;
char *c;
#else
#ifdef INTVAL
int v;
char *c;
#endif
#endif
PER_USE_OR_RETURN(self, NULL);
l=self->len;
#ifdef INTKEY
UNLESS(keys=PyString_FromStringAndSize(NULL,l*sizeof(int))) goto err;
UNLESS(c=PyString_AsString(keys)) goto err;
for(i=0; i < l; i++)
{
v=self->data[i].key;
*c++ = (int)( v & 0xff);
*c++ = (int)((v >> 8) & 0xff);
*c++ = (int)((v >> 16) & 0xff);
*c++ = (int)((v >> 24) & 0xff);
}
#else
UNLESS(keys=PyTuple_New(self->len)) goto err;
for(i=0; i<l; i++)
{
r=self->data[i].key;
Py_INCREF(r);
PyTuple_SET_ITEM(keys,i,r);
}
#endif
#ifdef INTVAL
UNLESS(values=PyString_FromStringAndSize(NULL,l*sizeof(int))) goto err;
UNLESS(c=PyString_AsString(values)) goto err;
for(i=0; i < l; i++)
{
v=self->data[i].value;
*c++ = (int)( v & 0xff);
*c++ = (int)((v >> 8) & 0xff);
*c++ = (int)((v >> 16) & 0xff);
*c++ = (int)((v >> 24) & 0xff);
}
#else
UNLESS(values=PyTuple_New(self->len)) goto err;
for(i=0; i<l; i++)
{
r=self->data[i].value;
Py_INCREF(r);
PyTuple_SET_ITEM(values,i,r);
}
#endif
PER_ALLOW_DEACTIVATION(self);
r=Py_BuildValue("OO",keys,values);
Py_DECREF(keys);
Py_DECREF(values);
return r;
err:
PER_ALLOW_DEACTIVATION(self);
Py_XDECREF(keys);
Py_XDECREF(values);
return NULL;
}
static PyObject *
bucket_setstate(Bucket *self, PyObject *args)
{
PyObject *r, *keys=0, *values=0;
int i, l, v;
Item *d;
#ifdef INTKEY
char *ck;
#endif
#ifdef INTVAL
char *cv;
#endif
UNLESS(PyArg_ParseTuple(args,"O",&r)) goto err;
UNLESS(PyArg_ParseTuple(r,"OO",&keys,&values)) goto err;
if((l=PyObject_Length(keys)) < 0) goto err;
#ifdef INTKEY
l/=4;
UNLESS(ck=PyString_AsString(keys)) goto err;
#endif
if((v=PyObject_Length(values)) < 0) goto err;
#ifdef INTVAL
v/=4;
UNLESS(cv=PyString_AsString(values)) goto err;
#endif
if(l!=v)
{
PyErr_SetString(PyExc_ValueError,
"number of keys differs from number of values");
goto err;
}
if(l > self->size)
if(self->data)
{
UNLESS(d=PyRealloc(self->data, sizeof(Item)*l)) goto err;
self->data=d;
self->size=l;
}
else
{
UNLESS(d=PyMalloc(sizeof(Item)*l)) goto err;
self->data=d;
self->size=l;
}
else d=self->data;
#ifdef INTKEY
for(i=l; --i >= 0; d++)
{
v = ((int)(unsigned char)*ck++) ;
v |= ((int)(unsigned char)*ck++) << 8;
v |= ((int)(unsigned char)*ck++) << 16;
v |= ((int)(unsigned char)*ck++) << 24;
d->key=v;
}
#else
for(i=0; i<l; i++, d++)
{
UNLESS(r=PySequence_GetItem(keys,i)) goto err;
if(i < self->len) Py_DECREF(d->key);
d->key=r;
}
#endif
d=self->data;
#ifdef INTVAL
for(i=l; --i >= 0; d++)
{
v = ((int)(unsigned char)*cv++) ;
v |= ((int)(unsigned char)*cv++) << 8;
v |= ((int)(unsigned char)*cv++) << 16;
v |= ((int)(unsigned char)*cv++) << 24;
d->value=v;
}
#else
for(i=0; i<l; i++, d++)
{
UNLESS(r=PySequence_GetItem(values,i)) goto err;
if(i < self->len) Py_DECREF(d->value);
d->value=r;
}
#endif
self->len=l;
PER_ALLOW_DEACTIVATION(self);
Py_INCREF(Py_None);
return Py_None;
err:
PER_ALLOW_DEACTIVATION(self);
return NULL;
}
static PyObject *
bucket_has_key(Bucket *self, PyObject *args)
{
PyObject *key;
UNLESS(PyArg_ParseTuple(args,"O",&key)) return NULL;
return _bucket_get(self, key, 1);
}
static PyObject *
bucket_getm(Bucket *self, PyObject *args)
{
PyObject *key, *d=Py_None, *r;
UNLESS(PyArg_ParseTuple(args, "O|O", &key, &d)) return NULL;
if((r=_bucket_get(self, key, 0))) return r;
PyErr_Clear();
Py_INCREF(d);
return d;
}
static struct PyMethodDef Bucket_methods[] = {
{"__getstate__", (PyCFunction)bucket_getstate, METH_VARARGS,
"__getstate__() -- Return the picklable state of the object"},
{"__setstate__", (PyCFunction)bucket_setstate, METH_VARARGS,
"__setstate__() -- Set the state of the object"},
{"keys", (PyCFunction)bucket_keys, METH_VARARGS,
"keys() -- Return the keys"},
{"has_key", (PyCFunction)bucket_has_key, METH_VARARGS,
"has_key(key) -- Test whether the bucket contains the given key"},
{"values", (PyCFunction)bucket_values, METH_VARARGS,
"values() -- Return the values"},
{"items", (PyCFunction)bucket_items, METH_VARARGS,
"items() -- Return the items"},
{"clear", (PyCFunction)bucket_clear, METH_VARARGS,
"clear() -- Remove all of the items from the bucket"},
{"map", (PyCFunction)bucket_map, METH_VARARGS,
"map(keys) -- map a sorted sequence of keys into values\n\n"
"Invalid keys are skipped"},
{"get", (PyCFunction)bucket_getm, METH_VARARGS,
"get(key[,default]) -- Look up a value\n\n"
"Return the default (or None) if the key is not found."
},
#ifdef PERSISTENT
{"_p___reinit__", (PyCFunction)bucket__p___reinit__, METH_VARARGS,
"_p___reinit__() -- Reinitialize from a newly created copy"},
{"_p_deactivate", (PyCFunction)bucket__p___reinit__, METH_VARARGS,
"_p_deactivate() -- Reinitialize from a newly created copy"},
#endif
{NULL, NULL} /* sentinel */
};
static PyObject *
BTree_getstate(BTree *self, PyObject *args)
{
PyObject *r=0, *item;
int i;
PER_USE_OR_RETURN(self, NULL);
UNLESS(r=PyTuple_New(self->len)) goto err;
for(i=self->len; --i >= 0; )
{
UNLESS(item=Py_BuildValue(KEY_PARSE "Oi",
self->data[i].key, self->data[i].value,
self->data[i].count))
goto err;
PyTuple_SET_ITEM(r,i,item);
}
PER_ALLOW_DEACTIVATION(self);
return r;
err:
PER_ALLOW_DEACTIVATION(self);
Py_DECREF(r);
return NULL;
}
static PyObject *
BTree_setstate(BTree *self, PyObject *args)
{
PyObject *state, *v=0;
BTreeItem *d;
int l, i;
UNLESS(PyArg_ParseTuple(args,"O",&state)) return NULL;
if((l=PyTuple_Size(state))<0) return NULL;
if(l>self->size)
{
if(self->data)
{
UNLESS(d=PyRealloc(self->data, sizeof(BTreeItem)*l)) goto err;
self->data=d;
self->size=l;
}
else
{
UNLESS(self->data=PyMalloc(sizeof(BTreeItem)*l)) goto err;
self->size=l;
}
}
for(i=self->len, d=self->data; --i >= 0; d++)
{
DECREF_KEY(d->key);
Py_DECREF(d->value);
}
for(self->len=0, self->count=0, i=0, d=self->data; i < l;
i++, d++, self->len++)
{
UNLESS(PyArg_ParseTuple(PyTuple_GET_ITEM(state,i),
KEY_PARSE "Oi",
&(d->key), &(d->value), &(d->count)))
goto err;
#ifndef INTKEY
Py_INCREF(d->key);
#endif
Py_INCREF(d->value);
self->count+=d->count;
}
PER_ALLOW_DEACTIVATION(self);
Py_INCREF(Py_None);
return Py_None;
err:
PER_ALLOW_DEACTIVATION(self);
return NULL;
}
static PyObject *
BTree_elements(BTree *self, PyObject *args, char type)
{
PyObject *f=0, *l=0;
int fi, li;
UNLESS(PyArg_ParseTuple(args,"|OO",&f, &l)) return NULL;
PER_USE_OR_RETURN(self, NULL);
if(f && f != Py_None)
{
fi=BTree_index(self, f, 0);
if(fi==-9) goto err;
}
else fi=0;
if(l)
{
li=BTree_index(self, l, 1);
if(li==-9) goto err;
li++;
}
else li=self->count;
PER_ALLOW_DEACTIVATION(self);
return newBTreeItems(self,type,fi,li);
err:
PER_ALLOW_DEACTIVATION(self);
return NULL;
}
static PyObject *
BTree_keys(BTree *self, PyObject *args)
{
return BTree_elements(self,args,'k');
}
static PyObject *
BTree_values(BTree *self, PyObject *args)
{
return BTree_elements(self,args,'v');
}
static PyObject *
BTree_items(BTree *self, PyObject *args)
{
return BTree_elements(self,args,'i');
}
static PyObject *
BTree_has_key(BTree *self, PyObject *args)
{
PyObject *key;
UNLESS(PyArg_ParseTuple(args,"O",&key)) return NULL;
return _BTree_get(self, key, 1);
}
static PyObject *
BTree_getm(BTree *self, PyObject *args)
{
PyObject *key, *d=Py_None, *r;
UNLESS(PyArg_ParseTuple(args, "O|O", &key, &d)) return NULL;
if((r=_BTree_get(self, key, 0))) return r;
PyErr_Clear();
Py_INCREF(d);
return d;
}
static struct PyMethodDef BTree_methods[] = {
{"__getstate__", (PyCFunction)BTree_getstate, METH_VARARGS,
"__getstate__() -- Return the picklable state of the object"},
{"__setstate__", (PyCFunction)BTree_setstate, METH_VARARGS,
"__setstate__() -- Set the state of the object"},
{"has_key", (PyCFunction)BTree_has_key, METH_VARARGS,
"has_key(key) -- Test whether the bucket contains the given key"},
{"keys", (PyCFunction)BTree_keys, METH_VARARGS,
"keys() -- Return the keys"},
{"values", (PyCFunction)BTree_values, METH_VARARGS,
"values() -- Return the values"},
{"items", (PyCFunction)BTree_items, METH_VARARGS,
"items() -- Return the items"},
{"clear", (PyCFunction)BTree_clear, METH_VARARGS,
"clear() -- Remove all of the items from the BTree"},
{"map", (PyCFunction)BTree_map, METH_VARARGS,
"map(keys) -- map a sorted sequence of keys into values\n\n"
"Invalid keys are skipped"},
{"get", (PyCFunction)BTree_getm, METH_VARARGS,
"get(key[,default]) -- Look up a value\n\n"
"Return the default (or None) if the key is not found."
},
#ifdef PERSISTENT
{"_p___reinit__", (PyCFunction)BTree__p___reinit__, METH_VARARGS,
"_p___reinit__() -- Reinitialize from a newly created copy"},
{"_p_deactivate", (PyCFunction)BTree__p___reinit__, METH_VARARGS,
"_p_deactivate() -- Reinitialize from a newly created copy"},
#endif
{NULL, NULL} /* sentinel */
};
static void
Bucket_dealloc(Bucket *self)
{
int i;
for(i=self->len; --i >= 0; )
{
DECREF_KEY(self->data[i].key);
DECREF_VALUE(self->data[i].value);
}
free(self->data);
PER_DEL(self);
PyMem_DEL(self);
}
static void
BTree_dealloc(BTree *self)
{
int i;
for(i=self->len; --i >= 0; )
{
DECREF_KEY(self->data[i].key);
Py_DECREF(self->data[i].value);
}
free(self->data);
PER_DEL(self);
PyMem_DEL(self);
}
/* Code to access Bucket objects as mappings */
static int
Bucket_length( Bucket *self)
{
int r;
PER_USE_OR_RETURN(self, -1);
r=self->len;
PER_ALLOW_DEACTIVATION(self);
return r;
}
static PyMappingMethods Bucket_as_mapping = {
(inquiry)Bucket_length, /*mp_length*/
(binaryfunc)bucket_get, /*mp_subscript*/
(objobjargproc)bucket_setitem, /*mp_ass_subscript*/
};
static int
BTree_length( BTree *self)
{
int r;
PER_USE_OR_RETURN(self, -1);
r=self->count;
PER_ALLOW_DEACTIVATION(self);
return r;
}
static PyMappingMethods BTree_as_mapping = {
(inquiry)BTree_length, /*mp_length*/
(binaryfunc)BTree_get, /*mp_subscript*/
(objobjargproc)BTree_setitem, /*mp_ass_subscript*/
};
static PyObject *
bucket_repr(Bucket *self)
{
static PyObject *format;
PyObject *r, *t;
UNLESS(format) UNLESS(format=PyString_FromString("Bucket(%s)")) return NULL;
UNLESS(t=PyTuple_New(1)) return NULL;
UNLESS(r=bucket_items(self,NULL)) goto err;
PyTuple_SET_ITEM(t,0,r);
r=t;
ASSIGN(r,PyString_Format(format,r));
return r;
err:
Py_DECREF(t);
return NULL;
}
static PyExtensionClass BucketType = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"Bucket", /*tp_name*/
sizeof(Bucket), /*tp_basicsize*/
0, /*tp_itemsize*/
/*********** methods ***********************/
(destructor)Bucket_dealloc, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)0, /*obsolete tp_getattr*/
(setattrfunc)0, /*obsolete tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)bucket_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
&Bucket_as_mapping, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/
(ternaryfunc)0, /*tp_call*/
(reprfunc)0, /*tp_str*/
(getattrofunc)0, /*tp_getattro*/
0, /*tp_setattro*/
/* Space for future expansion */
0L,0L,
"Mapping type implemented as sorted list of items",
METHOD_CHAIN(Bucket_methods),
EXTENSIONCLASS_BASICNEW_FLAG,
};
static PyExtensionClass BTreeType = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"BTree", /*tp_name*/
sizeof(BTree), /*tp_basicsize*/
0, /*tp_itemsize*/
/************* methods ********************/
(destructor)BTree_dealloc, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)0, /*obsolete tp_getattr*/
(setattrfunc)0, /*obsolete tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
&BTree_as_mapping, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/
(ternaryfunc)0, /*tp_call*/
(reprfunc)0, /*tp_str*/
(getattrofunc)0,
0, /*tp_setattro*/
/* Space for future expansion */
0L,0L,
"Mapping type implemented as sorted list of items",
METHOD_CHAIN(BTree_methods),
EXTENSIONCLASS_BASICNEW_FLAG,
};
static struct PyMethodDef module_methods[] = {
{NULL, NULL} /* sentinel */
};
void
#ifdef INTKEY
#ifdef INTVAL
initIIBTree()
#define MODNAME "IIBTree"
#else
initIOBTree()
#define MODNAME "IOBTree"
#endif
#else
#ifdef INTVAL
initOIBTree()
#define MODNAME "OIBTree"
#else
initBTree()
#define MODNAME "BTree"
#endif
#endif
{
PyObject *m, *d;
char *rev="$Revision: 1.17 $";
UNLESS(PyExtensionClassCAPI=PyCObject_Import("ExtensionClass","CAPI"))
return;
#ifdef PERSISTENT
if(cPersistenceCAPI=PyCObject_Import("cPersistence","CAPI"))
{
BucketType.methods.link=cPersistenceCAPI->methods;
BucketType.tp_getattro=cPersistenceCAPI->getattro;
BucketType.tp_setattro=cPersistenceCAPI->setattro;
BTreeType.methods.link=cPersistenceCAPI->methods;
BTreeType.tp_getattro=cPersistenceCAPI->getattro;
BTreeType.tp_setattro=cPersistenceCAPI->setattro;
}
else return;
#else
BucketType.tp_getattro=PyExtensionClassCAPI->getattro;
BTreeType.tp_getattro=PyExtensionClassCAPI->getattro;
#endif
/* Create the module and add the functions */
m = Py_InitModule4(MODNAME, module_methods,
BTree_module_documentation,
(PyObject*)NULL,PYTHON_API_VERSION);
/* Add some symbolic constants to the module */
d = PyModule_GetDict(m);
PyExtensionClass_Export(d,"Bucket",BucketType);
PyExtensionClass_Export(d,"BTree",BTreeType);
PyDict_SetItemString(d, "__version__",
PyString_FromStringAndSize(rev+11,strlen(rev+11)-2));
/* Check for errors */
if (PyErr_Occurred())
Py_FatalError("can't initialize module BTree");
}
...@@ -47,10 +47,10 @@ ...@@ -47,10 +47,10 @@
############################################################################## ##############################################################################
"""Database connection support """Database connection support
$Id: Connection.py,v 1.2 1999/04/28 11:10:48 jim Exp $""" $Id: Connection.py,v 1.3 1999/05/07 01:03:02 jim Exp $"""
__version__='$Revision: 1.2 $'[11:-2] __version__='$Revision: 1.3 $'[11:-2]
from PickleCache import PickleCache from cPickleCache import PickleCache
from bpthread import allocate_lock from bpthread import allocate_lock
from POSException import ConflictError from POSException import ConflictError
from cStringIO import StringIO from cStringIO import StringIO
...@@ -75,7 +75,7 @@ class Connection: ...@@ -75,7 +75,7 @@ class Connection:
self._storage=storage self._storage=storage
self.new_oid=storage.new_oid self.new_oid=storage.new_oid
self._version=version self._version=version
self._cache=cache=PickleCache(cache_size, cache_deactivate_after) self._cache=cache=PickleCache(self, cache_size, cache_deactivate_after)
self._incrgc=cache.incrgc self._incrgc=cache.incrgc
self._invalidated={} self._invalidated={}
lock=allocate_lock() lock=allocate_lock()
...@@ -94,38 +94,25 @@ class Connection: ...@@ -94,38 +94,25 @@ class Connection:
if cache.has_key(oid): return cache[oid] if cache.has_key(oid): return cache[oid]
__traceback_info__=oid __traceback_info__=oid
p=self._storage.load(oid, self._version) p, serial = self._storage.load(oid, self._version)
file=StringIO(p) file=StringIO(p)
unpickler=Unpickler(file) unpickler=Unpickler(file)
unpickler.persistent_load=self._persistent_load unpickler.persistent_load=self._persistent_load
object = unpickler.load() object = unpickler.load()
if type(object) is tt: klass, args = object
klass, args = object if (args is None or
if (args is None or not args and not hasattr(klass,'__getinitargs__')):
not args and not hasattr(klass,'__getinitargs__')): object=klass.__basicnew__()
if type(klass) is ct:
object=HelperClass()
object.__class__=klass
else: object=klass.__basicnew__()
else:
object=apply(klass,args)
object.__dict__.clear()
else: else:
object=apply(klass,args)
object.__dict__.clear() object.__dict__.clear()
klass=object.__class__
object._p_oid=oid
if type(klass) is ct: object._p_jar=self
d=object.__dict__ object._p_changed=None
d['_p_oid']=oid object._p_serial=serial
d['_p_jar']=self
d['_p_changed']=None
else:
object._p_oid=oid
object._p_jar=self
object._p_changed=None
cache[oid]=object cache[oid]=object
return object return object
...@@ -143,18 +130,10 @@ class Connection: ...@@ -143,18 +130,10 @@ class Connection:
# to create the instance wo hitting the db, so go for it! # to create the instance wo hitting the db, so go for it!
oid, klass = oid oid, klass = oid
if cache.has_key(oid): return cache[oid] if cache.has_key(oid): return cache[oid]
if type(klass) is ct: object=klass.__basicnew__()
object=HelperClass() object._p_oid=oid
object.__class__=klass object._p_jar=self
d=object.__dict__ object._p_changed=None
d['_p_oid']=oid
d['_p_jar']=self
d['_p_changed']=None
else:
object=klass.__basicnew__()
object._p_oid=oid
object._p_jar=self
object._p_changed=None
cache[oid]=object cache[oid]=object
return object return object
...@@ -236,6 +215,7 @@ class Connection: ...@@ -236,6 +215,7 @@ class Connection:
object=stack[-1] object=stack[-1]
del stack[-1] del stack[-1]
oid=object._p_oid oid=object._p_oid
serial=object._p_serial
if self._invalidated.has_key(oid): raise ConflictError, oid if self._invalidated.has_key(oid): raise ConflictError, oid
cls = object.__class__ cls = object.__class__
if hasattr(cls, '__getinitargs__'): if hasattr(cls, '__getinitargs__'):
...@@ -249,7 +229,7 @@ class Connection: ...@@ -249,7 +229,7 @@ class Connection:
state=object.__getstate__() state=object.__getstate__()
dump(state) dump(state)
p=file() p=file()
dbstore(oid,p,version,transaction) object._p_serial=dbstore(oid,serial,p,version,transaction)
object._p_changed=0 object._p_changed=0
cache[oid]=object cache[oid]=object
...@@ -287,7 +267,7 @@ class Connection: ...@@ -287,7 +267,7 @@ class Connection:
self._r() self._r()
raise ConflictError, oid raise ConflictError, oid
self._r() self._r()
p=self._storage.load(oid, self._version) p, serial = self._storage.load(oid, self._version)
file=StringIO(p) file=StringIO(p)
unpickler=Unpickler(file) unpickler=Unpickler(file)
unpickler.persistent_load=self._persistent_load unpickler.persistent_load=self._persistent_load
...@@ -298,6 +278,7 @@ class Connection: ...@@ -298,6 +278,7 @@ class Connection:
else: else:
d=object.__dict__ d=object.__dict__
for k,v in state.items(): d[k]=v for k,v in state.items(): d[k]=v
object._p_serial=serial
def tpc_abort(self, transaction): def tpc_abort(self, transaction):
self._storage.tpc_abort(transaction) self._storage.tpc_abort(transaction)
......
...@@ -47,8 +47,8 @@ ...@@ -47,8 +47,8 @@
############################################################################## ##############################################################################
"""Database objects """Database objects
$Id: DB.py,v 1.2 1999/04/28 11:10:48 jim Exp $""" $Id: DB.py,v 1.3 1999/05/07 01:03:02 jim Exp $"""
__version__='$Revision: 1.2 $'[11:-2] __version__='$Revision: 1.3 $'[11:-2]
import cPickle, cStringIO, sys import cPickle, cStringIO, sys
from Connection import Connection from Connection import Connection
...@@ -90,7 +90,7 @@ class DB: ...@@ -90,7 +90,7 @@ class DB:
t=Transaction() t=Transaction()
t.description='initial database creation' t.description='initial database creation'
storage.tpc_begin(t) storage.tpc_begin(t)
storage.store('\0\0\0\0\0\0\0\0', file.getvalue(), '', t) storage.store('\0\0\0\0\0\0\0\0', None, file.getvalue(), '', t)
storage.tpc_finish(t) storage.tpc_finish(t)
# Allocate locks: # Allocate locks:
...@@ -389,8 +389,8 @@ class DB: ...@@ -389,8 +389,8 @@ class DB:
def getCacheDeactivateAfter(self): return self._cache_deactivate_after def getCacheDeactivateAfter(self): return self._cache_deactivate_after
def getCacheSize(self): return self._cache_size def getCacheSize(self): return self._cache_size
def getPoolSize(self): return self._pool_size def getPoolSize(self): return self._pool_size
def getVersionCacheDeactivateAfter(self): return def getVersionCacheDeactivateAfter(self):
self._version_cache_deactivate_after return self._version_cache_deactivate_after
def getVersionCacheSize(self): return self._version_cache_size def getVersionCacheSize(self): return self._version_cache_size
def getVersionPoolSize(self): return self._version_pool_size def getVersionPoolSize(self): return self._version_pool_size
......
############################################################################## """File-based ZODB storage
#
# Copyright (c) 1996-1998, Digital Creations, Fredericksburg, VA, USA. Files are arranged as follows.
# All rights reserved.
# - The first 4 bytes are a file identifier.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are - The rest of the file consists of a sequence of transaction
# met: "records".
#
# o Redistributions of source code must retain the above copyright A transaction record consists of:
# notice, this list of conditions, and the disclaimer that follows.
# - 8-byte transaction record, which is also a time stamp.
# o Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions, and the following disclaimer in - 8-byte previous-transaction file position.
# the documentation and/or other materials provided with the
# distribution. - 8-byte transaction record length - 8.
#
# o Neither the name of Digital Creations nor the names of its - 1-byte status code
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission. - 2-byte length of user name
#
# - 2-byte length of description
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS IS*
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - 4-byte length of extension attributes
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL - user name
# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - description
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * A sequence of data records
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - 8-byte redundant transaction length -8
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE. A data record consists of
#
# - 8-byte oid.
# If you have questions regarding this software, contact:
# - 8-byte serial, which is a type stamp that matches the
# Digital Creations, L.C. transaction timestamp.
# 910 Princess Ann Street
# Fredericksburge, Virginia 22401 - 8-byte previous-record file-position.
#
# info@digicool.com - 8-byte beginning of transaction record file position.
#
# (540) 371-6909 - 2-byte version length
#
############################################################################## - 8-byte data length
"""File-based BoboPOS3 storage
? 8-byte position of non-version data
(if version length > 0)
? 8-byte position of previous record in this version
(if version length > 0)
? version string
(if version length > 0)
? data
(data length > 0)
? 8-byte position of data record containing data
(data length > 0)
Note that the lengths and positions are all big-endian.
Also, the object ids time stamps are big-endian, so comparisons
are meaningful.
""" """
__version__='$Revision: 1.2 $'[11:-2] __version__='$Revision: 1.3 $'[11:-2]
import struct, time, os, bpthread import struct, time, os, bpthread
now=time.time now=time.time
from struct import pack, unpack from struct import pack, unpack
from cPickle import dumps
import POSException import POSException
from TimeStamp import TimeStamp
t32 = 1L << 32
def p64(v, pack=struct.pack):
if v < t32: h=0
else:
h=v/t32
v=v%t32
return pack(">II", h, v)
def u64(v, unpack=struct.unpack):
h, v = unpack(">ii", v)
if v < 0: v=t32-v
if h:
if h < 0: h=t32-h
v=h*t32+v
return v
z64='\0'*8
def cp(f1, f2, l):
read=f1.read
write=f2.write
n=8192
while l > 0:
if n > l: n=l
d=read(n)
write(d)
l = l - len(d)
def warn(log, message, *data):
log("%s warn: %s\n" % (packed_version, (message % data)))
def error(log, message, *data):
log("%s ERROR: %s\n" % (packed_version, (message % data)))
def panic(log, message, *data):
message=message%data
log("%s ERROR: %s\n" % (packed_version, message))
raise CorruptedTransactionRecordError, message
class FileStorageError: pass class FileStorageError: pass
...@@ -70,11 +134,24 @@ class CorruptedFileStorageError(FileStorageError, ...@@ -70,11 +134,24 @@ class CorruptedFileStorageError(FileStorageError,
class CorruptedTransactionError(CorruptedFileStorageError): pass class CorruptedTransactionError(CorruptedFileStorageError): pass
class CorruptedDataError(CorruptedFileStorageError): pass class CorruptedDataError(CorruptedFileStorageError): pass
packed_version='FS20'
class FileStorage: class FileStorage:
_packt=0 _packt=0
_transaction=None _transaction=None
_serial=z64
def __init__(self, file_name, create=0, log=lambda s: None, read_only=0,
stop=None):
def __init__(self, file_name, create=0): if read_only:
if create:
raise ValueError, "can\'t create a read-only file"
elif stop is not None:
raise ValueError, "time-travel is only supported in read-only mode"
if stop is None: stop='\377'*8
self.__name__=file_name self.__name__=file_name
self._tfile=open(file_name+'.tmp','w+b') self._tfile=open(file_name+'.tmp','w+b')
index, vindex, tindex = self._newIndexes() index, vindex, tindex = self._newIndexes()
...@@ -105,58 +182,87 @@ class FileStorage: ...@@ -105,58 +182,87 @@ class FileStorage:
self._oid='\0\0\0\0\0\0\0\0' self._oid='\0\0\0\0\0\0\0\0'
return return
if os.path.exists(file_name): file=open(file_name,'r+b') if os.path.exists(file_name):
else: file=open(file_name,'w+b') file=open(file_name, read_only and 'rb' or 'r+b')
else:
if read_only:
raise ValueError, "can\'t create a read-only file"
file=open(file_name,'w+b')
self._file=file self._file=file
self._pos, self._tpos, self._oid = read_index( self._pos, self._tpos, self._oid, tid = read_index(
file, index, vindex, tindex) file, file_name, index, vindex, tindex, stop, log)
self._ts=tid=TimeStamp(tid)
t=time.time()
t=apply(TimeStamp,(time.gmtime(t)[:5]+(t%60,)))
if tid > t:
warn(log, "%s Database records in the future", file_name);
def __len__(self): return len(self._index) def __len__(self): return len(self._index)
def _newIndexes(self): return {}, {}, [] def _newIndexes(self): return {}, {}, []
def abortVersion(self, version): def abortVersion(self, version, transaction):
if transaction is not self._transaction:
raise POSException.StorageTransactionError(self, transaction)
self._a() self._a()
try: try:
pos=self._vindex[version] pos=self._vindex[version]
spos=p64(pos)
file=self._file file=self._file
seek=file.seek seek=file.seek
read=file.read read=file.read
file=self._tfile file=self._tfile
write=file.write write=file.write
tell=file.tell tell=file.tell
tloc=self._pos tloc=p64(self._pos)
tappend=self._tappend tappend=self._tappend
index=self._index index=self._index
pack=struct.pack pack=struct.pack
unpack=struct.unpack unpack=struct.unpack
serial=self._serial
while pos: while pos:
seek(pos) seek(pos)
h=read(30) h=read(58)
oid=h[:8] oid=h[:8]
if index[oid]==pos: if index[oid]==pos:
tappend(oid, tell()) tappend(oid, tell())
pc=h[-8:-4] # Position of committed (non-version) data pc=h[-16:-8] # Position of committed (non-version) data
write(pack(">8siiHi4s", oid,pos,tloc,0,0,pc)) write(pack(
pos=unpack(">i",h[-4:])[0] ">"
"8s" "8s" "8s" "8s" "H" "8s" "8s",
oid, serial, spos, tloc, 0, z64, pc
))
spos=h[-8:]
pos=u64(spos)
del self._vindex[version]
finally: self._r() finally: self._r()
def close(self): def close(self):
self._file.close() self._file.close()
# Eventuallly, we should save_index # Eventuallly, we should save_index
def commitVersion(self, src, dest): def commitVersion(self, src, dest, transaction):
if transaction is not self._transaction:
raise POSException.StorageTransactionError(self, transaction)
self._a() self._a()
try: try:
pos=self._vindex[version] pos=self._vindex[version]
spos=p64(pos)
file=self._file file=self._file
seek=file.seek seek=file.seek
read=file.read read=file.read
file=self._tfile file=self._tfile
write=file.write write=file.write
tell=file.tell tell=file.tell
tloc=self._pos tloc=p64(self._pos)
tappend=self._tappend tappend=self._tappend
index=self._index index=self._index
pack=struct.pack pack=struct.pack
...@@ -165,14 +271,20 @@ class FileStorage: ...@@ -165,14 +271,20 @@ class FileStorage:
while pos: while pos:
seek(pos) seek(pos)
h=read(30) h=read(58)
oid=h[:8] oid=h[:8]
if index[oid]==pos: if index[oid]==pos:
tappend(oid, tell()) tappend(oid, tell())
write(pack(">8siiHi4s", oid,pos,tloc,destlen,0,h[-8:-4])) write(pack(">8s" "8s" "8s" "H" "8s" "8s",
oid, spos, tloc,destlen, z64, h[-16:-8]))
write(dest) write(dest)
write(pack(">i",pos)) write(spos)
pos=unpack(">i",h[-4:])[0]
spos=h[-8:]
pos=u64(spos)
del self._vindex[version]
finally: self._r() finally: self._r()
def getName(self): return self.__name__ def getName(self): return self.__name__
...@@ -180,31 +292,8 @@ class FileStorage: ...@@ -180,31 +292,8 @@ class FileStorage:
def getSize(self): return self._pos def getSize(self): return self._pos
def history(self, oid, version, length=1): def history(self, oid, version, length=1):
self._a() # TBD
try: pass
# not done
index=self._index
file=self._file
seek=file.seek
read=file.read
hist=[]
pos=index[oid]
while length:
seek(pos)
h=read(22)
doid, prev, tloc, vlen, plen = unpack(">8siiHi", h)
if vlen and not hist:
pnc=read(4)
if vlen != len(version) or read(vlen) != version:
pos=unpack(">i", pnc)
contiue
pos=prev
seek(tloc)
h=read(21)
finally: self._r()
def load(self, oid, version, _stuff=None): def load(self, oid, version, _stuff=None):
self._a() self._a()
...@@ -213,21 +302,21 @@ class FileStorage: ...@@ -213,21 +302,21 @@ class FileStorage:
file=self._file file=self._file
file.seek(pos) file.seek(pos)
read=file.read read=file.read
h=read(22) h=read(42)
doid,prev,tloc,vlen,plen = unpack(">8siiHi", h) doid,serial,prev,tloc,vlen,plen = unpack(">8s8s8s8sH8s", h)
if doid != oid: raise CorruptedDataError, h if doid != oid: raise CorruptedDataError, h
if vlen: if vlen:
pnv=read(4) pnv=read(8) # Read location of non-version data
if (not version or len(version) != vlen or if (not version or len(version) != vlen or
(read(4) # skip past version link (read(8) # skip past version link
and version != read(vlen)) and version != read(vlen))
): ):
return _loadBack(file, oid, pnv) return _loadBack(file, oid, u64(pnv))
# If we get here, then either this was not a version record, # If we get here, then either this was not a version record,
# or we've already read past the version data! # or we've already read past the version data!
if plen: return read(plen) if plen != z64: return read(u64(plen)), serial
return _loadBack(file, oid, pnv) return _loadBack(file, oid, u64(pnv))
finally: self._r() finally: self._r()
def modifiedInVersion(self, oid): def modifiedInVersion(self, oid):
...@@ -236,10 +325,11 @@ class FileStorage: ...@@ -236,10 +325,11 @@ class FileStorage:
pos=self._index[oid] pos=self._index[oid]
file=self._file file=self._file
file.seek(pos) file.seek(pos)
doid,prev,tloc,vlen = unpack(">8siiH", file.read(18)) doid,serial,prev,tloc,vlen = unpack(">8s8s8s8sH", file.read(34))
if doid != oid: raise CorruptedDataError, h if doid != oid:
raise CorruptedDataError, h
if vlen: if vlen:
seek(8,1) seek(16,1)
return read(vlen) return read(vlen)
return '' return ''
finally: self._r() finally: self._r()
...@@ -261,121 +351,10 @@ class FileStorage: ...@@ -261,121 +351,10 @@ class FileStorage:
else: return self.new_oid(last[:-1]) else: return self.new_oid(last[:-1])
def pack(self, t, rf): def pack(self, t, rf):
self._a() # TBD
try: pass
# we're going to leave this undone for a while!
# This is hellacious. Hold on to your butts!
# First, prevent undos before t:
self._packt=t
index, vindex, tindex = self._newIndexes()
# Now we know the part of the file containing transactions
# written before t will not be touched. We are free to
# work on it.
self._sync__lock.release()
# Phase 1: pack the old records
ofile=open(self.__name__,'r+b')
import Transaction
stop=Transaction.time2id(t)
opos, otpos, maxoid = read_index(file, index, vindex, tindex, stop)
read=ofile.read
seek=ofile.seek
pfile=open(self.__name__+'.pk','w+b')
write=pfile.write
unpack=struct.unpack
rootl=['\0'*8]
rootd={}
inroot=rootd.has_key
while rootl:
oid=rootl[-1]
del rootl[-1]
if inroot[oid]: continue
pos=index[oid]
seek(pos)
h=read(22)
doid,prev,tloc,vlen,plen = unpack(">8siiHi", h)
if doid != oid: raise CorruptedDataError, h
if vlen:
pnv=read(4)
return _loadBack(file, oid, read(4))
if plen: return read(plen)
return _loadBack(file, oid, pnv)
for oid in rootd.keys(): del index[oid]
del index['\0'*8]
unreachable=index.has_key
seek(4)
pos=4
tpos=0
while 1:
# Read the transaction record
h=read(21)
if not h: break
tid, prev, tl, status, ul, dl = unpack(">8siicHH", h)
if tid >= stop: break
tpos=pos
tend=tpos+tl
if status=='u':
# Undone transaction, skip it
pos=tpos+tl+4
seek(pos)
continue
user=read(ul)
desc=read(dl)
pos=tpos+21+ul+dl
while pos < tend:
# Read the data records for this transaction
h=read(22)
oid,prev,tloc,vlen,plen = unpack(">8siiHi", h)
dlen=22+(plen or 4)+vlen
if vlen:
dlen=vlen+8
seek(8,1)
version=read(vlen)
vindex[version]=pos
pos=pos+dlen
if pos != tend:
raise CorruptedTransactionError, lastp
# Read the (intentionally redundant) transaction length
h=read(4)
if len(h) != 4: raise CorruptedTransactionError, h
if unpack(">i",h)[0] != tl:
raise CorruptedTransactionError, h
pos=pos+4
for oid, p in tindex:
index[oid]=p # Record the position
del tindex[:]
# Phase 2: copy the new records, adjusting all of the
# location pointers. We'll get the commit lock for this part.
finally: self._r()
def store(self, oid, data, version, transaction): def store(self, oid, serial, data, version, transaction):
if transaction is not self._transaction: if transaction is not self._transaction:
raise POSException.StorageTransactionError(self, transaction) raise POSException.StorageTransactionError(self, transaction)
...@@ -386,13 +365,14 @@ class FileStorage: ...@@ -386,13 +365,14 @@ class FileStorage:
if old: if old:
file=self._file file=self._file
file.seek(old) file.seek(old)
h=file.read(22) h=file.read(42)
doid,prev,tloc,vlen,plen = unpack(">8siiHi", h) doid,oserial,sprev,stloc,vlen,splen = unpack(">8s8s8s8sH8s", h)
if doid != oid: raise CorruptedDataError, h if doid != oid: raise CorruptedDataError, h
if serial != oserial: raise POSException.ConflictError
if vlen: if vlen:
pnv=read(4) pnv=read(8) # non-version data pointer
if (len(version) != vlen or if (len(version) != vlen or
(read(4) # skip past version link (read(8) # skip past version link
and version != read(vlen)) and version != read(vlen))
): ):
raise POSException.VersionLockError, oid raise POSException.VersionLockError, oid
...@@ -401,16 +381,24 @@ class FileStorage: ...@@ -401,16 +381,24 @@ class FileStorage:
write=tfile.write write=tfile.write
self._tappend(oid, tfile.tell()) self._tappend(oid, tfile.tell())
pos=self._pos pos=self._pos
write(pack(">8siiHi",oid,old,pos,len(version),len(data))) serial=self._serial
write(pack(">8s8s8s8sH8s",
oid,serial,p64(old),p64(pos),
len(version),p64(len(data))
)
)
if version: if version:
if pnv: write(pnv) if pnv: write(pnv)
else: write(pack(">i",old)) else: write(p64(old))
# Link to last record for this version: # Link to last record for this version:
vindex=self._vindex vindex=self._vindex
write(pack(">i",vindex[version])) write(p64(vindex[version]))
vindex[version]=pos vindex[version]=pos
write(version) write(version)
write(data) write(data)
return serial
finally: self._r() finally: self._r()
def supportsUndo(self): return 0 # for now def supportsUndo(self): return 0 # for now
...@@ -435,6 +423,10 @@ class FileStorage: ...@@ -435,6 +423,10 @@ class FileStorage:
self._transaction=transaction self._transaction=transaction
del self._tindex[:] # Just to be sure! del self._tindex[:] # Just to be sure!
self._tfile.seek(0) self._tfile.seek(0)
t=time.time()
t=apply(TimeStamp,(time.gmtime(t)[:5]+(t%60,)))
self._ts=t=t.laterThan(self._ts)
self._serial=`t`
finally: self._r() finally: self._r()
def tpc_finish(self, transaction, f=None): def tpc_finish(self, transaction, f=None):
...@@ -445,33 +437,35 @@ class FileStorage: ...@@ -445,33 +437,35 @@ class FileStorage:
file=self._file file=self._file
write=file.write write=file.write
tfile=self._tfile tfile=self._tfile
read=tfile.read
dlen=tfile.tell() dlen=tfile.tell()
tfile.seek(0) tfile.seek(0)
id=transaction.id id=self._serial
user=transaction.user user=transaction.user
desc=transaction.description desc=transaction.description
tlen=21+len(user)+len(desc) ext=transaction._extension
if ext: ext=dumps(ext,1)
else: ext=""
tlen=33+len(user)+len(desc)+len(ext)
pos=self._pos pos=self._pos
file.seek(pos) file.seek(pos)
stpos=p64(self._tpos)
tl=tlen+dlen tl=tlen+dlen
write(pack(">8siicHH", stl=p64(tl)
id, self._tpos, tl, ' ', len(user), len(desc))) write(pack(
write(user) ">8s" "8s" "8s" "c" "H" "H" "I"
write(desc) ,id, stpos, stl, ' ', len(user), len(desc), len(ext),
))
if user: write(user)
if desc: write(desc)
if ext: write(ext)
assert dlen >= 0 cp(tfile, file, dlen)
while dlen > 0:
d=read(min(dlen,8192))
write(d)
d=len(d)
assert dlen >= d
dlen=dlen-d
write(pack(">i", tl)) write(stl)
file.flush() file.flush()
self._tpos=pos self._tpos=pos
self._pos=pos+tl+4 self._pos=pos+tl+8
index=self._index index=self._index
dpos=pos+tlen dpos=pos+tlen
...@@ -483,29 +477,19 @@ class FileStorage: ...@@ -483,29 +477,19 @@ class FileStorage:
finally: self._r() finally: self._r()
def undo(self, transaction_id): def undo(self, transaction_id):
# TBD
pass pass
def undoLog(self, version, first, last, path): def undoLog(self, version, first, last, path):
# TBD
return [] return []
def versionEmpty(self, version): def versionEmpty(self, version):
self._a() return not self._vindex.has_key(version)
try:
pos=self._index[oid]
file=self._file
file.seek(pos)
doid,prev,tloc,vlen = unpack(">8siiH", file.read(18))
if doid != oid: raise CorruptedDataError, h
if not vlen or vlen != len(version): return 1
seek(4,1)
return read(vlen) != version
finally: self._r()
packed_version='FS10' def read_index(file, name, index, vindex, tindex, stop='\377'*8,
def read_index(file, index, vindex, tindex, stop='\377'*8): log=lambda s: None):
indexpos=index.get indexpos=index.get
vndexpos=vindex.get vndexpos=vindex.get
tappend=tindex.append tappend=tindex.append
...@@ -524,19 +508,62 @@ def read_index(file, index, vindex, tindex, stop='\377'*8): ...@@ -524,19 +508,62 @@ def read_index(file, index, vindex, tindex, stop='\377'*8):
pos=4 pos=4
unpack=struct.unpack unpack=struct.unpack
tpos=0 tpos=0
maxoid='\0\0\0\0\0\0\0\0' maxoid=ltid=z64
tid='\0'*7+'\1'
while 1: while 1:
# Read the transaction record # Read the transaction record
h=read(21) h=read(33)
if not h: break if not h: break
if len(h) != 21: raise CorruptedTransactionError, h if len(h) != 33:
tid, prev, tl, status, ul, dl = unpack(">8siicHH",h) warn(log, '%s truncated at %s', name, pos)
if (prev != tpos seek(pos)
or status not in ' up' or ul > tl or dl > tl file.truncate()
or tl > file_size or tl+pos >= file_size): break
raise CorruptedTransactionRecordError, h
tid, sprev, stl, status, ul, dl, el = unpack(">8s8s8scHHi",h)
if el < 0: el=t32-el
if tid <= ltid:
warn(log, "%s time-stamp reduction at %s", name, pos)
ltid=tid
prev=u64(sprev)
tl=u64(stl)
if tl+pos+8 > file_size:
# Hm, the data were truncated. They may also be corrupted,
# in which case, we don't want to totally lose the data.
warn(log, "%s truncated, possibly due to damaged records at %s",
name, pos)
try:
i=0
while 1:
if os.path.exists('%s.tr%s' % (name, i)):
i=i+1
else:
o=open('%s.tr%s' % (name, i),'wb')
seek(pos)
cp(file, o, file_size-pos)
o.close()
break
except:
error(log, "couldn\'t write truncated data for %s", name)
raise POSException.StorageSystemError, (
"Couldn't save truncated data")
seek(pos)
file.truncate()
break
if status not in ' up':
warn(log,'%s has invalid status, %s, at %s', name, status, pos)
if prev != tpos or ul > tl or dl > tl or el > tl:
panic(log,'%s has invalid transaction header at %s', name, pos)
if tid >= stop: break if tid >= stop: break
tpos=pos tpos=pos
tend=tpos+tl tend=tpos+tl
...@@ -544,45 +571,56 @@ def read_index(file, index, vindex, tindex, stop='\377'*8): ...@@ -544,45 +571,56 @@ def read_index(file, index, vindex, tindex, stop='\377'*8):
# Undone transaction, skip it # Undone transaction, skip it
pos=tpos+tl pos=tpos+tl
seek(pos) seek(pos)
h=read(4) h=read(8)
if len(h) != 4: raise CorruptedTransactionError, h if h != stl:
if unpack(">i",h)[0] != tl: panic(log, '%s has inconsistent transaction length at %s',
raise CorruptedTransactionError, h name, pos)
pos=pos+4 pos=pos+8
continue continue
pos=tpos+21+ul+dl pos=tpos+33+ul+dl
while pos < tend: while pos < tend:
# Read the data records for this transaction # Read the data records for this transaction
seek(pos) seek(pos)
h=read(22) h=read(42)
oid,prev,tloc,vlen,plen = unpack(">8siiHi", h) oid,serial,sprev,stloc,vlen,splen = unpack(">8s8s8s8sH8s", h)
dlen=22+(plen or 4)+vlen prev=u64(sprev)
if pos+dlen > tend or tloc != tpos: tloc=u64(stloc)
raise CorruptedDataError, h plen=u64(splen)
if indexpos(oid,0) != prev:
raise CorruptedDataError, h dlen=42+(plen or 8)+vlen
tappend((oid,pos)) tappend((oid,pos))
if vlen: if vlen:
dlen=vlen+8 dlen=vlen+16
seek(8,1) seek(8,1)
pv=u64(read(8))
version=read(vlen) version=read(vlen)
if vndexpos(version, 0) != pv:
panic(log,"%s incorrect previous version pointer at %s",
name, pos)
vindex[version]=pos vindex[version]=pos
if pos+dlen > tend or tloc != tpos:
panic(log,"%s data record exceeds transaction record at %s",
name, pos)
if indexpos(oid,0) != prev:
panic(log,"%s incorrect previous pointer at %s",
name, pos)
pos=pos+dlen pos=pos+dlen
if pos != tend: if pos != tend:
raise CorruptedTransactionError, lastp panic(log,"%s data records don't add up at %s",name,tpos)
# Read the (intentionally redundant) transaction length # Read the (intentionally redundant) transaction length
seek(pos) seek(pos)
h=read(4) h=read(8)
if len(h) != 4: raise CorruptedTransactionError, h if h != stl:
if unpack(">i",h)[0] != tl: panic(log, "%s redundant transaction length check failed at %s",
raise CorruptedTransactionError, h name, pos)
pos=pos+4 pos=pos+8
for oid, p in tindex: for oid, p in tindex:
maxoid=max(maxoid,oid) maxoid=max(maxoid,oid)
...@@ -590,7 +628,7 @@ def read_index(file, index, vindex, tindex, stop='\377'*8): ...@@ -590,7 +628,7 @@ def read_index(file, index, vindex, tindex, stop='\377'*8):
del tindex[:] del tindex[:]
return pos, tpos, maxoid return pos, tpos, maxoid, ltid
def _loadBack(file, oid, back): def _loadBack(file, oid, back):
...@@ -598,8 +636,12 @@ def _loadBack(file, oid, back): ...@@ -598,8 +636,12 @@ def _loadBack(file, oid, back):
old=unpack(">i",back)[0] old=unpack(">i",back)[0]
if not old: raise KeyError, oid if not old: raise KeyError, oid
file.seek(old) file.seek(old)
h=file.read(22) h=file.read(42)
doid,prev,tloc,vlen,plen = unpack(">8siiHi", h) doid,serial,prev,tloc,vlen,plen = unpack(">8s8s8s8sH8s", h)
if doid != oid or vlen: raise CorruptedDataError, h if doid != oid or vlen:
if plen: return read(plen) panic(lambda x: None,
"%s version record back pointer points to "
"invalid record as %s", name, back)
if plen: return read(plen), serial
back=read(4) # We got a back pointer! back=read(4) # We got a back pointer!
/* $Id: IIBTree.c,v 1.4 1997/10/10 18:23:37 jim Exp $ */
#define INTKEY int
#define INTVAL int
static char *needed_to_make_release_happy="$Id: IIBTree.c,v 1.4 1997/10/10 18:23:37 jim Exp $";
#include "BTree.c"
/* $Id: IOBTree.c,v 1.4 1997/10/10 18:24:11 jim Exp $ */
static char *needed_to_make_release_happy=
"$Id: IOBTree.c,v 1.4 1997/10/10 18:24:11 jim Exp $";
#define INTKEY int
#include "BTree.c"
/* $Id: OIBTree.c,v 1.4 1997/10/10 18:24:35 jim Exp $ */
static char *needed_to_make_release_happy=
"$Id: OIBTree.c,v 1.4 1997/10/10 18:24:35 jim Exp $";
#define INTVAL int
#include "BTree.c"
##############################################################################
#
# Copyright (c) 1996-1998, Digital Creations, Fredericksburg, VA, USA.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# o Redistributions of source code must retain the above copyright
# notice, this list of conditions, and the disclaimer that follows.
#
# o Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions, and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# o Neither the name of Digital Creations nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS IS*
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL
# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
#
#
# If you have questions regarding this software, contact:
#
# Digital Creations, L.C.
# 910 Princess Ann Street
# Fredericksburge, Virginia 22401
#
# info@digicool.com
#
# (540) 371-6909
#
##############################################################################
'''Python implementation of a persistent base types
$Id: Persistence.py,v 1.17 1998/11/11 02:00:56 jim Exp $'''
__version__='$Revision: 1.17 $'[11:-2]
_marker=[]
class Persistent:
"""\
Persistent object support mix-in class
When a persistent object is loaded from a database, the object's
data is not immediately loaded. Loading of the objects data is
defered until an attempt is made to access an attribute of the
object.
The object also tries to keep track of whether it has changed. It
is easy for this to be done incorrectly. For this reason, methods
of subclasses that change state other than by setting attributes
should: 'self.__changed__(1)' to flag instances as changed.
You must not override the object's '__getattr__' and '__setattr__'
methods. If you override the objects '__getstate__' method, then
you must be careful not to include any attributes with names
starting with '_p_' or '_v_' in the state.
"""
_p_oid=None # A Persistent object-id, unique within a jar
_p_changed=0 # The object state: None=ghost, 0=normal, 1=changed
_p_jar=None # The last jar that this object was stored in.
def _p_deactivate(self):
d=self.__dict__
oid=d['_p_oid']
jar=d['_p_jar']
d.clear()
d['_p_oid']=oid
d['_p_jar']=jar
d['_p_changed']=None
def __getattr__(self,key):
'Get an item'
if self._p_changed is None and key[:3] != '_p_':
self._p_jar.setstate(self)
if self.__dict__.has_key(key): return self.__dict__[key]
raise AttributeError, key
def __setattr__(self,key,value):
' '
changed=self._p_changed
if changed:
self.__dict__[key]=value
return
k=key[:3]
if k=='_p_' or k=='_v_':
if key=='_p_changed':
if changed == value: return
if value:
if changed: return
try:
if self._p_jar and self._p_oid:
get_transaction().register(self)
except: pass
elif value is None:
if self._p_jar and self._p_oid:
return self._p_deactivate()
return
value=not not value
self.__dict__[key]=value
return
jar=self._p_jar
if jar is None:
self.__dict__[key]=value
return
d=self.__dict__
if changed is None:
d['_p_changed']=1
jar.setstate(self)
d[key]=value
try:
get_transaction().register(self)
d['_p_changed']=1
except: pass
def __changed__(self,v=_marker):
if v is _marker: return not not self._p_changed
self._p_changed = not not v
def __getstate__(self):
# First, update my state, if necessary:
if self._p_changed is None: self._p_jar.setstate(self)
state={}
d=self.__dict__
for k,v in d.items():
if k[:3] != '_p_' and k[:3] != '_v_': state[k]=v
return state
def __setstate__(self,state):
self.__dict__.update(state)
def __repr__(self):
' '
return '<%s instance at %s>' % (self.__class__.__name__,
hex(id(self)))
...@@ -48,12 +48,12 @@ ...@@ -48,12 +48,12 @@
__doc__='''Python implementation of persistent base types __doc__='''Python implementation of persistent base types
$Id: PersistentMapping.py,v 1.3 1998/11/11 02:00:56 jim Exp $''' $Id: PersistentMapping.py,v 1.4 1999/05/07 01:03:03 jim Exp $'''
__version__='$Revision: 1.3 $'[11:-2] __version__='$Revision: 1.4 $'[11:-2]
import Persistence import Persistence
class PM(Persistence.Persistent): class PersistentMapping(Persistence.Persistent):
"""A persistent wrapper for mapping objects. """A persistent wrapper for mapping objects.
This class allows wrapping of mapping objects so that This class allows wrapping of mapping objects so that
...@@ -113,4 +113,3 @@ class PM(Persistence.Persistent): ...@@ -113,4 +113,3 @@ class PM(Persistence.Persistent):
def values(self): def values(self):
return map(lambda k, d=self: d[k], self.keys()) return map(lambda k, d=self: d[k], self.keys())
PersistentMapping=PM
##############################################################################
#
# Copyright (c) 1996-1998, Digital Creations, Fredericksburg, VA, USA.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# o Redistributions of source code must retain the above copyright
# notice, this list of conditions, and the disclaimer that follows.
#
# o Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions, and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# o Neither the name of Digital Creations nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS IS*
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL
# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
#
#
# If you have questions regarding this software, contact:
#
# Digital Creations, L.C.
# 910 Princess Ann Street
# Fredericksburge, Virginia 22401
#
# info@digicool.com
#
# (540) 371-6909
#
##############################################################################
__doc__='''PickleJar Object Cache
$Id: PickleCache.py,v 1.5 1998/11/11 02:00:56 jim Exp $'''
__version__='$Revision: 1.5 $'[11:-2]
from sys import getrefcount
class PickleCache:
def __init__(self, cache_size, cache_age=1000):
if cache_size < 1: cache_size=1
self.cache_size=cache_size
self.data, self.cache_ids, self.cache_location ={}, [], 0
for a in 'keys', 'items', 'values', 'has_key':
setattr(self,a,getattr(self.data,a))
def __getitem__(self, key):
v=self.data[key]
self.incrgc()
return v
def incrgc(self):
# Do cache GC
cache=self.data
n=min(len(cache)/self.cache_size,10)
if n:
l=self.cache_location
ids=self.cache_ids
while n:
if not l:
ids=self.cache_ids=cache.keys()
l=len(ids)
l=l-1
n=n-1
id=ids[l]
if getrefcount(cache[id]) <= 2:
del cache[id]
self.cache_location=l
def __setitem__(self, key, v):
self.data[key]=v
self.incrgc()
def __delitem__(self, key):
del self.data[key]
self.incrgc()
def __len__(self): return len(self.data)
def values(self): return self.data.values()
def full_sweep(self):
cache=self.data
for id in cache.keys():
if getrefcount(cache[id]) <= 2: del cache[id]
def minimize(self):
cache=self.data
keys=cache.keys()
rc=getrefcount
last=None
l=len(cache)
while l != last:
for id in keys():
if rc(cache[id]) <= 2: del cache[id]
cache[id]._p_deactivate()
l=len(cache)
*shared* *shared*
cPersistence cPersistence.c -I../ExtensionClass -I../python cPersistence cPersistence.c -I../../Components/ExtensionClass
cPickleCache cPickleCache.c -I../ExtensionClass -I../python cPickleCache cPickleCache.c -I../../Components/ExtensionClass
cPickleJar ./cPickleJar.c -I../ExtensionClass
iTree ./iTree.c -I../ExtensionClass
intSet ./intSet.c -I/projects/_/ExtensionClass -I/projects/_/cPersistence -I/projects/_/python
BTree ./BTree.c -I/projects/_/ExtensionClass -I/projects/_/cPersistence -I/projects/_/python
IIBTree ./IIBTree.c -I/projects/_/ExtensionClass -I/projects/_/cPersistence -I/projects/_/python
IOBTree ./IOBTree.c -I/projects/_/ExtensionClass -I/projects/_/cPersistence -I/projects/_/python
OIBTree ./OIBTree.c -I/projects/_/ExtensionClass -I/projects/_/cPersistence -I/projects/_/python
#cPickleJar cPickleJar.c -I../../Components/ExtensionClass
#iTree iTree.c -I../../Components/ExtensionClass
...@@ -47,29 +47,26 @@ ...@@ -47,29 +47,26 @@
############################################################################## ##############################################################################
"""Transaction management """Transaction management
$Id: Transaction.py,v 1.3 1998/11/11 02:00:56 jim Exp $""" $Id: Transaction.py,v 1.4 1999/05/07 01:03:03 jim Exp $"""
__version__='$Revision: 1.3 $'[11:-2] __version__='$Revision: 1.4 $'[11:-2]
import time, sys, struct import time, sys, struct
from struct import pack from struct import pack
from string import split, strip, join from string import split, strip, join
ConflictError="" from POSException import ConflictError
class Transaction: class Transaction:
'Simple transaction objects for single-threaded applications.' 'Simple transaction objects for single-threaded applications.'
user='' user=''
description='' description=''
_connections=None _connections=None
_extension=None
def __init__(self, def __init__(self,
time=time.time, pack=struct.pack, gmtime=time.gmtime): time=time.time, pack=struct.pack, gmtime=time.gmtime):
self._objects=[] self._objects=[]
self._append=self._objects.append self._append=self._objects.append
self.time=now=time()
y,mo,d,h,m=gmtime(now)[:5]
s=int((now%60)*1000000)
self.id=pack("<II", (((y*12+mo)*31+d)*24+h)*60+m, s)
self._note=self._user=self._description='' self._note=self._user=self._description=''
if self._connections: if self._connections:
for c in self._connections.values(): c.close() for c in self._connections.values(): c.close()
...@@ -136,15 +133,20 @@ class Transaction: ...@@ -136,15 +133,20 @@ class Transaction:
'Register the given object for transaction control.' 'Register the given object for transaction control.'
self._append(object) self._append(object)
def remark(self, text): def note(self, text):
if self.description: if self.description:
self.description = "%s\n\n%s" % (self.description, strip(text)) self.description = "%s\n\n%s" % (self.description, strip(text))
else: else:
self.description = strip(text) self.description = strip(text)
def setUser(self, user_name, path='/'): def setUser(self, user_name, path='/'):
self.user="%s %s" % (path, user_name) self.user="%s %s" % (path, user_name)
def setExtendedInfo(self, name, value):
ext=self._extension
if ext is None:
ext=self._extension={}
ext[name]=value
############################################################################ ############################################################################
...@@ -175,9 +177,4 @@ del _t ...@@ -175,9 +177,4 @@ del _t
import __main__ import __main__
__main__.__builtins__.get_transaction=get_transaction __main__.__builtins__.get_transaction=get_transaction
def time2id(now, gmtime=time.gmtime, pack=struct.pack):
y,m,d,h,m=gmtime(now)[:5]
s=int((now%60)*1000000)
return pack("<II", ((y*12+m)*31+d)*24, s)
...@@ -50,9 +50,9 @@ ...@@ -50,9 +50,9 @@
This module provides a wrapper that causes a database connection to be created This module provides a wrapper that causes a database connection to be created
and used when bobo publishes a bobo_application object. and used when bobo publishes a bobo_application object.
""" """
__version__='$Revision: 1.1 $'[11:-2] __version__='$Revision: 1.2 $'[11:-2]
class BoboApplication: class ZApplicationWrapper:
def __init__(self, db, name, klass= None, klass_args= (), def __init__(self, db, name, klass= None, klass_args= (),
version_cookie_name=None): version_cookie_name=None):
...@@ -94,8 +94,14 @@ class BoboApplication: ...@@ -94,8 +94,14 @@ class BoboApplication:
return v return v
__call__=__bobo_traverse__ # A convenience for command-line use
def __call__(self, connection=None):
db, aname, version_support = self._stuff
if connection is None:
connection=db.open()
return connection.root()[aname]
class Cleanup: pass class Cleanup: pass
......
...@@ -45,7 +45,26 @@ ...@@ -45,7 +45,26 @@
# (540) 371-6909 # (540) 371-6909
# #
############################################################################## ##############################################################################
import sys, ExtensionClass, TimeStamp, cPersistence, Persistence
# This is lame. Don't look. :(
sys.modules['cPersistence']=cPersistence
Persistent=cPersistence.Persistent
del cPersistence
# Install Persistent and PersistentMapping in Persistence
if not hasattr(Persistence, 'Persistent'):
Persistence.Persistent=Persistent
Persistent.__module__='Persistence'
if not hasattr(Persistence, 'PersistentMapping'):
from PersistentMapping import PersistentMapping
Persistence.PersistentMapping=PersistentMapping
PersistentMapping.__module__='Persistence'
del PersistentMapping
from DB import DB from DB import DB
from Persistence import Persistent
from POSException import *
import Transaction
del Transaction
/*********************************************************************** /***********************************************************************
$Id: cPersistence.c,v 1.25 1998/11/11 02:00:56 jim Exp $ $Id: cPersistence.c,v 1.26 1999/05/07 01:03:03 jim Exp $
C Persistence Module C Persistence Module
...@@ -12,16 +12,19 @@ ...@@ -12,16 +12,19 @@
*****************************************************************************/ *****************************************************************************/
static char *what_string = "$Id: cPersistence.c,v 1.25 1998/11/11 02:00:56 jim Exp $"; static char *what_string = "$Id: cPersistence.c,v 1.26 1999/05/07 01:03:03 jim Exp $";
#include <time.h> #include <string.h>
#include "cPersistence.h" #include "cPersistence.h"
#define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;} #define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;}
#define UNLESS(E) if(!(E)) #define UNLESS(E) if(!(E))
#define UNLESS_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V) #define UNLESS_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V)
#define OBJECT(V) ((PyObject*)(V))
static PyObject *py_keys, *py_setstate, *py___dict__; static PyObject *py_keys, *py_setstate, *py___dict__, *py_timeTime;
static PyObject *TimeStamp;
#ifdef DEBUG_LOG #ifdef DEBUG_LOG
static PyObject *debug_log=0; static PyObject *debug_log=0;
...@@ -35,8 +38,8 @@ call_debug(char *event, cPersistentObject *self) ...@@ -35,8 +38,8 @@ call_debug(char *event, cPersistentObject *self)
/* /*
printf("%s %p\n",event,self->ob_type->tp_name); printf("%s %p\n",event,self->ob_type->tp_name);
*/ */
r=PyObject_CallFunction(debug_log,"s(ss#i)",event, r=PyObject_CallFunction(debug_log,"s(sOi)",event,
self->ob_type->tp_name, self->oid, 8, self->ob_type->tp_name, self->oid,
self->state); self->state);
Py_XDECREF(r); Py_XDECREF(r);
} }
...@@ -48,6 +51,7 @@ init_strings() ...@@ -48,6 +51,7 @@ init_strings()
#define INIT_STRING(S) py_ ## S = PyString_FromString(#S) #define INIT_STRING(S) py_ ## S = PyString_FromString(#S)
INIT_STRING(keys); INIT_STRING(keys);
INIT_STRING(setstate); INIT_STRING(setstate);
INIT_STRING(timeTime);
INIT_STRING(__dict__); INIT_STRING(__dict__);
#undef INIT_STRING #undef INIT_STRING
} }
...@@ -201,14 +205,16 @@ static PyObject * ...@@ -201,14 +205,16 @@ static PyObject *
Per___changed__(cPersistentObject *self, PyObject *args) Per___changed__(cPersistentObject *self, PyObject *args)
{ {
PyObject *v=0; PyObject *v=0;
if (args && ! PyArg_ParseTuple(args, "|O",&v)) return NULL; if (args && ! PyArg_ParseTuple(args, "|O",&v)) return NULL;
if (v && ! PyObject_IsTrue(v)) if (! v) return PyObject_GetAttrString(OBJECT(self), "_p_changed");
if (PyObject_IsTrue(v))
{ {
PyErr_SetString(PyExc_TypeError, if (changed(self) < 0) return NULL;
"Only true arguments are allowed.");
return NULL;
} }
if (changed(self) < 0) return NULL; else if (self->state >= 0) self->state=cPersistent_UPTODATE_STATE;
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
...@@ -368,35 +374,30 @@ Per_dealloc(self) ...@@ -368,35 +374,30 @@ Per_dealloc(self)
PyMem_DEL(self); PyMem_DEL(self);
} }
static PyObject *
orNothing(PyObject *v)
{
if (! v) v=Py_None;
Py_INCREF(v);
return v;
}
static PyObject * static PyObject *
Per_getattr(cPersistentObject *self, PyObject *oname, char *name, Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
PyObject *(*getattrf)(PyObject *, PyObject*)) PyObject *(*getattrf)(PyObject *, PyObject*))
{ {
char *n=name; char *n=name;
if(*n++=='_') if(n && *n++=='_')
if(*n++=='p' && *n++=='_') if(*n++=='p' && *n++=='_')
{ {
switch(*n++) switch(*n++)
{ {
case 'o': case 'o':
if(*n++=='i' && *n++=='d' && ! *n) if(*n++=='i' && *n++=='d' && ! *n) return orNothing(self->oid);
return PyString_FromStringAndSize(self->oid, 8);
break; break;
case 'j': case 'j':
if(*n++=='a' && *n++=='r' && ! *n) if(*n++=='a' && *n++=='r' && ! *n) return orNothing(self->jar);
{
if(self->jar)
{
Py_INCREF(self->jar);
return self->jar;
}
else
{
Py_INCREF(Py_None);
return Py_None;
}
}
break; break;
case 'c': case 'c':
if(strcmp(n,"hanged")==0) if(strcmp(n,"hanged")==0)
...@@ -410,11 +411,32 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name, ...@@ -410,11 +411,32 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
cPersistent_CHANGED_STATE); cPersistent_CHANGED_STATE);
} }
break; break;
case 's':
if(strcmp(n,"erial")==0)
return PyString_FromStringAndSize(self->serial, 8);
break;
case 'm':
if(strcmp(n,"time")==0)
{
UPDATE_STATE_IF_NECESSARY(self, NULL);
self->atime=((long)(time(NULL)/3))%65536;
oname=PyString_FromStringAndSize(self->serial, 8);
if (! oname) return oname;
ASSIGN(oname, PyObject_CallFunction(TimeStamp, "O", oname));
if (! oname) return oname;
ASSIGN(oname, PyObject_GetAttr(oname, py_timeTime));
if (! oname) return oname;
ASSIGN(oname, PyObject_CallObject(oname, NULL));
return oname;
}
break;
} }
return getattrf((PyObject *)self, oname); return getattrf((PyObject *)self, oname);
} }
if(! (*name++=='_' && *name++=='_' && if(! (name && *name++=='_' && *name++=='_' &&
(strcmp(name,"dict__")==0 || strcmp(name,"class__")==0 (strcmp(name,"dict__")==0 || strcmp(name,"class__")==0
|| strcmp(name, "of__")==0))) || strcmp(name, "of__")==0)))
{ {
...@@ -429,10 +451,11 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name, ...@@ -429,10 +451,11 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
static PyObject* static PyObject*
Per_getattro(cPersistentObject *self, PyObject *name) Per_getattro(cPersistentObject *self, PyObject *name)
{ {
char *s; char *s=NULL;
UNLESS(s=PyString_AsString(name)) return NULL; if (PyString_Check(name))
return Per_getattr(self,name,s, PyExtensionClassCAPI->getattro); UNLESS(s=PyString_AsString(name)) return NULL;
return Per_getattr(self, name, s, PyExtensionClassCAPI->getattro);
} }
static int static int
...@@ -456,27 +479,36 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v, ...@@ -456,27 +479,36 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v,
{ {
if(name[3]=='o' && name[4]=='i' && name[5]=='d' && ! name[6]) if(name[3]=='o' && name[4]=='i' && name[5]=='d' && ! name[6])
{ {
if (! v) return bad_delattr(); Py_XINCREF(v);
if (PyString_Check(v) && PyString_GET_SIZE(v)==8) ASSIGN(self->oid, v);
memcpy(self->oid, PyString_AS_STRING(v), 8);
else
{
PyErr_SetString(PyExc_AttributeError,
"_p_oid must be an 8-character string");
return -1;
}
return 0; return 0;
} }
if(name[3]=='j' && name[4]=='a' && name[5]=='r' && ! name[6]) if(name[3]=='j' && name[4]=='a' && name[5]=='r' && ! name[6])
{ {
Py_XINCREF(v);
ASSIGN(self->jar, v); ASSIGN(self->jar, v);
Py_XINCREF(self->jar);
return 0; return 0;
} }
if(strcmp(name+3,"changed")==0) if(name[3]=='s' && strcmp(name+4,"erial")==0)
{ {
if (! v) return bad_delattr(); if (v)
if (v==Py_None) {
if (PyString_Check(v) && PyString_Size(v)==8)
memcpy(self->serial, PyString_AS_STRING(v), 8);
else
{
PyErr_SetString(PyExc_ValueError,
"_p_serial must be an 8-character string");
return -1;
}
}
else
memset(self->serial, 0, 8);
return 0;
}
if(name[3]=='c' && strcmp(name+4,"hanged")==0)
{
if (! v || v==Py_None)
{ {
if (Per__p_deactivate(self, NULL)) Py_DECREF(Py_None); if (Per__p_deactivate(self, NULL)) Py_DECREF(Py_None);
return 0; return 0;
...@@ -585,8 +617,15 @@ void ...@@ -585,8 +617,15 @@ void
initcPersistence() initcPersistence()
{ {
PyObject *m, *d; PyObject *m, *d;
char *rev="$Revision: 1.25 $"; char *rev="$Revision: 1.26 $";
TimeStamp=PyString_FromString("TimeStamp");
if (! TimeStamp) return;
ASSIGN(TimeStamp, PyImport_Import(TimeStamp));
if (! TimeStamp) return;
ASSIGN(TimeStamp, PyObject_GetAttrString(TimeStamp, "TimeStamp"));
if (! TimeStamp) return;
m = Py_InitModule4("cPersistence", cP_methods, m = Py_InitModule4("cPersistence", cP_methods,
"", "",
(PyObject*)NULL,PYTHON_API_VERSION); (PyObject*)NULL,PYTHON_API_VERSION);
......
/* /*
$Id: cPersistence.h,v 1.10 1998/11/11 02:00:56 jim Exp $ $Id: cPersistence.h,v 1.11 1999/05/07 01:03:03 jim Exp $
Definitions to facilitate making cPersistent subclasses in C. Definitions to facilitate making cPersistent subclasses in C.
...@@ -15,9 +15,11 @@ ...@@ -15,9 +15,11 @@
#define cPersistent_HEAD PyObject_HEAD \ #define cPersistent_HEAD PyObject_HEAD \
PyObject *jar; \ PyObject *jar; \
char oid[8]; \ PyObject *oid; \
char serial[8]; \
unsigned short atime; \ unsigned short atime; \
signed char state; \ signed char state; \
unsigned char reserved; \
#define cPersistent_GHOST_STATE -1 #define cPersistent_GHOST_STATE -1
#define cPersistent_UPTODATE_STATE 0 #define cPersistent_UPTODATE_STATE 0
...@@ -43,6 +45,8 @@ typedef struct { ...@@ -43,6 +45,8 @@ typedef struct {
static cPersistenceCAPIstruct *cPersistenceCAPI; static cPersistenceCAPIstruct *cPersistenceCAPI;
#define cPersistanceModuleName "cPersistence"
#define PER_USE_OR_RETURN(O,R) { \ #define PER_USE_OR_RETURN(O,R) { \
if ((O)->state==cPersistent_GHOST_STATE && \ if ((O)->state==cPersistent_GHOST_STATE && \
cPersistenceCAPI->setstate((PyObject*)(O)) < 0) \ cPersistenceCAPI->setstate((PyObject*)(O)) < 0) \
...@@ -57,6 +61,10 @@ static cPersistenceCAPIstruct *cPersistenceCAPI; ...@@ -57,6 +61,10 @@ static cPersistenceCAPIstruct *cPersistenceCAPI;
((O)->state==cPersistent_STICKY_STATE && \ ((O)->state==cPersistent_STICKY_STATE && \
((O)->state=cPersistent_UPTODATE_STATE)) ((O)->state=cPersistent_UPTODATE_STATE))
#define PER_PREVENT_DEACTIVATION(O) \
((O)->state==cPersistent_UPTODATE_STATE && \
((O)->state=cPersistent_STICKY_STATE))
#define PER_DEL(O) Py_XDECREF((O)->jar) #define PER_DEL(O) Py_XDECREF((O)->jar)
#endif #endif
......
static char *what_string = "$Id: cPickleCache.c,v 1.16 1998/11/11 02:00:56 jim Exp $"; static char *what_string = "$Id: cPickleCache.c,v 1.17 1999/05/07 01:03:03 jim Exp $";
#define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;} #define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;}
#define UNLESS(E) if(!(E)) #define UNLESS(E) if(!(E))
...@@ -12,12 +12,10 @@ static char *what_string = "$Id: cPickleCache.c,v 1.16 1998/11/11 02:00:56 jim E ...@@ -12,12 +12,10 @@ static char *what_string = "$Id: cPickleCache.c,v 1.16 1998/11/11 02:00:56 jim E
static PyObject *py_reload, *py__p_jar, *py__p_deactivate; static PyObject *py_reload, *py__p_jar, *py__p_deactivate;
/* Declarations for objects of type cCache */
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
PyObject *data; PyObject *data;
PyObject *jar;
int position; int position;
int cache_size; int cache_size;
int cache_age; int cache_age;
...@@ -65,8 +63,11 @@ gc_item(ccobject *self, PyObject *key, PyObject *v, long now, int dt) ...@@ -65,8 +63,11 @@ gc_item(ccobject *self, PyObject *key, PyObject *v, long now, int dt)
return PyDict_DelItem(self->data, key); return PyDict_DelItem(self->data, key);
} }
if (dt && v->ob_type->tp_basicsize >= sizeof(cPersistentObject) && if (dt &&
((cPersistentObject*)v)->state==cPersistent_UPTODATE_STATE) (! PyExtensionClass_Check(v)) &&
((cPersistentObject*)v)->jar==self->jar /* I'm paranoid */ &&
((cPersistentObject*)v)->state==cPersistent_UPTODATE_STATE
)
{ {
now -= ((cPersistentObject*)v)->atime; now -= ((cPersistentObject*)v)->atime;
if (now < 0) now += 65536; if (now < 0) now += 65536;
...@@ -281,13 +282,15 @@ static struct PyMethodDef cc_methods[] = { ...@@ -281,13 +282,15 @@ static struct PyMethodDef cc_methods[] = {
}; };
static ccobject * static ccobject *
newccobject(int cache_size, int cache_age) newccobject(PyObject *jar, int cache_size, int cache_age)
{ {
ccobject *self; ccobject *self;
UNLESS(self = PyObject_NEW(ccobject, &Cctype)) return NULL; UNLESS(self = PyObject_NEW(ccobject, &Cctype)) return NULL;
if(self->data=PyDict_New()) if(self->data=PyDict_New())
{ {
self->jar=jar;
Py_INCREF(jar);
self->position=0; self->position=0;
self->cache_size=cache_size; self->cache_size=cache_size;
self->cache_age=cache_age < 1 ? 1 : cache_age; self->cache_age=cache_age < 1 ? 1 : cache_age;
...@@ -312,6 +315,7 @@ static void ...@@ -312,6 +315,7 @@ static void
cc_dealloc(ccobject *self) cc_dealloc(ccobject *self)
{ {
Py_XDECREF(self->data); Py_XDECREF(self->data);
Py_XDECREF(self->jar);
PyMem_DEL(self); PyMem_DEL(self);
} }
...@@ -404,10 +408,24 @@ cc_subscript(ccobject *self, PyObject *key) ...@@ -404,10 +408,24 @@ cc_subscript(ccobject *self, PyObject *key)
return r; return r;
} }
static PyExtensionClass *Persistent=0;
static int static int
cc_ass_sub(ccobject *self, PyObject *key, PyObject *v) cc_ass_sub(ccobject *self, PyObject *key, PyObject *v)
{ {
if(v) return PyDict_SetItem(self->data, key, v); if(v)
{
if (PyExtensionClass_Check(v) ||
(PyExtensionInstance_Check(v) &&
ExtensionClassSubclassInstance_Check(v, Persistent)
)
)
return PyDict_SetItem(self->data, key, v);
PyErr_SetString(PyExc_ValueError,
"Cache values must be persistent objects or classes.");
return -1;
}
return PyDict_DelItem(self->data, key); return PyDict_DelItem(self->data, key);
} }
...@@ -446,8 +464,10 @@ static PyObject * ...@@ -446,8 +464,10 @@ static PyObject *
cCM_new(PyObject *self, PyObject *args) cCM_new(PyObject *self, PyObject *args)
{ {
int cache_size=100, cache_age=1000; int cache_size=100, cache_age=1000;
UNLESS(PyArg_ParseTuple(args, "|ii", &cache_size, &cache_age)) return NULL; PyObject *jar;
return (PyObject*)newccobject(cache_size,cache_age);
UNLESS(PyArg_ParseTuple(args, "O|ii", &jar, &cache_size, &cache_age)) return NULL;
return (PyObject*)newccobject(jar, cache_size,cache_age);
} }
static struct PyMethodDef cCM_methods[] = { static struct PyMethodDef cCM_methods[] = {
...@@ -459,10 +479,20 @@ void ...@@ -459,10 +479,20 @@ void
initcPickleCache() initcPickleCache()
{ {
PyObject *m, *d; PyObject *m, *d;
char *rev="$Revision: 1.16 $"; char *rev="$Revision: 1.17 $";
Cctype.ob_type=&PyType_Type; Cctype.ob_type=&PyType_Type;
UNLESS(ExtensionClassImported) return;
/* Get the Persistent base class */
UNLESS(m=PyString_FromString(cPersistanceModuleName)) return;
ASSIGN(m, PyImport_Import(m));
UNLESS(m) return;
ASSIGN(m, PyObject_GetAttrString(m, "Persistent"));
UNLESS(m) return;
Persistent=(PyExtensionClass *)m;
m = Py_InitModule4("cPickleCache", cCM_methods, "", m = Py_InitModule4("cPickleCache", cCM_methods, "",
(PyObject*)NULL,PYTHON_API_VERSION); (PyObject*)NULL,PYTHON_API_VERSION);
......
/***********************************************************
Copyright
Copyright 1997 Digital Creations, L.L.C., 910 Princess Anne
Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
rights reserved.
******************************************************************/
static char intSet_module_documentation[] =
""
"\n$Id: intSet.c,v 1.10 1998/11/11 02:00:56 jim Exp $"
;
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <malloc.h>
#include <time.h>
#include "cPersistence.h"
static void PyVar_Assign(PyObject **v, PyObject *e) { Py_XDECREF(*v); *v=e;}
#define ASSIGN(V,E) PyVar_Assign(&(V),(E))
#define UNLESS(E) if(!(E))
#define UNLESS_ASSIGN(V,E) ASSIGN(V,E); UNLESS(V)
#define RETURN_NONE Py_INCREF(Py_None); return Py_None
#define MIN_INTSET_ALLOC 8
#define INTSET_DATA_TYPE int
#define INTSET_DATA_FLAG "l"
typedef struct {
cPersistent_HEAD
int size, len;
INTSET_DATA_TYPE *data;
} intSet;
staticforward PyExtensionClass intSetType;
#define OBJECT(O) ((PyObject*)(O))
#define INTSET(O) ((intSet*)(O))
static PyObject *
_PER_RETURN(intSet *self, PyObject *r)
{
PER_ALLOW_DEACTIVATION(self);
return r;
}
static int
_PER_INT_RETURN(intSet *self, int r)
{
PER_ALLOW_DEACTIVATION(self);
return r;
}
/* We want to be sticky most of the time */
#define PER_RETURN(O,R) R
#define PER_INT_RETURN(O,R) R
#undef PER_ALLOW_DEACTIVATION
#define PER_ALLOW_DEACTIVATION(O)
static PyObject *
intSet_has_key(intSet *self, PyObject *args)
{
int min, max, i, l;
INTSET_DATA_TYPE k, key;
UNLESS(PyArg_ParseTuple(args,INTSET_DATA_FLAG,&key)) return NULL;
PER_USE_OR_RETURN(self, NULL);
for(min=0, max=self->len, i=max/2, l=max; i != l; l=i, i=(min+max)/2)
{
k=self->data[i];
if(k == key) return PER_RETURN(self, PyInt_FromLong(1));
if(k > key) max=i;
else min=i;
}
return PER_RETURN(self, PyInt_FromLong(0));
}
static int
intSet_grow(intSet *self, int l)
{
int g;
INTSET_DATA_TYPE *data;
if(self->data)
{
g=self->size*2;
if(g < l) g=l;
UNLESS(data=realloc(self->data, sizeof(INTSET_DATA_TYPE)*g))
{
PyErr_NoMemory();
return -1;
}
self->data=data;
self->size=g;
}
else
{
g=l < MIN_INTSET_ALLOC ? MIN_INTSET_ALLOC : l;
UNLESS(self->data=malloc(sizeof(INTSET_DATA_TYPE)*g))
{
PyErr_NoMemory();
return -1;
}
self->size=g;
}
return 0;
}
static INTSET_DATA_TYPE
intSet_modify(intSet *self, INTSET_DATA_TYPE ikey, int add)
{
int min, max, i, l;
INTSET_DATA_TYPE *data, k;
PER_USE_OR_RETURN(self, -1);
data=self->data;
for(min=0, max=self->len, i=max/2, l=max; i != l; l=i, i=(min+max)/2)
{
k=data[i];
if(k == ikey)
{
if(! add)
{
data+=i;
self->len--;
if(i < (self->len))
memmove(data, data+1, (self->len-i)*sizeof(INTSET_DATA_TYPE));
if(PER_CHANGED(self) < 0) return PER_INT_RETURN(self, -1);
}
return PER_INT_RETURN(self, 0);
}
if(k > ikey) max=i;
else min=i;
}
if(!add) return PER_INT_RETURN(self, 0);
if(self->len >= self->size && intSet_grow(self,self->len+1) < 0)
return PER_INT_RETURN(self, -1);
if(max != i) i++;
data=self->data+i;
if(self->len > i)
memmove(data+1,data,(self->len-i)*sizeof(INTSET_DATA_TYPE));
*data=ikey;
self->len++;
if(PER_CHANGED(self) < 0) return PER_INT_RETURN(self, -1);
return PER_INT_RETURN(self, ikey);
}
static PyObject *
intSet_insert(intSet *self, PyObject *args)
{
INTSET_DATA_TYPE key;
UNLESS(PyArg_ParseTuple(args,INTSET_DATA_FLAG,&key)) return NULL;
if(intSet_modify(self, key, 1) < 0) return NULL;
RETURN_NONE;
}
static PyObject *
intSet_remove(intSet *self, PyObject *args)
{
INTSET_DATA_TYPE key;
UNLESS(PyArg_ParseTuple(args,INTSET_DATA_FLAG,&key)) return NULL;
if(intSet_modify(self, key, 0) < 0) return NULL;
RETURN_NONE;
}
static PyObject *
intSet_clear(intSet *self, PyObject *args)
{
self->len=0;
if(PER_CHANGED(self) < 0) return PER_RETURN(self, NULL);
RETURN_NONE;
}
static PyObject *
intSet___getstate__(intSet *self, PyObject *args)
{
PyObject *r=0;
int i, l;
char *c;
INTSET_DATA_TYPE *d;
PER_USE_OR_RETURN(self, NULL);
l=self->len;
UNLESS(r=PyString_FromStringAndSize(NULL,l*4)) goto err;
UNLESS(c=PyString_AsString(r)) goto err;
d=self->data;
for(i=0; i < l; i++, *d++)
{
*c++ = (int)( *d & 0xff);
*c++ = (int)((*d >> 8) & 0xff);
*c++ = (int)((*d >> 16) & 0xff);
*c++ = (int)((*d >> 24) & 0xff);
}
return PER_RETURN(self, r);
err:
Py_DECREF(r);
return PER_RETURN(self, NULL);
}
static PyObject *
intSet___setstate__(intSet *self, PyObject *args)
{
PyObject *data;
int i, l, v;
char *c;
INTSET_DATA_TYPE k;
UNLESS(PyArg_ParseTuple(args,"O",&data)) return PER_RETURN(self, NULL);
UNLESS(c=PyString_AsString(data)) return PER_RETURN(self, NULL);
if((l=PyString_Size(data)) < 0) return PER_RETURN(self, NULL);
l/=4;
intSet_clear(self, NULL);
if(l > self->size && intSet_grow(self,l) < 0)
return PER_RETURN(self, NULL);
PyErr_Clear();
for(i=0; i < l; i++)
{
v = ((int)(unsigned char)*c++) ;
v |= ((int)(unsigned char)*c++) << 8;
v |= ((int)(unsigned char)*c++) << 16;
v |= ((int)(unsigned char)*c++) << 24;
self->data[i]=v;
}
self->len=l;
Py_INCREF(Py_None);
return PER_RETURN(self, Py_None);
}
static PyObject *
intSet_set_operation(intSet *self, PyObject *other,
int cpysrc, int cpyboth, int cpyoth)
{
intSet *r=0, *o;
int i, l, io, lo, ir;
INTSET_DATA_TYPE *d, *od, v, vo, dif;
if(other->ob_type != self->ob_type)
{
PyErr_SetString(PyExc_TypeError,
"intSet set operations require same-type operands");
return NULL;
}
o=INTSET(other);
PER_USE_OR_RETURN(self, NULL);
PER_USE_OR_RETURN((intSet*)other, NULL);
od=o->data;
d=self->data;
UNLESS(r=INTSET(PyObject_CallObject(OBJECT(self->ob_type), NULL)))
goto err;
for(i=0, l=self->len, io=0, lo=o->len; i < l && io < lo; )
{
v=d[i];
vo=od[io];
if(v < vo)
{
if(cpysrc)
{
if(r->len >= r->size && intSet_grow(r,0) < 0) goto err;
r->data[r->len]=v;
r->len++;
}
i++;
}
else if(v==vo)
{
if(cpyboth)
{
if(r->len >= r->size && intSet_grow(r,0) < 0) goto err;
r->data[r->len]=v;
r->len++;
}
i++;
io++;
}
else
{
if(cpyoth)
{
if(r->len >= r->size && intSet_grow(r,0) < 0) goto err;
r->data[r->len]=vo;
r->len++;
}
io++;
}
}
if(cpysrc && i < l)
{
l-=i;
if(r->len+l > r->size && intSet_grow(r,r->len+l) < 0) goto err;
memcpy(r->data+r->len, d+i, l*sizeof(INTSET_DATA_TYPE));
r->len += l;
}
else if(cpyoth && io < lo)
{
lo-=io;
if(r->len+lo > r->size && intSet_grow(r,r->len+lo) < 0) goto err;
memcpy(r->data+r->len, od+io, lo*sizeof(INTSET_DATA_TYPE));
r->len += lo;
}
return OBJECT(r);
err:
PER_ALLOW_DEACTIVATION(self);
PER_ALLOW_DEACTIVATION(o);
Py_DECREF(r);
return NULL;
}
static PyObject *
intSet_add(intSet *self, PyObject *other)
{
return intSet_set_operation(self,other,1,1,1);
}
static PyObject *
intSet_union(intSet *self, PyObject *args)
{
PyObject *other;
UNLESS(PyArg_ParseTuple(args,"O",&other)) return NULL;
return intSet_set_operation(self,other,1,1,1);
}
static PyObject *
intSet_intersection(intSet *self, PyObject *args)
{
PyObject *other;
UNLESS(PyArg_ParseTuple(args,"O",&other)) return NULL;
return intSet_set_operation(self,other,0,1,0);
}
static PyObject *
intSet_difference(intSet *self, PyObject *args)
{
PyObject *other;
UNLESS(PyArg_ParseTuple(args,"O",&other)) return NULL;
return intSet_set_operation(self,other,1,0,0);
}
static PyObject *
intSet__p___reinit__(intSet *self, PyObject *args)
{
PyObject *dict;
if(self->state==cPersistent_UPTODATE_STATE
&& HasInstDict(self) && (dict=INSTANCE_DICT(self)))
{
PyDict_Clear(dict);
self->state=cPersistent_GHOST_STATE;
}
Py_INCREF(Py_None);
return Py_None;
}
static struct PyMethodDef intSet_methods[] = {
{"has_key", (PyCFunction)intSet_has_key, METH_VARARGS,
"has_key(id) -- Test whether the set has the given id"},
{"insert", (PyCFunction)intSet_insert, METH_VARARGS,
"insert(id,[ignored]) -- Add an id to the set"},
{"remove", (PyCFunction)intSet_remove, METH_VARARGS,
"remove(id) -- Remove an id from the set"},
{"clear", (PyCFunction)intSet_clear, METH_VARARGS,
"clear() -- Remove all of the ids from the set"},
{"union", (PyCFunction)intSet_union, METH_VARARGS,
"union(other) -- Return the union of the set with another set"},
{"intersection", (PyCFunction)intSet_intersection, METH_VARARGS,
"intersection(other) -- "
"Return the intersection of the set with another set"},
{"difference", (PyCFunction)intSet_difference, METH_VARARGS,
"difference(other) -- Return the difference of the set with another set"
},
{"__getstate__", (PyCFunction)intSet___getstate__, METH_VARARGS,
"__getstate__() -- get the persistent state"},
{"__setstate__", (PyCFunction)intSet___setstate__, METH_VARARGS,
"__setstate__() -- set the persistent state"},
{"_p___reinit__", (PyCFunction)intSet__p___reinit__, METH_VARARGS,
"_p___reinit__(oid,jar,copy) -- Reinitialize from a newly created copy"},
{"_p_deactivate", (PyCFunction)intSet__p___reinit__, METH_VARARGS,
"_p_deactivate(oid,jar,copy) -- Reinitialize from a newly created copy"},
{NULL, NULL} /* sentinel */
};
static void
intSet_dealloc(intSet *self)
{
free(self->data);
PER_DEL(self);
PyMem_DEL(self);
}
static PyObject *
intSet_getattr(intSet *self, PyObject *name)
{
return Py_FindAttr((PyObject *)self, name);
}
/* Code to handle accessing intSet objects as sequence objects */
static int
intSet_length(intSet *self)
{
PER_USE_OR_RETURN(self,-1);
return PER_INT_RETURN(self,self->len);
}
static PyObject *
intSet_repeat(intSet *self, int n)
{
PyErr_SetString(PyExc_TypeError,
"intSet objects do not support repetition");
return NULL;
}
static PyObject *
intSet_item(intSet *self, int i)
{
PyObject *e;
PER_USE_OR_RETURN(self,NULL);
if(i >= 0 && i < self->len)
return PER_RETURN(self,PyInt_FromLong(self->data[i]));
UNLESS(e=PyInt_FromLong(i)) goto err;
PyErr_SetObject(PyExc_IndexError, e);
Py_DECREF(e);
err:
PER_ALLOW_DEACTIVATION(self)
return NULL;
}
static PyObject *
intSet_slice(intSet *self, int ilow, int ihigh)
{
PyErr_SetString(PyExc_TypeError,
"intSet objects do not support slicing");
return NULL;
}
static int
intSet_ass_item(intSet *self, int i, PyObject *v)
{
PyErr_SetString(PyExc_TypeError,
"intSet objects do not support item assignment");
return -1;
}
static int
intSet_ass_slice(PyListObject *self, int ilow, int ihigh, PyObject *v)
{
PyErr_SetString(PyExc_TypeError,
"intSet objects do not support slice assignment");
return -1;
}
static PySequenceMethods intSet_as_sequence = {
(inquiry)intSet_length, /*sq_length*/
(binaryfunc)intSet_add, /*sq_concat*/
(intargfunc)intSet_repeat, /*sq_repeat*/
(intargfunc)intSet_item, /*sq_item*/
(intintargfunc)intSet_slice, /*sq_slice*/
(intobjargproc)intSet_ass_item, /*sq_ass_item*/
(intintobjargproc)intSet_ass_slice, /*sq_ass_slice*/
};
static PyExtensionClass intSetType = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"intSet", /*tp_name*/
sizeof(intSet), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)intSet_dealloc, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)0, /*obsolete tp_getattr*/
(setattrfunc)0, /*obsolete tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/
&intSet_as_sequence, /*tp_as_sequence*/
0, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/
(ternaryfunc)0, /*tp_call*/
(reprfunc)0, /*tp_str*/
(getattrofunc)intSet_getattr, /*tp_getattro*/
0, /*tp_setattro*/
/* Space for future expansion */
0L,0L,
"A set of integers",
METHOD_CHAIN(intSet_methods),
EXTENSIONCLASS_BASICNEW_FLAG,
};
static struct PyMethodDef module_methods[] = {
{NULL, NULL} /* sentinel */
};
void
initintSet()
{
PyObject *m, *d;
char *rev="$Revision: 1.10 $";
UNLESS(ExtensionClassImported) return;
if(cPersistenceCAPI=PyCObject_Import("cPersistence","CAPI"))
{
intSetType.methods.link=cPersistenceCAPI->methods;
intSetType.tp_getattro=cPersistenceCAPI->getattro;
intSetType.tp_setattro=cPersistenceCAPI->setattro;
}
else return;
/* Create the module and add the functions */
m = Py_InitModule4("intSet", module_methods,
intSet_module_documentation,
(PyObject*)NULL,PYTHON_API_VERSION);
/* Add some symbolic constants to the module */
d = PyModule_GetDict(m);
PyExtensionClass_Export(d,"intSet",intSetType);
PyDict_SetItemString(d, "__version__",
PyString_FromStringAndSize(rev+11,strlen(rev+11)-2));
#include "dcprotect.h"
/* Check for errors */
if (PyErr_Occurred())
Py_FatalError("can't initialize module intSet");
}
/**************************************************************************
Revision Log:
$Log: intSet.c,v $
Revision 1.10 1998/11/11 02:00:56 jim
alpha1
Revision 1.9 1998/03/24 15:17:34 jim
*** empty log message ***
Revision 1.8 1998/03/24 15:15:33 jim
Brought reinit/deactivate machinery up to date.
Revision 1.7 1998/02/18 22:19:39 jim
Fixed C inheritence problem. Waaaaaaa.
Revision 1.6 1997/12/12 23:48:07 jim
Added basicnew support.
Revision 1.5 1997/11/13 20:47:13 jim
Fixed some bad return values.
Revision 1.4 1997/11/13 20:38:39 jim
added dcprotect
Revision 1.3 1997/10/01 02:45:58 jim
Minor reformat.
Revision 1.2 1997/09/08 18:41:59 jim
Added logic to save data in binary form.
Revision 1.1 1997/08/05 14:55:22 jim
*** empty log message ***
**************************************************************************/
/*********************************************************************** /***********************************************************************
$Id: cPersistence.c,v 1.25 1998/11/11 02:00:56 jim Exp $ $Id: cPersistence.c,v 1.26 1999/05/07 01:03:03 jim Exp $
C Persistence Module C Persistence Module
...@@ -12,16 +12,19 @@ ...@@ -12,16 +12,19 @@
*****************************************************************************/ *****************************************************************************/
static char *what_string = "$Id: cPersistence.c,v 1.25 1998/11/11 02:00:56 jim Exp $"; static char *what_string = "$Id: cPersistence.c,v 1.26 1999/05/07 01:03:03 jim Exp $";
#include <time.h> #include <string.h>
#include "cPersistence.h" #include "cPersistence.h"
#define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;} #define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;}
#define UNLESS(E) if(!(E)) #define UNLESS(E) if(!(E))
#define UNLESS_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V) #define UNLESS_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V)
#define OBJECT(V) ((PyObject*)(V))
static PyObject *py_keys, *py_setstate, *py___dict__; static PyObject *py_keys, *py_setstate, *py___dict__, *py_timeTime;
static PyObject *TimeStamp;
#ifdef DEBUG_LOG #ifdef DEBUG_LOG
static PyObject *debug_log=0; static PyObject *debug_log=0;
...@@ -35,8 +38,8 @@ call_debug(char *event, cPersistentObject *self) ...@@ -35,8 +38,8 @@ call_debug(char *event, cPersistentObject *self)
/* /*
printf("%s %p\n",event,self->ob_type->tp_name); printf("%s %p\n",event,self->ob_type->tp_name);
*/ */
r=PyObject_CallFunction(debug_log,"s(ss#i)",event, r=PyObject_CallFunction(debug_log,"s(sOi)",event,
self->ob_type->tp_name, self->oid, 8, self->ob_type->tp_name, self->oid,
self->state); self->state);
Py_XDECREF(r); Py_XDECREF(r);
} }
...@@ -48,6 +51,7 @@ init_strings() ...@@ -48,6 +51,7 @@ init_strings()
#define INIT_STRING(S) py_ ## S = PyString_FromString(#S) #define INIT_STRING(S) py_ ## S = PyString_FromString(#S)
INIT_STRING(keys); INIT_STRING(keys);
INIT_STRING(setstate); INIT_STRING(setstate);
INIT_STRING(timeTime);
INIT_STRING(__dict__); INIT_STRING(__dict__);
#undef INIT_STRING #undef INIT_STRING
} }
...@@ -201,14 +205,16 @@ static PyObject * ...@@ -201,14 +205,16 @@ static PyObject *
Per___changed__(cPersistentObject *self, PyObject *args) Per___changed__(cPersistentObject *self, PyObject *args)
{ {
PyObject *v=0; PyObject *v=0;
if (args && ! PyArg_ParseTuple(args, "|O",&v)) return NULL; if (args && ! PyArg_ParseTuple(args, "|O",&v)) return NULL;
if (v && ! PyObject_IsTrue(v)) if (! v) return PyObject_GetAttrString(OBJECT(self), "_p_changed");
if (PyObject_IsTrue(v))
{ {
PyErr_SetString(PyExc_TypeError, if (changed(self) < 0) return NULL;
"Only true arguments are allowed.");
return NULL;
} }
if (changed(self) < 0) return NULL; else if (self->state >= 0) self->state=cPersistent_UPTODATE_STATE;
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
...@@ -368,35 +374,30 @@ Per_dealloc(self) ...@@ -368,35 +374,30 @@ Per_dealloc(self)
PyMem_DEL(self); PyMem_DEL(self);
} }
static PyObject *
orNothing(PyObject *v)
{
if (! v) v=Py_None;
Py_INCREF(v);
return v;
}
static PyObject * static PyObject *
Per_getattr(cPersistentObject *self, PyObject *oname, char *name, Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
PyObject *(*getattrf)(PyObject *, PyObject*)) PyObject *(*getattrf)(PyObject *, PyObject*))
{ {
char *n=name; char *n=name;
if(*n++=='_') if(n && *n++=='_')
if(*n++=='p' && *n++=='_') if(*n++=='p' && *n++=='_')
{ {
switch(*n++) switch(*n++)
{ {
case 'o': case 'o':
if(*n++=='i' && *n++=='d' && ! *n) if(*n++=='i' && *n++=='d' && ! *n) return orNothing(self->oid);
return PyString_FromStringAndSize(self->oid, 8);
break; break;
case 'j': case 'j':
if(*n++=='a' && *n++=='r' && ! *n) if(*n++=='a' && *n++=='r' && ! *n) return orNothing(self->jar);
{
if(self->jar)
{
Py_INCREF(self->jar);
return self->jar;
}
else
{
Py_INCREF(Py_None);
return Py_None;
}
}
break; break;
case 'c': case 'c':
if(strcmp(n,"hanged")==0) if(strcmp(n,"hanged")==0)
...@@ -410,11 +411,32 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name, ...@@ -410,11 +411,32 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
cPersistent_CHANGED_STATE); cPersistent_CHANGED_STATE);
} }
break; break;
case 's':
if(strcmp(n,"erial")==0)
return PyString_FromStringAndSize(self->serial, 8);
break;
case 'm':
if(strcmp(n,"time")==0)
{
UPDATE_STATE_IF_NECESSARY(self, NULL);
self->atime=((long)(time(NULL)/3))%65536;
oname=PyString_FromStringAndSize(self->serial, 8);
if (! oname) return oname;
ASSIGN(oname, PyObject_CallFunction(TimeStamp, "O", oname));
if (! oname) return oname;
ASSIGN(oname, PyObject_GetAttr(oname, py_timeTime));
if (! oname) return oname;
ASSIGN(oname, PyObject_CallObject(oname, NULL));
return oname;
}
break;
} }
return getattrf((PyObject *)self, oname); return getattrf((PyObject *)self, oname);
} }
if(! (*name++=='_' && *name++=='_' && if(! (name && *name++=='_' && *name++=='_' &&
(strcmp(name,"dict__")==0 || strcmp(name,"class__")==0 (strcmp(name,"dict__")==0 || strcmp(name,"class__")==0
|| strcmp(name, "of__")==0))) || strcmp(name, "of__")==0)))
{ {
...@@ -429,10 +451,11 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name, ...@@ -429,10 +451,11 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
static PyObject* static PyObject*
Per_getattro(cPersistentObject *self, PyObject *name) Per_getattro(cPersistentObject *self, PyObject *name)
{ {
char *s; char *s=NULL;
UNLESS(s=PyString_AsString(name)) return NULL; if (PyString_Check(name))
return Per_getattr(self,name,s, PyExtensionClassCAPI->getattro); UNLESS(s=PyString_AsString(name)) return NULL;
return Per_getattr(self, name, s, PyExtensionClassCAPI->getattro);
} }
static int static int
...@@ -456,27 +479,36 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v, ...@@ -456,27 +479,36 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v,
{ {
if(name[3]=='o' && name[4]=='i' && name[5]=='d' && ! name[6]) if(name[3]=='o' && name[4]=='i' && name[5]=='d' && ! name[6])
{ {
if (! v) return bad_delattr(); Py_XINCREF(v);
if (PyString_Check(v) && PyString_GET_SIZE(v)==8) ASSIGN(self->oid, v);
memcpy(self->oid, PyString_AS_STRING(v), 8);
else
{
PyErr_SetString(PyExc_AttributeError,
"_p_oid must be an 8-character string");
return -1;
}
return 0; return 0;
} }
if(name[3]=='j' && name[4]=='a' && name[5]=='r' && ! name[6]) if(name[3]=='j' && name[4]=='a' && name[5]=='r' && ! name[6])
{ {
Py_XINCREF(v);
ASSIGN(self->jar, v); ASSIGN(self->jar, v);
Py_XINCREF(self->jar);
return 0; return 0;
} }
if(strcmp(name+3,"changed")==0) if(name[3]=='s' && strcmp(name+4,"erial")==0)
{ {
if (! v) return bad_delattr(); if (v)
if (v==Py_None) {
if (PyString_Check(v) && PyString_Size(v)==8)
memcpy(self->serial, PyString_AS_STRING(v), 8);
else
{
PyErr_SetString(PyExc_ValueError,
"_p_serial must be an 8-character string");
return -1;
}
}
else
memset(self->serial, 0, 8);
return 0;
}
if(name[3]=='c' && strcmp(name+4,"hanged")==0)
{
if (! v || v==Py_None)
{ {
if (Per__p_deactivate(self, NULL)) Py_DECREF(Py_None); if (Per__p_deactivate(self, NULL)) Py_DECREF(Py_None);
return 0; return 0;
...@@ -585,8 +617,15 @@ void ...@@ -585,8 +617,15 @@ void
initcPersistence() initcPersistence()
{ {
PyObject *m, *d; PyObject *m, *d;
char *rev="$Revision: 1.25 $"; char *rev="$Revision: 1.26 $";
TimeStamp=PyString_FromString("TimeStamp");
if (! TimeStamp) return;
ASSIGN(TimeStamp, PyImport_Import(TimeStamp));
if (! TimeStamp) return;
ASSIGN(TimeStamp, PyObject_GetAttrString(TimeStamp, "TimeStamp"));
if (! TimeStamp) return;
m = Py_InitModule4("cPersistence", cP_methods, m = Py_InitModule4("cPersistence", cP_methods,
"", "",
(PyObject*)NULL,PYTHON_API_VERSION); (PyObject*)NULL,PYTHON_API_VERSION);
......
/* /*
$Id: cPersistence.h,v 1.10 1998/11/11 02:00:56 jim Exp $ $Id: cPersistence.h,v 1.11 1999/05/07 01:03:03 jim Exp $
Definitions to facilitate making cPersistent subclasses in C. Definitions to facilitate making cPersistent subclasses in C.
...@@ -15,9 +15,11 @@ ...@@ -15,9 +15,11 @@
#define cPersistent_HEAD PyObject_HEAD \ #define cPersistent_HEAD PyObject_HEAD \
PyObject *jar; \ PyObject *jar; \
char oid[8]; \ PyObject *oid; \
char serial[8]; \
unsigned short atime; \ unsigned short atime; \
signed char state; \ signed char state; \
unsigned char reserved; \
#define cPersistent_GHOST_STATE -1 #define cPersistent_GHOST_STATE -1
#define cPersistent_UPTODATE_STATE 0 #define cPersistent_UPTODATE_STATE 0
...@@ -43,6 +45,8 @@ typedef struct { ...@@ -43,6 +45,8 @@ typedef struct {
static cPersistenceCAPIstruct *cPersistenceCAPI; static cPersistenceCAPIstruct *cPersistenceCAPI;
#define cPersistanceModuleName "cPersistence"
#define PER_USE_OR_RETURN(O,R) { \ #define PER_USE_OR_RETURN(O,R) { \
if ((O)->state==cPersistent_GHOST_STATE && \ if ((O)->state==cPersistent_GHOST_STATE && \
cPersistenceCAPI->setstate((PyObject*)(O)) < 0) \ cPersistenceCAPI->setstate((PyObject*)(O)) < 0) \
...@@ -57,6 +61,10 @@ static cPersistenceCAPIstruct *cPersistenceCAPI; ...@@ -57,6 +61,10 @@ static cPersistenceCAPIstruct *cPersistenceCAPI;
((O)->state==cPersistent_STICKY_STATE && \ ((O)->state==cPersistent_STICKY_STATE && \
((O)->state=cPersistent_UPTODATE_STATE)) ((O)->state=cPersistent_UPTODATE_STATE))
#define PER_PREVENT_DEACTIVATION(O) \
((O)->state==cPersistent_UPTODATE_STATE && \
((O)->state=cPersistent_STICKY_STATE))
#define PER_DEL(O) Py_XDECREF((O)->jar) #define PER_DEL(O) Py_XDECREF((O)->jar)
#endif #endif
......
static char *what_string = "$Id: cPickleCache.c,v 1.16 1998/11/11 02:00:56 jim Exp $"; static char *what_string = "$Id: cPickleCache.c,v 1.17 1999/05/07 01:03:03 jim Exp $";
#define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;} #define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;}
#define UNLESS(E) if(!(E)) #define UNLESS(E) if(!(E))
...@@ -12,12 +12,10 @@ static char *what_string = "$Id: cPickleCache.c,v 1.16 1998/11/11 02:00:56 jim E ...@@ -12,12 +12,10 @@ static char *what_string = "$Id: cPickleCache.c,v 1.16 1998/11/11 02:00:56 jim E
static PyObject *py_reload, *py__p_jar, *py__p_deactivate; static PyObject *py_reload, *py__p_jar, *py__p_deactivate;
/* Declarations for objects of type cCache */
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
PyObject *data; PyObject *data;
PyObject *jar;
int position; int position;
int cache_size; int cache_size;
int cache_age; int cache_age;
...@@ -65,8 +63,11 @@ gc_item(ccobject *self, PyObject *key, PyObject *v, long now, int dt) ...@@ -65,8 +63,11 @@ gc_item(ccobject *self, PyObject *key, PyObject *v, long now, int dt)
return PyDict_DelItem(self->data, key); return PyDict_DelItem(self->data, key);
} }
if (dt && v->ob_type->tp_basicsize >= sizeof(cPersistentObject) && if (dt &&
((cPersistentObject*)v)->state==cPersistent_UPTODATE_STATE) (! PyExtensionClass_Check(v)) &&
((cPersistentObject*)v)->jar==self->jar /* I'm paranoid */ &&
((cPersistentObject*)v)->state==cPersistent_UPTODATE_STATE
)
{ {
now -= ((cPersistentObject*)v)->atime; now -= ((cPersistentObject*)v)->atime;
if (now < 0) now += 65536; if (now < 0) now += 65536;
...@@ -281,13 +282,15 @@ static struct PyMethodDef cc_methods[] = { ...@@ -281,13 +282,15 @@ static struct PyMethodDef cc_methods[] = {
}; };
static ccobject * static ccobject *
newccobject(int cache_size, int cache_age) newccobject(PyObject *jar, int cache_size, int cache_age)
{ {
ccobject *self; ccobject *self;
UNLESS(self = PyObject_NEW(ccobject, &Cctype)) return NULL; UNLESS(self = PyObject_NEW(ccobject, &Cctype)) return NULL;
if(self->data=PyDict_New()) if(self->data=PyDict_New())
{ {
self->jar=jar;
Py_INCREF(jar);
self->position=0; self->position=0;
self->cache_size=cache_size; self->cache_size=cache_size;
self->cache_age=cache_age < 1 ? 1 : cache_age; self->cache_age=cache_age < 1 ? 1 : cache_age;
...@@ -312,6 +315,7 @@ static void ...@@ -312,6 +315,7 @@ static void
cc_dealloc(ccobject *self) cc_dealloc(ccobject *self)
{ {
Py_XDECREF(self->data); Py_XDECREF(self->data);
Py_XDECREF(self->jar);
PyMem_DEL(self); PyMem_DEL(self);
} }
...@@ -404,10 +408,24 @@ cc_subscript(ccobject *self, PyObject *key) ...@@ -404,10 +408,24 @@ cc_subscript(ccobject *self, PyObject *key)
return r; return r;
} }
static PyExtensionClass *Persistent=0;
static int static int
cc_ass_sub(ccobject *self, PyObject *key, PyObject *v) cc_ass_sub(ccobject *self, PyObject *key, PyObject *v)
{ {
if(v) return PyDict_SetItem(self->data, key, v); if(v)
{
if (PyExtensionClass_Check(v) ||
(PyExtensionInstance_Check(v) &&
ExtensionClassSubclassInstance_Check(v, Persistent)
)
)
return PyDict_SetItem(self->data, key, v);
PyErr_SetString(PyExc_ValueError,
"Cache values must be persistent objects or classes.");
return -1;
}
return PyDict_DelItem(self->data, key); return PyDict_DelItem(self->data, key);
} }
...@@ -446,8 +464,10 @@ static PyObject * ...@@ -446,8 +464,10 @@ static PyObject *
cCM_new(PyObject *self, PyObject *args) cCM_new(PyObject *self, PyObject *args)
{ {
int cache_size=100, cache_age=1000; int cache_size=100, cache_age=1000;
UNLESS(PyArg_ParseTuple(args, "|ii", &cache_size, &cache_age)) return NULL; PyObject *jar;
return (PyObject*)newccobject(cache_size,cache_age);
UNLESS(PyArg_ParseTuple(args, "O|ii", &jar, &cache_size, &cache_age)) return NULL;
return (PyObject*)newccobject(jar, cache_size,cache_age);
} }
static struct PyMethodDef cCM_methods[] = { static struct PyMethodDef cCM_methods[] = {
...@@ -459,10 +479,20 @@ void ...@@ -459,10 +479,20 @@ void
initcPickleCache() initcPickleCache()
{ {
PyObject *m, *d; PyObject *m, *d;
char *rev="$Revision: 1.16 $"; char *rev="$Revision: 1.17 $";
Cctype.ob_type=&PyType_Type; Cctype.ob_type=&PyType_Type;
UNLESS(ExtensionClassImported) return;
/* Get the Persistent base class */
UNLESS(m=PyString_FromString(cPersistanceModuleName)) return;
ASSIGN(m, PyImport_Import(m));
UNLESS(m) return;
ASSIGN(m, PyObject_GetAttrString(m, "Persistent"));
UNLESS(m) return;
Persistent=(PyExtensionClass *)m;
m = Py_InitModule4("cPickleCache", cCM_methods, "", m = Py_InitModule4("cPickleCache", cCM_methods, "",
(PyObject*)NULL,PYTHON_API_VERSION); (PyObject*)NULL,PYTHON_API_VERSION);
......
...@@ -48,12 +48,12 @@ ...@@ -48,12 +48,12 @@
__doc__='''Python implementation of persistent base types __doc__='''Python implementation of persistent base types
$Id: mapping.py,v 1.3 1998/11/11 02:00:56 jim Exp $''' $Id: mapping.py,v 1.4 1999/05/07 01:03:03 jim Exp $'''
__version__='$Revision: 1.3 $'[11:-2] __version__='$Revision: 1.4 $'[11:-2]
import Persistence import Persistence
class PM(Persistence.Persistent): class PersistentMapping(Persistence.Persistent):
"""A persistent wrapper for mapping objects. """A persistent wrapper for mapping objects.
This class allows wrapping of mapping objects so that This class allows wrapping of mapping objects so that
...@@ -113,4 +113,3 @@ class PM(Persistence.Persistent): ...@@ -113,4 +113,3 @@ class PM(Persistence.Persistent):
def values(self): def values(self):
return map(lambda k, d=self: d[k], self.keys()) return map(lambda k, d=self: d[k], self.keys())
PersistentMapping=PM
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment