Commit d0c552ac authored by Jim Fulton's avatar Jim Fulton

Restored accurate items length. Added separate (untested) methods for

getting key max and min values.

Added (back) PER_USE macro to make persistence handling sane.
parent 8b49f51a
...@@ -85,11 +85,26 @@ ...@@ -85,11 +85,26 @@
static char BTree_module_documentation[] = static char BTree_module_documentation[] =
"" ""
"\n$Id: BTreeTemplate.c,v 1.2 2001/02/01 22:46:17 jim Exp $" "\n$Id: BTreeTemplate.c,v 1.3 2001/02/04 18:00:31 jim Exp $"
; ;
#include "cPersistence.h" #include "cPersistence.h"
/***************************************************************
The following are macros that ought to be in cPersistence.h */
#ifndef PER_USE
#define PER_USE(O) \
(((O)->state != cPersistent_GHOST_STATE \
|| (cPersistenceCAPI->setstate((PyObject*)(O)) >= 0)) \
? (((O)->state==cPersistent_UPTODATE_STATE) \
? ((O)->state=cPersistent_STICKY_STATE) : 1) : 0)
#define Ghost_Test(O) ((O)->state == cPersistent_GHOST_STATE)
#endif
/***************************************************************/
static void PyVar_Assign(PyObject **v, PyObject *e) { Py_XDECREF(*v); *v=e;} static void PyVar_Assign(PyObject **v, PyObject *e) { Py_XDECREF(*v); *v=e;}
#define ASSIGN(V,E) PyVar_Assign(&(V),(E)) #define ASSIGN(V,E) PyVar_Assign(&(V),(E))
#define ASSIGNC(V,E) (Py_INCREF((E)), PyVar_Assign(&(V),(E))) #define ASSIGNC(V,E) (Py_INCREF((E)), PyVar_Assign(&(V),(E)))
...@@ -124,13 +139,6 @@ typedef struct bucket_s { ...@@ -124,13 +139,6 @@ typedef struct bucket_s {
Item *data; Item *data;
} Bucket; } Bucket;
static int
useBucket(Bucket *b)
{
PER_USE_OR_RETURN(b, -1);
return 0;
}
staticforward PyExtensionClass BucketType; staticforward PyExtensionClass BucketType;
#define BUCKET(O) ((Bucket*)(O)) #define BUCKET(O) ((Bucket*)(O))
...@@ -314,20 +322,42 @@ BTreeItems_length_or_nonzero(BTreeItems *self, int nonzero) ...@@ -314,20 +322,42 @@ BTreeItems_length_or_nonzero(BTreeItems *self, int nonzero)
static int static int
BTreeItems_length( BTreeItems *self) BTreeItems_length( BTreeItems *self)
{ {
/* Waaa. We need to lie about the length. return BTreeItems_length_or_nonzero(self, 0);
}
Getting the length could be quite expensive.
Python calls length if we do: x[-1], and we want static int
x[-1] to be cheap, as it's a useful way to get the firstBucketOffset(Bucket **bucket, int *offset)
maximum key in a range of keys. {
Bucket *b;
If we didn't provide a length, Python would
not use it and pass the negative index to us, *offset = (*bucket)->len - 1;
but then list and map wouldn't work, as they insist while ((*bucket)->len < 1)
on being able to get a length and do the right thing {
even when the length is a lie! b=(*bucket)->next;
*/ if (b==NULL) return 0;
return 0; /*BTreeItems_length_or_nonzero(self, 0);*/ Py_INCREF(b);
PER_ALLOW_DEACTIVATION((*bucket));
ASSIGNB((*bucket), b);
UNLESS (PER_USE(*bucket)) return -1;
*offset = 0;
}
}
static int
lastBucketOffset(Bucket **bucket, int *offset, Bucket *firstbucket, int i)
{
Bucket *b;
*offset = (*bucket)->len - 1;
while ((*bucket)->len < 1)
{
b=PreviousBucket((*bucket), firstbucket, i);
if (b==NULL) return 0;
PER_ALLOW_DEACTIVATION((*bucket));
ASSIGNB((*bucket), b);
UNLESS (PER_USE(*bucket)) return -1;
*offset = (*bucket)->len - 1;
}
} }
/* /*
...@@ -362,21 +392,15 @@ BTreeItems_seek(BTreeItems *self, int i) ...@@ -362,21 +392,15 @@ BTreeItems_seek(BTreeItems *self, int i)
Py_INCREF(currentbucket); Py_INCREF(currentbucket);
currentoffset = self->first; currentoffset = self->first;
if (useBucket(currentbucket) < 0) goto err; UNLESS (PER_USE(currentbucket)) goto err;
/* We need to be careful that we have a valid offset! */ /* We need to be careful that we have a valid offset! */
if (currentoffset >= currentbucket->len) if (currentoffset >= currentbucket->len)
{ {
currentoffset = currentbucket->len - 1; switch (firstBucketOffset(&currentbucket, &currentoffset))
while (currentbucket->len < 1)
{ {
b=currentbucket->next; case 0: goto no_match;
if (b==NULL) goto no_match; case -1: goto err;
Py_INCREF(b);
PER_ALLOW_DEACTIVATION(currentbucket);
ASSIGNB(currentbucket, b);
if (useBucket(currentbucket) < 0) goto err;
currentoffset = 0;
} }
} }
pseudoindex = 0; pseudoindex = 0;
...@@ -386,27 +410,23 @@ BTreeItems_seek(BTreeItems *self, int i) ...@@ -386,27 +410,23 @@ BTreeItems_seek(BTreeItems *self, int i)
/* Position to the end of the sequence. */ /* Position to the end of the sequence. */
ASSIGNBC(currentbucket, self->lastbucket); ASSIGNBC(currentbucket, self->lastbucket);
currentoffset = self->last; currentoffset = self->last;
if (useBucket(currentbucket) < 0) goto err; UNLESS (PER_USE(currentbucket)) goto err;
/* We need to be careful that we have a valid offset! */ /* We need to be careful that we have a valid offset! */
if (currentoffset >= currentbucket->len) if (currentoffset >= currentbucket->len)
{ {
currentoffset = currentbucket->len - 1; switch (lastBucketOffset(&currentbucket, &currentoffset,
while (currentbucket->len < 1) self->firstbucket, i))
{ {
b=PreviousBucket(currentbucket, self->firstbucket, i); case 0: goto no_match;
if (b==NULL) goto no_match; case -1: goto err;
PER_ALLOW_DEACTIVATION(currentbucket);
ASSIGNB(currentbucket, b);
if (useBucket(currentbucket) < 0) goto err;
currentoffset = currentbucket->len - 1;
} }
} }
pseudoindex = -1; pseudoindex = -1;
} }
else else
{ {
if (useBucket(currentbucket) < 0) goto err; UNLESS (PER_USE(currentbucket)) goto err;
/* We need to be careful that we have a valid offset! */ /* We need to be careful that we have a valid offset! */
if (currentoffset >= currentbucket->len) goto no_match; if (currentoffset >= currentbucket->len) goto no_match;
...@@ -431,7 +451,7 @@ BTreeItems_seek(BTreeItems *self, int i) ...@@ -431,7 +451,7 @@ BTreeItems_seek(BTreeItems *self, int i)
PER_ALLOW_DEACTIVATION(currentbucket); PER_ALLOW_DEACTIVATION(currentbucket);
ASSIGNB(currentbucket, b); ASSIGNB(currentbucket, b);
if (useBucket(currentbucket) < 0) goto err; UNLESS (PER_USE(currentbucket)) goto err;
delta += currentoffset; delta += currentoffset;
pseudoindex -= currentoffset + 1; pseudoindex -= currentoffset + 1;
...@@ -468,7 +488,7 @@ BTreeItems_seek(BTreeItems *self, int i) ...@@ -468,7 +488,7 @@ BTreeItems_seek(BTreeItems *self, int i)
Py_INCREF(b); Py_INCREF(b);
PER_ALLOW_DEACTIVATION(currentbucket); PER_ALLOW_DEACTIVATION(currentbucket);
ASSIGNB(currentbucket, b); ASSIGNB(currentbucket, b);
if (useBucket(currentbucket) < 0) goto err; UNLESS (PER_USE(currentbucket)) goto err;
currentoffset = 0; currentoffset = 0;
if (currentbucket->len) break; if (currentbucket->len) break;
} }
...@@ -691,48 +711,6 @@ Twople(PyObject *i1, PyObject *i2) ...@@ -691,48 +711,6 @@ Twople(PyObject *i1, PyObject *i2)
return t; return t;
} }
/*
** BTree_ini
**
** Broadtree intializer; allocates the inital bucket
**
** Arguments: self The BTree to initialize
**
** Returns: 0 on success
** -1 on error
*/
static int
BTree_ini(BTree *self)
{
PyObject *b;
UNLESS (b=PyObject_CallObject(OBJECT(&BucketType), NULL)) return -1;
self->data->value=b;
self->len=1;
self->firstbucket = BUCKET(b);
Py_INCREF(b);
return 0;
}
/*
** BTree_init
**
** BTree Initializer; allocates space for 2 BTreeItems
**
** Arguments: self The BTree
**
** Returns: 0 on success
** -1 on error (from BTree_ini)
*/
static int
BTree_init(BTree *self)
{
UNLESS (self->data=PyMalloc(sizeof(BTreeItem)*2)) return -1;
self->size=2;
return BTree_ini(self);
}
/* /*
** _bucket_get ** _bucket_get
** **
...@@ -831,40 +809,48 @@ _BTree_get(BTree *self, PyObject *keyarg ...@@ -831,40 +809,48 @@ _BTree_get(BTree *self, PyObject *keyarg
PER_USE_OR_RETURN(self, NULL); PER_USE_OR_RETURN(self, NULL);
UNLESS (self->data) if (BTree_init(self) < 0) goto err; if (self->len)
for (min=0, max=self->len, i=max/2; max-min > 1; i=(min+max)/2)
{ {
cmp=TEST_KEY(self->data[i].key, key); for (min=0, max=self->len, i=max/2; max-min > 1; i=(min+max)/2)
if (cmp < 0) min=i; {
else if (cmp == 0) cmp=TEST_KEY(self->data[i].key, key);
{ if (cmp < 0) min=i;
min=i; else if (cmp == 0)
break; {
} min=i;
else max=i; break;
} }
else max=i;
if (Bucket_Check(self->data[min].value)) }
r=_bucket_get(BUCKET(self->data[min].value), keyarg
if (Bucket_Check(self->data[min].value))
r=_bucket_get(BUCKET(self->data[min].value), keyarg
#ifndef NOVAL #ifndef NOVAL
, has_key , has_key
#endif #endif
); );
else else
r=_BTree_get( BTREE(self->data[min].value), keyarg r=_BTree_get( BTREE(self->data[min].value), keyarg
#ifndef NOVAL #ifndef NOVAL
, has_key , has_key
#endif #endif
); );
}
else
{ /* No data */
#ifndef NOVAL
UNLESS (has_key)
{
PyErr_SetObject(PyExc_KeyError, keyarg);
r=NULL;
}
else
#endif
r=PyInt_FromLong(0);
}
PER_ALLOW_DEACTIVATION(self); PER_ALLOW_DEACTIVATION(self);
return r; return r;
err:
PER_ALLOW_DEACTIVATION(self);
return NULL;
} }
static PyObject * static PyObject *
...@@ -1512,6 +1498,55 @@ Bucket_findRangeEnd(Bucket *self, PyObject *keyarg, int low, int *offset) ...@@ -1512,6 +1498,55 @@ Bucket_findRangeEnd(Bucket *self, PyObject *keyarg, int low, int *offset)
return i; return i;
} }
static PyObject *
Bucket_maxminKey(Bucket *self, PyObject *args, int min)
{
PyObject *key=0;
int rc, offset;
if (args && ! PyArg_ParseTuple(args, "|O", &key)) return NULL;
PER_USE_OR_RETURN(self, NULL);
UNLESS (self->data && self->len) goto empty;
/* Find the low range */
if (key)
{
if ((rc = Bucket_findRangeEnd(self, key, min, &offset)) <= 0)
{
if (rc < 0) return NULL;
goto empty;
}
}
else if (min) offset = 0;
else offset = self->len -1;
COPY_KEY_TO_OBJECT(key, self->data[offset].key);
PER_ALLOW_DEACTIVATION(self);
return key;
empty:
PyErr_SetString(PyExc_ValueError, "empty bucket");
err:
PER_ALLOW_DEACTIVATION(self);
return NULL;
}
static PyObject *
Bucket_minKey(Bucket *self, PyObject *args)
{
return Bucket_maxminKey(self, args, 1);
}
static PyObject *
Bucket_maxKey(Bucket *self, PyObject *args)
{
return Bucket_maxminKey(self, args, 0);
}
static int static int
Bucket_rangeSearch(Bucket *self, PyObject *args, int *low, int *high) Bucket_rangeSearch(Bucket *self, PyObject *args, int *low, int *high)
{ {
...@@ -1761,17 +1796,17 @@ _BTree_clear(BTree *self) ...@@ -1761,17 +1796,17 @@ _BTree_clear(BTree *self)
{ {
int i; int i;
UNLESS (self->data) return 0;
for (i=self->len; --i >= 0; ) for (i=self->len; --i >= 0; )
{ {
DECREF_KEY(self->data[i].key); if (i) DECREF_KEY(self->data[i].key);
Py_DECREF(self->data[i].value); Py_DECREF(self->data[i].value);
} }
i=BTree_ini(self); Py_XDECREF(self->firstbucket);
self->firstbucket=NULL;
self->len=0;
return i; return 0;
} }
/* /*
...@@ -2006,15 +2041,19 @@ static struct PyMethodDef Bucket_methods[] = { ...@@ -2006,15 +2041,19 @@ static struct PyMethodDef Bucket_methods[] = {
"keys() -- Return the keys"}, "keys() -- Return the keys"},
{"has_key", (PyCFunction) bucket_has_key, METH_VARARGS, {"has_key", (PyCFunction) bucket_has_key, METH_VARARGS,
"has_key(key) -- Test whether the bucket contains the given key"}, "has_key(key) -- Test whether the bucket contains the given key"},
{"clear", (PyCFunction) bucket_clear, METH_VARARGS,
"clear() -- Remove all of the items from the bucket"},
{"maxKey", (PyCFunction) Bucket_maxKey, METH_VARARGS,
"maxKey([key]) -- Fine the maximum key\n\n"
"If an argument is given, find the maximum <= the argument"},
{"minKey", (PyCFunction) Bucket_minKey, METH_VARARGS,
"minKey([key]) -- Fine the minimum key\n\n"
"If an argument is given, find the minimum >= the argument"},
#ifndef NOVAL #ifndef NOVAL
{"values", (PyCFunction) bucket_values, METH_VARARGS, {"values", (PyCFunction) bucket_values, METH_VARARGS,
"values() -- Return the values"}, "values() -- Return the values"},
{"items", (PyCFunction) bucket_items, METH_VARARGS, {"items", (PyCFunction) bucket_items, METH_VARARGS,
"items() -- Return the items"}, "items() -- Return the items"},
#endif
{"clear", (PyCFunction) bucket_clear, METH_VARARGS,
"clear() -- Remove all of the items from the bucket"},
#ifndef NOVAL
{"get", (PyCFunction) bucket_getm, METH_VARARGS, {"get", (PyCFunction) bucket_getm, METH_VARARGS,
"get(key[,default]) -- Look up a value\n\n" "get(key[,default]) -- Look up a value\n\n"
"Return the default (or None) if the key is not found." "Return the default (or None) if the key is not found."
...@@ -2189,6 +2228,94 @@ BTree_findRangeEnd(BTree *self, PyObject *keyarg, int low, ...@@ -2189,6 +2228,94 @@ BTree_findRangeEnd(BTree *self, PyObject *keyarg, int low,
return i; return i;
} }
static PyObject *
BTree_maxminKey(BTree *self, PyObject *args, int min)
{
PyObject *key=0;
Bucket *bucket = NULL;
int offset, rc;
UNLESS (PyArg_ParseTuple(args, "|O", &key)) return NULL;
PER_USE_OR_RETURN(self, NULL);
UNLESS (self->data && self->len) goto empty;
/* Find the range */
if (key)
{
if ((rc = BTree_findRangeEnd(self, key, min, &bucket, &offset)) <= 0)
{
if (rc < 0) goto err;
goto empty;
}
PER_ALLOW_DEACTIVATION(self);
PER_USE_OR_RETURN(bucket, NULL);
}
else if (min)
{
bucket = self->firstbucket;
Py_INCREF(bucket);
PER_ALLOW_DEACTIVATION(self);
PER_USE_OR_RETURN(bucket, NULL);
offset = 0;
if (offset >= bucket->len)
{
switch (firstBucketOffset(&bucket, &offset))
{
case 0: goto empty;
case -1: goto err;
}
}
}
else
{
bucket = BTree_lastBucket(self);
PER_ALLOW_DEACTIVATION(self);
PER_USE_OR_RETURN(bucket, NULL);
if (bucket->len)
offset = bucket->len - 1;
else
{
switch (lastBucketOffset(&bucket, &offset, self->firstbucket, -1))
{
case 0: goto empty;
case -1: goto err;
}
}
}
COPY_KEY_TO_OBJECT(key, bucket->data[offset].key);
PER_ALLOW_DEACTIVATION(bucket);
Py_DECREF(bucket);
return key;
empty:
PyErr_SetString(PyExc_ValueError, "empty tree");
err:
PER_ALLOW_DEACTIVATION(self);
if (bucket)
{
PER_ALLOW_DEACTIVATION(bucket);
Py_DECREF(bucket);
}
return NULL;
}
static PyObject *
BTree_minKey(BTree *self, PyObject *args)
{
return BTree_maxminKey(self, args, 1);
}
static PyObject *
BTree_maxKey(BTree *self, PyObject *args)
{
return BTree_maxminKey(self, args, 0);
}
/* /*
** BTree_rangeSearch ** BTree_rangeSearch
...@@ -2221,7 +2348,7 @@ BTree_rangeSearch(BTree *self, PyObject *args ...@@ -2221,7 +2348,7 @@ BTree_rangeSearch(BTree *self, PyObject *args
if (f && f != Py_None) if (f && f != Py_None)
{ {
UNLESS (rc = BTree_findRangeEnd(self, f, 1, &lowbucket, &lowoffset)) if ((rc = BTree_findRangeEnd(self, f, 1, &lowbucket, &lowoffset)) <= 0)
{ {
if (rc < 0) goto err; if (rc < 0) goto err;
goto empty; goto empty;
...@@ -2238,8 +2365,7 @@ BTree_rangeSearch(BTree *self, PyObject *args ...@@ -2238,8 +2365,7 @@ BTree_rangeSearch(BTree *self, PyObject *args
if (l && l != Py_None) if (l && l != Py_None)
{ {
UNLESS (rc = BTree_findRangeEnd(self, l, 0, if ((rc = BTree_findRangeEnd(self, l, 0, &highbucket, &highoffset)) <= 0)
&highbucket, &highoffset))
{ {
Py_DECREF(lowbucket); Py_DECREF(lowbucket);
if (rc < 0) goto err; if (rc < 0) goto err;
...@@ -2338,7 +2464,7 @@ BTree_has_key(BTree *self, PyObject *args) ...@@ -2338,7 +2464,7 @@ BTree_has_key(BTree *self, PyObject *args)
} }
static PyObject * static PyObject *
BTree_addUnique(BTree *self, PyObject *args) BTree_addUnique(BTree *self, PyObject *args)
{ {
int grew; int grew;
PyObject *key, *v; PyObject *key, *v;
...@@ -2373,6 +2499,12 @@ static struct PyMethodDef BTree_methods[] = { ...@@ -2373,6 +2499,12 @@ static struct PyMethodDef BTree_methods[] = {
"get(key[,default]) -- Look up a value\n\n" "get(key[,default]) -- Look up a value\n\n"
"Return the default (or None) if the key is not found." "Return the default (or None) if the key is not found."
}, },
{"maxKey", (PyCFunction) BTree_maxKey, METH_VARARGS,
"maxKey([key]) -- Fine the maximum key\n\n"
"If an argument is given, find the maximum <= the argument"},
{"minKey", (PyCFunction) BTree_minKey, METH_VARARGS,
"minKey([key]) -- Fine the minimum key\n\n"
"If an argument is given, find the minimum >= the argument"},
#endif #endif
{"clear", (PyCFunction) BTree_clear, METH_VARARGS, {"clear", (PyCFunction) BTree_clear, METH_VARARGS,
"clear() -- Remove all of the items from the BTree"}, "clear() -- Remove all of the items from the BTree"},
...@@ -2612,7 +2744,7 @@ INITMODULE () ...@@ -2612,7 +2744,7 @@ INITMODULE ()
d = PyModule_GetDict(m); d = PyModule_GetDict(m);
PyDict_SetItemString(d, "__version__", PyDict_SetItemString(d, "__version__",
PyString_FromString("$Revision: 1.2 $")); PyString_FromString("$Revision: 1.3 $"));
PyExtensionClass_Export(d,PREFIX "Bucket",BucketType); PyExtensionClass_Export(d,PREFIX "Bucket",BucketType);
PyExtensionClass_Export(d,PREFIX "BTree",BTreeType); PyExtensionClass_Export(d,PREFIX "BTree",BTreeType);
......
...@@ -147,7 +147,7 @@ class TestBTrees(TestCase): ...@@ -147,7 +147,7 @@ class TestBTrees(TestCase):
assert x == i, (x,i) assert x == i, (x,i)
i = i + 1 i = i + 1
# BTree items must lie about their lengths, so we convert to list # BTree items must lie about their lengths, so we convert to list
assert len(list(v)) == 100, len(v) assert len(v) == 100, len(v)
#assert len(v) == 100, len(v) #assert len(v) == 100, len(v)
def testItemsWorks(self): def testItemsWorks(self):
......
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