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

*** empty log message ***

parent 3765adf4
......@@ -48,12 +48,12 @@
__doc__='''Python implementation of persistent base types
$Id: PersistentMapping.py,v 1.3 1998/11/11 02:00:56 jim Exp $'''
__version__='$Revision: 1.3 $'[11:-2]
$Id: PersistentMapping.py,v 1.4 1999/05/07 01:03:03 jim Exp $'''
__version__='$Revision: 1.4 $'[11:-2]
import Persistence
class PM(Persistence.Persistent):
class PersistentMapping(Persistence.Persistent):
"""A persistent wrapper for mapping objects.
This class allows wrapping of mapping objects so that
......@@ -113,4 +113,3 @@ class PM(Persistence.Persistent):
def values(self):
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
......@@ -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"
#define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;}
#define UNLESS(E) if(!(E))
#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
static PyObject *debug_log=0;
......@@ -35,8 +38,8 @@ call_debug(char *event, cPersistentObject *self)
/*
printf("%s %p\n",event,self->ob_type->tp_name);
*/
r=PyObject_CallFunction(debug_log,"s(ss#i)",event,
self->ob_type->tp_name, self->oid, 8,
r=PyObject_CallFunction(debug_log,"s(sOi)",event,
self->ob_type->tp_name, self->oid,
self->state);
Py_XDECREF(r);
}
......@@ -48,6 +51,7 @@ init_strings()
#define INIT_STRING(S) py_ ## S = PyString_FromString(#S)
INIT_STRING(keys);
INIT_STRING(setstate);
INIT_STRING(timeTime);
INIT_STRING(__dict__);
#undef INIT_STRING
}
......@@ -201,14 +205,16 @@ static PyObject *
Per___changed__(cPersistentObject *self, PyObject *args)
{
PyObject *v=0;
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,
"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);
return Py_None;
}
......@@ -368,35 +374,30 @@ Per_dealloc(self)
PyMem_DEL(self);
}
static PyObject *
orNothing(PyObject *v)
{
if (! v) v=Py_None;
Py_INCREF(v);
return v;
}
static PyObject *
Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
PyObject *(*getattrf)(PyObject *, PyObject*))
{
char *n=name;
if(*n++=='_')
if(n && *n++=='_')
if(*n++=='p' && *n++=='_')
{
switch(*n++)
{
case 'o':
if(*n++=='i' && *n++=='d' && ! *n)
return PyString_FromStringAndSize(self->oid, 8);
if(*n++=='i' && *n++=='d' && ! *n) return orNothing(self->oid);
break;
case 'j':
if(*n++=='a' && *n++=='r' && ! *n)
{
if(self->jar)
{
Py_INCREF(self->jar);
return self->jar;
}
else
{
Py_INCREF(Py_None);
return Py_None;
}
}
if(*n++=='a' && *n++=='r' && ! *n) return orNothing(self->jar);
break;
case 'c':
if(strcmp(n,"hanged")==0)
......@@ -410,11 +411,32 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
cPersistent_CHANGED_STATE);
}
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);
}
if(! (*name++=='_' && *name++=='_' &&
if(! (name && *name++=='_' && *name++=='_' &&
(strcmp(name,"dict__")==0 || strcmp(name,"class__")==0
|| strcmp(name, "of__")==0)))
{
......@@ -429,10 +451,11 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
static PyObject*
Per_getattro(cPersistentObject *self, PyObject *name)
{
char *s;
char *s=NULL;
if (PyString_Check(name))
UNLESS(s=PyString_AsString(name)) return NULL;
return Per_getattr(self,name,s, PyExtensionClassCAPI->getattro);
return Per_getattr(self, name, s, PyExtensionClassCAPI->getattro);
}
static int
......@@ -456,27 +479,36 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v,
{
if(name[3]=='o' && name[4]=='i' && name[5]=='d' && ! name[6])
{
if (! v) return bad_delattr();
if (PyString_Check(v) && PyString_GET_SIZE(v)==8)
memcpy(self->oid, PyString_AS_STRING(v), 8);
else
{
PyErr_SetString(PyExc_AttributeError,
"_p_oid must be an 8-character string");
return -1;
}
Py_XINCREF(v);
ASSIGN(self->oid, v);
return 0;
}
if(name[3]=='j' && name[4]=='a' && name[5]=='r' && ! name[6])
{
Py_XINCREF(v);
ASSIGN(self->jar, v);
Py_XINCREF(self->jar);
return 0;
}
if(strcmp(name+3,"changed")==0)
if(name[3]=='s' && strcmp(name+4,"erial")==0)
{
if (v)
{
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) return bad_delattr();
if (v==Py_None)
if (! v || v==Py_None)
{
if (Per__p_deactivate(self, NULL)) Py_DECREF(Py_None);
return 0;
......@@ -585,7 +617,14 @@ void
initcPersistence()
{
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,
"",
......
/*
$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.
......@@ -15,9 +15,11 @@
#define cPersistent_HEAD PyObject_HEAD \
PyObject *jar; \
char oid[8]; \
PyObject *oid; \
char serial[8]; \
unsigned short atime; \
signed char state; \
unsigned char reserved; \
#define cPersistent_GHOST_STATE -1
#define cPersistent_UPTODATE_STATE 0
......@@ -43,6 +45,8 @@ typedef struct {
static cPersistenceCAPIstruct *cPersistenceCAPI;
#define cPersistanceModuleName "cPersistence"
#define PER_USE_OR_RETURN(O,R) { \
if ((O)->state==cPersistent_GHOST_STATE && \
cPersistenceCAPI->setstate((PyObject*)(O)) < 0) \
......@@ -57,6 +61,10 @@ static cPersistenceCAPIstruct *cPersistenceCAPI;
((O)->state==cPersistent_STICKY_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)
#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 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
static PyObject *py_reload, *py__p_jar, *py__p_deactivate;
/* Declarations for objects of type cCache */
typedef struct {
PyObject_HEAD
PyObject *data;
PyObject *jar;
int position;
int cache_size;
int cache_age;
......@@ -65,8 +63,11 @@ gc_item(ccobject *self, PyObject *key, PyObject *v, long now, int dt)
return PyDict_DelItem(self->data, key);
}
if (dt && v->ob_type->tp_basicsize >= sizeof(cPersistentObject) &&
((cPersistentObject*)v)->state==cPersistent_UPTODATE_STATE)
if (dt &&
(! PyExtensionClass_Check(v)) &&
((cPersistentObject*)v)->jar==self->jar /* I'm paranoid */ &&
((cPersistentObject*)v)->state==cPersistent_UPTODATE_STATE
)
{
now -= ((cPersistentObject*)v)->atime;
if (now < 0) now += 65536;
......@@ -281,13 +282,15 @@ static struct PyMethodDef cc_methods[] = {
};
static ccobject *
newccobject(int cache_size, int cache_age)
newccobject(PyObject *jar, int cache_size, int cache_age)
{
ccobject *self;
UNLESS(self = PyObject_NEW(ccobject, &Cctype)) return NULL;
if(self->data=PyDict_New())
{
self->jar=jar;
Py_INCREF(jar);
self->position=0;
self->cache_size=cache_size;
self->cache_age=cache_age < 1 ? 1 : cache_age;
......@@ -312,6 +315,7 @@ static void
cc_dealloc(ccobject *self)
{
Py_XDECREF(self->data);
Py_XDECREF(self->jar);
PyMem_DEL(self);
}
......@@ -404,10 +408,24 @@ cc_subscript(ccobject *self, PyObject *key)
return r;
}
static PyExtensionClass *Persistent=0;
static int
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);
}
......@@ -446,8 +464,10 @@ static PyObject *
cCM_new(PyObject *self, PyObject *args)
{
int cache_size=100, cache_age=1000;
UNLESS(PyArg_ParseTuple(args, "|ii", &cache_size, &cache_age)) return NULL;
return (PyObject*)newccobject(cache_size,cache_age);
PyObject *jar;
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[] = {
......@@ -459,10 +479,20 @@ void
initcPickleCache()
{
PyObject *m, *d;
char *rev="$Revision: 1.16 $";
char *rev="$Revision: 1.17 $";
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, "",
(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 @@
##############################################################################
"""Database connection support
$Id: Connection.py,v 1.2 1999/04/28 11:10:48 jim Exp $"""
__version__='$Revision: 1.2 $'[11:-2]
$Id: Connection.py,v 1.3 1999/05/07 01:03:02 jim Exp $"""
__version__='$Revision: 1.3 $'[11:-2]
from PickleCache import PickleCache
from cPickleCache import PickleCache
from bpthread import allocate_lock
from POSException import ConflictError
from cStringIO import StringIO
......@@ -75,7 +75,7 @@ class Connection:
self._storage=storage
self.new_oid=storage.new_oid
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._invalidated={}
lock=allocate_lock()
......@@ -94,38 +94,25 @@ class Connection:
if cache.has_key(oid): return cache[oid]
__traceback_info__=oid
p=self._storage.load(oid, self._version)
p, serial = self._storage.load(oid, self._version)
file=StringIO(p)
unpickler=Unpickler(file)
unpickler.persistent_load=self._persistent_load
object = unpickler.load()
if type(object) is tt:
klass, args = object
if (args is None or
not args and not hasattr(klass,'__getinitargs__')):
if type(klass) is ct:
object=HelperClass()
object.__class__=klass
else: object=klass.__basicnew__()
object=klass.__basicnew__()
else:
object=apply(klass,args)
object.__dict__.clear()
else:
object.__dict__.clear()
klass=object.__class__
if type(klass) is ct:
d=object.__dict__
d['_p_oid']=oid
d['_p_jar']=self
d['_p_changed']=None
else:
object._p_oid=oid
object._p_jar=self
object._p_changed=None
object._p_serial=serial
cache[oid]=object
return object
......@@ -143,14 +130,6 @@ class Connection:
# to create the instance wo hitting the db, so go for it!
oid, klass = oid
if cache.has_key(oid): return cache[oid]
if type(klass) is ct:
object=HelperClass()
object.__class__=klass
d=object.__dict__
d['_p_oid']=oid
d['_p_jar']=self
d['_p_changed']=None
else:
object=klass.__basicnew__()
object._p_oid=oid
object._p_jar=self
......@@ -236,6 +215,7 @@ class Connection:
object=stack[-1]
del stack[-1]
oid=object._p_oid
serial=object._p_serial
if self._invalidated.has_key(oid): raise ConflictError, oid
cls = object.__class__
if hasattr(cls, '__getinitargs__'):
......@@ -249,7 +229,7 @@ class Connection:
state=object.__getstate__()
dump(state)
p=file()
dbstore(oid,p,version,transaction)
object._p_serial=dbstore(oid,serial,p,version,transaction)
object._p_changed=0
cache[oid]=object
......@@ -287,7 +267,7 @@ class Connection:
self._r()
raise ConflictError, oid
self._r()
p=self._storage.load(oid, self._version)
p, serial = self._storage.load(oid, self._version)
file=StringIO(p)
unpickler=Unpickler(file)
unpickler.persistent_load=self._persistent_load
......@@ -298,6 +278,7 @@ class Connection:
else:
d=object.__dict__
for k,v in state.items(): d[k]=v
object._p_serial=serial
def tpc_abort(self, transaction):
self._storage.tpc_abort(transaction)
......
......@@ -47,8 +47,8 @@
##############################################################################
"""Database objects
$Id: DB.py,v 1.2 1999/04/28 11:10:48 jim Exp $"""
__version__='$Revision: 1.2 $'[11:-2]
$Id: DB.py,v 1.3 1999/05/07 01:03:02 jim Exp $"""
__version__='$Revision: 1.3 $'[11:-2]
import cPickle, cStringIO, sys
from Connection import Connection
......@@ -90,7 +90,7 @@ class DB:
t=Transaction()
t.description='initial database creation'
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)
# Allocate locks:
......@@ -389,8 +389,8 @@ class DB:
def getCacheDeactivateAfter(self): return self._cache_deactivate_after
def getCacheSize(self): return self._cache_size
def getPoolSize(self): return self._pool_size
def getVersionCacheDeactivateAfter(self): return
self._version_cache_deactivate_after
def getVersionCacheDeactivateAfter(self):
return self._version_cache_deactivate_after
def getVersionCacheSize(self): return self._version_cache_size
def getVersionPoolSize(self): return self._version_pool_size
......
##############################################################################
#
# 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
#
##############################################################################
"""File-based BoboPOS3 storage
"""File-based ZODB storage
Files are arranged as follows.
- The first 4 bytes are a file identifier.
- The rest of the file consists of a sequence of transaction
"records".
A transaction record consists of:
- 8-byte transaction record, which is also a time stamp.
- 8-byte previous-transaction file position.
- 8-byte transaction record length - 8.
- 1-byte status code
- 2-byte length of user name
- 2-byte length of description
- 4-byte length of extension attributes
- user name
- description
* A sequence of data records
- 8-byte redundant transaction length -8
A data record consists of
- 8-byte oid.
- 8-byte serial, which is a type stamp that matches the
transaction timestamp.
- 8-byte previous-record file-position.
- 8-byte beginning of transaction record file position.
- 2-byte version length
- 8-byte data length
? 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
now=time.time
from struct import pack, unpack
from cPickle import dumps
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
......@@ -70,11 +134,24 @@ class CorruptedFileStorageError(FileStorageError,
class CorruptedTransactionError(CorruptedFileStorageError): pass
class CorruptedDataError(CorruptedFileStorageError): pass
packed_version='FS20'
class FileStorage:
_packt=0
_transaction=None
_serial=z64
def __init__(self, file_name, create=0, log=lambda s: None, read_only=0,
stop=None):
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
def __init__(self, file_name, create=0):
self.__name__=file_name
self._tfile=open(file_name+'.tmp','w+b')
index, vindex, tindex = self._newIndexes()
......@@ -105,58 +182,87 @@ class FileStorage:
self._oid='\0\0\0\0\0\0\0\0'
return
if os.path.exists(file_name): file=open(file_name,'r+b')
else: file=open(file_name,'w+b')
if os.path.exists(file_name):
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._pos, self._tpos, self._oid = read_index(
file, index, vindex, tindex)
self._pos, self._tpos, self._oid, tid = read_index(
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 _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()
try:
pos=self._vindex[version]
spos=p64(pos)
file=self._file
seek=file.seek
read=file.read
file=self._tfile
write=file.write
tell=file.tell
tloc=self._pos
tloc=p64(self._pos)
tappend=self._tappend
index=self._index
pack=struct.pack
unpack=struct.unpack
serial=self._serial
while pos:
seek(pos)
h=read(30)
h=read(58)
oid=h[:8]
if index[oid]==pos:
tappend(oid, tell())
pc=h[-8:-4] # Position of committed (non-version) data
write(pack(">8siiHi4s", oid,pos,tloc,0,0,pc))
pos=unpack(">i",h[-4:])[0]
pc=h[-16:-8] # Position of committed (non-version) data
write(pack(
">"
"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()
def close(self):
self._file.close()
# 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()
try:
pos=self._vindex[version]
spos=p64(pos)
file=self._file
seek=file.seek
read=file.read
file=self._tfile
write=file.write
tell=file.tell
tloc=self._pos
tloc=p64(self._pos)
tappend=self._tappend
index=self._index
pack=struct.pack
......@@ -165,14 +271,20 @@ class FileStorage:
while pos:
seek(pos)
h=read(30)
h=read(58)
oid=h[:8]
if index[oid]==pos:
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(pack(">i",pos))
pos=unpack(">i",h[-4:])[0]
write(spos)
spos=h[-8:]
pos=u64(spos)
del self._vindex[version]
finally: self._r()
def getName(self): return self.__name__
......@@ -180,31 +292,8 @@ class FileStorage:
def getSize(self): return self._pos
def history(self, oid, version, length=1):
self._a()
try:
# 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()
# TBD
pass
def load(self, oid, version, _stuff=None):
self._a()
......@@ -213,21 +302,21 @@ class FileStorage:
file=self._file
file.seek(pos)
read=file.read
h=read(22)
doid,prev,tloc,vlen,plen = unpack(">8siiHi", h)
h=read(42)
doid,serial,prev,tloc,vlen,plen = unpack(">8s8s8s8sH8s", h)
if doid != oid: raise CorruptedDataError, h
if vlen:
pnv=read(4)
pnv=read(8) # Read location of non-version data
if (not version or len(version) != vlen or
(read(4) # skip past version link
(read(8) # skip past version link
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,
# or we've already read past the version data!
if plen: return read(plen)
return _loadBack(file, oid, pnv)
if plen != z64: return read(u64(plen)), serial
return _loadBack(file, oid, u64(pnv))
finally: self._r()
def modifiedInVersion(self, oid):
......@@ -236,10 +325,11 @@ class FileStorage:
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
doid,serial,prev,tloc,vlen = unpack(">8s8s8s8sH", file.read(34))
if doid != oid:
raise CorruptedDataError, h
if vlen:
seek(8,1)
seek(16,1)
return read(vlen)
return ''
finally: self._r()
......@@ -261,121 +351,10 @@ class FileStorage:
else: return self.new_oid(last[:-1])
def pack(self, t, rf):
self._a()
try:
# 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()
# TBD
pass
def store(self, oid, data, version, transaction):
def store(self, oid, serial, data, version, transaction):
if transaction is not self._transaction:
raise POSException.StorageTransactionError(self, transaction)
......@@ -386,13 +365,14 @@ class FileStorage:
if old:
file=self._file
file.seek(old)
h=file.read(22)
doid,prev,tloc,vlen,plen = unpack(">8siiHi", h)
h=file.read(42)
doid,oserial,sprev,stloc,vlen,splen = unpack(">8s8s8s8sH8s", h)
if doid != oid: raise CorruptedDataError, h
if serial != oserial: raise POSException.ConflictError
if vlen:
pnv=read(4)
pnv=read(8) # non-version data pointer
if (len(version) != vlen or
(read(4) # skip past version link
(read(8) # skip past version link
and version != read(vlen))
):
raise POSException.VersionLockError, oid
......@@ -401,16 +381,24 @@ class FileStorage:
write=tfile.write
self._tappend(oid, tfile.tell())
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 pnv: write(pnv)
else: write(pack(">i",old))
else: write(p64(old))
# Link to last record for this version:
vindex=self._vindex
write(pack(">i",vindex[version]))
write(p64(vindex[version]))
vindex[version]=pos
write(version)
write(data)
return serial
finally: self._r()
def supportsUndo(self): return 0 # for now
......@@ -435,6 +423,10 @@ class FileStorage:
self._transaction=transaction
del self._tindex[:] # Just to be sure!
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()
def tpc_finish(self, transaction, f=None):
......@@ -445,33 +437,35 @@ class FileStorage:
file=self._file
write=file.write
tfile=self._tfile
read=tfile.read
dlen=tfile.tell()
tfile.seek(0)
id=transaction.id
id=self._serial
user=transaction.user
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
file.seek(pos)
stpos=p64(self._tpos)
tl=tlen+dlen
write(pack(">8siicHH",
id, self._tpos, tl, ' ', len(user), len(desc)))
write(user)
write(desc)
assert dlen >= 0
while dlen > 0:
d=read(min(dlen,8192))
write(d)
d=len(d)
assert dlen >= d
dlen=dlen-d
write(pack(">i", tl))
stl=p64(tl)
write(pack(
">8s" "8s" "8s" "c" "H" "H" "I"
,id, stpos, stl, ' ', len(user), len(desc), len(ext),
))
if user: write(user)
if desc: write(desc)
if ext: write(ext)
cp(tfile, file, dlen)
write(stl)
file.flush()
self._tpos=pos
self._pos=pos+tl+4
self._pos=pos+tl+8
index=self._index
dpos=pos+tlen
......@@ -483,29 +477,19 @@ class FileStorage:
finally: self._r()
def undo(self, transaction_id):
# TBD
pass
def undoLog(self, version, first, last, path):
# TBD
return []
def versionEmpty(self, version):
self._a()
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()
return not self._vindex.has_key(version)
packed_version='FS10'
def read_index(file, index, vindex, tindex, stop='\377'*8):
def read_index(file, name, index, vindex, tindex, stop='\377'*8,
log=lambda s: None):
indexpos=index.get
vndexpos=vindex.get
tappend=tindex.append
......@@ -524,19 +508,62 @@ def read_index(file, index, vindex, tindex, stop='\377'*8):
pos=4
unpack=struct.unpack
tpos=0
maxoid='\0\0\0\0\0\0\0\0'
maxoid=ltid=z64
tid='\0'*7+'\1'
while 1:
# Read the transaction record
h=read(21)
h=read(33)
if not h: break
if len(h) != 21: raise CorruptedTransactionError, h
tid, prev, tl, status, ul, dl = unpack(">8siicHH",h)
if (prev != tpos
or status not in ' up' or ul > tl or dl > tl
or tl > file_size or tl+pos >= file_size):
raise CorruptedTransactionRecordError, h
if len(h) != 33:
warn(log, '%s truncated at %s', name, pos)
seek(pos)
file.truncate()
break
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
tpos=pos
tend=tpos+tl
......@@ -544,45 +571,56 @@ def read_index(file, index, vindex, tindex, stop='\377'*8):
# Undone transaction, skip it
pos=tpos+tl
seek(pos)
h=read(4)
if len(h) != 4: raise CorruptedTransactionError, h
if unpack(">i",h)[0] != tl:
raise CorruptedTransactionError, h
pos=pos+4
h=read(8)
if h != stl:
panic(log, '%s has inconsistent transaction length at %s',
name, pos)
pos=pos+8
continue
pos=tpos+21+ul+dl
pos=tpos+33+ul+dl
while pos < tend:
# Read the data records for this transaction
seek(pos)
h=read(22)
oid,prev,tloc,vlen,plen = unpack(">8siiHi", h)
dlen=22+(plen or 4)+vlen
if pos+dlen > tend or tloc != tpos:
raise CorruptedDataError, h
if indexpos(oid,0) != prev:
raise CorruptedDataError, h
h=read(42)
oid,serial,sprev,stloc,vlen,splen = unpack(">8s8s8s8sH8s", h)
prev=u64(sprev)
tloc=u64(stloc)
plen=u64(splen)
dlen=42+(plen or 8)+vlen
tappend((oid,pos))
if vlen:
dlen=vlen+8
dlen=vlen+16
seek(8,1)
pv=u64(read(8))
version=read(vlen)
if vndexpos(version, 0) != pv:
panic(log,"%s incorrect previous version pointer at %s",
name, 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
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
seek(pos)
h=read(4)
if len(h) != 4: raise CorruptedTransactionError, h
if unpack(">i",h)[0] != tl:
raise CorruptedTransactionError, h
pos=pos+4
h=read(8)
if h != stl:
panic(log, "%s redundant transaction length check failed at %s",
name, pos)
pos=pos+8
for oid, p in tindex:
maxoid=max(maxoid,oid)
......@@ -590,7 +628,7 @@ def read_index(file, index, vindex, tindex, stop='\377'*8):
del tindex[:]
return pos, tpos, maxoid
return pos, tpos, maxoid, ltid
def _loadBack(file, oid, back):
......@@ -598,8 +636,12 @@ def _loadBack(file, oid, back):
old=unpack(">i",back)[0]
if not old: raise KeyError, oid
file.seek(old)
h=file.read(22)
doid,prev,tloc,vlen,plen = unpack(">8siiHi", h)
if doid != oid or vlen: raise CorruptedDataError, h
if plen: return read(plen)
h=file.read(42)
doid,serial,prev,tloc,vlen,plen = unpack(">8s8s8s8sH8s", h)
if doid != oid or vlen:
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!
/* $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 @@
__doc__='''Python implementation of persistent base types
$Id: PersistentMapping.py,v 1.3 1998/11/11 02:00:56 jim Exp $'''
__version__='$Revision: 1.3 $'[11:-2]
$Id: PersistentMapping.py,v 1.4 1999/05/07 01:03:03 jim Exp $'''
__version__='$Revision: 1.4 $'[11:-2]
import Persistence
class PM(Persistence.Persistent):
class PersistentMapping(Persistence.Persistent):
"""A persistent wrapper for mapping objects.
This class allows wrapping of mapping objects so that
......@@ -113,4 +113,3 @@ class PM(Persistence.Persistent):
def values(self):
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*
cPersistence cPersistence.c -I../ExtensionClass -I../python
cPickleCache cPickleCache.c -I../ExtensionClass -I../python
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
cPersistence cPersistence.c -I../../Components/ExtensionClass
cPickleCache cPickleCache.c -I../../Components/ExtensionClass
#cPickleJar cPickleJar.c -I../../Components/ExtensionClass
#iTree iTree.c -I../../Components/ExtensionClass
......@@ -47,29 +47,26 @@
##############################################################################
"""Transaction management
$Id: Transaction.py,v 1.3 1998/11/11 02:00:56 jim Exp $"""
__version__='$Revision: 1.3 $'[11:-2]
$Id: Transaction.py,v 1.4 1999/05/07 01:03:03 jim Exp $"""
__version__='$Revision: 1.4 $'[11:-2]
import time, sys, struct
from struct import pack
from string import split, strip, join
ConflictError=""
from POSException import ConflictError
class Transaction:
'Simple transaction objects for single-threaded applications.'
user=''
description=''
_connections=None
_extension=None
def __init__(self,
time=time.time, pack=struct.pack, gmtime=time.gmtime):
self._objects=[]
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=''
if self._connections:
for c in self._connections.values(): c.close()
......@@ -136,7 +133,7 @@ class Transaction:
'Register the given object for transaction control.'
self._append(object)
def remark(self, text):
def note(self, text):
if self.description:
self.description = "%s\n\n%s" % (self.description, strip(text))
else:
......@@ -145,6 +142,11 @@ class Transaction:
def setUser(self, user_name, path='/'):
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
############################################################################
......@@ -176,8 +178,3 @@ del _t
import __main__
__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 @@
This module provides a wrapper that causes a database connection to be created
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= (),
version_cookie_name=None):
......@@ -94,8 +94,14 @@ class BoboApplication:
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
......
......@@ -45,7 +45,26 @@
# (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 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
......@@ -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"
#define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;}
#define UNLESS(E) if(!(E))
#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
static PyObject *debug_log=0;
......@@ -35,8 +38,8 @@ call_debug(char *event, cPersistentObject *self)
/*
printf("%s %p\n",event,self->ob_type->tp_name);
*/
r=PyObject_CallFunction(debug_log,"s(ss#i)",event,
self->ob_type->tp_name, self->oid, 8,
r=PyObject_CallFunction(debug_log,"s(sOi)",event,
self->ob_type->tp_name, self->oid,
self->state);
Py_XDECREF(r);
}
......@@ -48,6 +51,7 @@ init_strings()
#define INIT_STRING(S) py_ ## S = PyString_FromString(#S)
INIT_STRING(keys);
INIT_STRING(setstate);
INIT_STRING(timeTime);
INIT_STRING(__dict__);
#undef INIT_STRING
}
......@@ -201,14 +205,16 @@ static PyObject *
Per___changed__(cPersistentObject *self, PyObject *args)
{
PyObject *v=0;
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,
"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);
return Py_None;
}
......@@ -368,35 +374,30 @@ Per_dealloc(self)
PyMem_DEL(self);
}
static PyObject *
orNothing(PyObject *v)
{
if (! v) v=Py_None;
Py_INCREF(v);
return v;
}
static PyObject *
Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
PyObject *(*getattrf)(PyObject *, PyObject*))
{
char *n=name;
if(*n++=='_')
if(n && *n++=='_')
if(*n++=='p' && *n++=='_')
{
switch(*n++)
{
case 'o':
if(*n++=='i' && *n++=='d' && ! *n)
return PyString_FromStringAndSize(self->oid, 8);
if(*n++=='i' && *n++=='d' && ! *n) return orNothing(self->oid);
break;
case 'j':
if(*n++=='a' && *n++=='r' && ! *n)
{
if(self->jar)
{
Py_INCREF(self->jar);
return self->jar;
}
else
{
Py_INCREF(Py_None);
return Py_None;
}
}
if(*n++=='a' && *n++=='r' && ! *n) return orNothing(self->jar);
break;
case 'c':
if(strcmp(n,"hanged")==0)
......@@ -410,11 +411,32 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
cPersistent_CHANGED_STATE);
}
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);
}
if(! (*name++=='_' && *name++=='_' &&
if(! (name && *name++=='_' && *name++=='_' &&
(strcmp(name,"dict__")==0 || strcmp(name,"class__")==0
|| strcmp(name, "of__")==0)))
{
......@@ -429,10 +451,11 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
static PyObject*
Per_getattro(cPersistentObject *self, PyObject *name)
{
char *s;
char *s=NULL;
if (PyString_Check(name))
UNLESS(s=PyString_AsString(name)) return NULL;
return Per_getattr(self,name,s, PyExtensionClassCAPI->getattro);
return Per_getattr(self, name, s, PyExtensionClassCAPI->getattro);
}
static int
......@@ -456,27 +479,36 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v,
{
if(name[3]=='o' && name[4]=='i' && name[5]=='d' && ! name[6])
{
if (! v) return bad_delattr();
if (PyString_Check(v) && PyString_GET_SIZE(v)==8)
memcpy(self->oid, PyString_AS_STRING(v), 8);
else
{
PyErr_SetString(PyExc_AttributeError,
"_p_oid must be an 8-character string");
return -1;
}
Py_XINCREF(v);
ASSIGN(self->oid, v);
return 0;
}
if(name[3]=='j' && name[4]=='a' && name[5]=='r' && ! name[6])
{
Py_XINCREF(v);
ASSIGN(self->jar, v);
Py_XINCREF(self->jar);
return 0;
}
if(strcmp(name+3,"changed")==0)
if(name[3]=='s' && strcmp(name+4,"erial")==0)
{
if (v)
{
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) return bad_delattr();
if (v==Py_None)
if (! v || v==Py_None)
{
if (Per__p_deactivate(self, NULL)) Py_DECREF(Py_None);
return 0;
......@@ -585,7 +617,14 @@ void
initcPersistence()
{
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,
"",
......
/*
$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.
......@@ -15,9 +15,11 @@
#define cPersistent_HEAD PyObject_HEAD \
PyObject *jar; \
char oid[8]; \
PyObject *oid; \
char serial[8]; \
unsigned short atime; \
signed char state; \
unsigned char reserved; \
#define cPersistent_GHOST_STATE -1
#define cPersistent_UPTODATE_STATE 0
......@@ -43,6 +45,8 @@ typedef struct {
static cPersistenceCAPIstruct *cPersistenceCAPI;
#define cPersistanceModuleName "cPersistence"
#define PER_USE_OR_RETURN(O,R) { \
if ((O)->state==cPersistent_GHOST_STATE && \
cPersistenceCAPI->setstate((PyObject*)(O)) < 0) \
......@@ -57,6 +61,10 @@ static cPersistenceCAPIstruct *cPersistenceCAPI;
((O)->state==cPersistent_STICKY_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)
#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 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
static PyObject *py_reload, *py__p_jar, *py__p_deactivate;
/* Declarations for objects of type cCache */
typedef struct {
PyObject_HEAD
PyObject *data;
PyObject *jar;
int position;
int cache_size;
int cache_age;
......@@ -65,8 +63,11 @@ gc_item(ccobject *self, PyObject *key, PyObject *v, long now, int dt)
return PyDict_DelItem(self->data, key);
}
if (dt && v->ob_type->tp_basicsize >= sizeof(cPersistentObject) &&
((cPersistentObject*)v)->state==cPersistent_UPTODATE_STATE)
if (dt &&
(! PyExtensionClass_Check(v)) &&
((cPersistentObject*)v)->jar==self->jar /* I'm paranoid */ &&
((cPersistentObject*)v)->state==cPersistent_UPTODATE_STATE
)
{
now -= ((cPersistentObject*)v)->atime;
if (now < 0) now += 65536;
......@@ -281,13 +282,15 @@ static struct PyMethodDef cc_methods[] = {
};
static ccobject *
newccobject(int cache_size, int cache_age)
newccobject(PyObject *jar, int cache_size, int cache_age)
{
ccobject *self;
UNLESS(self = PyObject_NEW(ccobject, &Cctype)) return NULL;
if(self->data=PyDict_New())
{
self->jar=jar;
Py_INCREF(jar);
self->position=0;
self->cache_size=cache_size;
self->cache_age=cache_age < 1 ? 1 : cache_age;
......@@ -312,6 +315,7 @@ static void
cc_dealloc(ccobject *self)
{
Py_XDECREF(self->data);
Py_XDECREF(self->jar);
PyMem_DEL(self);
}
......@@ -404,10 +408,24 @@ cc_subscript(ccobject *self, PyObject *key)
return r;
}
static PyExtensionClass *Persistent=0;
static int
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);
}
......@@ -446,8 +464,10 @@ static PyObject *
cCM_new(PyObject *self, PyObject *args)
{
int cache_size=100, cache_age=1000;
UNLESS(PyArg_ParseTuple(args, "|ii", &cache_size, &cache_age)) return NULL;
return (PyObject*)newccobject(cache_size,cache_age);
PyObject *jar;
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[] = {
......@@ -459,10 +479,20 @@ void
initcPickleCache()
{
PyObject *m, *d;
char *rev="$Revision: 1.16 $";
char *rev="$Revision: 1.17 $";
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, "",
(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
......@@ -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"
#define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;}
#define UNLESS(E) if(!(E))
#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
static PyObject *debug_log=0;
......@@ -35,8 +38,8 @@ call_debug(char *event, cPersistentObject *self)
/*
printf("%s %p\n",event,self->ob_type->tp_name);
*/
r=PyObject_CallFunction(debug_log,"s(ss#i)",event,
self->ob_type->tp_name, self->oid, 8,
r=PyObject_CallFunction(debug_log,"s(sOi)",event,
self->ob_type->tp_name, self->oid,
self->state);
Py_XDECREF(r);
}
......@@ -48,6 +51,7 @@ init_strings()
#define INIT_STRING(S) py_ ## S = PyString_FromString(#S)
INIT_STRING(keys);
INIT_STRING(setstate);
INIT_STRING(timeTime);
INIT_STRING(__dict__);
#undef INIT_STRING
}
......@@ -201,14 +205,16 @@ static PyObject *
Per___changed__(cPersistentObject *self, PyObject *args)
{
PyObject *v=0;
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,
"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);
return Py_None;
}
......@@ -368,35 +374,30 @@ Per_dealloc(self)
PyMem_DEL(self);
}
static PyObject *
orNothing(PyObject *v)
{
if (! v) v=Py_None;
Py_INCREF(v);
return v;
}
static PyObject *
Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
PyObject *(*getattrf)(PyObject *, PyObject*))
{
char *n=name;
if(*n++=='_')
if(n && *n++=='_')
if(*n++=='p' && *n++=='_')
{
switch(*n++)
{
case 'o':
if(*n++=='i' && *n++=='d' && ! *n)
return PyString_FromStringAndSize(self->oid, 8);
if(*n++=='i' && *n++=='d' && ! *n) return orNothing(self->oid);
break;
case 'j':
if(*n++=='a' && *n++=='r' && ! *n)
{
if(self->jar)
{
Py_INCREF(self->jar);
return self->jar;
}
else
{
Py_INCREF(Py_None);
return Py_None;
}
}
if(*n++=='a' && *n++=='r' && ! *n) return orNothing(self->jar);
break;
case 'c':
if(strcmp(n,"hanged")==0)
......@@ -410,11 +411,32 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
cPersistent_CHANGED_STATE);
}
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);
}
if(! (*name++=='_' && *name++=='_' &&
if(! (name && *name++=='_' && *name++=='_' &&
(strcmp(name,"dict__")==0 || strcmp(name,"class__")==0
|| strcmp(name, "of__")==0)))
{
......@@ -429,10 +451,11 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
static PyObject*
Per_getattro(cPersistentObject *self, PyObject *name)
{
char *s;
char *s=NULL;
if (PyString_Check(name))
UNLESS(s=PyString_AsString(name)) return NULL;
return Per_getattr(self,name,s, PyExtensionClassCAPI->getattro);
return Per_getattr(self, name, s, PyExtensionClassCAPI->getattro);
}
static int
......@@ -456,27 +479,36 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v,
{
if(name[3]=='o' && name[4]=='i' && name[5]=='d' && ! name[6])
{
if (! v) return bad_delattr();
if (PyString_Check(v) && PyString_GET_SIZE(v)==8)
memcpy(self->oid, PyString_AS_STRING(v), 8);
else
{
PyErr_SetString(PyExc_AttributeError,
"_p_oid must be an 8-character string");
return -1;
}
Py_XINCREF(v);
ASSIGN(self->oid, v);
return 0;
}
if(name[3]=='j' && name[4]=='a' && name[5]=='r' && ! name[6])
{
Py_XINCREF(v);
ASSIGN(self->jar, v);
Py_XINCREF(self->jar);
return 0;
}
if(strcmp(name+3,"changed")==0)
if(name[3]=='s' && strcmp(name+4,"erial")==0)
{
if (v)
{
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) return bad_delattr();
if (v==Py_None)
if (! v || v==Py_None)
{
if (Per__p_deactivate(self, NULL)) Py_DECREF(Py_None);
return 0;
......@@ -585,7 +617,14 @@ void
initcPersistence()
{
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,
"",
......
/*
$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.
......@@ -15,9 +15,11 @@
#define cPersistent_HEAD PyObject_HEAD \
PyObject *jar; \
char oid[8]; \
PyObject *oid; \
char serial[8]; \
unsigned short atime; \
signed char state; \
unsigned char reserved; \
#define cPersistent_GHOST_STATE -1
#define cPersistent_UPTODATE_STATE 0
......@@ -43,6 +45,8 @@ typedef struct {
static cPersistenceCAPIstruct *cPersistenceCAPI;
#define cPersistanceModuleName "cPersistence"
#define PER_USE_OR_RETURN(O,R) { \
if ((O)->state==cPersistent_GHOST_STATE && \
cPersistenceCAPI->setstate((PyObject*)(O)) < 0) \
......@@ -57,6 +61,10 @@ static cPersistenceCAPIstruct *cPersistenceCAPI;
((O)->state==cPersistent_STICKY_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)
#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 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
static PyObject *py_reload, *py__p_jar, *py__p_deactivate;
/* Declarations for objects of type cCache */
typedef struct {
PyObject_HEAD
PyObject *data;
PyObject *jar;
int position;
int cache_size;
int cache_age;
......@@ -65,8 +63,11 @@ gc_item(ccobject *self, PyObject *key, PyObject *v, long now, int dt)
return PyDict_DelItem(self->data, key);
}
if (dt && v->ob_type->tp_basicsize >= sizeof(cPersistentObject) &&
((cPersistentObject*)v)->state==cPersistent_UPTODATE_STATE)
if (dt &&
(! PyExtensionClass_Check(v)) &&
((cPersistentObject*)v)->jar==self->jar /* I'm paranoid */ &&
((cPersistentObject*)v)->state==cPersistent_UPTODATE_STATE
)
{
now -= ((cPersistentObject*)v)->atime;
if (now < 0) now += 65536;
......@@ -281,13 +282,15 @@ static struct PyMethodDef cc_methods[] = {
};
static ccobject *
newccobject(int cache_size, int cache_age)
newccobject(PyObject *jar, int cache_size, int cache_age)
{
ccobject *self;
UNLESS(self = PyObject_NEW(ccobject, &Cctype)) return NULL;
if(self->data=PyDict_New())
{
self->jar=jar;
Py_INCREF(jar);
self->position=0;
self->cache_size=cache_size;
self->cache_age=cache_age < 1 ? 1 : cache_age;
......@@ -312,6 +315,7 @@ static void
cc_dealloc(ccobject *self)
{
Py_XDECREF(self->data);
Py_XDECREF(self->jar);
PyMem_DEL(self);
}
......@@ -404,10 +408,24 @@ cc_subscript(ccobject *self, PyObject *key)
return r;
}
static PyExtensionClass *Persistent=0;
static int
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);
}
......@@ -446,8 +464,10 @@ static PyObject *
cCM_new(PyObject *self, PyObject *args)
{
int cache_size=100, cache_age=1000;
UNLESS(PyArg_ParseTuple(args, "|ii", &cache_size, &cache_age)) return NULL;
return (PyObject*)newccobject(cache_size,cache_age);
PyObject *jar;
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[] = {
......@@ -459,10 +479,20 @@ void
initcPickleCache()
{
PyObject *m, *d;
char *rev="$Revision: 1.16 $";
char *rev="$Revision: 1.17 $";
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, "",
(PyObject*)NULL,PYTHON_API_VERSION);
......
......@@ -48,12 +48,12 @@
__doc__='''Python implementation of persistent base types
$Id: mapping.py,v 1.3 1998/11/11 02:00:56 jim Exp $'''
__version__='$Revision: 1.3 $'[11:-2]
$Id: mapping.py,v 1.4 1999/05/07 01:03:03 jim Exp $'''
__version__='$Revision: 1.4 $'[11:-2]
import Persistence
class PM(Persistence.Persistent):
class PersistentMapping(Persistence.Persistent):
"""A persistent wrapper for mapping objects.
This class allows wrapping of mapping objects so that
......@@ -113,4 +113,3 @@ class PM(Persistence.Persistent):
def values(self):
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