tested django-newsletter
This commit is contained in:
@@ -0,0 +1,790 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
All Rights Reserved.
|
||||
|
||||
This software is subject to the provisions of the Zope Public License,
|
||||
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#define BTREEITEMSTEMPLATE_C "$Id$\n"
|
||||
|
||||
/* A BTreeItems struct is returned from calling .items(), .keys() or
|
||||
* .values() on a BTree-based data structure, and is also the result of
|
||||
* taking slices of those. It represents a contiguous slice of a BTree.
|
||||
*
|
||||
* The start of the slice is in firstbucket, at offset first. The end of
|
||||
* the slice is in lastbucket, at offset last. Both endpoints are inclusive.
|
||||
* It must possible to get from firstbucket to lastbucket via following
|
||||
* bucket 'next' pointers zero or more times. firstbucket, first, lastbucket,
|
||||
* and last are readonly after initialization. An empty slice is represented
|
||||
* by firstbucket == lastbucket == currentbucket == NULL.
|
||||
*
|
||||
* 'kind' determines whether this slice represents 'k'eys alone, 'v'alues
|
||||
* alone, or 'i'items (key+value pairs). 'kind' is also readonly after
|
||||
* initialization.
|
||||
*
|
||||
* The combination of currentbucket, currentoffset and pseudoindex acts as
|
||||
* a search finger. Offset currentoffset in bucket currentbucket is at index
|
||||
* pseudoindex, where pseudoindex==0 corresponds to offset first in bucket
|
||||
* firstbucket, and pseudoindex==-1 corresponds to offset last in bucket
|
||||
* lastbucket. The function BTreeItems_seek() can be used to set this combo
|
||||
* correctly for any in-bounds index, and uses this combo on input to avoid
|
||||
* needing to search from the start (or end) on each call. Calling
|
||||
* BTreeItems_seek() with consecutive larger positions is very efficent.
|
||||
* Calling it with consecutive smaller positions is more efficient than if
|
||||
* a search finger weren't being used at all, but is still quadratic time
|
||||
* in the number of buckets in the slice.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
PyObject_HEAD
|
||||
Bucket *firstbucket; /* First bucket */
|
||||
Bucket *currentbucket; /* Current bucket (search finger) */
|
||||
Bucket *lastbucket; /* Last bucket */
|
||||
int currentoffset; /* Offset in currentbucket */
|
||||
int pseudoindex; /* search finger index */
|
||||
int first; /* Start offset in firstbucket */
|
||||
int last; /* End offset in lastbucket */
|
||||
char kind; /* 'k', 'v', 'i' */
|
||||
} BTreeItems;
|
||||
|
||||
#define ITEMS(O)((BTreeItems*)(O))
|
||||
|
||||
static PyObject *
|
||||
newBTreeItems(char kind,
|
||||
Bucket *lowbucket, int lowoffset,
|
||||
Bucket *highbucket, int highoffset);
|
||||
|
||||
static void
|
||||
BTreeItems_dealloc(BTreeItems *self)
|
||||
{
|
||||
Py_XDECREF(self->firstbucket);
|
||||
Py_XDECREF(self->lastbucket);
|
||||
Py_XDECREF(self->currentbucket);
|
||||
PyObject_DEL(self);
|
||||
}
|
||||
|
||||
static Py_ssize_t
|
||||
BTreeItems_length_or_nonzero(BTreeItems *self, int nonzero)
|
||||
{
|
||||
Py_ssize_t r;
|
||||
Bucket *b, *next;
|
||||
|
||||
b = self->firstbucket;
|
||||
if (b == NULL)
|
||||
return 0;
|
||||
|
||||
r = self->last + 1 - self->first;
|
||||
|
||||
if (nonzero && r > 0)
|
||||
/* Short-circuit if all we care about is nonempty */
|
||||
return 1;
|
||||
|
||||
if (b == self->lastbucket)
|
||||
return r;
|
||||
|
||||
Py_INCREF(b);
|
||||
PER_USE_OR_RETURN(b, -1);
|
||||
while ((next = b->next))
|
||||
{
|
||||
r += b->len;
|
||||
if (nonzero && r > 0)
|
||||
/* Short-circuit if all we care about is nonempty */
|
||||
break;
|
||||
|
||||
if (next == self->lastbucket)
|
||||
break; /* we already counted the last bucket */
|
||||
|
||||
Py_INCREF(next);
|
||||
PER_UNUSE(b);
|
||||
Py_DECREF(b);
|
||||
b = next;
|
||||
PER_USE_OR_RETURN(b, -1);
|
||||
}
|
||||
PER_UNUSE(b);
|
||||
Py_DECREF(b);
|
||||
|
||||
return r >= 0 ? r : 0;
|
||||
}
|
||||
|
||||
static Py_ssize_t
|
||||
BTreeItems_length(BTreeItems *self)
|
||||
{
|
||||
return BTreeItems_length_or_nonzero(self, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** BTreeItems_seek
|
||||
**
|
||||
** Find the ith position in the BTreeItems.
|
||||
**
|
||||
** Arguments: self The BTree
|
||||
** i the index to seek to, in 0 .. len(self)-1, or in
|
||||
** -len(self) .. -1, as for indexing a Python sequence.
|
||||
**
|
||||
**
|
||||
** Returns 0 if successful, -1 on failure to seek (like out-of-bounds).
|
||||
** Upon successful return, index i is at offset self->currentoffset in bucket
|
||||
** self->currentbucket.
|
||||
*/
|
||||
static int
|
||||
BTreeItems_seek(BTreeItems *self, Py_ssize_t i)
|
||||
{
|
||||
int delta, pseudoindex, currentoffset;
|
||||
Bucket *b, *currentbucket;
|
||||
int error;
|
||||
|
||||
pseudoindex = self->pseudoindex;
|
||||
currentoffset = self->currentoffset;
|
||||
currentbucket = self->currentbucket;
|
||||
if (currentbucket == NULL)
|
||||
goto no_match;
|
||||
|
||||
delta = i - pseudoindex;
|
||||
while (delta > 0) /* move right */
|
||||
{
|
||||
int max;
|
||||
/* Want to move right delta positions; the most we can move right in
|
||||
* this bucket is currentbucket->len - currentoffset - 1 positions.
|
||||
*/
|
||||
PER_USE_OR_RETURN(currentbucket, -1);
|
||||
max = currentbucket->len - currentoffset - 1;
|
||||
b = currentbucket->next;
|
||||
PER_UNUSE(currentbucket);
|
||||
if (delta <= max)
|
||||
{
|
||||
currentoffset += delta;
|
||||
pseudoindex += delta;
|
||||
if (currentbucket == self->lastbucket
|
||||
&& currentoffset > self->last)
|
||||
goto no_match;
|
||||
break;
|
||||
}
|
||||
/* Move to start of next bucket. */
|
||||
if (currentbucket == self->lastbucket || b == NULL)
|
||||
goto no_match;
|
||||
currentbucket = b;
|
||||
pseudoindex += max + 1;
|
||||
delta -= max + 1;
|
||||
currentoffset = 0;
|
||||
}
|
||||
while (delta < 0) /* move left */
|
||||
{
|
||||
int status;
|
||||
/* Want to move left -delta positions; the most we can move left in
|
||||
* this bucket is currentoffset positions.
|
||||
*/
|
||||
if ((-delta) <= currentoffset)
|
||||
{
|
||||
currentoffset += delta;
|
||||
pseudoindex += delta;
|
||||
if (currentbucket == self->firstbucket
|
||||
&& currentoffset < self->first)
|
||||
goto no_match;
|
||||
break;
|
||||
}
|
||||
/* Move to end of previous bucket. */
|
||||
if (currentbucket == self->firstbucket)
|
||||
goto no_match;
|
||||
status = PreviousBucket(¤tbucket, self->firstbucket);
|
||||
if (status == 0)
|
||||
goto no_match;
|
||||
else if (status < 0)
|
||||
return -1;
|
||||
pseudoindex -= currentoffset + 1;
|
||||
delta += currentoffset + 1;
|
||||
PER_USE_OR_RETURN(currentbucket, -1);
|
||||
currentoffset = currentbucket->len - 1;
|
||||
PER_UNUSE(currentbucket);
|
||||
}
|
||||
|
||||
assert(pseudoindex == i);
|
||||
|
||||
/* Alas, the user may have mutated the bucket since the last time we
|
||||
* were called, and if they deleted stuff, we may be pointing into
|
||||
* trash memory now.
|
||||
*/
|
||||
PER_USE_OR_RETURN(currentbucket, -1);
|
||||
error = currentoffset < 0 || currentoffset >= currentbucket->len;
|
||||
PER_UNUSE(currentbucket);
|
||||
if (error)
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"the bucket being iterated changed size");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Py_INCREF(currentbucket);
|
||||
Py_DECREF(self->currentbucket);
|
||||
self->currentbucket = currentbucket;
|
||||
self->currentoffset = currentoffset;
|
||||
self->pseudoindex = pseudoindex;
|
||||
return 0;
|
||||
|
||||
no_match:
|
||||
IndexError(i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* Return the right kind ('k','v','i') of entry from bucket b at offset i.
|
||||
* b must be activated. Returns NULL on error.
|
||||
*/
|
||||
static PyObject *
|
||||
getBucketEntry(Bucket *b, int i, char kind)
|
||||
{
|
||||
PyObject *result = NULL;
|
||||
|
||||
assert(b);
|
||||
assert(0 <= i && i < b->len);
|
||||
|
||||
switch (kind)
|
||||
{
|
||||
case 'k':
|
||||
COPY_KEY_TO_OBJECT(result, b->keys[i]);
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
COPY_VALUE_TO_OBJECT(result, b->values[i]);
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
{
|
||||
PyObject *key;
|
||||
PyObject *value;;
|
||||
|
||||
COPY_KEY_TO_OBJECT(key, b->keys[i]);
|
||||
if (!key)
|
||||
break;
|
||||
|
||||
COPY_VALUE_TO_OBJECT(value, b->values[i]);
|
||||
if (!value)
|
||||
{
|
||||
Py_DECREF(key);
|
||||
break;
|
||||
}
|
||||
|
||||
result = PyTuple_New(2);
|
||||
if (result)
|
||||
{
|
||||
PyTuple_SET_ITEM(result, 0, key);
|
||||
PyTuple_SET_ITEM(result, 1, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
Py_DECREF(key);
|
||||
Py_DECREF(value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
PyErr_SetString(PyExc_AssertionError,
|
||||
"getBucketEntry: unknown kind");
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
** BTreeItems_item
|
||||
**
|
||||
** Arguments: self a BTreeItems structure
|
||||
** i Which item to inspect
|
||||
**
|
||||
** Returns: the BTreeItems_item_BTree of self->kind, i
|
||||
** (ie pulls the ith item out)
|
||||
*/
|
||||
static PyObject *
|
||||
BTreeItems_item(BTreeItems *self, Py_ssize_t i)
|
||||
{
|
||||
PyObject *result;
|
||||
|
||||
if (BTreeItems_seek(self, i) < 0)
|
||||
return NULL;
|
||||
|
||||
PER_USE_OR_RETURN(self->currentbucket, NULL);
|
||||
result = getBucketEntry(self->currentbucket, self->currentoffset,
|
||||
self->kind);
|
||||
PER_UNUSE(self->currentbucket);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
** BTreeItems_slice
|
||||
**
|
||||
** Creates a new BTreeItems structure representing the slice
|
||||
** between the low and high range
|
||||
**
|
||||
** Arguments: self The old BTreeItems structure
|
||||
** ilow The start index
|
||||
** ihigh The end index
|
||||
**
|
||||
** Returns: BTreeItems item
|
||||
*/
|
||||
static PyObject *
|
||||
BTreeItems_slice(BTreeItems *self, Py_ssize_t ilow, Py_ssize_t ihigh)
|
||||
{
|
||||
Bucket *lowbucket;
|
||||
Bucket *highbucket;
|
||||
int lowoffset;
|
||||
int highoffset;
|
||||
Py_ssize_t length = -1; /* len(self), but computed only if needed */
|
||||
|
||||
/* Complications:
|
||||
* A Python slice never raises IndexError, but BTreeItems_seek does.
|
||||
* Python did only part of index normalization before calling this:
|
||||
* ilow may be < 0 now, and ihigh may be arbitrarily large. It's
|
||||
* our responsibility to clip them.
|
||||
* A Python slice is exclusive of the high index, but a BTreeItems
|
||||
* struct is inclusive on both ends.
|
||||
*/
|
||||
|
||||
/* First adjust ilow and ihigh to be legit endpoints in the Python
|
||||
* sense (ilow inclusive, ihigh exclusive). This block duplicates the
|
||||
* logic from Python's list_slice function (slicing for builtin lists).
|
||||
*/
|
||||
if (ilow < 0)
|
||||
ilow = 0;
|
||||
else
|
||||
{
|
||||
if (length < 0)
|
||||
length = BTreeItems_length(self);
|
||||
if (ilow > length)
|
||||
ilow = length;
|
||||
}
|
||||
|
||||
if (ihigh < ilow)
|
||||
ihigh = ilow;
|
||||
else
|
||||
{
|
||||
if (length < 0)
|
||||
length = BTreeItems_length(self);
|
||||
if (ihigh > length)
|
||||
ihigh = length;
|
||||
}
|
||||
assert(0 <= ilow && ilow <= ihigh);
|
||||
assert(length < 0 || ihigh <= length);
|
||||
|
||||
/* Now adjust for that our struct is inclusive on both ends. This is
|
||||
* easy *except* when the slice is empty: there's no good way to spell
|
||||
* that in an inclusive-on-both-ends scheme. For example, if the
|
||||
* slice is btree.items([:0]), ilow == ihigh == 0 at this point, and if
|
||||
* we were to subtract 1 from ihigh that would get interpreted by
|
||||
* BTreeItems_seek as meaning the *entire* set of items. Setting ilow==1
|
||||
* and ihigh==0 doesn't work either, as BTreeItems_seek raises IndexError
|
||||
* if we attempt to seek to ilow==1 when the underlying sequence is empty.
|
||||
* It seems simplest to deal with empty slices as a special case here.
|
||||
*/
|
||||
if (ilow == ihigh) /* empty slice */
|
||||
{
|
||||
lowbucket = highbucket = NULL;
|
||||
lowoffset = 1;
|
||||
highoffset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(ilow < ihigh);
|
||||
--ihigh; /* exclusive -> inclusive */
|
||||
|
||||
if (BTreeItems_seek(self, ilow) < 0)
|
||||
return NULL;
|
||||
lowbucket = self->currentbucket;
|
||||
lowoffset = self->currentoffset;
|
||||
|
||||
if (BTreeItems_seek(self, ihigh) < 0)
|
||||
return NULL;
|
||||
|
||||
highbucket = self->currentbucket;
|
||||
highoffset = self->currentoffset;
|
||||
}
|
||||
return newBTreeItems(self->kind,
|
||||
lowbucket, lowoffset, highbucket, highoffset);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
BTreeItems_subscript(BTreeItems *self, PyObject* subscript)
|
||||
{
|
||||
Py_ssize_t len = BTreeItems_length_or_nonzero(self, 0);
|
||||
|
||||
if (PyIndex_Check(subscript))
|
||||
{
|
||||
Py_ssize_t i = PyNumber_AsSsize_t(subscript, PyExc_IndexError);
|
||||
if (i == -1 && PyErr_Occurred())
|
||||
return NULL;
|
||||
if (i < 0)
|
||||
i += len;
|
||||
return BTreeItems_item(self, i);
|
||||
}
|
||||
if (PySlice_Check(subscript))
|
||||
{
|
||||
Py_ssize_t start, stop, step, slicelength;
|
||||
|
||||
#ifdef PY3K
|
||||
#define SLICEOBJ(x) (x)
|
||||
#else
|
||||
#define SLICEOBJ(x) (PySliceObject*)(x)
|
||||
#endif
|
||||
|
||||
if (PySlice_GetIndicesEx(SLICEOBJ(subscript), len,
|
||||
&start, &stop, &step, &slicelength) < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (step != 1)
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"slices must have step size of 1");
|
||||
return NULL;
|
||||
}
|
||||
return BTreeItems_slice(self, start, stop);
|
||||
}
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"Unknown index type: must be int or slice");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Py3K doesn't honor sequence slicing, so implement via mapping */
|
||||
static PyMappingMethods BTreeItems_as_mapping = {
|
||||
(lenfunc)BTreeItems_length, /* mp_length */
|
||||
(binaryfunc)BTreeItems_subscript, /* mp_subscript */
|
||||
};
|
||||
|
||||
static PySequenceMethods BTreeItems_as_sequence =
|
||||
{
|
||||
(lenfunc) BTreeItems_length, /* sq_length */
|
||||
(binaryfunc)0, /* sq_concat */
|
||||
(ssizeargfunc)0, /* sq_repeat */
|
||||
(ssizeargfunc) BTreeItems_item, /* sq_item */
|
||||
#ifndef PY3K
|
||||
/* Py3K doesn't honor this slot */
|
||||
(ssizessizeargfunc) BTreeItems_slice, /* sq_slice */
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Number Method items (just for nb_nonzero!) */
|
||||
|
||||
static int
|
||||
BTreeItems_nonzero(BTreeItems *self)
|
||||
{
|
||||
return BTreeItems_length_or_nonzero(self, 1);
|
||||
}
|
||||
|
||||
static PyNumberMethods BTreeItems_as_number_for_nonzero = {
|
||||
0, /* nb_add */
|
||||
0, /* nb_subtract */
|
||||
0, /* nb_multiply */
|
||||
#ifndef PY3K
|
||||
0, /* nb_divide */
|
||||
#endif
|
||||
0, /* nb_remainder */
|
||||
0, /* nb_divmod */
|
||||
0, /* nb_power */
|
||||
0, /* nb_negative */
|
||||
0, /* nb_positive */
|
||||
0, /* nb_absolute */
|
||||
(inquiry)BTreeItems_nonzero /* nb_nonzero */
|
||||
};
|
||||
|
||||
static PyTypeObject BTreeItemsType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
MOD_NAME_PREFIX "BTreeItems", /* tp_name */
|
||||
sizeof(BTreeItems), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
/* methods */
|
||||
(destructor) BTreeItems_dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* obsolete tp_getattr */
|
||||
0, /* obsolete tp_setattr */
|
||||
0, /* tp_compare */
|
||||
0, /* tp_repr */
|
||||
&BTreeItems_as_number_for_nonzero, /* tp_as_number */
|
||||
&BTreeItems_as_sequence, /* tp_as_sequence */
|
||||
&BTreeItems_as_mapping, /* 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 */
|
||||
};
|
||||
|
||||
/* Returns a new BTreeItems object representing the contiguous slice from
|
||||
* offset lowoffset in bucket lowbucket through offset highoffset in bucket
|
||||
* highbucket, inclusive. Pass lowbucket == NULL for an empty slice.
|
||||
* The currentbucket is set to lowbucket, currentoffset ot lowoffset, and
|
||||
* pseudoindex to 0. kind is 'k', 'v' or 'i' (see BTreeItems struct docs).
|
||||
*/
|
||||
static PyObject *
|
||||
newBTreeItems(char kind,
|
||||
Bucket *lowbucket, int lowoffset,
|
||||
Bucket *highbucket, int highoffset)
|
||||
{
|
||||
BTreeItems *self;
|
||||
|
||||
UNLESS (self = PyObject_NEW(BTreeItems, &BTreeItemsType))
|
||||
return NULL;
|
||||
self->kind=kind;
|
||||
|
||||
self->first=lowoffset;
|
||||
self->last=highoffset;
|
||||
|
||||
if (! lowbucket || ! highbucket
|
||||
|| (lowbucket == highbucket && lowoffset > highoffset))
|
||||
{
|
||||
self->firstbucket = 0;
|
||||
self->lastbucket = 0;
|
||||
self->currentbucket = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Py_INCREF(lowbucket);
|
||||
self->firstbucket = lowbucket;
|
||||
Py_INCREF(highbucket);
|
||||
self->lastbucket = highbucket;
|
||||
Py_INCREF(lowbucket);
|
||||
self->currentbucket = lowbucket;
|
||||
}
|
||||
|
||||
self->currentoffset = lowoffset;
|
||||
self->pseudoindex = 0;
|
||||
|
||||
return OBJECT(self);
|
||||
}
|
||||
|
||||
static int
|
||||
nextBTreeItems(SetIteration *i)
|
||||
{
|
||||
if (i->position >= 0)
|
||||
{
|
||||
if (i->position)
|
||||
{
|
||||
DECREF_KEY(i->key);
|
||||
DECREF_VALUE(i->value);
|
||||
}
|
||||
|
||||
if (BTreeItems_seek(ITEMS(i->set), i->position) >= 0)
|
||||
{
|
||||
Bucket *currentbucket;
|
||||
|
||||
currentbucket = BUCKET(ITEMS(i->set)->currentbucket);
|
||||
UNLESS(PER_USE(currentbucket))
|
||||
{
|
||||
/* Mark iteration terminated, so that finiSetIteration doesn't
|
||||
* try to redundantly decref the key and value
|
||||
*/
|
||||
i->position = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
COPY_KEY(i->key, currentbucket->keys[ITEMS(i->set)->currentoffset]);
|
||||
INCREF_KEY(i->key);
|
||||
|
||||
COPY_VALUE(i->value,
|
||||
currentbucket->values[ITEMS(i->set)->currentoffset]);
|
||||
INCREF_VALUE(i->value);
|
||||
|
||||
i->position ++;
|
||||
|
||||
PER_UNUSE(currentbucket);
|
||||
}
|
||||
else
|
||||
{
|
||||
i->position = -1;
|
||||
PyErr_Clear();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nextTreeSetItems(SetIteration *i)
|
||||
{
|
||||
if (i->position >= 0)
|
||||
{
|
||||
if (i->position)
|
||||
{
|
||||
DECREF_KEY(i->key);
|
||||
}
|
||||
|
||||
if (BTreeItems_seek(ITEMS(i->set), i->position) >= 0)
|
||||
{
|
||||
Bucket *currentbucket;
|
||||
|
||||
currentbucket = BUCKET(ITEMS(i->set)->currentbucket);
|
||||
UNLESS(PER_USE(currentbucket))
|
||||
{
|
||||
/* Mark iteration terminated, so that finiSetIteration doesn't
|
||||
* try to redundantly decref the key and value
|
||||
*/
|
||||
i->position = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
COPY_KEY(i->key, currentbucket->keys[ITEMS(i->set)->currentoffset]);
|
||||
INCREF_KEY(i->key);
|
||||
|
||||
i->position ++;
|
||||
|
||||
PER_UNUSE(currentbucket);
|
||||
}
|
||||
else
|
||||
{
|
||||
i->position = -1;
|
||||
PyErr_Clear();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Support for the iteration protocol new in Python 2.2. */
|
||||
|
||||
static PyTypeObject BTreeIter_Type;
|
||||
|
||||
/* The type of iterator objects, returned by e.g. iter(IIBTree()). */
|
||||
typedef struct
|
||||
{
|
||||
PyObject_HEAD
|
||||
/* We use a BTreeItems object because it's convenient and flexible.
|
||||
* We abuse it two ways:
|
||||
* 1. We set currentbucket to NULL when the iteration is finished.
|
||||
* 2. We don't bother keeping pseudoindex in synch.
|
||||
*/
|
||||
BTreeItems *pitems;
|
||||
} BTreeIter;
|
||||
|
||||
/* Return a new iterator object, to traverse the keys and/or values
|
||||
* represented by pitems. pitems must not be NULL. Returns NULL if error.
|
||||
*/
|
||||
static BTreeIter *
|
||||
BTreeIter_new(BTreeItems *pitems)
|
||||
{
|
||||
BTreeIter *result;
|
||||
|
||||
assert(pitems != NULL);
|
||||
result = PyObject_New(BTreeIter, &BTreeIter_Type);
|
||||
if (result)
|
||||
{
|
||||
Py_INCREF(pitems);
|
||||
result->pitems = pitems;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* The iterator's tp_dealloc slot. */
|
||||
static void
|
||||
BTreeIter_dealloc(BTreeIter *bi)
|
||||
{
|
||||
Py_DECREF(bi->pitems);
|
||||
PyObject_Del(bi);
|
||||
}
|
||||
|
||||
/* The implementation of the iterator's tp_iternext slot. Returns "the next"
|
||||
* item; returns NULL if error; returns NULL without setting an error if the
|
||||
* iteration is exhausted (that's the way to terminate the iteration protocol).
|
||||
*/
|
||||
static PyObject *
|
||||
BTreeIter_next(BTreeIter *bi, PyObject *args)
|
||||
{
|
||||
PyObject *result = NULL; /* until proven innocent */
|
||||
BTreeItems *items = bi->pitems;
|
||||
int i = items->currentoffset;
|
||||
Bucket *bucket = items->currentbucket;
|
||||
|
||||
if (bucket == NULL) /* iteration termination is sticky */
|
||||
return NULL;
|
||||
|
||||
PER_USE_OR_RETURN(bucket, NULL);
|
||||
if (i >= bucket->len)
|
||||
{
|
||||
/* We never leave this routine normally with i >= len: somebody
|
||||
* else mutated the current bucket.
|
||||
*/
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"the bucket being iterated changed size");
|
||||
/* Arrange for that this error is sticky too. */
|
||||
items->currentoffset = INT_MAX;
|
||||
goto Done;
|
||||
}
|
||||
|
||||
/* Build the result object, from bucket at offset i. */
|
||||
result = getBucketEntry(bucket, i, items->kind);
|
||||
|
||||
/* Advance position for next call. */
|
||||
if (bucket == items->lastbucket && i >= items->last)
|
||||
{
|
||||
/* Next call should terminate the iteration. */
|
||||
Py_DECREF(items->currentbucket);
|
||||
items->currentbucket = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
if (i >= bucket->len)
|
||||
{
|
||||
Py_XINCREF(bucket->next);
|
||||
items->currentbucket = bucket->next;
|
||||
Py_DECREF(bucket);
|
||||
i = 0;
|
||||
}
|
||||
items->currentoffset = i;
|
||||
}
|
||||
|
||||
Done:
|
||||
PER_UNUSE(bucket);
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
BTreeIter_getiter(PyObject *it)
|
||||
{
|
||||
Py_INCREF(it);
|
||||
return it;
|
||||
}
|
||||
|
||||
static PyTypeObject BTreeIter_Type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
MODULE_NAME MOD_NAME_PREFIX "TreeIterator", /* tp_name */
|
||||
sizeof(BTreeIter), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
/* methods */
|
||||
(destructor)BTreeIter_dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_compare */
|
||||
0, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
0, /*PyObject_GenericGetAttr,*/ /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||
0, /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
(getiterfunc)BTreeIter_getiter, /* tp_iter */
|
||||
(iternextfunc)BTreeIter_next, /* tp_iternext */
|
||||
0, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
};
|
||||
@@ -0,0 +1,671 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
All Rights Reserved.
|
||||
|
||||
This software is subject to the provisions of the Zope Public License,
|
||||
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include "Python.h"
|
||||
/* include structmember.h for offsetof */
|
||||
#include "structmember.h"
|
||||
#include "bytesobject.h"
|
||||
|
||||
#ifdef PERSISTENT
|
||||
#include "persistent/cPersistence.h"
|
||||
#else
|
||||
#define PER_USE_OR_RETURN(self, NULL)
|
||||
#define PER_ALLOW_DEACTIVATION(self)
|
||||
#define PER_PREVENT_DEACTIVATION(self)
|
||||
#define PER_DEL(self)
|
||||
#define PER_USE(O) 1
|
||||
#define PER_ACCESSED(O) 1
|
||||
#endif
|
||||
|
||||
#include "_compat.h"
|
||||
|
||||
/* So sue me. This pair gets used all over the place, so much so that it
|
||||
* interferes with understanding non-persistence parts of algorithms.
|
||||
* PER_UNUSE can be used after a successul PER_USE or PER_USE_OR_RETURN.
|
||||
* It allows the object to become ghostified, and tells the persistence
|
||||
* machinery that the object's fields were used recently.
|
||||
*/
|
||||
#define PER_UNUSE(OBJ) do { \
|
||||
PER_ALLOW_DEACTIVATION(OBJ); \
|
||||
PER_ACCESSED(OBJ); \
|
||||
} while (0)
|
||||
|
||||
/* The tp_name slots of the various BTree types contain the fully
|
||||
* qualified names of the types, e.g. zodb.btrees.OOBTree.OOBTree.
|
||||
* The full name is usd to support pickling and because it is not
|
||||
* possible to modify the __module__ slot of a type dynamically. (This
|
||||
* may be a bug in Python 2.2).
|
||||
*
|
||||
* The MODULE_NAME here used to be "BTrees._". We actually want the module
|
||||
* name to point to the Python module rather than the C, so the underline
|
||||
* is now removed.
|
||||
*/
|
||||
#define MODULE_NAME "BTrees." MOD_NAME_PREFIX "BTree."
|
||||
|
||||
static PyObject *sort_str, *reverse_str, *__setstate___str;
|
||||
static PyObject *_bucket_type_str, *max_internal_size_str, *max_leaf_size_str;
|
||||
static PyObject *ConflictError = NULL;
|
||||
|
||||
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 OBJECT(O) ((PyObject*)(O))
|
||||
|
||||
#define MIN_BUCKET_ALLOC 16
|
||||
|
||||
#define SameType_Check(O1, O2) (Py_TYPE((O1))==Py_TYPE((O2)))
|
||||
|
||||
#define ASSERT(C, S, R) if (! (C)) { \
|
||||
PyErr_SetString(PyExc_AssertionError, (S)); return (R); }
|
||||
|
||||
|
||||
#ifdef NEED_LONG_LONG_SUPPORT
|
||||
/* Helper code used to support long long instead of int. */
|
||||
|
||||
#ifndef PY_LONG_LONG
|
||||
#error "PY_LONG_LONG required but not defined"
|
||||
#endif
|
||||
|
||||
#ifdef NEED_LONG_LONG_KEYS
|
||||
static int
|
||||
longlong_check(PyObject *ob)
|
||||
{
|
||||
if (INT_CHECK(ob))
|
||||
return 1;
|
||||
|
||||
if (PyLong_Check(ob)) {
|
||||
int overflow;
|
||||
(void)PyLong_AsLongLongAndOverflow(ob, &overflow);
|
||||
if (overflow)
|
||||
goto overflow;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
overflow:
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"longlong_check: long integer out of range");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static PyObject *
|
||||
longlong_as_object(PY_LONG_LONG val)
|
||||
{
|
||||
if ((val > LONG_MAX) || (val < LONG_MIN))
|
||||
return PyLong_FromLongLong(val);
|
||||
return INT_FROM_LONG((long)val);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
longlong_convert(PyObject *ob, PY_LONG_LONG *value)
|
||||
{
|
||||
#ifndef PY3K
|
||||
if (PyInt_Check(ob))
|
||||
{
|
||||
(*value) = (PY_LONG_LONG)PyInt_AS_LONG(ob);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!PyLong_Check(ob))
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "expected integer key");
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
PY_LONG_LONG val;
|
||||
int overflow;
|
||||
val = PyLong_AsLongLongAndOverflow(ob, &overflow);
|
||||
if (overflow)
|
||||
goto overflow;
|
||||
(*value) = val;
|
||||
return 1;
|
||||
}
|
||||
overflow:
|
||||
PyErr_SetString(PyExc_ValueError, "long integer out of range");
|
||||
return 0;
|
||||
}
|
||||
#endif /* NEED_LONG_LONG_SUPPORT */
|
||||
|
||||
|
||||
/* Various kinds of BTree and Bucket structs are instances of
|
||||
* "sized containers", and have a common initial layout:
|
||||
* The stuff needed for all Python objects, or all Persistent objects.
|
||||
* int size: The maximum number of things that could be contained
|
||||
* without growing the container.
|
||||
* int len: The number of things currently contained.
|
||||
*
|
||||
* Invariant: 0 <= len <= size.
|
||||
*
|
||||
* A sized container typically goes on to declare one or more pointers
|
||||
* to contiguous arrays with 'size' elements each, the initial 'len' of
|
||||
* which are currently in use.
|
||||
*/
|
||||
#ifdef PERSISTENT
|
||||
#define sizedcontainer_HEAD \
|
||||
cPersistent_HEAD \
|
||||
int size; \
|
||||
int len;
|
||||
#else
|
||||
#define sizedcontainer_HEAD \
|
||||
PyObject_HEAD \
|
||||
int size; \
|
||||
int len;
|
||||
#endif
|
||||
|
||||
/* Nothing is actually of type Sized, but (pointers to) BTree nodes and
|
||||
* Buckets can be cast to Sized* in contexts that only need to examine
|
||||
* the members common to all sized containers.
|
||||
*/
|
||||
typedef struct Sized_s {
|
||||
sizedcontainer_HEAD
|
||||
} Sized;
|
||||
|
||||
#define SIZED(O) ((Sized*)(O))
|
||||
|
||||
/* A Bucket wraps contiguous vectors of keys and values. Keys are unique,
|
||||
* and stored in sorted order. The 'values' pointer may be NULL if the
|
||||
* Bucket is used to implement a set. Buckets serving as leafs of BTrees
|
||||
* are chained together via 'next', so that the entire BTree contents
|
||||
* can be traversed in sorted order quickly and easily.
|
||||
*/
|
||||
typedef struct Bucket_s {
|
||||
sizedcontainer_HEAD
|
||||
struct Bucket_s *next; /* the bucket with the next-larger keys */
|
||||
KEY_TYPE *keys; /* 'len' keys, in increasing order */
|
||||
VALUE_TYPE *values; /* 'len' corresponding values; NULL if a set */
|
||||
} Bucket;
|
||||
|
||||
#define BUCKET(O) ((Bucket*)(O))
|
||||
|
||||
/* A BTree is complicated. See Maintainer.txt.
|
||||
*/
|
||||
|
||||
typedef struct BTreeItem_s {
|
||||
KEY_TYPE key;
|
||||
Sized *child; /* points to another BTree, or to a Bucket of some sort */
|
||||
} BTreeItem;
|
||||
|
||||
typedef struct BTree_s {
|
||||
sizedcontainer_HEAD
|
||||
|
||||
/* firstbucket points to the bucket containing the smallest key in
|
||||
* the BTree. This is found by traversing leftmost child pointers
|
||||
* (data[0].child) until reaching a Bucket.
|
||||
*/
|
||||
Bucket *firstbucket;
|
||||
|
||||
/* The BTree points to 'len' children, via the "child" fields of the data
|
||||
* array. There are len-1 keys in the 'key' fields, stored in increasing
|
||||
* order. data[0].key is unused. For i in 0 .. len-1, all keys reachable
|
||||
* from data[i].child are >= data[i].key and < data[i+1].key, at the
|
||||
* endpoints pretending that data[0].key is minus infinity and
|
||||
* data[len].key is positive infinity.
|
||||
*/
|
||||
BTreeItem *data;
|
||||
long max_internal_size;
|
||||
long max_leaf_size;
|
||||
} BTree;
|
||||
|
||||
static PyTypeObject BTreeType;
|
||||
static PyTypeObject BucketType;
|
||||
|
||||
#define BTREE(O) ((BTree*)(O))
|
||||
|
||||
/* Use BTREE_SEARCH to find which child pointer to follow.
|
||||
* RESULT An int lvalue to hold the index i such that SELF->data[i].child
|
||||
* is the correct node to search next.
|
||||
* SELF A pointer to a BTree node.
|
||||
* KEY The key you're looking for, of type KEY_TYPE.
|
||||
* ONERROR What to do if key comparison raises an exception; for example,
|
||||
* perhaps 'return NULL'.
|
||||
*
|
||||
* See Maintainer.txt for discussion: this is optimized in subtle ways.
|
||||
* It's recommended that you call this at the start of a routine, waiting
|
||||
* to check for self->len == 0 after.
|
||||
*/
|
||||
#define BTREE_SEARCH(RESULT, SELF, KEY, ONERROR) { \
|
||||
int _lo = 0; \
|
||||
int _hi = (SELF)->len; \
|
||||
int _i, _cmp; \
|
||||
for (_i = _hi >> 1; _i > _lo; _i = (_lo + _hi) >> 1) { \
|
||||
TEST_KEY_SET_OR(_cmp, (SELF)->data[_i].key, (KEY)) \
|
||||
ONERROR; \
|
||||
if (_cmp < 0) _lo = _i; \
|
||||
else if (_cmp > 0) _hi = _i; \
|
||||
else /* equal */ break; \
|
||||
} \
|
||||
(RESULT) = _i; \
|
||||
}
|
||||
|
||||
/* SetIteration structs are used in the internal set iteration protocol.
|
||||
* When you want to iterate over a set or bucket or BTree (even an
|
||||
* individual key!),
|
||||
* 1. Declare a new iterator:
|
||||
* SetIteration si = {0,0,0};
|
||||
* Using "{0,0,0}" or "{0,0}" appear most common. Only one {0} is
|
||||
* necssary. At least one must be given so that finiSetIteration() works
|
||||
* correctly even if you don't get around to calling initSetIteration().
|
||||
* 2. Initialize it via
|
||||
* initSetIteration(&si, PyObject *s, useValues)
|
||||
* It's an error if that returns an int < 0. In case of error on the
|
||||
* init call, calling finiSetIteration(&si) is optional. But if the
|
||||
* init call succeeds, you must eventually call finiSetIteration(),
|
||||
* and whether or not subsequent calls to si.next() fail.
|
||||
* 3. Get the first element:
|
||||
* if (si.next(&si) < 0) { there was an error }
|
||||
* If the set isn't empty, this sets si.position to an int >= 0,
|
||||
* si.key to the element's key (of type KEY_TYPE), and maybe si.value to
|
||||
* the element's value (of type VALUE_TYPE). si.value is defined
|
||||
* iff si.usesValue is true.
|
||||
* 4. Process all the elements:
|
||||
* while (si.position >= 0) {
|
||||
* do something with si.key and/or si.value;
|
||||
* if (si.next(&si) < 0) { there was an error; }
|
||||
* }
|
||||
* 5. Finalize the SetIterator:
|
||||
* finiSetIteration(&si);
|
||||
* This is mandatory! si may contain references to iterator objects,
|
||||
* keys and values, and they must be cleaned up else they'll leak. If
|
||||
* this were C++ we'd hide that in the destructor, but in C you have to
|
||||
* do it by hand.
|
||||
*/
|
||||
typedef struct SetIteration_s
|
||||
{
|
||||
PyObject *set; /* the set, bucket, BTree, ..., being iterated */
|
||||
int position; /* initialized to 0; set to -1 by next() when done */
|
||||
int usesValue; /* true iff 'set' has values & we iterate them */
|
||||
KEY_TYPE key; /* next() sets to next key */
|
||||
VALUE_TYPE value; /* next() may set to next value */
|
||||
int (*next)(struct SetIteration_s*); /* function to get next key+value */
|
||||
} SetIteration;
|
||||
|
||||
/* Finish the set iteration protocol. This MUST be called by everyone
|
||||
* who starts a set iteration, unless the initial call to initSetIteration
|
||||
* failed; in that case, and only that case, calling finiSetIteration is
|
||||
* optional.
|
||||
*/
|
||||
static void
|
||||
finiSetIteration(SetIteration *i)
|
||||
{
|
||||
assert(i != NULL);
|
||||
if (i->set == NULL)
|
||||
return;
|
||||
Py_DECREF(i->set);
|
||||
i->set = NULL; /* so it doesn't hurt to call this again */
|
||||
|
||||
if (i->position > 0) {
|
||||
/* next() was called at least once, but didn't finish iterating
|
||||
* (else position would be negative). So the cached key and
|
||||
* value need to be cleaned up.
|
||||
*/
|
||||
DECREF_KEY(i->key);
|
||||
if (i->usesValue) {
|
||||
DECREF_VALUE(i->value);
|
||||
}
|
||||
}
|
||||
i->position = -1; /* stop any stray next calls from doing harm */
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
IndexError(int i)
|
||||
{
|
||||
PyObject *v;
|
||||
|
||||
v = INT_FROM_LONG(i);
|
||||
if (!v) {
|
||||
v = Py_None;
|
||||
Py_INCREF(v);
|
||||
}
|
||||
PyErr_SetObject(PyExc_IndexError, v);
|
||||
Py_DECREF(v);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Search for the bucket immediately preceding *current, in the bucket chain
|
||||
* starting at first. current, *current and first must not be NULL.
|
||||
*
|
||||
* Return:
|
||||
* 1 *current holds the correct bucket; this is a borrowed reference
|
||||
* 0 no such bucket exists; *current unaltered
|
||||
* -1 error; *current unaltered
|
||||
*/
|
||||
static int
|
||||
PreviousBucket(Bucket **current, Bucket *first)
|
||||
{
|
||||
Bucket *trailing = NULL; /* first travels; trailing follows it */
|
||||
int result = 0;
|
||||
|
||||
assert(current && *current && first);
|
||||
if (first == *current)
|
||||
return 0;
|
||||
|
||||
do {
|
||||
trailing = first;
|
||||
PER_USE_OR_RETURN(first, -1);
|
||||
first = first->next;
|
||||
|
||||
((trailing)->state==cPersistent_STICKY_STATE
|
||||
&&
|
||||
((trailing)->state=cPersistent_UPTODATE_STATE));
|
||||
|
||||
PER_ACCESSED(trailing);
|
||||
|
||||
if (first == *current) {
|
||||
*current = trailing;
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
} while (first);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void *
|
||||
BTree_Malloc(size_t sz)
|
||||
{
|
||||
void *r;
|
||||
|
||||
ASSERT(sz > 0, "non-positive size malloc", NULL);
|
||||
|
||||
r = malloc(sz);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
PyErr_NoMemory();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *
|
||||
BTree_Realloc(void *p, size_t sz)
|
||||
{
|
||||
void *r;
|
||||
|
||||
ASSERT(sz > 0, "non-positive size realloc", NULL);
|
||||
|
||||
if (p)
|
||||
r = realloc(p, sz);
|
||||
else
|
||||
r = malloc(sz);
|
||||
|
||||
UNLESS (r)
|
||||
PyErr_NoMemory();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Shared keyword-argument list for BTree/Bucket
|
||||
* (iter)?(keys|values|items)
|
||||
*/
|
||||
static char *search_keywords[] = {"min", "max",
|
||||
"excludemin", "excludemax",
|
||||
0};
|
||||
|
||||
#include "BTreeItemsTemplate.c"
|
||||
#include "BucketTemplate.c"
|
||||
#include "SetTemplate.c"
|
||||
#include "BTreeTemplate.c"
|
||||
#include "TreeSetTemplate.c"
|
||||
#include "SetOpTemplate.c"
|
||||
#include "MergeTemplate.c"
|
||||
|
||||
static struct PyMethodDef module_methods[] = {
|
||||
{"difference", (PyCFunction) difference_m, METH_VARARGS,
|
||||
"difference(o1, o2) -- "
|
||||
"compute the difference between o1 and o2"
|
||||
},
|
||||
{"union", (PyCFunction) union_m, METH_VARARGS,
|
||||
"union(o1, o2) -- compute the union of o1 and o2\n"
|
||||
},
|
||||
{"intersection", (PyCFunction) intersection_m, METH_VARARGS,
|
||||
"intersection(o1, o2) -- "
|
||||
"compute the intersection of o1 and o2"
|
||||
},
|
||||
#ifdef MERGE
|
||||
{"weightedUnion", (PyCFunction) wunion_m, METH_VARARGS,
|
||||
"weightedUnion(o1, o2 [, w1, w2]) -- compute the union of o1 and o2\n"
|
||||
"\nw1 and w2 are weights."
|
||||
},
|
||||
{"weightedIntersection", (PyCFunction) wintersection_m, METH_VARARGS,
|
||||
"weightedIntersection(o1, o2 [, w1, w2]) -- "
|
||||
"compute the intersection of o1 and o2\n"
|
||||
"\nw1 and w2 are weights."
|
||||
},
|
||||
#endif
|
||||
#ifdef MULTI_INT_UNION
|
||||
{"multiunion", (PyCFunction) multiunion_m, METH_VARARGS,
|
||||
"multiunion(seq) -- compute union of a sequence of integer sets.\n"
|
||||
"\n"
|
||||
"Each element of seq must be an integer set, or convertible to one\n"
|
||||
"via the set iteration protocol. The union returned is an IISet."
|
||||
},
|
||||
#endif
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
static char BTree_module_documentation[] =
|
||||
"\n"
|
||||
MASTER_ID
|
||||
BTREEITEMSTEMPLATE_C
|
||||
"$Id$\n"
|
||||
BTREETEMPLATE_C
|
||||
BUCKETTEMPLATE_C
|
||||
KEYMACROS_H
|
||||
MERGETEMPLATE_C
|
||||
SETOPTEMPLATE_C
|
||||
SETTEMPLATE_C
|
||||
TREESETTEMPLATE_C
|
||||
VALUEMACROS_H
|
||||
BTREEITEMSTEMPLATE_C
|
||||
;
|
||||
|
||||
int
|
||||
init_persist_type(PyTypeObject *type)
|
||||
{
|
||||
#ifdef PY3K
|
||||
((PyObject*)type)->ob_type = &PyType_Type;
|
||||
#else
|
||||
type->ob_type = &PyType_Type;
|
||||
#endif
|
||||
type->tp_base = cPersistenceCAPI->pertype;
|
||||
|
||||
if (PyType_Ready(type) < 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef PY3K
|
||||
static struct PyModuleDef moduledef = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"_" MOD_NAME_PREFIX "BTree", /* m_name */
|
||||
BTree_module_documentation, /* m_doc */
|
||||
-1, /* m_size */
|
||||
module_methods, /* m_methods */
|
||||
NULL, /* m_reload */
|
||||
NULL, /* m_traverse */
|
||||
NULL, /* m_clear */
|
||||
NULL, /* m_free */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static PyObject*
|
||||
module_init(void)
|
||||
{
|
||||
PyObject *module, *mod_dict, *interfaces, *conflicterr;
|
||||
|
||||
#ifdef KEY_TYPE_IS_PYOBJECT
|
||||
object_ = PyTuple_GetItem(Py_TYPE(Py_None)->tp_bases, 0);
|
||||
if (object_ == NULL)
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
sort_str = INTERN("sort");
|
||||
if (!sort_str)
|
||||
return NULL;
|
||||
reverse_str = INTERN("reverse");
|
||||
if (!reverse_str)
|
||||
return NULL;
|
||||
__setstate___str = INTERN("__setstate__");
|
||||
if (!__setstate___str)
|
||||
return NULL;
|
||||
_bucket_type_str = INTERN("_bucket_type");
|
||||
if (!_bucket_type_str)
|
||||
return NULL;
|
||||
|
||||
max_internal_size_str = INTERN("max_internal_size");
|
||||
if (! max_internal_size_str)
|
||||
return NULL;
|
||||
max_leaf_size_str = INTERN("max_leaf_size");
|
||||
if (! max_leaf_size_str)
|
||||
return NULL;
|
||||
|
||||
/* Grab the ConflictError class */
|
||||
interfaces = PyImport_ImportModule("BTrees.Interfaces");
|
||||
if (interfaces != NULL)
|
||||
{
|
||||
conflicterr = PyObject_GetAttrString(interfaces, "BTreesConflictError");
|
||||
if (conflicterr != NULL)
|
||||
ConflictError = conflicterr;
|
||||
Py_DECREF(interfaces);
|
||||
}
|
||||
|
||||
if (ConflictError == NULL)
|
||||
{
|
||||
Py_INCREF(PyExc_ValueError);
|
||||
ConflictError=PyExc_ValueError;
|
||||
}
|
||||
|
||||
/* Initialize the PyPersist_C_API and the type objects. */
|
||||
#ifdef PY3K
|
||||
cPersistenceCAPI = (cPersistenceCAPIstruct *)PyCapsule_Import(
|
||||
"persistent.cPersistence.CAPI", 0);
|
||||
#else
|
||||
cPersistenceCAPI = (cPersistenceCAPIstruct *)PyCObject_Import(
|
||||
"persistent.cPersistence", "CAPI");
|
||||
#endif
|
||||
if (cPersistenceCAPI == NULL) {
|
||||
/* The Capsule API attempts to import 'persistent' and then
|
||||
* walk down to the specified attribute using getattr. If the C
|
||||
* extensions aren't available, this can result in an
|
||||
* AttributeError being raised. Let that percolate up as an
|
||||
* ImportError so it can be caught in the expected way.
|
||||
*/
|
||||
if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_ImportError)) {
|
||||
PyErr_SetString(PyExc_ImportError, "persistent C extension unavailable");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef PY3K
|
||||
#define _SET_TYPE(typ) ((PyObject*)(&typ))->ob_type = &PyType_Type
|
||||
#else
|
||||
#define _SET_TYPE(typ) (typ).ob_type = &PyType_Type
|
||||
#endif
|
||||
_SET_TYPE(BTreeItemsType);
|
||||
_SET_TYPE(BTreeIter_Type);
|
||||
BTreeIter_Type.tp_getattro = PyObject_GenericGetAttr;
|
||||
BucketType.tp_new = PyType_GenericNew;
|
||||
SetType.tp_new = PyType_GenericNew;
|
||||
BTreeType.tp_new = PyType_GenericNew;
|
||||
TreeSetType.tp_new = PyType_GenericNew;
|
||||
if (!init_persist_type(&BucketType))
|
||||
return NULL;
|
||||
if (!init_persist_type(&BTreeType))
|
||||
return NULL;
|
||||
if (!init_persist_type(&SetType))
|
||||
return NULL;
|
||||
if (!init_persist_type(&TreeSetType))
|
||||
return NULL;
|
||||
|
||||
if (PyDict_SetItem(BTreeType.tp_dict, _bucket_type_str,
|
||||
(PyObject *)&BucketType) < 0)
|
||||
{
|
||||
fprintf(stderr, "btree failed\n");
|
||||
return NULL;
|
||||
}
|
||||
if (PyDict_SetItem(TreeSetType.tp_dict, _bucket_type_str,
|
||||
(PyObject *)&SetType) < 0)
|
||||
{
|
||||
fprintf(stderr, "bucket failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create the module and add the functions */
|
||||
#ifdef PY3K
|
||||
module = PyModule_Create(&moduledef);
|
||||
#else
|
||||
module = Py_InitModule4("_" MOD_NAME_PREFIX "BTree",
|
||||
module_methods, BTree_module_documentation,
|
||||
(PyObject *)NULL, PYTHON_API_VERSION);
|
||||
#endif
|
||||
|
||||
/* Add some symbolic constants to the module */
|
||||
mod_dict = PyModule_GetDict(module);
|
||||
if (PyDict_SetItemString(mod_dict, MOD_NAME_PREFIX "Bucket",
|
||||
(PyObject *)&BucketType) < 0)
|
||||
return NULL;
|
||||
if (PyDict_SetItemString(mod_dict, MOD_NAME_PREFIX "BTree",
|
||||
(PyObject *)&BTreeType) < 0)
|
||||
return NULL;
|
||||
if (PyDict_SetItemString(mod_dict, MOD_NAME_PREFIX "Set",
|
||||
(PyObject *)&SetType) < 0)
|
||||
return NULL;
|
||||
if (PyDict_SetItemString(mod_dict, MOD_NAME_PREFIX "TreeSet",
|
||||
(PyObject *)&TreeSetType) < 0)
|
||||
return NULL;
|
||||
if (PyDict_SetItemString(mod_dict, MOD_NAME_PREFIX "TreeIterator",
|
||||
(PyObject *)&BTreeIter_Type) < 0)
|
||||
return NULL;
|
||||
/* We also want to be able to access these constants without the prefix
|
||||
* so that code can more easily exchange modules (particularly the integer
|
||||
* and long modules, but also others). The TreeIterator is only internal,
|
||||
* so we don't bother to expose that.
|
||||
*/
|
||||
if (PyDict_SetItemString(mod_dict, "Bucket",
|
||||
(PyObject *)&BucketType) < 0)
|
||||
return NULL;
|
||||
if (PyDict_SetItemString(mod_dict, "BTree",
|
||||
(PyObject *)&BTreeType) < 0)
|
||||
return NULL;
|
||||
if (PyDict_SetItemString(mod_dict, "Set",
|
||||
(PyObject *)&SetType) < 0)
|
||||
return NULL;
|
||||
if (PyDict_SetItemString(mod_dict, "TreeSet",
|
||||
(PyObject *)&TreeSetType) < 0)
|
||||
return NULL;
|
||||
#if defined(ZODB_64BIT_INTS) && defined(NEED_LONG_LONG_SUPPORT)
|
||||
if (PyDict_SetItemString(mod_dict, "using64bits", Py_True) < 0)
|
||||
return NULL;
|
||||
#else
|
||||
if (PyDict_SetItemString(mod_dict, "using64bits", Py_False) < 0)
|
||||
return NULL;
|
||||
#endif
|
||||
return module;
|
||||
}
|
||||
|
||||
#ifdef PY3K
|
||||
PyMODINIT_FUNC INITMODULE(void)
|
||||
{
|
||||
return module_init();
|
||||
}
|
||||
#else
|
||||
PyMODINIT_FUNC INITMODULE(void)
|
||||
{
|
||||
module_init();
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,429 @@
|
||||
=====================
|
||||
Developer Information
|
||||
=====================
|
||||
|
||||
This document provides information for developers who maintain or extend
|
||||
`BTrees`.
|
||||
|
||||
Macros
|
||||
======
|
||||
|
||||
`BTrees` are defined using a "template", roughly akin to a C++ template. To
|
||||
create a new family of `BTrees`, create a source file that defines macros used
|
||||
to handle differences in key and value types:
|
||||
|
||||
|
||||
Configuration Macros
|
||||
--------------------
|
||||
|
||||
``MASTER_ID``
|
||||
|
||||
A string to hold an RCS/CVS Id key to be included in compiled binaries.
|
||||
|
||||
``MOD_NAME_PREFIX``
|
||||
|
||||
A string (like "IO" or "OO") that provides the prefix used for the module.
|
||||
This gets used to generate type names and the internal module name string.
|
||||
|
||||
``DEFAULT_MAX_BUCKET_SIZE``
|
||||
|
||||
An int giving the maximum bucket size (number of key/value pairs). When a
|
||||
bucket gets larger than this due to an insertion *into a BTREE*, it
|
||||
splits. Inserting into a bucket directly doesn't split, and functions
|
||||
that produce a bucket output (e.g., ``union()``) also have no bound on how
|
||||
large a bucket may get. Someday this will be tunable on `BTree`.
|
||||
instances.
|
||||
|
||||
``DEFAULT_MAX_BTREE_SIZE``
|
||||
|
||||
An ``int`` giving the maximum size (number of children) of an internal
|
||||
btree node. Someday this will be tunable on ``BTree`` instances.
|
||||
|
||||
|
||||
Macros for Keys
|
||||
---------------
|
||||
|
||||
``KEY_TYPE``
|
||||
|
||||
The C type declaration for keys (e.g., ``int`` or ``PyObject*``).
|
||||
|
||||
``KEY_TYPE_IS_PYOBJECT``
|
||||
|
||||
Define if ``KEY_TYPE`` is a ``PyObject*`, else ``undef``.
|
||||
|
||||
``KEY_CHECK(K)``
|
||||
|
||||
Tests whether the ``PyObject* K`` can be converted to the (``C``) key type
|
||||
(``KEY_TYPE``). The macro should return a boolean (zero for false,
|
||||
non-zero for true). When it returns false, its caller should probably set
|
||||
a ``TypeError`` exception.
|
||||
|
||||
``KEY_CHECK_ON_SET(K)``
|
||||
|
||||
Like ``KEY_CHECK``, but only checked during ``__setitem__``.
|
||||
|
||||
``TEST_KEY_SET_OR(V, K, T)``
|
||||
|
||||
Like Python's ``cmp()``. Compares K(ey) to T(arget), where ``K``
|
||||
and ``T`` are ``C`` values of type `KEY_TYPE`. ``V`` is assigned an `int`
|
||||
value depending on the outcome::
|
||||
|
||||
< 0 if K < T
|
||||
== 0 if K == T
|
||||
> 0 if K > T
|
||||
|
||||
This macro acts like an ``if``, where the following statement is executed
|
||||
only if a Python exception has been raised because the values could not be
|
||||
compared.
|
||||
|
||||
``DECREF_KEY(K)``
|
||||
|
||||
``K`` is a value of ``KEY_TYPE``. If ``KEY_TYPE`` is a flavor of
|
||||
``PyObject*``, write this to do ``Py_DECREF(K)``. Else (e.g.,
|
||||
``KEY_TYPE`` is ``int``) make it a nop.
|
||||
|
||||
``INCREF_KEY(K)``
|
||||
|
||||
``K`` is a value of `KEY_TYPE`. If `KEY_TYPE` is a flavor of
|
||||
``PyObject*``, write this to do ``Py_INCREF(K)``. Else (e.g., `KEY_TYPE`
|
||||
is ``int``) make it a nop.
|
||||
|
||||
``COPY_KEY(K, E)``
|
||||
|
||||
Like ``K=E``. Copy a key from ``E`` to ``K``, both of ``KEY_TYPE``. Note
|
||||
that this doesn't ``decref K`` or ``incref E`` when ``KEY_TYPE`` is a
|
||||
``PyObject*``; the caller is responsible for keeping refcounts straight.
|
||||
|
||||
``COPY_KEY_TO_OBJECT(O, K)``
|
||||
|
||||
Roughly like ``O=K``. ``O`` is a ``PyObject*``, and the macro must build
|
||||
a Python object form of ``K``, assign it to ``O``, and ensure that ``O``
|
||||
owns the reference to its new value. It may do this by creating a new
|
||||
Python object based on ``K`` (e.g., ``PyInt_FromLong(K)`` when
|
||||
``KEY_TYPE`` is ``int``), or simply by doing ``Py_INCREF(K)`` if
|
||||
``KEY_TYPE`` is a ``PyObject*``.
|
||||
|
||||
``COPY_KEY_FROM_ARG(TARGET, ARG, STATUS)``
|
||||
|
||||
Copy an argument to the target without creating a new reference to
|
||||
``ARG``. ``ARG`` is a ``PyObject*``, and ``TARGET`` is of type
|
||||
``KEY_TYPE``. If this can't be done (for example, ``KEY_CHECK(ARG)``
|
||||
returns false), set a Python error and set status to ``0``. If there is
|
||||
no error, leave status alone.
|
||||
|
||||
|
||||
Macros for Values
|
||||
-----------------
|
||||
|
||||
``VALUE_TYPE``
|
||||
|
||||
The C type declaration for values (e.g., ``int`` or ``PyObject*``).
|
||||
|
||||
``VALUE_TYPE_IS_PYOBJECT``
|
||||
|
||||
Define if ``VALUE_TYPE`` is a ``PyObject*``, else ``undef``.
|
||||
|
||||
``TEST_VALUE(X, Y)``
|
||||
|
||||
Like Python's ``cmp()``. Compares ``X`` to ``Y``, where ``X`` & ``Y`` are
|
||||
``C`` values of type ``VALUE_TYPE``. The macro returns an ``int``, with
|
||||
value::
|
||||
|
||||
< 0 if X < Y
|
||||
== 0 if X == Y
|
||||
> 0 if X > Y
|
||||
|
||||
Bug: There is no provision for determining whether the comparison attempt
|
||||
failed (set a Python exception).
|
||||
|
||||
``DECREF_VALUE(K)``
|
||||
|
||||
Like ``DECREF_KEY``, except applied to values of ``VALUE_TYPE``.
|
||||
|
||||
``INCREF_VALUE(K)``
|
||||
|
||||
Like ``INCREF_KEY``, except applied to values of ``VALUE_TYPE``.
|
||||
|
||||
``COPY_VALUE(K, E)``
|
||||
|
||||
Like ``COPY_KEY``, except applied to values of ``VALUE_TYPE``.
|
||||
|
||||
``COPY_VALUE_TO_OBJECT(O, K)``
|
||||
|
||||
Like ``COPY_KEY_TO_OBJECT``, except applied to values of ``VALUE_TYPE``.
|
||||
|
||||
``COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS)``
|
||||
|
||||
Like ``COPY_KEY_FROM_ARG``, except applied to values of ``VALUE_TYPE``.
|
||||
|
||||
``NORMALIZE_VALUE(V, MIN)``
|
||||
|
||||
Normalize the value, ``V``, using the parameter ``MIN``. This is almost
|
||||
certainly a YAGNI. It is a no-op for most types. For integers, ``V`` is
|
||||
replaced by ``V/MIN`` only if ``MIN > 0``.
|
||||
|
||||
|
||||
Macros for Set Operations
|
||||
-------------------------
|
||||
|
||||
``MERGE_DEFAULT``
|
||||
|
||||
A value of ``VALUE_TYPE`` specifying the value to associate with set
|
||||
elements when sets are merged with mappings via weighed union or weighted
|
||||
intersection.
|
||||
|
||||
``MERGE(O1, w1, O2, w2)``
|
||||
|
||||
Performs a weighted merge of two values, ``O1`` and ``O2``, using weights
|
||||
``w1`` and ``w2``. The result must be of ``VALUE_TYPE``. Note that
|
||||
weighted unions and weighted intersections are not enabled if this macro
|
||||
is left undefined.
|
||||
|
||||
``MERGE_WEIGHT(O, w)``
|
||||
|
||||
Computes a weighted value for ``O``. The result must be of
|
||||
``VALUE_TYPE``. This is used for "filling out" weighted unions, i.e. to
|
||||
compute a weighted value for keys that appear in only one of the input
|
||||
mappings. If left undefined, ``MERGE_WEIGHT`` defaults to::
|
||||
|
||||
#define MERGE_WEIGHT(O, w) (O)
|
||||
|
||||
``MULTI_INT_UNION``
|
||||
|
||||
The value doesn't matter. If defined, `SetOpTemplate.c` compiles code for
|
||||
a ``multiunion()`` function (compute a union of many input sets at high
|
||||
speed). This currently makes sense only for structures with integer keys.
|
||||
|
||||
|
||||
BTree Clues
|
||||
===========
|
||||
|
||||
More or less random bits of helpful info.
|
||||
|
||||
+ In papers and textbooks, this flavor of BTree is usually called a B+-Tree,
|
||||
where "+" is a superscript.
|
||||
|
||||
+ All keys and all values live in the bucket leaf nodes. Keys in interior
|
||||
(BTree) nodes merely serve to guide a search efficiently toward the correct
|
||||
leaf.
|
||||
|
||||
+ When a key is deleted, it's physically removed from the bucket it's in, but
|
||||
this doesn't propagate back up the tree: since keys in interior nodes only
|
||||
serve to guide searches, it's OK-- and saves time --to leave "stale" keys in
|
||||
interior nodes.
|
||||
|
||||
+ No attempt is made to rebalance the tree after a deletion, unless a bucket
|
||||
thereby becomes entirely empty. "Classic BTrees" do rebalance, keeping all
|
||||
buckets at least half full (provided there are enough keys in the entire
|
||||
tree to fill half a bucket). The tradeoffs are murky. Pathological cases
|
||||
in the presence of deletion do exist. Pathologies include trees tending
|
||||
toward only one key per bucket, and buckets at differing depths (all buckets
|
||||
are at the same depth in a classic BTree).
|
||||
|
||||
+ ``DEFAULT_MAX_BUCKET_SIZE`` and ``DEFAULT_MAX_BTREE_SIZE`` are chosen mostly
|
||||
to "even out" pickle sizes in storage. That's why, e.g., an `IIBTree` has
|
||||
larger values than an `OOBTree`: pickles store ints more efficiently than
|
||||
they can store arbitrary Python objects.
|
||||
|
||||
+ In a non-empty BTree, every bucket node contains at least one key, and every
|
||||
BTree node contains at least one child and a non-NULL firstbucket pointer.
|
||||
However, a BTree node may not contain any keys.
|
||||
|
||||
+ An empty BTree consists solely of a BTree node with ``len==0`` and
|
||||
``firstbucket==NULL``.
|
||||
|
||||
+ Although a BTree can become unbalanced under a mix of inserts and deletes
|
||||
(meaning both that there's nothing stronger that can be said about buckets
|
||||
than that they're not empty, and that buckets can appear at different
|
||||
depths), a BTree node always has children of the same kind: they're all
|
||||
buckets, or they're all BTree nodes.
|
||||
|
||||
|
||||
The ``BTREE_SEARCH`` Macro
|
||||
==========================
|
||||
|
||||
For notational ease, consider a fixed BTree node ``x``, and let
|
||||
|
||||
::
|
||||
|
||||
K(i) mean x->data.key[i]
|
||||
C(i) mean all the keys reachable from x->data.child[i]
|
||||
|
||||
For each ``i`` in ``0`` to ``x->len-1`` inclusive,
|
||||
|
||||
::
|
||||
|
||||
K(i) <= C(i) < K(i+1)
|
||||
|
||||
is a BTree node invariant, where we pretend that ``K(0)`` holds a key smaller
|
||||
than any possible key, and ``K(x->len)`` holds a key larger than any possible
|
||||
key. (Note that ``K(x->len)`` doesn't actually exist, and ``K(0)`` is never
|
||||
used although space for it exists in non-empty BTree nodes.)
|
||||
|
||||
When searching for a key ``k``, then, the child pointer we want to follow is
|
||||
the one at index ``i`` such that ``K(i) <= k < K(i+1)``. There can be at most
|
||||
one such ``i``, since the ``K(i)`` are strictly increasing. And there is at
|
||||
least one such ``i`` provided the tree isn't empty (so that ``0 < len``). For
|
||||
the moment, assume the tree isn't empty (we'll get back to that later).
|
||||
|
||||
The macro's chief loop invariant is
|
||||
|
||||
::
|
||||
|
||||
K(lo) < k < K(hi)
|
||||
|
||||
This holds trivially at the start, since ``lo`` is set to ``0``, and ``hi`` to
|
||||
``x->len``, and we pretend ``K(0)`` is minus infinity and ``K(len)`` is plus
|
||||
infinity. Inside the loop, if ``K(i) < k`` we set ``lo`` to ``i``, and if
|
||||
``K(i) > k`` we set ``hi`` to ``i``. These obviously preserve the invariant.
|
||||
If ``K(i) == k``, the loop breaks and sets the result to ``i``, and since
|
||||
``K(i) == k`` in that case ``i`` is obviously the correct result.
|
||||
|
||||
Other cases depend on how ``i = floor((lo + hi)/2)`` works, exactly. Suppose
|
||||
``lo + d = hi`` for some ``d >= 0``. Then ``i = floor((lo + lo + d)/2) =
|
||||
floor(lo + d/2) = lo + floor(d/2)``. So:
|
||||
|
||||
a. ``[d == 0] (lo == i == hi)`` if and only if ``(lo == hi)``.
|
||||
b. ``[d == 1] (lo == i < hi)`` if and only if ``(lo+1 == hi)``.
|
||||
c. ``[d > 1] (lo < i < hi)`` if and only if ``(lo+1 < hi)``.
|
||||
|
||||
If the node is empty ``(x->len == 0)``, then ``lo==i==hi==0`` at the start,
|
||||
and the loop exits immediately (the first ``i > lo`` test fails), without
|
||||
entering the body.
|
||||
|
||||
Else ``lo < hi`` at the start, and the invariant ``K(lo) < k < K(hi)`` holds.
|
||||
|
||||
If ``lo+1 < hi``, we're in case (c): ``i`` is strictly between ``lo`` and
|
||||
``hi``, so the loop body is entered, and regardless of whether the body sets
|
||||
the new ``lo`` or the new ``hi`` to ``i``, the new ``lo`` is strictly less
|
||||
than the new ``hi``, and the difference between the new ``lo`` and new ``hi``
|
||||
is strictly less than the difference between the old ``lo`` and old ``hi``.
|
||||
So long as the new ``lo + 1`` remains < the new ``hi``, we stay in this case.
|
||||
We can't stay in this case forever, though: because ``hi-lo`` decreases on
|
||||
each trip but remains > ``0``, ``lo+1 == hi`` must eventually become true.
|
||||
(In fact, it becomes true quickly, in about ``log2(x->len)`` trips; the point
|
||||
is more that ``lo`` doesn't equal ``hi`` when the loop ends, it has to end
|
||||
with ``lo+1==hi`` and ``i==lo``).
|
||||
|
||||
Then we're in case (b): ``i==lo==hi-1`` then, and the loop exits. The
|
||||
invariant still holds, with ``lo==i`` and ``hi==lo+1==i+1``::
|
||||
|
||||
K(i) < k < K(i+1)
|
||||
|
||||
so ``i`` is again the correct answer.
|
||||
|
||||
|
||||
Optimization points:
|
||||
--------------------
|
||||
|
||||
+ Division by 2 is done via shift rather via "/2". These are signed ints, and
|
||||
almost all C compilers treat signed int division as truncating, and shifting
|
||||
is not the same as truncation for signed int division. The compiler has no
|
||||
way to know these values aren't negative, so has to generate longer-winded
|
||||
code for "/2". But we know these values aren't negative, and exploit it.
|
||||
|
||||
+ The order of _cmp comparisons matters. We're in an interior BTree node, and
|
||||
are looking at only a tiny fraction of all the keys that exist. So finding
|
||||
the key exactly in this node is unlikely, and checking ``_cmp == 0`` is a
|
||||
waste of time to the same extent. It doesn't matter whether we check for
|
||||
``_cmp < 0`` or ``_cmp > 0`` first, so long as we do both before worrying
|
||||
about equality.
|
||||
|
||||
+ At the start of a routine, it's better to run this macro even if ``x->len``
|
||||
is ``0`` (check for that afterwards). We just called a function and so
|
||||
probably drained the pipeline. If the first thing we do then is read up
|
||||
``self->len`` and check it against ``0``, we just sit there waiting for the
|
||||
data to get read up, and then another immediate test-and-branch, and for a
|
||||
very unlikely case (BTree nodes are rarely empty). It's better to get into
|
||||
the loop right away so the normal case makes progress ASAP.
|
||||
|
||||
|
||||
The ``BUCKET_SEARCH`` Macro
|
||||
===========================
|
||||
|
||||
This has a different job than ``BTREE_SEARCH``: the key ``0`` slot is
|
||||
legitimate in a bucket, and we want to find the index at which the key
|
||||
belongs. If the key is larger than the bucket's largest key, a new slot at
|
||||
index len is where it belongs, else it belongs at the smallest ``i`` with
|
||||
``keys[i]`` >= the key we're looking for. We also need to know whether or not
|
||||
the key is present (``BTREE_SEARCH`` didn't care; it only wanted to find the
|
||||
next node to search).
|
||||
|
||||
The mechanics of the search are quite similar, though. The primary
|
||||
loop invariant changes to (say we're searching for key ``k``)::
|
||||
|
||||
K(lo-1) < k < K(hi)
|
||||
|
||||
where ``K(i)`` means ``keys[i]``, and we pretend ``K(-1)`` is minus infinity
|
||||
and ``K(len)`` is plus infinity.
|
||||
|
||||
If the bucket is empty, ``lo=hi=i=0`` at the start, the loop body is never
|
||||
entered, and the macro sets ``INDEX`` to 0 and ``ABSENT`` to true. That's why
|
||||
``_cmp`` is initialized to 1 (``_cmp`` becomes ``ABSENT``).
|
||||
|
||||
Else the bucket is not empty, lo<hi at the start, and the loop body is
|
||||
entered. The invariant is obviously satisfied then, as ``lo=0`` and
|
||||
``hi=len``.
|
||||
|
||||
If ``K[i]<k``, ``lo`` is set to ``i+1``, preserving that ``K(lo-1) = K[i] <
|
||||
k``.
|
||||
|
||||
If ``K[i]>k``, ``hi`` is set to ``i``, preserving that ``K[hi] = K[i] > k``.
|
||||
|
||||
If the loop exits after either of those, ``_cmp != 0``, so ``ABSENT`` becomes
|
||||
true.
|
||||
|
||||
If ``K[i]=k``, the loop breaks, so that ``INDEX`` becomes ``i``, and
|
||||
``ABSENT`` becomes false (``_cmp=0`` in this case).
|
||||
|
||||
The same case analysis for ``BTREE_SEARCH`` on ``lo`` and ``hi`` holds here:
|
||||
|
||||
a. ``(lo == i == hi)`` if and only if ``(lo == hi)``.
|
||||
b. ``(lo == i < hi)`` if and only if ``(lo+1 == hi)``.
|
||||
c. ``(lo < i < hi)`` if and only if ``(lo+1 < hi)``.
|
||||
|
||||
So long as ``lo+1 < hi``, we're in case (c), and either break with equality
|
||||
(in which case the right results are obviously computed) or narrow the range.
|
||||
If equality doesn't obtain, the range eventually narrows to cases (a) or (b).
|
||||
|
||||
To go from (c) to (a), we must have ``lo+2==hi`` at the start, and
|
||||
``K[i]=K[lo+1]<k``. Then the new lo gets set to ``i+1 = lo+2 = hi``, and the
|
||||
loop exits with ``lo=hi=i`` and ``_cmp<0``. This is correct, because we know
|
||||
that ``k != K(i)`` (loop invariant! we actually know something stronger, that
|
||||
``k < K(hi)``; since ``i=hi``, this implies ``k != K(i)``).
|
||||
|
||||
Else (c) eventually falls into case (b), ``lo+1==hi`` and ``i==lo``. The
|
||||
invariant tells us ``K(lo-1) < k < K(hi) = K(lo+1)``, so if the key is present
|
||||
it must be at ``K(lo)``. ``i==lo`` in this case, so we test ``K(lo)`` against
|
||||
``k``. As always, if equality obtains we do the right thing, else case #b
|
||||
becomes case (a).
|
||||
|
||||
When (b) becomes (a), the last comparison was non-equal, so ``_cmp`` is
|
||||
non-zero, and the loop exits because ``lo==hi==i`` in case (a). The invariant
|
||||
then tells us ``K(lo-1) < k < K(lo)``, so the key is in fact not present, it's
|
||||
correct to exit with ``_cmp`` non-zero, and ``i==lo`` is again the index at
|
||||
which ``k`` belongs.
|
||||
|
||||
|
||||
Optimization points:
|
||||
--------------------
|
||||
|
||||
+ As for ``BTREE_SEARCH``, shifting of signed ints is cheaper than division.
|
||||
|
||||
+ Unlike as for ``BTREE_SEARCH``, there's nothing special about searching an
|
||||
empty bucket, and the macro computes thoroughly sensible results in that
|
||||
case.
|
||||
|
||||
+ The order of ``_cmp`` comparisons differs from ``BTREE_SEARCH``. When
|
||||
searching a bucket, it's much more likely (than when searching a BTree node)
|
||||
that the key is present, so testing ``__cmp==0`` isn't a systematic waste of
|
||||
cycles. At the extreme, if all searches are successful (key present), on
|
||||
average this saves one comparison per search, against leaving the
|
||||
determination of ``_cmp==0`` implicit (as ``BTREE_SEARCH`` does). But even
|
||||
on successful searches, ``__cmp != 0`` is a more popular outcome than
|
||||
``__cmp == 0`` across iterations (unless the bucket has only a few keys), so
|
||||
it's important to check one of the inequality cases first. It turns out
|
||||
it's better on average to check ``K(i) < key`` (than to check ``K(i) >
|
||||
key``), because when it pays it narrows the range more (we get a little
|
||||
boost from setting ``lo=i+1`` in this case; the other case sets ``hi=i``,
|
||||
which isn't as much of a narrowing).
|
||||
@@ -0,0 +1,112 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
|
||||
'IFBucket', 'IFSet', 'IFBTree', 'IFTreeSet',
|
||||
'union', 'intersection', 'difference',
|
||||
'weightedUnion', 'weightedIntersection', 'multiunion',
|
||||
)
|
||||
|
||||
from zope.interface import moduleProvides
|
||||
|
||||
from .Interfaces import IIntegerFloatBTreeModule
|
||||
from ._base import Bucket
|
||||
from ._base import MERGE
|
||||
from ._base import MERGE_WEIGHT_numeric
|
||||
from ._base import MERGE_DEFAULT_float
|
||||
from ._base import Set
|
||||
from ._base import Tree as BTree
|
||||
from ._base import TreeSet
|
||||
from ._base import _TreeIterator
|
||||
from ._base import difference as _difference
|
||||
from ._base import intersection as _intersection
|
||||
from ._base import multiunion as _multiunion
|
||||
from ._base import set_operation as _set_operation
|
||||
from ._base import to_int as _to_key
|
||||
from ._base import to_float as _to_value
|
||||
from ._base import union as _union
|
||||
from ._base import weightedIntersection as _weightedIntersection
|
||||
from ._base import weightedUnion as _weightedUnion
|
||||
from ._base import _fix_pickle
|
||||
from ._compat import import_c_extension
|
||||
|
||||
_BUCKET_SIZE = 120
|
||||
_TREE_SIZE = 500
|
||||
using64bits = False
|
||||
|
||||
class IFBucketPy(Bucket):
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class IFSetPy(Set):
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class IFBTreePy(BTree):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class IFTreeSetPy(TreeSet):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class IFTreeIteratorPy(_TreeIterator):
|
||||
pass
|
||||
|
||||
|
||||
# Can't declare forward refs, so fix up afterwards:
|
||||
|
||||
IFBucketPy._mapping_type = IFBucketPy._bucket_type = IFBucketPy
|
||||
IFBucketPy._set_type = IFSetPy
|
||||
|
||||
IFSetPy._mapping_type = IFBucketPy
|
||||
IFSetPy._set_type = IFSetPy._bucket_type = IFSetPy
|
||||
|
||||
IFBTreePy._mapping_type = IFBTreePy._bucket_type = IFBucketPy
|
||||
IFBTreePy._set_type = IFSetPy
|
||||
|
||||
IFTreeSetPy._mapping_type = IFBucketPy
|
||||
IFTreeSetPy._set_type = IFTreeSetPy._bucket_type = IFSetPy
|
||||
|
||||
|
||||
differencePy = _set_operation(_difference, IFSetPy)
|
||||
unionPy = _set_operation(_union, IFSetPy)
|
||||
intersectionPy = _set_operation(_intersection, IFSetPy)
|
||||
multiunionPy = _set_operation(_multiunion, IFSetPy)
|
||||
weightedUnionPy = _set_operation(_weightedUnion, IFSetPy)
|
||||
weightedIntersectionPy = _set_operation(_weightedIntersection, IFSetPy)
|
||||
|
||||
import_c_extension(globals())
|
||||
|
||||
_fix_pickle(globals(), __name__)
|
||||
|
||||
moduleProvides(IIntegerFloatBTreeModule)
|
||||
@@ -0,0 +1,113 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
|
||||
'IIBucket', 'IISet', 'IIBTree', 'IITreeSet',
|
||||
'union', 'intersection', 'difference',
|
||||
'weightedUnion', 'weightedIntersection', 'multiunion',
|
||||
)
|
||||
|
||||
from zope.interface import moduleProvides
|
||||
|
||||
from .Interfaces import IIntegerIntegerBTreeModule
|
||||
from ._base import Bucket
|
||||
from ._base import MERGE
|
||||
from ._base import MERGE_WEIGHT_numeric
|
||||
from ._base import MERGE_DEFAULT_int
|
||||
from ._base import Set
|
||||
from ._base import Tree as BTree
|
||||
from ._base import TreeSet
|
||||
from ._base import _TreeIterator
|
||||
from ._base import difference as _difference
|
||||
from ._base import intersection as _intersection
|
||||
from ._base import multiunion as _multiunion
|
||||
from ._base import set_operation as _set_operation
|
||||
from ._base import to_int as _to_key
|
||||
_to_value = _to_key
|
||||
from ._base import union as _union
|
||||
from ._base import weightedIntersection as _weightedIntersection
|
||||
from ._base import weightedUnion as _weightedUnion
|
||||
from ._base import _fix_pickle
|
||||
from ._compat import import_c_extension
|
||||
|
||||
_BUCKET_SIZE = 120
|
||||
_TREE_SIZE = 500
|
||||
using64bits = False
|
||||
|
||||
|
||||
class IIBucketPy(Bucket):
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class IISetPy(Set):
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class IIBTreePy(BTree):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class IITreeSetPy(TreeSet):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class IITreeIteratorPy(_TreeIterator):
|
||||
pass
|
||||
|
||||
|
||||
# Can't declare forward refs, so fix up afterwards:
|
||||
|
||||
IIBucketPy._mapping_type = IIBucketPy._bucket_type = IIBucketPy
|
||||
IIBucketPy._set_type = IISetPy
|
||||
|
||||
IISetPy._mapping_type = IIBucketPy
|
||||
IISetPy._set_type = IISetPy._bucket_type = IISetPy
|
||||
|
||||
IIBTreePy._mapping_type = IIBTreePy._bucket_type = IIBucketPy
|
||||
IIBTreePy._set_type = IISetPy
|
||||
|
||||
IITreeSetPy._mapping_type = IIBucketPy
|
||||
IITreeSetPy._set_type = IITreeSetPy._bucket_type = IISetPy
|
||||
|
||||
|
||||
differencePy = _set_operation(_difference, IISetPy)
|
||||
unionPy = _set_operation(_union, IISetPy)
|
||||
intersectionPy = _set_operation(_intersection, IISetPy)
|
||||
multiunionPy = _set_operation(_multiunion, IISetPy)
|
||||
weightedUnionPy = _set_operation(_weightedUnion, IISetPy)
|
||||
weightedIntersectionPy = _set_operation(_weightedIntersection, IISetPy)
|
||||
|
||||
import_c_extension(globals())
|
||||
|
||||
_fix_pickle(globals(), __name__)
|
||||
|
||||
moduleProvides(IIntegerIntegerBTreeModule)
|
||||
@@ -0,0 +1,95 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
|
||||
'IOBucket', 'IOSet', 'IOBTree', 'IOTreeSet',
|
||||
'union', 'intersection', 'difference', 'multiunion',
|
||||
)
|
||||
|
||||
from zope.interface import moduleProvides
|
||||
|
||||
from .Interfaces import IIntegerObjectBTreeModule
|
||||
from ._base import Bucket
|
||||
from ._base import MERGE_WEIGHT_default
|
||||
from ._base import Set
|
||||
from ._base import Tree as BTree
|
||||
from ._base import TreeSet
|
||||
from ._base import _TreeIterator
|
||||
from ._base import difference as _difference
|
||||
from ._base import intersection as _intersection
|
||||
from ._base import multiunion as _multiunion
|
||||
from ._base import set_operation as _set_operation
|
||||
from ._base import to_int as _to_key
|
||||
from ._base import to_ob as _to_value
|
||||
from ._base import union as _union
|
||||
from ._base import _fix_pickle
|
||||
from ._compat import import_c_extension
|
||||
|
||||
_BUCKET_SIZE = 60
|
||||
_TREE_SIZE = 500
|
||||
using64bits = False
|
||||
|
||||
|
||||
class IOBucketPy(Bucket):
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_default
|
||||
|
||||
|
||||
class IOSetPy(Set):
|
||||
_to_key = _to_key
|
||||
|
||||
|
||||
class IOBTreePy(BTree):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_default
|
||||
|
||||
|
||||
class IOTreeSetPy(TreeSet):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
|
||||
class IOTreeIteratorPy(_TreeIterator):
|
||||
pass
|
||||
|
||||
|
||||
# Can't declare forward refs, so fix up afterwards:
|
||||
|
||||
IOBucketPy._mapping_type = IOBucketPy._bucket_type = IOBucketPy
|
||||
IOBucketPy._set_type = IOSetPy
|
||||
|
||||
IOSetPy._mapping_type = IOBucketPy
|
||||
IOSetPy._set_type = IOSetPy._bucket_type = IOSetPy
|
||||
|
||||
IOBTreePy._mapping_type = IOBTreePy._bucket_type = IOBucketPy
|
||||
IOBTreePy._set_type = IOSetPy
|
||||
|
||||
IOTreeSetPy._mapping_type = IOBucketPy
|
||||
IOTreeSetPy._set_type = IOTreeSetPy._bucket_type = IOSetPy
|
||||
|
||||
|
||||
differencePy = _set_operation(_difference, IOSetPy)
|
||||
unionPy = _set_operation(_union, IOSetPy)
|
||||
intersectionPy = _set_operation(_intersection, IOSetPy)
|
||||
multiunionPy = _set_operation(_multiunion, IOSetPy)
|
||||
|
||||
import_c_extension(globals())
|
||||
|
||||
_fix_pickle(globals(), __name__)
|
||||
|
||||
moduleProvides(IIntegerObjectBTreeModule)
|
||||
@@ -0,0 +1,527 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from zope.interface import Interface, Attribute
|
||||
|
||||
|
||||
class ICollection(Interface):
|
||||
|
||||
def clear():
|
||||
"""Remove all of the items from the collection."""
|
||||
|
||||
def __nonzero__():
|
||||
"""Check if the collection is non-empty.
|
||||
|
||||
Return a true value if the collection is non-empty and a
|
||||
false value otherwise.
|
||||
"""
|
||||
|
||||
|
||||
class IReadSequence(Interface):
|
||||
|
||||
def __getitem__(index):
|
||||
"""Return the value at the given index.
|
||||
|
||||
An IndexError is raised if the index cannot be found.
|
||||
"""
|
||||
|
||||
def __getslice__(index1, index2):
|
||||
"""Return a subsequence from the original sequence.
|
||||
|
||||
The subsequence includes the items from index1 up to, but not
|
||||
including, index2.
|
||||
"""
|
||||
|
||||
class IKeyed(ICollection):
|
||||
|
||||
def has_key(key):
|
||||
"""Check whether the object has an item with the given key.
|
||||
|
||||
Return a true value if the key is present, else a false value.
|
||||
"""
|
||||
|
||||
def keys(min=None, max=None, excludemin=False, excludemax=False):
|
||||
"""Return an IReadSequence containing the keys in the collection.
|
||||
|
||||
The type of the IReadSequence is not specified. It could be a list
|
||||
or a tuple or some other type.
|
||||
|
||||
All arguments are optional, and may be specified as keyword
|
||||
arguments, or by position.
|
||||
|
||||
If a min is specified, then output is constrained to keys greater
|
||||
than or equal to the given min, and, if excludemin is specified and
|
||||
true, is further constrained to keys strictly greater than min. A
|
||||
min value of None is ignored. If min is None or not specified, and
|
||||
excludemin is true, the smallest key is excluded.
|
||||
|
||||
If a max is specified, then output is constrained to keys less than
|
||||
or equal to the given max, and, if excludemax is specified and
|
||||
true, is further constrained to keys strictly less than max. A max
|
||||
value of None is ignored. If max is None or not specified, and
|
||||
excludemax is true, the largest key is excluded.
|
||||
"""
|
||||
|
||||
def maxKey(key=None):
|
||||
"""Return the maximum key.
|
||||
|
||||
If a key argument if provided and not None, return the largest key
|
||||
that is less than or equal to the argument. Raise an exception if
|
||||
no such key exists.
|
||||
"""
|
||||
|
||||
def minKey(key=None):
|
||||
"""Return the minimum key.
|
||||
|
||||
If a key argument if provided and not None, return the smallest key
|
||||
that is greater than or equal to the argument. Raise an exception
|
||||
if no such key exists.
|
||||
"""
|
||||
|
||||
|
||||
class ISetMutable(IKeyed):
|
||||
|
||||
def insert(key):
|
||||
"""Add the key (value) to the set.
|
||||
|
||||
If the key was already in the set, return 0, otherwise return 1.
|
||||
"""
|
||||
|
||||
def remove(key):
|
||||
"""Remove the key from the set.
|
||||
|
||||
Raises KeyError if key is not in the set.
|
||||
"""
|
||||
|
||||
def update(seq):
|
||||
"""Add the items from the given sequence to the set."""
|
||||
|
||||
|
||||
class ISized(Interface):
|
||||
"""An object that supports __len__."""
|
||||
|
||||
def __len__():
|
||||
"""Return the number of items in the container."""
|
||||
|
||||
|
||||
class IKeySequence(IKeyed, ISized):
|
||||
|
||||
def __getitem__(index):
|
||||
"""Return the key in the given index position.
|
||||
|
||||
This allows iteration with for loops and use in functions,
|
||||
like map and list, that read sequences.
|
||||
"""
|
||||
|
||||
|
||||
class ISet(IKeySequence, ISetMutable):
|
||||
pass
|
||||
|
||||
|
||||
class ITreeSet(IKeyed, ISetMutable):
|
||||
pass
|
||||
|
||||
class IMinimalDictionary(ISized, IKeyed):
|
||||
|
||||
def get(key, default):
|
||||
"""Get the value associated with the given key.
|
||||
|
||||
Return the default if has_key(key) is false.
|
||||
"""
|
||||
|
||||
def __getitem__(key):
|
||||
"""Get the value associated with the given key.
|
||||
|
||||
Raise KeyError if has_key(key) is false.
|
||||
"""
|
||||
|
||||
def __setitem__(key, value):
|
||||
"""Set the value associated with the given key."""
|
||||
|
||||
def __delitem__(key):
|
||||
"""Delete the value associated with the given key.
|
||||
|
||||
Raise KeyError if has_key(key) is false.
|
||||
"""
|
||||
|
||||
def values(min=None, max=None, excludemin=False, excludemax=False):
|
||||
"""Return an IReadSequence containing the values in the collection.
|
||||
|
||||
The type of the IReadSequence is not specified. It could be a list
|
||||
or a tuple or some other type.
|
||||
|
||||
All arguments are optional, and may be specified as keyword
|
||||
arguments, or by position.
|
||||
|
||||
If a min is specified, then output is constrained to values whose
|
||||
keys are greater than or equal to the given min, and, if excludemin
|
||||
is specified and true, is further constrained to values whose keys
|
||||
are strictly greater than min. A min value of None is ignored. If
|
||||
min is None or not specified, and excludemin is true, the value
|
||||
corresponding to the smallest key is excluded.
|
||||
|
||||
If a max is specified, then output is constrained to values whose
|
||||
keys are less than or equal to the given max, and, if excludemax is
|
||||
specified and true, is further constrained to values whose keys are
|
||||
strictly less than max. A max value of None is ignored. If max is
|
||||
None or not specified, and excludemax is true, the value
|
||||
corresponding to the largest key is excluded.
|
||||
"""
|
||||
|
||||
def items(min=None, max=None, excludemin=False, excludemax=False):
|
||||
"""Return an IReadSequence containing the items in the collection.
|
||||
|
||||
An item is a 2-tuple, a (key, value) pair.
|
||||
|
||||
The type of the IReadSequence is not specified. It could be a list
|
||||
or a tuple or some other type.
|
||||
|
||||
All arguments are optional, and may be specified as keyword
|
||||
arguments, or by position.
|
||||
|
||||
If a min is specified, then output is constrained to items whose
|
||||
keys are greater than or equal to the given min, and, if excludemin
|
||||
is specified and true, is further constrained to items whose keys
|
||||
are strictly greater than min. A min value of None is ignored. If
|
||||
min is None or not specified, and excludemin is true, the item with
|
||||
the smallest key is excluded.
|
||||
|
||||
If a max is specified, then output is constrained to items whose
|
||||
keys are less than or equal to the given max, and, if excludemax is
|
||||
specified and true, is further constrained to items whose keys are
|
||||
strictly less than max. A max value of None is ignored. If max is
|
||||
None or not specified, and excludemax is true, the item with the
|
||||
largest key is excluded.
|
||||
"""
|
||||
|
||||
class IDictionaryIsh(IMinimalDictionary):
|
||||
|
||||
def update(collection):
|
||||
"""Add the items from the given collection object to the collection.
|
||||
|
||||
The input collection must be a sequence of (key, value) 2-tuples,
|
||||
or an object with an 'items' method that returns a sequence of
|
||||
(key, value) pairs.
|
||||
"""
|
||||
|
||||
def byValue(minValue):
|
||||
"""Return a sequence of (value, key) pairs, sorted by value.
|
||||
|
||||
Values < minValue are omitted and other values are "normalized" by
|
||||
the minimum value. This normalization may be a noop, but, for
|
||||
integer values, the normalization is division.
|
||||
"""
|
||||
|
||||
def setdefault(key, d):
|
||||
"""D.setdefault(k, d) -> D.get(k, d), also set D[k]=d if k not in D.
|
||||
|
||||
Return the value like get() except that if key is missing, d is both
|
||||
returned and inserted into the dictionary as the value of k.
|
||||
|
||||
Note that, unlike as for Python's dict.setdefault(), d is not
|
||||
optional. Python defaults d to None, but that doesn't make sense
|
||||
for mappings that can't have None as a value (for example, an
|
||||
IIBTree can have only integers as values).
|
||||
"""
|
||||
|
||||
def pop(key, d):
|
||||
"""D.pop(k[, d]) -> v, remove key and return the corresponding value.
|
||||
|
||||
If key is not found, d is returned if given, otherwise KeyError is
|
||||
raised.
|
||||
"""
|
||||
|
||||
|
||||
class IBTree(IDictionaryIsh):
|
||||
|
||||
def insert(key, value):
|
||||
"""Insert a key and value into the collection.
|
||||
|
||||
If the key was already in the collection, then there is no
|
||||
change and 0 is returned.
|
||||
|
||||
If the key was not already in the collection, then the item is
|
||||
added and 1 is returned.
|
||||
|
||||
This method is here to allow one to generate random keys and
|
||||
to insert and test whether the key was there in one operation.
|
||||
|
||||
A standard idiom for generating new keys will be::
|
||||
|
||||
key = generate_key()
|
||||
while not t.insert(key, value):
|
||||
key=generate_key()
|
||||
"""
|
||||
|
||||
|
||||
class IMerge(Interface):
|
||||
"""Object with methods for merging sets, buckets, and trees.
|
||||
|
||||
These methods are supplied in modules that define collection
|
||||
classes with particular key and value types. The operations apply
|
||||
only to collections from the same module. For example, the
|
||||
IIBTree.union can only be used with IIBTree.IIBTree,
|
||||
IIBTree.IIBucket, IIBTree.IISet, and IIBTree.IITreeSet.
|
||||
|
||||
The implementing module has a value type. The IOBTree and OOBTree
|
||||
modules have object value type. The IIBTree and OIBTree modules
|
||||
have integer value types. Other modules may be defined in the
|
||||
future that have other value types.
|
||||
|
||||
The individual types are classified into set (Set and TreeSet) and
|
||||
mapping (Bucket and BTree) types.
|
||||
"""
|
||||
|
||||
def difference(c1, c2):
|
||||
"""Return the keys or items in c1 for which there is no key in c2.
|
||||
|
||||
If c1 is None, then None is returned. If c2 is None, then c1
|
||||
is returned.
|
||||
|
||||
If neither c1 nor c2 is None, the output is a Set if c1 is a Set or
|
||||
TreeSet, and is a Bucket if c1 is a Bucket or BTree.
|
||||
"""
|
||||
|
||||
def union(c1, c2):
|
||||
"""Compute the Union of c1 and c2.
|
||||
|
||||
If c1 is None, then c2 is returned, otherwise, if c2 is None,
|
||||
then c1 is returned.
|
||||
|
||||
The output is a Set containing keys from the input
|
||||
collections.
|
||||
"""
|
||||
|
||||
def intersection(c1, c2):
|
||||
"""Compute the intersection of c1 and c2.
|
||||
|
||||
If c1 is None, then c2 is returned, otherwise, if c2 is None,
|
||||
then c1 is returned.
|
||||
|
||||
The output is a Set containing matching keys from the input
|
||||
collections.
|
||||
"""
|
||||
|
||||
|
||||
class IBTreeModule(Interface):
|
||||
"""These are available in all modules (IOBTree, OIBTree, OOBTree, IIBTree,
|
||||
IFBTree, LFBTree, LOBTree, OLBTree, and LLBTree).
|
||||
"""
|
||||
|
||||
BTree = Attribute(
|
||||
"""The IBTree for this module.
|
||||
|
||||
Also available as [prefix]BTree, as in IOBTree.""")
|
||||
|
||||
Bucket = Attribute(
|
||||
"""The leaf-node data buckets used by the BTree.
|
||||
|
||||
(IBucket is not currently defined in this file, but is essentially
|
||||
IDictionaryIsh, with the exception of __nonzero__, as of this
|
||||
writing.)
|
||||
|
||||
Also available as [prefix]Bucket, as in IOBucket.""")
|
||||
|
||||
TreeSet = Attribute(
|
||||
"""The ITreeSet for this module.
|
||||
|
||||
Also available as [prefix]TreeSet, as in IOTreeSet.""")
|
||||
|
||||
Set = Attribute(
|
||||
"""The ISet for this module: the leaf-node data buckets used by the
|
||||
TreeSet.
|
||||
|
||||
Also available as [prefix]BTree, as in IOSet.""")
|
||||
|
||||
|
||||
class IIMerge(IMerge):
|
||||
"""Merge collections with integer value type.
|
||||
|
||||
A primary intent is to support operations with no or integer
|
||||
values, which are used as "scores" to rate indiviual keys. That
|
||||
is, in this context, a BTree or Bucket is viewed as a set with
|
||||
scored keys, using integer scores.
|
||||
"""
|
||||
|
||||
def weightedUnion(c1, c2, weight1=1, weight2=1):
|
||||
"""Compute the weighted union of c1 and c2.
|
||||
|
||||
If c1 and c2 are None, the output is (0, None).
|
||||
|
||||
If c1 is None and c2 is not None, the output is (weight2, c2).
|
||||
|
||||
If c1 is not None and c2 is None, the output is (weight1, c1).
|
||||
|
||||
Else, and hereafter, c1 is not None and c2 is not None.
|
||||
|
||||
If c1 and c2 are both sets, the output is 1 and the (unweighted)
|
||||
union of the sets.
|
||||
|
||||
Else the output is 1 and a Bucket whose keys are the union of c1 and
|
||||
c2's keys, and whose values are::
|
||||
|
||||
v1*weight1 + v2*weight2
|
||||
|
||||
where:
|
||||
|
||||
v1 is 0 if the key is not in c1
|
||||
1 if the key is in c1 and c1 is a set
|
||||
c1[key] if the key is in c1 and c1 is a mapping
|
||||
|
||||
v2 is 0 if the key is not in c2
|
||||
1 if the key is in c2 and c2 is a set
|
||||
c2[key] if the key is in c2 and c2 is a mapping
|
||||
|
||||
Note that c1 and c2 must be collections.
|
||||
"""
|
||||
|
||||
def weightedIntersection(c1, c2, weight1=1, weight2=1):
|
||||
"""Compute the weighted intersection of c1 and c2.
|
||||
|
||||
If c1 and c2 are None, the output is (0, None).
|
||||
|
||||
If c1 is None and c2 is not None, the output is (weight2, c2).
|
||||
|
||||
If c1 is not None and c2 is None, the output is (weight1, c1).
|
||||
|
||||
Else, and hereafter, c1 is not None and c2 is not None.
|
||||
|
||||
If c1 and c2 are both sets, the output is the sum of the weights
|
||||
and the (unweighted) intersection of the sets.
|
||||
|
||||
Else the output is 1 and a Bucket whose keys are the intersection of
|
||||
c1 and c2's keys, and whose values are::
|
||||
|
||||
v1*weight1 + v2*weight2
|
||||
|
||||
where:
|
||||
|
||||
v1 is 1 if c1 is a set
|
||||
c1[key] if c1 is a mapping
|
||||
|
||||
v2 is 1 if c2 is a set
|
||||
c2[key] if c2 is a mapping
|
||||
|
||||
Note that c1 and c2 must be collections.
|
||||
"""
|
||||
|
||||
|
||||
class IMergeIntegerKey(IMerge):
|
||||
"""IMerge-able objects with integer keys.
|
||||
|
||||
Concretely, this means the types in IOBTree and IIBTree.
|
||||
"""
|
||||
|
||||
def multiunion(seq):
|
||||
"""Return union of (zero or more) integer sets, as an integer set.
|
||||
|
||||
seq is a sequence of objects each convertible to an integer set.
|
||||
These objects are convertible to an integer set:
|
||||
|
||||
+ An integer, which is added to the union.
|
||||
|
||||
+ A Set or TreeSet from the same module (for example, an
|
||||
IIBTree.TreeSet for IIBTree.multiunion()). The elements of the
|
||||
set are added to the union.
|
||||
|
||||
+ A Bucket or BTree from the same module (for example, an
|
||||
IOBTree.IOBTree for IOBTree.multiunion()). The keys of the
|
||||
mapping are added to the union.
|
||||
|
||||
The union is returned as a Set from the same module (for example,
|
||||
IIBTree.multiunion() returns an IIBTree.IISet).
|
||||
|
||||
The point to this method is that it can run much faster than
|
||||
doing a sequence of two-input union() calls. Under the covers,
|
||||
all the integers in all the inputs are sorted via a single
|
||||
linear-time radix sort, then duplicates are removed in a second
|
||||
linear-time pass.
|
||||
"""
|
||||
|
||||
class IBTreeFamily(Interface):
|
||||
"""the 64-bit or 32-bit family"""
|
||||
IO = Attribute('The IIntegerObjectBTreeModule for this family')
|
||||
OI = Attribute('The IObjectIntegerBTreeModule for this family')
|
||||
II = Attribute('The IIntegerIntegerBTreeModule for this family')
|
||||
IF = Attribute('The IIntegerFloatBTreeModule for this family')
|
||||
OO = Attribute('The IObjectObjectBTreeModule for this family')
|
||||
maxint = Attribute('The maximum integer storable in this family')
|
||||
minint = Attribute('The minimum integer storable in this family')
|
||||
|
||||
|
||||
class IIntegerObjectBTreeModule(IBTreeModule, IMerge):
|
||||
"""keys, or set values, are integers; values are objects.
|
||||
|
||||
describes IOBTree and LOBTree"""
|
||||
|
||||
family = Attribute('The IBTreeFamily of this module')
|
||||
|
||||
|
||||
class IObjectIntegerBTreeModule(IBTreeModule, IIMerge):
|
||||
"""keys, or set values, are objects; values are integers.
|
||||
|
||||
Object keys (and set values) must sort reliably (for instance, *not* on
|
||||
object id)! Homogenous key types recommended.
|
||||
|
||||
describes OIBTree and LOBTree"""
|
||||
|
||||
family = Attribute('The IBTreeFamily of this module')
|
||||
|
||||
|
||||
class IIntegerIntegerBTreeModule(IBTreeModule, IIMerge, IMergeIntegerKey):
|
||||
"""keys, or set values, are integers; values are also integers.
|
||||
|
||||
describes IIBTree and LLBTree"""
|
||||
|
||||
family = Attribute('The IBTreeFamily of this module')
|
||||
|
||||
|
||||
class IObjectObjectBTreeModule(IBTreeModule, IMerge):
|
||||
"""keys, or set values, are objects; values are also objects.
|
||||
|
||||
Object keys (and set values) must sort reliably (for instance, *not* on
|
||||
object id)! Homogenous key types recommended.
|
||||
|
||||
describes OOBTree"""
|
||||
|
||||
# Note that there's no ``family`` attribute; all families include
|
||||
# the OO flavor of BTrees.
|
||||
|
||||
|
||||
class IIntegerFloatBTreeModule(IBTreeModule, IMerge):
|
||||
"""keys, or set values, are integers; values are floats.
|
||||
|
||||
describes IFBTree and LFBTree"""
|
||||
|
||||
family = Attribute('The IBTreeFamily of this module')
|
||||
|
||||
|
||||
try:
|
||||
from ZODB.POSException import BTreesConflictError
|
||||
except ImportError:
|
||||
class BTreesConflictError(ValueError):
|
||||
@property
|
||||
def reason(self):
|
||||
return self.args[-1]
|
||||
|
||||
###############################################################
|
||||
# IMPORTANT NOTE
|
||||
#
|
||||
# Getting the length of a BTree, TreeSet, or output of keys,
|
||||
# values, or items of same is expensive. If you need to get the
|
||||
# length, you need to maintain this separately.
|
||||
#
|
||||
# Eventually, I need to express this through the interfaces.
|
||||
#
|
||||
################################################################
|
||||
@@ -0,0 +1,113 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
|
||||
'LFBucket', 'LFSet', 'LFBTree', 'LFTreeSet',
|
||||
'union', 'intersection', 'difference',
|
||||
'weightedUnion', 'weightedIntersection', 'multiunion',
|
||||
)
|
||||
|
||||
from zope.interface import moduleProvides
|
||||
|
||||
from .Interfaces import IIntegerFloatBTreeModule
|
||||
from ._base import Bucket
|
||||
from ._base import MERGE
|
||||
from ._base import MERGE_WEIGHT_numeric
|
||||
from ._base import MERGE_DEFAULT_float
|
||||
from ._base import Set
|
||||
from ._base import Tree as BTree
|
||||
from ._base import TreeSet
|
||||
from ._base import _TreeIterator
|
||||
from ._base import difference as _difference
|
||||
from ._base import intersection as _intersection
|
||||
from ._base import multiunion as _multiunion
|
||||
from ._base import set_operation as _set_operation
|
||||
from ._base import to_long as _to_key
|
||||
from ._base import to_float as _to_value
|
||||
from ._base import union as _union
|
||||
from ._base import weightedIntersection as _weightedIntersection
|
||||
from ._base import weightedUnion as _weightedUnion
|
||||
from ._base import _fix_pickle
|
||||
from ._compat import import_c_extension
|
||||
|
||||
_BUCKET_SIZE = 120
|
||||
_TREE_SIZE = 500
|
||||
using64bits = True
|
||||
|
||||
|
||||
class LFBucketPy(Bucket):
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class LFSetPy(Set):
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class LFBTreePy(BTree):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class LFTreeSetPy(TreeSet):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class LFTreeIteratorPy(_TreeIterator):
|
||||
pass
|
||||
|
||||
|
||||
# Can't declare forward refs, so fix up afterwards:
|
||||
|
||||
LFBucketPy._mapping_type = LFBucketPy._bucket_type = LFBucketPy
|
||||
LFBucketPy._set_type = LFSetPy
|
||||
|
||||
LFSetPy._mapping_type = LFBucketPy
|
||||
LFSetPy._set_type = LFSetPy._bucket_type = LFSetPy
|
||||
|
||||
LFBTreePy._mapping_type = LFBTreePy._bucket_type = LFBucketPy
|
||||
LFBTreePy._set_type = LFSetPy
|
||||
|
||||
LFTreeSetPy._mapping_type = LFBucketPy
|
||||
LFTreeSetPy._set_type = LFTreeSetPy._bucket_type = LFSetPy
|
||||
|
||||
|
||||
differencePy = _set_operation(_difference, LFSetPy)
|
||||
unionPy = _set_operation(_union, LFSetPy)
|
||||
intersectionPy = _set_operation(_intersection, LFSetPy)
|
||||
multiunionPy = _set_operation(_multiunion, LFSetPy)
|
||||
weightedUnionPy = _set_operation(_weightedUnion, LFSetPy)
|
||||
weightedIntersectionPy = _set_operation(_weightedIntersection, LFSetPy)
|
||||
|
||||
import_c_extension(globals())
|
||||
|
||||
_fix_pickle(globals(), __name__)
|
||||
|
||||
moduleProvides(IIntegerFloatBTreeModule)
|
||||
@@ -0,0 +1,113 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
|
||||
'LLBucket', 'LLSet', 'LLBTree', 'LLTreeSet',
|
||||
'union', 'intersection', 'difference',
|
||||
'weightedUnion', 'weightedIntersection', 'multiunion',
|
||||
)
|
||||
|
||||
from zope.interface import moduleProvides
|
||||
|
||||
from .Interfaces import IIntegerIntegerBTreeModule
|
||||
from ._base import Bucket
|
||||
from ._base import MERGE
|
||||
from ._base import MERGE_WEIGHT_numeric
|
||||
from ._base import MERGE_DEFAULT_int
|
||||
from ._base import Set
|
||||
from ._base import Tree as BTree
|
||||
from ._base import TreeSet
|
||||
from ._base import _TreeIterator
|
||||
from ._base import difference as _difference
|
||||
from ._base import intersection as _intersection
|
||||
from ._base import multiunion as _multiunion
|
||||
from ._base import set_operation as _set_operation
|
||||
from ._base import to_long as _to_key
|
||||
from ._base import to_long as _to_value
|
||||
from ._base import union as _union
|
||||
from ._base import weightedIntersection as _weightedIntersection
|
||||
from ._base import weightedUnion as _weightedUnion
|
||||
from ._base import _fix_pickle
|
||||
from ._compat import import_c_extension
|
||||
|
||||
_BUCKET_SIZE = 120
|
||||
_TREE_SIZE = 500
|
||||
using64bits = True
|
||||
|
||||
|
||||
class LLBucketPy(Bucket):
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class LLSetPy(Set):
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class LLBTreePy(BTree):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class LLTreeSetPy(TreeSet):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class LLTreeIteratorPy(_TreeIterator):
|
||||
pass
|
||||
|
||||
|
||||
# Can't declare forward refs, so fix up afterwards:
|
||||
|
||||
LLBucketPy._mapping_type = LLBucketPy._bucket_type = LLBucketPy
|
||||
LLBucketPy._set_type = LLSetPy
|
||||
|
||||
LLSetPy._mapping_type = LLBucketPy
|
||||
LLSetPy._set_type = LLSetPy._bucket_type = LLSetPy
|
||||
|
||||
LLBTreePy._mapping_type = LLBTreePy._bucket_type = LLBucketPy
|
||||
LLBTreePy._set_type = LLSetPy
|
||||
|
||||
LLTreeSetPy._mapping_type = LLBucketPy
|
||||
LLTreeSetPy._set_type = LLTreeSetPy._bucket_type = LLSetPy
|
||||
|
||||
|
||||
differencePy = _set_operation(_difference, LLSetPy)
|
||||
unionPy = _set_operation(_union, LLSetPy)
|
||||
intersectionPy = _set_operation(_intersection, LLSetPy)
|
||||
multiunionPy = _set_operation(_multiunion, LLSetPy)
|
||||
weightedUnionPy = _set_operation(_weightedUnion, LLSetPy)
|
||||
weightedIntersectionPy = _set_operation(_weightedIntersection, LLSetPy)
|
||||
|
||||
import_c_extension(globals())
|
||||
|
||||
_fix_pickle(globals(), __name__)
|
||||
|
||||
moduleProvides(IIntegerIntegerBTreeModule)
|
||||
@@ -0,0 +1,96 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
|
||||
'LOBucket', 'LOSet', 'LOBTree', 'LOTreeSet',
|
||||
'union', 'intersection', 'difference', 'multiunion',
|
||||
)
|
||||
|
||||
from zope.interface import moduleProvides
|
||||
|
||||
from .Interfaces import IIntegerObjectBTreeModule
|
||||
from ._base import Bucket
|
||||
from ._base import MERGE_WEIGHT_default
|
||||
from ._base import Set
|
||||
from ._base import Tree as BTree
|
||||
from ._base import TreeSet
|
||||
from ._base import _TreeIterator
|
||||
from ._base import difference as _difference
|
||||
from ._base import intersection as _intersection
|
||||
from ._base import multiunion as _multiunion
|
||||
from ._base import set_operation as _set_operation
|
||||
from ._base import to_long as _to_key
|
||||
from ._base import to_ob as _to_value
|
||||
from ._base import union as _union
|
||||
from ._base import _fix_pickle
|
||||
from ._compat import import_c_extension
|
||||
|
||||
_BUCKET_SIZE = 60
|
||||
_TREE_SIZE = 500
|
||||
using64bits = True
|
||||
|
||||
|
||||
class LOBucketPy(Bucket):
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_default
|
||||
|
||||
|
||||
class LOSetPy(Set):
|
||||
_to_key = _to_key
|
||||
|
||||
|
||||
class LOBTreePy(BTree):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_default
|
||||
|
||||
|
||||
class LOTreeSetPy(TreeSet):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
|
||||
|
||||
class LOTreeIteratorPy(_TreeIterator):
|
||||
pass
|
||||
|
||||
|
||||
# Can't declare forward refs, so fix up afterwards:
|
||||
|
||||
LOBucketPy._mapping_type = LOBucketPy._bucket_type = LOBucketPy
|
||||
LOBucketPy._set_type = LOSetPy
|
||||
|
||||
LOSetPy._mapping_type = LOBucketPy
|
||||
LOSetPy._set_type = LOSetPy._bucket_type = LOSetPy
|
||||
|
||||
LOBTreePy._mapping_type = LOBTreePy._bucket_type = LOBucketPy
|
||||
LOBTreePy._set_type = LOSetPy
|
||||
|
||||
LOTreeSetPy._mapping_type = LOBucketPy
|
||||
LOTreeSetPy._set_type = LOTreeSetPy._bucket_type = LOSetPy
|
||||
|
||||
|
||||
differencePy = _set_operation(_difference, LOSetPy)
|
||||
unionPy = _set_operation(_union, LOSetPy)
|
||||
intersectionPy = _set_operation(_intersection, LOSetPy)
|
||||
multiunionPy = _set_operation(_multiunion, LOSetPy)
|
||||
|
||||
import_c_extension(globals())
|
||||
|
||||
_fix_pickle(globals(), __name__)
|
||||
|
||||
moduleProvides(IIntegerObjectBTreeModule)
|
||||
@@ -0,0 +1,58 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import persistent
|
||||
|
||||
class Length(persistent.Persistent):
|
||||
"""BTree lengths are often too expensive to compute.
|
||||
|
||||
Objects that use BTrees need to keep track of lengths themselves.
|
||||
This class provides an object for doing this.
|
||||
|
||||
As a bonus, the object support application-level conflict
|
||||
resolution.
|
||||
|
||||
It is tempting to to assign length objects to __len__ attributes
|
||||
to provide instance-specific __len__ methods. However, this no
|
||||
longer works as expected, because new-style classes cache
|
||||
class-defined slot methods (like __len__) in C type slots. Thus,
|
||||
instance-defined slot fillers are ignored.
|
||||
"""
|
||||
# class-level default required to keep copy.deepcopy happy -- see
|
||||
# https://bugs.launchpad.net/zodb/+bug/516653
|
||||
value = 0
|
||||
|
||||
def __init__(self, v=0):
|
||||
self.value = v
|
||||
|
||||
def __getstate__(self):
|
||||
return self.value
|
||||
|
||||
def __setstate__(self, v):
|
||||
self.value = v
|
||||
|
||||
def set(self, v):
|
||||
"Set the length value to v."
|
||||
self.value = v
|
||||
|
||||
def _p_resolveConflict(self, old, s1, s2):
|
||||
return s1 + s2 - old
|
||||
|
||||
def change(self, delta):
|
||||
"Add delta to the length value."
|
||||
self.value += delta
|
||||
|
||||
def __call__(self, *args):
|
||||
"Return the current length value."
|
||||
return self.value
|
||||
@@ -0,0 +1,349 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
All Rights Reserved.
|
||||
|
||||
This software is subject to the provisions of the Zope Public License,
|
||||
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#define MERGETEMPLATE_C "$Id$\n"
|
||||
|
||||
/****************************************************************************
|
||||
Set operations
|
||||
****************************************************************************/
|
||||
|
||||
static int
|
||||
merge_output(Bucket *r, SetIteration *i, int mapping)
|
||||
{
|
||||
if (r->len >= r->size && Bucket_grow(r, -1, !mapping) < 0)
|
||||
return -1;
|
||||
COPY_KEY(r->keys[r->len], i->key);
|
||||
INCREF_KEY(r->keys[r->len]);
|
||||
if (mapping) {
|
||||
COPY_VALUE(r->values[r->len], i->value);
|
||||
INCREF_VALUE(r->values[r->len]);
|
||||
}
|
||||
r->len++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The "reason" argument is a little integer giving "a reason" for the
|
||||
* error. In the Zope3 codebase, these are mapped to explanatory strings
|
||||
* via zodb/btrees/interfaces.py.
|
||||
*/
|
||||
static PyObject *
|
||||
merge_error(int p1, int p2, int p3, int reason)
|
||||
{
|
||||
PyObject *r;
|
||||
|
||||
UNLESS (r=Py_BuildValue("iiii", p1, p2, p3, reason)) r=Py_None;
|
||||
if (ConflictError == NULL) {
|
||||
ConflictError = PyExc_ValueError;
|
||||
Py_INCREF(ConflictError);
|
||||
}
|
||||
PyErr_SetObject(ConflictError, r);
|
||||
if (r != Py_None)
|
||||
{
|
||||
Py_DECREF(r);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* It's hard to explain "the rules" for bucket_merge, in large part because
|
||||
* any automatic conflict-resolution scheme is going to be incorrect for
|
||||
* some endcases of *some* app. The scheme here is pretty conservative,
|
||||
* and should be OK for most apps. It's easier to explain what the code
|
||||
* allows than what it forbids:
|
||||
*
|
||||
* Leaving things alone: it's OK if both s2 and s3 leave a piece of s1
|
||||
* alone (don't delete the key, and don't change the value).
|
||||
*
|
||||
* Key deletion: a transaction (s2 or s3) can delete a key (from s1), but
|
||||
* only if the other transaction (of s2 and s3) doesn't delete the same key.
|
||||
* However, it's not OK for s2 and s3 to, between them, end up deleting all
|
||||
* the keys. This is a higher-level constraint, due to that the caller of
|
||||
* bucket_merge() doesn't have enough info to unlink the resulting empty
|
||||
* bucket from its BTree correctly. It's also not OK if s2 or s3 are empty,
|
||||
* because the transaction that emptied the bucket unlinked the bucket from
|
||||
* the tree, and nothing we do here can get it linked back in again.
|
||||
*
|
||||
* Key insertion: s2 or s3 can add a new key, provided the other transaction
|
||||
* doesn't insert the same key. It's not OK even if they insert the same
|
||||
* <key, value> pair.
|
||||
*
|
||||
* Mapping value modification: s2 or s3 can modify the value associated
|
||||
* with a key in s1, provided the other transaction doesn't make a
|
||||
* modification of the same key to a different value. It's OK if s2 and s3
|
||||
* both give the same new value to the key while it's hard to be precise about
|
||||
* why, this doesn't seem consistent with that it's *not* OK for both to add
|
||||
* a new key mapping to the same value).
|
||||
*/
|
||||
static PyObject *
|
||||
bucket_merge(Bucket *s1, Bucket *s2, Bucket *s3)
|
||||
{
|
||||
Bucket *r=0;
|
||||
PyObject *s;
|
||||
SetIteration i1 = {0,0,0}, i2 = {0,0,0}, i3 = {0,0,0};
|
||||
int cmp12, cmp13, cmp23, mapping, set;
|
||||
|
||||
/* If either "after" bucket is empty, punt. */
|
||||
if (s2->len == 0 || s3->len == 0)
|
||||
{
|
||||
merge_error(-1, -1, -1, 12);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (initSetIteration(&i1, OBJECT(s1), 1) < 0)
|
||||
goto err;
|
||||
if (initSetIteration(&i2, OBJECT(s2), 1) < 0)
|
||||
goto err;
|
||||
if (initSetIteration(&i3, OBJECT(s3), 1) < 0)
|
||||
goto err;
|
||||
|
||||
mapping = i1.usesValue | i2.usesValue | i3.usesValue;
|
||||
set = !mapping;
|
||||
|
||||
if (mapping)
|
||||
r = (Bucket *)PyObject_CallObject((PyObject *)&BucketType, NULL);
|
||||
else
|
||||
r = (Bucket *)PyObject_CallObject((PyObject *)&SetType, NULL);
|
||||
if (r == NULL)
|
||||
goto err;
|
||||
|
||||
if (i1.next(&i1) < 0)
|
||||
goto err;
|
||||
if (i2.next(&i2) < 0)
|
||||
goto err;
|
||||
if (i3.next(&i3) < 0)
|
||||
goto err;
|
||||
|
||||
/* Consult zodb/btrees/interfaces.py for the meaning of the last
|
||||
* argument passed to merge_error().
|
||||
*/
|
||||
/* TODO: This isn't passing on errors raised by value comparisons. */
|
||||
while (i1.position >= 0 && i2.position >= 0 && i3.position >= 0)
|
||||
{
|
||||
TEST_KEY_SET_OR(cmp12, i1.key, i2.key) goto err;
|
||||
TEST_KEY_SET_OR(cmp13, i1.key, i3.key) goto err;
|
||||
if (cmp12==0)
|
||||
{
|
||||
if (cmp13==0)
|
||||
{
|
||||
if (set || (TEST_VALUE(i1.value, i2.value) == 0))
|
||||
{ /* change in i3 value or all same */
|
||||
if (merge_output(r, &i3, mapping) < 0) goto err;
|
||||
}
|
||||
else if (set || (TEST_VALUE(i1.value, i3.value) == 0))
|
||||
{ /* change in i2 value */
|
||||
if (merge_output(r, &i2, mapping) < 0) goto err;
|
||||
}
|
||||
else
|
||||
{ /* conflicting value changes in i2 and i3 */
|
||||
merge_error(i1.position, i2.position, i3.position, 1);
|
||||
goto err;
|
||||
}
|
||||
if (i1.next(&i1) < 0) goto err;
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
if (i3.next(&i3) < 0) goto err;
|
||||
}
|
||||
else if (cmp13 > 0)
|
||||
{ /* insert i3 */
|
||||
if (merge_output(r, &i3, mapping) < 0) goto err;
|
||||
if (i3.next(&i3) < 0) goto err;
|
||||
}
|
||||
else if (set || (TEST_VALUE(i1.value, i2.value) == 0))
|
||||
{ /* deleted in i3 */
|
||||
if (i3.position == 1)
|
||||
{
|
||||
/* Deleted the first item. This will modify the
|
||||
parent node, so we don't know if merging will be
|
||||
safe
|
||||
*/
|
||||
merge_error(i1.position, i2.position, i3.position, 13);
|
||||
goto err;
|
||||
}
|
||||
if (i1.next(&i1) < 0) goto err;
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
}
|
||||
else
|
||||
{ /* conflicting del in i3 and change in i2 */
|
||||
merge_error(i1.position, i2.position, i3.position, 2);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
else if (cmp13 == 0)
|
||||
{
|
||||
if (cmp12 > 0)
|
||||
{ /* insert i2 */
|
||||
if (merge_output(r, &i2, mapping) < 0) goto err;
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
}
|
||||
else if (set || (TEST_VALUE(i1.value, i3.value) == 0))
|
||||
{ /* deleted in i2 */
|
||||
if (i2.position == 1)
|
||||
{
|
||||
/* Deleted the first item. This will modify the
|
||||
parent node, so we don't know if merging will be
|
||||
safe
|
||||
*/
|
||||
merge_error(i1.position, i2.position, i3.position, 13);
|
||||
goto err;
|
||||
}
|
||||
if (i1.next(&i1) < 0) goto err;
|
||||
if (i3.next(&i3) < 0) goto err;
|
||||
}
|
||||
else
|
||||
{ /* conflicting del in i2 and change in i3 */
|
||||
merge_error(i1.position, i2.position, i3.position, 3);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ /* Both keys changed */
|
||||
TEST_KEY_SET_OR(cmp23, i2.key, i3.key) goto err;
|
||||
if (cmp23==0)
|
||||
{ /* dueling inserts or deletes */
|
||||
merge_error(i1.position, i2.position, i3.position, 4);
|
||||
goto err;
|
||||
}
|
||||
if (cmp12 > 0)
|
||||
{ /* insert i2 */
|
||||
if (cmp23 > 0)
|
||||
{ /* insert i3 first */
|
||||
if (merge_output(r, &i3, mapping) < 0) goto err;
|
||||
if (i3.next(&i3) < 0) goto err;
|
||||
}
|
||||
else
|
||||
{ /* insert i2 first */
|
||||
if (merge_output(r, &i2, mapping) < 0) goto err;
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
}
|
||||
}
|
||||
else if (cmp13 > 0)
|
||||
{ /* Insert i3 */
|
||||
if (merge_output(r, &i3, mapping) < 0) goto err;
|
||||
if (i3.next(&i3) < 0) goto err;
|
||||
}
|
||||
else
|
||||
{ /* 1<2 and 1<3: both deleted 1.key */
|
||||
merge_error(i1.position, i2.position, i3.position, 5);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (i2.position >= 0 && i3.position >= 0)
|
||||
{ /* New inserts */
|
||||
TEST_KEY_SET_OR(cmp23, i2.key, i3.key) goto err;
|
||||
if (cmp23==0)
|
||||
{ /* dueling inserts */
|
||||
merge_error(i1.position, i2.position, i3.position, 6);
|
||||
goto err;
|
||||
}
|
||||
if (cmp23 > 0)
|
||||
{ /* insert i3 */
|
||||
if (merge_output(r, &i3, mapping) < 0) goto err;
|
||||
if (i3.next(&i3) < 0) goto err;
|
||||
}
|
||||
else
|
||||
{ /* insert i2 */
|
||||
if (merge_output(r, &i2, mapping) < 0) goto err;
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
}
|
||||
}
|
||||
|
||||
while (i1.position >= 0 && i2.position >= 0)
|
||||
{ /* remainder of i1 deleted in i3 */
|
||||
TEST_KEY_SET_OR(cmp12, i1.key, i2.key) goto err;
|
||||
if (cmp12 > 0)
|
||||
{ /* insert i2 */
|
||||
if (merge_output(r, &i2, mapping) < 0) goto err;
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
}
|
||||
else if (cmp12==0 && (set || (TEST_VALUE(i1.value, i2.value) == 0)))
|
||||
{ /* delete i3 */
|
||||
if (i1.next(&i1) < 0) goto err;
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
}
|
||||
else
|
||||
{ /* Dueling deletes or delete and change */
|
||||
merge_error(i1.position, i2.position, i3.position, 7);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
while (i1.position >= 0 && i3.position >= 0)
|
||||
{ /* remainder of i1 deleted in i2 */
|
||||
TEST_KEY_SET_OR(cmp13, i1.key, i3.key) goto err;
|
||||
if (cmp13 > 0)
|
||||
{ /* insert i3 */
|
||||
if (merge_output(r, &i3, mapping) < 0) goto err;
|
||||
if (i3.next(&i3) < 0) goto err;
|
||||
}
|
||||
else if (cmp13==0 && (set || (TEST_VALUE(i1.value, i3.value) == 0)))
|
||||
{ /* delete i2 */
|
||||
if (i1.next(&i1) < 0) goto err;
|
||||
if (i3.next(&i3) < 0) goto err;
|
||||
}
|
||||
else
|
||||
{ /* Dueling deletes or delete and change */
|
||||
merge_error(i1.position, i2.position, i3.position, 8);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (i1.position >= 0)
|
||||
{ /* Dueling deletes */
|
||||
merge_error(i1.position, i2.position, i3.position, 9);
|
||||
goto err;
|
||||
}
|
||||
|
||||
while (i2.position >= 0)
|
||||
{ /* Inserting i2 at end */
|
||||
if (merge_output(r, &i2, mapping) < 0) goto err;
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
}
|
||||
|
||||
while (i3.position >= 0)
|
||||
{ /* Inserting i3 at end */
|
||||
if (merge_output(r, &i3, mapping) < 0) goto err;
|
||||
if (i3.next(&i3) < 0) goto err;
|
||||
}
|
||||
|
||||
/* If the output bucket is empty, conflict resolution doesn't have
|
||||
* enough info to unlink it from its containing BTree correctly.
|
||||
*/
|
||||
if (r->len == 0)
|
||||
{
|
||||
merge_error(-1, -1, -1, 10);
|
||||
goto err;
|
||||
}
|
||||
|
||||
finiSetIteration(&i1);
|
||||
finiSetIteration(&i2);
|
||||
finiSetIteration(&i3);
|
||||
|
||||
if (s1->next)
|
||||
{
|
||||
Py_INCREF(s1->next);
|
||||
r->next = s1->next;
|
||||
}
|
||||
s = bucket_getstate(r);
|
||||
Py_DECREF(r);
|
||||
|
||||
return s;
|
||||
|
||||
err:
|
||||
finiSetIteration(&i1);
|
||||
finiSetIteration(&i2);
|
||||
finiSetIteration(&i3);
|
||||
Py_XDECREF(r);
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
|
||||
'OIBucket', 'OISet', 'OIBTree', 'OITreeSet',
|
||||
'union', 'intersection', 'difference',
|
||||
'weightedUnion', 'weightedIntersection',
|
||||
)
|
||||
|
||||
from zope.interface import moduleProvides
|
||||
|
||||
from .Interfaces import IObjectIntegerBTreeModule
|
||||
from ._base import Bucket
|
||||
from ._base import MERGE
|
||||
from ._base import MERGE_WEIGHT_numeric
|
||||
from ._base import MERGE_DEFAULT_float
|
||||
from ._base import Set
|
||||
from ._base import Tree as BTree
|
||||
from ._base import TreeSet
|
||||
from ._base import _TreeIterator
|
||||
from ._base import difference as _difference
|
||||
from ._base import intersection as _intersection
|
||||
from ._base import set_operation as _set_operation
|
||||
from ._base import to_ob as _to_key
|
||||
from ._base import to_int as _to_value
|
||||
from ._base import union as _union
|
||||
from ._base import weightedIntersection as _weightedIntersection
|
||||
from ._base import weightedUnion as _weightedUnion
|
||||
from ._base import _fix_pickle
|
||||
from ._compat import import_c_extension
|
||||
|
||||
_BUCKET_SIZE = 60
|
||||
_TREE_SIZE = 250
|
||||
using64bits = True
|
||||
|
||||
class OIBucketPy(Bucket):
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class OISetPy(Set):
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class OIBTreePy(BTree):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class OITreeSetPy(TreeSet):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_float
|
||||
|
||||
|
||||
class OITreeIteratorPy(_TreeIterator):
|
||||
pass
|
||||
|
||||
|
||||
# Can't declare forward refs, so fix up afterwards:
|
||||
|
||||
OIBucketPy._mapping_type = OIBucketPy._bucket_type = OIBucketPy
|
||||
OIBucketPy._set_type = OISetPy
|
||||
|
||||
OISetPy._mapping_type = OIBucketPy
|
||||
OISetPy._set_type = OISetPy._bucket_type = OISetPy
|
||||
|
||||
OIBTreePy._mapping_type = OIBTreePy._bucket_type = OIBucketPy
|
||||
OIBTreePy._set_type = OISetPy
|
||||
|
||||
OITreeSetPy._mapping_type = OIBucketPy
|
||||
OITreeSetPy._set_type = OITreeSetPy._bucket_type = OISetPy
|
||||
|
||||
|
||||
differencePy = _set_operation(_difference, OISetPy)
|
||||
unionPy = _set_operation(_union, OISetPy)
|
||||
intersectionPy = _set_operation(_intersection, OISetPy)
|
||||
weightedUnionPy = _set_operation(_weightedUnion, OISetPy)
|
||||
weightedIntersectionPy = _set_operation(_weightedIntersection, OISetPy)
|
||||
|
||||
import_c_extension(globals())
|
||||
|
||||
_fix_pickle(globals(), __name__)
|
||||
|
||||
moduleProvides(IObjectIntegerBTreeModule)
|
||||
@@ -0,0 +1,111 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
|
||||
'OLBucket', 'OLSet', 'OLBTree', 'OLTreeSet',
|
||||
'union', 'intersection', 'difference',
|
||||
'weightedUnion', 'weightedIntersection',
|
||||
)
|
||||
|
||||
from zope.interface import moduleProvides
|
||||
|
||||
from .Interfaces import IObjectIntegerBTreeModule
|
||||
from ._base import Bucket
|
||||
from ._base import MERGE
|
||||
from ._base import MERGE_WEIGHT_numeric
|
||||
from ._base import MERGE_DEFAULT_int
|
||||
from ._base import Set
|
||||
from ._base import Tree as BTree
|
||||
from ._base import TreeSet
|
||||
from ._base import _TreeIterator
|
||||
from ._base import difference as _difference
|
||||
from ._base import intersection as _intersection
|
||||
from ._base import set_operation as _set_operation
|
||||
from ._base import to_ob as _to_key
|
||||
from ._base import to_long as _to_value
|
||||
from ._base import union as _union
|
||||
from ._base import weightedIntersection as _weightedIntersection
|
||||
from ._base import weightedUnion as _weightedUnion
|
||||
from ._base import _fix_pickle
|
||||
from ._compat import import_c_extension
|
||||
|
||||
_BUCKET_SIZE = 60
|
||||
_TREE_SIZE = 250
|
||||
using64bits = True
|
||||
|
||||
|
||||
class OLBucketPy(Bucket):
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class OLSetPy(Set):
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class OLBTreePy(BTree):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class OLTreeSetPy(TreeSet):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
MERGE = MERGE
|
||||
MERGE_WEIGHT = MERGE_WEIGHT_numeric
|
||||
MERGE_DEFAULT = MERGE_DEFAULT_int
|
||||
|
||||
|
||||
class OLTreeIteratorPy(_TreeIterator):
|
||||
pass
|
||||
|
||||
|
||||
# Can't declare forward refs, so fix up afterwards:
|
||||
|
||||
OLBucketPy._mapping_type = OLBucketPy._bucket_type = OLBucketPy
|
||||
OLBucketPy._set_type = OLSetPy
|
||||
|
||||
OLSetPy._mapping_type = OLBucketPy
|
||||
OLSetPy._set_type = OLSetPy._bucket_type = OLSetPy
|
||||
|
||||
OLBTreePy._mapping_type = OLBTreePy._bucket_type = OLBucketPy
|
||||
OLBTreePy._set_type = OLSetPy
|
||||
|
||||
OLTreeSetPy._mapping_type = OLBucketPy
|
||||
OLTreeSetPy._set_type = OLTreeSetPy._bucket_type = OLSetPy
|
||||
|
||||
|
||||
differencePy = _set_operation(_difference, OLSetPy)
|
||||
unionPy = _set_operation(_union, OLSetPy)
|
||||
intersectionPy = _set_operation(_intersection, OLSetPy)
|
||||
weightedUnionPy = _set_operation(_weightedUnion, OLSetPy)
|
||||
weightedIntersectionPy = _set_operation(_weightedIntersection, OLSetPy)
|
||||
|
||||
import_c_extension(globals())
|
||||
|
||||
_fix_pickle(globals(), __name__)
|
||||
|
||||
moduleProvides(IObjectIntegerBTreeModule)
|
||||
@@ -0,0 +1,91 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
|
||||
'OOBucket', 'OOSet', 'OOBTree', 'OOTreeSet',
|
||||
'union', 'intersection','difference',
|
||||
)
|
||||
|
||||
from zope.interface import moduleProvides
|
||||
|
||||
from .Interfaces import IObjectObjectBTreeModule
|
||||
from ._base import Bucket
|
||||
from ._base import Set
|
||||
from ._base import Tree as BTree
|
||||
from ._base import TreeSet
|
||||
from ._base import _TreeIterator
|
||||
from ._base import difference as _difference
|
||||
from ._base import intersection as _intersection
|
||||
from ._base import set_operation as _set_operation
|
||||
from ._base import to_ob as _to_key
|
||||
_to_value = _to_key
|
||||
from ._base import union as _union
|
||||
from ._base import _fix_pickle
|
||||
from ._compat import import_c_extension
|
||||
|
||||
_BUCKET_SIZE = 30
|
||||
_TREE_SIZE = 250
|
||||
using64bits = False
|
||||
|
||||
|
||||
class OOBucketPy(Bucket):
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
|
||||
|
||||
class OOSetPy(Set):
|
||||
_to_key = _to_key
|
||||
|
||||
|
||||
class OOBTreePy(BTree):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
|
||||
|
||||
class OOTreeSetPy(TreeSet):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
|
||||
|
||||
class OOTreeIteratorPy(_TreeIterator):
|
||||
pass
|
||||
|
||||
|
||||
# Can't declare forward refs, so fix up afterwards:
|
||||
|
||||
OOBucketPy._mapping_type = OOBucketPy._bucket_type = OOBucketPy
|
||||
OOBucketPy._set_type = OOSetPy
|
||||
|
||||
OOSetPy._mapping_type = OOBucketPy
|
||||
OOSetPy._set_type = OOSetPy._bucket_type = OOSetPy
|
||||
|
||||
OOBTreePy._mapping_type = OOBTreePy._bucket_type = OOBucketPy
|
||||
OOBTreePy._set_type = OOSetPy
|
||||
|
||||
OOTreeSetPy._mapping_type = OOBucketPy
|
||||
OOTreeSetPy._set_type = OOTreeSetPy._bucket_type = OOSetPy
|
||||
|
||||
|
||||
differencePy = _set_operation(_difference, OOSetPy)
|
||||
unionPy = _set_operation(_union, OOSetPy)
|
||||
intersectionPy = _set_operation(_intersection, OOSetPy)
|
||||
|
||||
import_c_extension(globals())
|
||||
|
||||
_fix_pickle(globals(), __name__)
|
||||
|
||||
moduleProvides(IObjectObjectBTreeModule)
|
||||
@@ -0,0 +1,557 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
All Rights Reserved.
|
||||
|
||||
This software is subject to the provisions of the Zope Public License,
|
||||
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
Set operations
|
||||
****************************************************************************/
|
||||
|
||||
#define SETOPTEMPLATE_C "$Id$\n"
|
||||
|
||||
#ifdef KEY_CHECK
|
||||
static int
|
||||
nextKeyAsSet(SetIteration *i)
|
||||
{
|
||||
if (i->position >= 0) {
|
||||
if (i->position) {
|
||||
DECREF_KEY(i->key);
|
||||
i->position = -1;
|
||||
}
|
||||
else
|
||||
i->position = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* initSetIteration
|
||||
*
|
||||
* Start the set iteration protocol. See the comments at struct SetIteration.
|
||||
*
|
||||
* Arguments
|
||||
* i The address of a SetIteration control struct.
|
||||
* s The address of the set, bucket, BTree, ..., to be iterated.
|
||||
* useValues Boolean; if true, and s has values (is a mapping), copy
|
||||
* them into i->value each time i->next() is called; else
|
||||
* ignore s's values even if s is a mapping.
|
||||
*
|
||||
* Return
|
||||
* 0 on success; -1 and an exception set if error.
|
||||
* i.usesValue is set to 1 (true) if s has values and useValues was
|
||||
* true; else usesValue is set to 0 (false).
|
||||
* i.set gets a new reference to s, or to some other object used to
|
||||
* iterate over s.
|
||||
* i.position is set to 0.
|
||||
* i.next is set to an appropriate iteration function.
|
||||
* i.key and i.value are left alone.
|
||||
*
|
||||
* Internal
|
||||
* i.position < 0 means iteration terminated.
|
||||
* i.position = 0 means iteration hasn't yet begun (next() hasn't
|
||||
* been called yet).
|
||||
* In all other cases, i.key, and possibly i.value, own references.
|
||||
* These must be cleaned up, either by next() routines, or by
|
||||
* finiSetIteration.
|
||||
* next() routines must ensure the above. They should return without
|
||||
* doing anything when i.position < 0.
|
||||
* It's the responsibility of {init, fini}setIteration to clean up
|
||||
* the reference in i.set, and to ensure that no stale references
|
||||
* live in i.key or i.value if iteration terminates abnormally.
|
||||
* A SetIteration struct has been cleaned up iff i.set is NULL.
|
||||
*/
|
||||
static int
|
||||
initSetIteration(SetIteration *i, PyObject *s, int useValues)
|
||||
{
|
||||
i->set = NULL;
|
||||
i->position = -1; /* set to 0 only on normal return */
|
||||
i->usesValue = 0; /* assume it's a set or that values aren't iterated */
|
||||
|
||||
if (PyObject_IsInstance(s, (PyObject *)&BucketType))
|
||||
{
|
||||
i->set = s;
|
||||
Py_INCREF(s);
|
||||
|
||||
if (useValues)
|
||||
{
|
||||
i->usesValue = 1;
|
||||
i->next = nextBucket;
|
||||
}
|
||||
else
|
||||
i->next = nextSet;
|
||||
}
|
||||
else if (PyObject_IsInstance(s, (PyObject *)&SetType))
|
||||
{
|
||||
i->set = s;
|
||||
Py_INCREF(s);
|
||||
i->next = nextSet;
|
||||
}
|
||||
else if (PyObject_IsInstance(s, (PyObject *)&BTreeType))
|
||||
{
|
||||
i->set = BTree_rangeSearch(BTREE(s), NULL, NULL, 'i');
|
||||
UNLESS(i->set) return -1;
|
||||
|
||||
if (useValues)
|
||||
{
|
||||
i->usesValue = 1;
|
||||
i->next = nextBTreeItems;
|
||||
}
|
||||
else
|
||||
i->next = nextTreeSetItems;
|
||||
}
|
||||
else if (PyObject_IsInstance(s, (PyObject *)&TreeSetType))
|
||||
{
|
||||
i->set = BTree_rangeSearch(BTREE(s), NULL, NULL, 'k');
|
||||
UNLESS(i->set) return -1;
|
||||
i->next = nextTreeSetItems;
|
||||
}
|
||||
#ifdef KEY_CHECK
|
||||
else if (KEY_CHECK(s))
|
||||
{
|
||||
int copied = 1;
|
||||
COPY_KEY_FROM_ARG(i->key, s, copied);
|
||||
UNLESS (copied) return -1;
|
||||
|
||||
INCREF_KEY(i->key);
|
||||
i->set = s;
|
||||
Py_INCREF(s);
|
||||
i->next = nextKeyAsSet;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "invalid argument");
|
||||
return -1;
|
||||
}
|
||||
|
||||
i->position = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef MERGE_WEIGHT
|
||||
#define MERGE_WEIGHT(O, w) (O)
|
||||
#endif
|
||||
|
||||
static int
|
||||
copyRemaining(Bucket *r, SetIteration *i, int merge,
|
||||
|
||||
/* See comment # 42 */
|
||||
#ifdef MERGE
|
||||
VALUE_TYPE w)
|
||||
#else
|
||||
int w)
|
||||
#endif
|
||||
{
|
||||
while (i->position >= 0)
|
||||
{
|
||||
if(r->len >= r->size && Bucket_grow(r, -1, ! merge) < 0) return -1;
|
||||
COPY_KEY(r->keys[r->len], i->key);
|
||||
INCREF_KEY(r->keys[r->len]);
|
||||
|
||||
if (merge)
|
||||
{
|
||||
COPY_VALUE(r->values[r->len], MERGE_WEIGHT(i->value, w));
|
||||
INCREF_VALUE(r->values[r->len]);
|
||||
}
|
||||
r->len++;
|
||||
if (i->next(i) < 0) return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is the workhorse for all set merge operations: the weighted and
|
||||
* unweighted flavors of union and intersection, and set difference. The
|
||||
* algorithm is conceptually simple but the code is complicated due to all
|
||||
* the options.
|
||||
*
|
||||
* s1, s2
|
||||
* The input collections to be merged.
|
||||
*
|
||||
* usevalues1, usevalues2
|
||||
* Booleans. In the output, should values from s1 (or s2) be used? This
|
||||
* only makes sense when an operation intends to support mapping outputs;
|
||||
* these should both be false for operations that want pure set outputs.
|
||||
*
|
||||
* w1, w2
|
||||
* If usevalues1(2) are true, these are the weights to apply to the
|
||||
* input values.
|
||||
*
|
||||
* c1
|
||||
* Boolean. Should keys that appear in c1 but not c2 appear in the output?
|
||||
* c12
|
||||
* Boolean. Should keys that appear in both inputs appear in the output?
|
||||
* c2
|
||||
* Boolean. Should keys that appear in c2 but not c1 appear in the output?
|
||||
*
|
||||
* Returns NULL if error, else a Set or Bucket, depending on whether a set or
|
||||
* mapping was requested.
|
||||
*/
|
||||
static PyObject *
|
||||
set_operation(PyObject *s1, PyObject *s2,
|
||||
int usevalues1, int usevalues2,
|
||||
|
||||
/* Comment # 42
|
||||
|
||||
The following ifdef works around a template/type problem
|
||||
|
||||
Weights are passed as integers. In particular, the weight passed by
|
||||
difference is one. This works fine in the int value and float value
|
||||
cases but makes no sense in the object value case. In the object
|
||||
value case, we don't do merging, so we don't use the weights, so it
|
||||
doesn't matter what they are.
|
||||
*/
|
||||
#ifdef MERGE
|
||||
VALUE_TYPE w1, VALUE_TYPE w2,
|
||||
#else
|
||||
int w1, int w2,
|
||||
#endif
|
||||
int c1, int c12, int c2)
|
||||
|
||||
|
||||
{
|
||||
Bucket *r=0;
|
||||
SetIteration i1 = {0,0,0}, i2 = {0,0,0};
|
||||
int cmp, merge;
|
||||
|
||||
if (initSetIteration(&i1, s1, usevalues1) < 0) goto err;
|
||||
if (initSetIteration(&i2, s2, usevalues2) < 0) goto err;
|
||||
merge = i1.usesValue | i2.usesValue;
|
||||
|
||||
if (merge)
|
||||
{
|
||||
#ifndef MERGE
|
||||
if (c12 && i1.usesValue && i2.usesValue) goto invalid_set_operation;
|
||||
#endif
|
||||
if (! i1.usesValue&& i2.usesValue)
|
||||
{
|
||||
SetIteration t;
|
||||
int i;
|
||||
|
||||
/* See comment # 42 above */
|
||||
#ifdef MERGE
|
||||
VALUE_TYPE v;
|
||||
#else
|
||||
int v;
|
||||
#endif
|
||||
|
||||
t=i1; i1=i2; i2=t;
|
||||
i=c1; c1=c2; c2=i;
|
||||
v=w1; w1=w2; w2=v;
|
||||
}
|
||||
#ifdef MERGE_DEFAULT
|
||||
i1.value=MERGE_DEFAULT;
|
||||
i2.value=MERGE_DEFAULT;
|
||||
#else
|
||||
if (i1.usesValue)
|
||||
{
|
||||
if (! i2.usesValue && c2) goto invalid_set_operation;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c1 || c12) goto invalid_set_operation;
|
||||
}
|
||||
#endif
|
||||
|
||||
UNLESS(r=BUCKET(PyObject_CallObject(OBJECT(&BucketType), NULL)))
|
||||
goto err;
|
||||
}
|
||||
else
|
||||
{
|
||||
UNLESS(r=BUCKET(PyObject_CallObject(OBJECT(&SetType), NULL)))
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (i1.next(&i1) < 0) goto err;
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
|
||||
while (i1.position >= 0 && i2.position >= 0)
|
||||
{
|
||||
TEST_KEY_SET_OR(cmp, i1.key, i2.key) goto err;
|
||||
if(cmp < 0)
|
||||
{
|
||||
if(c1)
|
||||
{
|
||||
if(r->len >= r->size && Bucket_grow(r, -1, ! merge) < 0) goto err;
|
||||
COPY_KEY(r->keys[r->len], i1.key);
|
||||
INCREF_KEY(r->keys[r->len]);
|
||||
if (merge)
|
||||
{
|
||||
COPY_VALUE(r->values[r->len], MERGE_WEIGHT(i1.value, w1));
|
||||
INCREF_VALUE(r->values[r->len]);
|
||||
}
|
||||
r->len++;
|
||||
}
|
||||
if (i1.next(&i1) < 0) goto err;
|
||||
}
|
||||
else if(cmp==0)
|
||||
{
|
||||
if(c12)
|
||||
{
|
||||
if(r->len >= r->size && Bucket_grow(r, -1, ! merge) < 0) goto err;
|
||||
COPY_KEY(r->keys[r->len], i1.key);
|
||||
INCREF_KEY(r->keys[r->len]);
|
||||
if (merge)
|
||||
{
|
||||
#ifdef MERGE
|
||||
r->values[r->len] = MERGE(i1.value, w1, i2.value, w2);
|
||||
#else
|
||||
COPY_VALUE(r->values[r->len], i1.value);
|
||||
INCREF_VALUE(r->values[r->len]);
|
||||
#endif
|
||||
}
|
||||
r->len++;
|
||||
}
|
||||
if (i1.next(&i1) < 0) goto err;
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(c2)
|
||||
{
|
||||
if(r->len >= r->size && Bucket_grow(r, -1, ! merge) < 0) goto err;
|
||||
COPY_KEY(r->keys[r->len], i2.key);
|
||||
INCREF_KEY(r->keys[r->len]);
|
||||
if (merge)
|
||||
{
|
||||
COPY_VALUE(r->values[r->len], MERGE_WEIGHT(i2.value, w2));
|
||||
INCREF_VALUE(r->values[r->len]);
|
||||
}
|
||||
r->len++;
|
||||
}
|
||||
if (i2.next(&i2) < 0) goto err;
|
||||
}
|
||||
}
|
||||
if(c1 && copyRemaining(r, &i1, merge, w1) < 0) goto err;
|
||||
if(c2 && copyRemaining(r, &i2, merge, w2) < 0) goto err;
|
||||
|
||||
|
||||
finiSetIteration(&i1);
|
||||
finiSetIteration(&i2);
|
||||
|
||||
return OBJECT(r);
|
||||
|
||||
#ifndef MERGE_DEFAULT
|
||||
invalid_set_operation:
|
||||
PyErr_SetString(PyExc_TypeError, "invalid set operation");
|
||||
#endif
|
||||
|
||||
err:
|
||||
finiSetIteration(&i1);
|
||||
finiSetIteration(&i2);
|
||||
Py_XDECREF(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
difference_m(PyObject *ignored, PyObject *args)
|
||||
{
|
||||
PyObject *o1, *o2;
|
||||
|
||||
UNLESS(PyArg_ParseTuple(args, "OO", &o1, &o2)) return NULL;
|
||||
|
||||
|
||||
if (o1 == Py_None || o2 == Py_None)
|
||||
{
|
||||
/* difference(None, X) -> None; difference(X, None) -> X */
|
||||
Py_INCREF(o1);
|
||||
return o1;
|
||||
}
|
||||
|
||||
return set_operation(o1, o2, 1, 0, /* preserve values from o1, ignore o2's */
|
||||
1, 0, /* o1's values multiplied by 1 */
|
||||
1, 0, 0); /* take only keys unique to o1 */
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
union_m(PyObject *ignored, PyObject *args)
|
||||
{
|
||||
PyObject *o1, *o2;
|
||||
|
||||
UNLESS(PyArg_ParseTuple(args, "OO", &o1, &o2)) return NULL;
|
||||
|
||||
if (o1 == Py_None)
|
||||
{
|
||||
Py_INCREF(o2);
|
||||
return o2;
|
||||
}
|
||||
else if (o2 == Py_None)
|
||||
{
|
||||
Py_INCREF(o1);
|
||||
return o1;
|
||||
}
|
||||
|
||||
return set_operation(o1, o2, 0, 0, /* ignore values in both */
|
||||
1, 1, /* the weights are irrelevant */
|
||||
1, 1, 1); /* take all keys */
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
intersection_m(PyObject *ignored, PyObject *args)
|
||||
{
|
||||
PyObject *o1, *o2;
|
||||
|
||||
UNLESS(PyArg_ParseTuple(args, "OO", &o1, &o2)) return NULL;
|
||||
|
||||
if (o1 == Py_None)
|
||||
{
|
||||
Py_INCREF(o2);
|
||||
return o2;
|
||||
}
|
||||
else if (o2 == Py_None)
|
||||
{
|
||||
Py_INCREF(o1);
|
||||
return o1;
|
||||
}
|
||||
|
||||
return set_operation(o1, o2, 0, 0, /* ignore values in both */
|
||||
1, 1, /* the weights are irrelevant */
|
||||
0, 1, 0); /* take only keys common to both */
|
||||
}
|
||||
|
||||
#ifdef MERGE
|
||||
|
||||
static PyObject *
|
||||
wunion_m(PyObject *ignored, PyObject *args)
|
||||
{
|
||||
PyObject *o1, *o2;
|
||||
VALUE_TYPE w1 = 1, w2 = 1;
|
||||
|
||||
UNLESS(PyArg_ParseTuple(args, "OO|" VALUE_PARSE VALUE_PARSE,
|
||||
&o1, &o2, &w1, &w2)
|
||||
) return NULL;
|
||||
|
||||
if (o1 == Py_None)
|
||||
return Py_BuildValue(VALUE_PARSE "O", (o2 == Py_None ? 0 : w2), o2);
|
||||
else if (o2 == Py_None)
|
||||
return Py_BuildValue(VALUE_PARSE "O", w1, o1);
|
||||
|
||||
o1 = set_operation(o1, o2, 1, 1, w1, w2, 1, 1, 1);
|
||||
if (o1)
|
||||
ASSIGN(o1, Py_BuildValue(VALUE_PARSE "O", (VALUE_TYPE)1, o1));
|
||||
|
||||
return o1;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
wintersection_m(PyObject *ignored, PyObject *args)
|
||||
{
|
||||
PyObject *o1, *o2;
|
||||
VALUE_TYPE w1 = 1, w2 = 1;
|
||||
|
||||
UNLESS(PyArg_ParseTuple(args, "OO|" VALUE_PARSE VALUE_PARSE,
|
||||
&o1, &o2, &w1, &w2)
|
||||
) return NULL;
|
||||
|
||||
if (o1 == Py_None)
|
||||
return Py_BuildValue(VALUE_PARSE "O", (o2 == Py_None ? 0 : w2), o2);
|
||||
else if (o2 == Py_None)
|
||||
return Py_BuildValue(VALUE_PARSE "O", w1, o1);
|
||||
|
||||
o1 = set_operation(o1, o2, 1, 1, w1, w2, 0, 1, 0);
|
||||
if (o1)
|
||||
ASSIGN(o1, Py_BuildValue(VALUE_PARSE "O",
|
||||
((o1->ob_type == (PyTypeObject*)(&SetType)) ? w2+w1 : 1),
|
||||
o1));
|
||||
|
||||
return o1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MULTI_INT_UNION
|
||||
#include "sorters.c"
|
||||
|
||||
/* Input is a sequence of integer sets (or convertible to sets by the
|
||||
set iteration protocol). Output is the union of the sets. The point
|
||||
is to run much faster than doing pairs of unions.
|
||||
*/
|
||||
static PyObject *
|
||||
multiunion_m(PyObject *ignored, PyObject *args)
|
||||
{
|
||||
PyObject *seq; /* input sequence */
|
||||
int n; /* length of input sequence */
|
||||
PyObject *set = NULL; /* an element of the input sequence */
|
||||
Bucket *result; /* result set */
|
||||
SetIteration setiter = {0};
|
||||
int i;
|
||||
|
||||
UNLESS(PyArg_ParseTuple(args, "O", &seq))
|
||||
return NULL;
|
||||
|
||||
n = PyObject_Length(seq);
|
||||
if (n < 0)
|
||||
return NULL;
|
||||
|
||||
/* Construct an empty result set. */
|
||||
result = BUCKET(PyObject_CallObject(OBJECT(&SetType), NULL));
|
||||
if (result == NULL)
|
||||
return NULL;
|
||||
|
||||
/* For each set in the input sequence, append its elements to the result
|
||||
set. At this point, we ignore the possibility of duplicates. */
|
||||
for (i = 0; i < n; ++i) {
|
||||
set = PySequence_GetItem(seq, i);
|
||||
if (set == NULL)
|
||||
goto Error;
|
||||
|
||||
/* If set is a bucket, do a straight resize + memcpy. */
|
||||
if (set->ob_type == (PyTypeObject*)&SetType ||
|
||||
set->ob_type == (PyTypeObject*)&BucketType)
|
||||
{
|
||||
Bucket *b = BUCKET(set);
|
||||
int status = 0;
|
||||
|
||||
UNLESS (PER_USE(b)) goto Error;
|
||||
if (b->len)
|
||||
status = bucket_append(result, b, 0, b->len, 0, i < n-1);
|
||||
PER_UNUSE(b);
|
||||
if (status < 0) goto Error;
|
||||
}
|
||||
else {
|
||||
/* No cheap way: iterate over set's elements one at a time. */
|
||||
if (initSetIteration(&setiter, set, 0) < 0) goto Error;
|
||||
if (setiter.next(&setiter) < 0) goto Error;
|
||||
while (setiter.position >= 0) {
|
||||
if (result->len >= result->size && Bucket_grow(result, -1, 1) < 0)
|
||||
goto Error;
|
||||
COPY_KEY(result->keys[result->len], setiter.key);
|
||||
++result->len;
|
||||
/* We know the key is an int, so no need to incref it. */
|
||||
if (setiter.next(&setiter) < 0) goto Error;
|
||||
}
|
||||
finiSetIteration(&setiter);
|
||||
}
|
||||
Py_DECREF(set);
|
||||
set = NULL;
|
||||
}
|
||||
|
||||
/* Combine, sort, remove duplicates, and reset the result's len.
|
||||
If the set shrinks (which happens if and only if there are
|
||||
duplicates), no point to realloc'ing the set smaller, as we
|
||||
expect the result set to be short-lived.
|
||||
*/
|
||||
if (result->len > 0) {
|
||||
size_t newlen; /* number of elements in final result set */
|
||||
newlen = sort_int_nodups(result->keys, (size_t)result->len);
|
||||
result->len = (int)newlen;
|
||||
}
|
||||
return (PyObject *)result;
|
||||
|
||||
Error:
|
||||
Py_DECREF(result);
|
||||
Py_XDECREF(set);
|
||||
finiSetIteration(&setiter);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,381 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
All Rights Reserved.
|
||||
|
||||
This software is subject to the provisions of the Zope Public License,
|
||||
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE
|
||||
|
||||
****************************************************************************/
|
||||
#include "_compat.h"
|
||||
|
||||
#define SETTEMPLATE_C "$Id$\n"
|
||||
|
||||
static PyObject *
|
||||
Set_insert(Bucket *self, PyObject *args)
|
||||
{
|
||||
PyObject *key;
|
||||
int i;
|
||||
|
||||
UNLESS (PyArg_ParseTuple(args, "O", &key))
|
||||
return NULL;
|
||||
if ( (i=_bucket_set(self, key, Py_None, 1, 1, 0)) < 0)
|
||||
return NULL;
|
||||
return INT_FROM_LONG(i);
|
||||
}
|
||||
|
||||
/* _Set_update and _TreeSet_update are identical except for the
|
||||
function they call to add the element to the set.
|
||||
*/
|
||||
|
||||
static int
|
||||
_Set_update(Bucket *self, PyObject *seq)
|
||||
{
|
||||
int n=0, ind=0;
|
||||
PyObject *iter, *v;
|
||||
|
||||
iter = PyObject_GetIter(seq);
|
||||
if (iter == NULL)
|
||||
return -1;
|
||||
|
||||
while (1) {
|
||||
v = PyIter_Next(iter);
|
||||
if (v == NULL) {
|
||||
if (PyErr_Occurred())
|
||||
goto err;
|
||||
else
|
||||
break;
|
||||
}
|
||||
ind = _bucket_set(self, v, Py_None, 1, 1, 0);
|
||||
Py_DECREF(v);
|
||||
if (ind < 0)
|
||||
goto err;
|
||||
else
|
||||
n += ind;
|
||||
}
|
||||
|
||||
err:
|
||||
Py_DECREF(iter);
|
||||
if (ind < 0)
|
||||
return -1;
|
||||
return n;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
Set_update(Bucket *self, PyObject *args)
|
||||
{
|
||||
PyObject *seq = NULL;
|
||||
int n = 0;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|O:update", &seq))
|
||||
return NULL;
|
||||
|
||||
if (seq) {
|
||||
n = _Set_update(self, seq);
|
||||
if (n < 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return INT_FROM_LONG(n);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
Set_remove(Bucket *self, PyObject *args)
|
||||
{
|
||||
PyObject *key;
|
||||
|
||||
UNLESS (PyArg_ParseTuple(args, "O", &key))
|
||||
return NULL;
|
||||
if (_bucket_set(self, key, NULL, 0, 1, 0) < 0)
|
||||
return NULL;
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static int
|
||||
_set_setstate(Bucket *self, PyObject *args)
|
||||
{
|
||||
PyObject *k, *items;
|
||||
Bucket *next=0;
|
||||
int i, l, copied=1;
|
||||
KEY_TYPE *keys;
|
||||
|
||||
UNLESS (PyArg_ParseTuple(args, "O|O", &items, &next))
|
||||
return -1;
|
||||
|
||||
if (!PyTuple_Check(items)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"tuple required for first state element");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((l=PyTuple_Size(items)) < 0)
|
||||
return -1;
|
||||
|
||||
for (i=self->len; --i >= 0; )
|
||||
{
|
||||
DECREF_KEY(self->keys[i]);
|
||||
}
|
||||
self->len=0;
|
||||
|
||||
if (self->next)
|
||||
{
|
||||
Py_DECREF(self->next);
|
||||
self->next=0;
|
||||
}
|
||||
|
||||
if (l > self->size)
|
||||
{
|
||||
UNLESS (keys=BTree_Realloc(self->keys, sizeof(KEY_TYPE)*l))
|
||||
return -1;
|
||||
self->keys=keys;
|
||||
self->size=l;
|
||||
}
|
||||
|
||||
for (i=0; i<l; i++)
|
||||
{
|
||||
k=PyTuple_GET_ITEM(items, i);
|
||||
COPY_KEY_FROM_ARG(self->keys[i], k, copied);
|
||||
UNLESS (copied)
|
||||
return -1;
|
||||
INCREF_KEY(self->keys[i]);
|
||||
}
|
||||
|
||||
self->len=l;
|
||||
|
||||
if (next)
|
||||
{
|
||||
self->next=next;
|
||||
Py_INCREF(next);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
set_setstate(Bucket *self, PyObject *args)
|
||||
{
|
||||
int r;
|
||||
|
||||
UNLESS (PyArg_ParseTuple(args, "O", &args))
|
||||
return NULL;
|
||||
|
||||
PER_PREVENT_DEACTIVATION(self);
|
||||
r=_set_setstate(self, args);
|
||||
PER_UNUSE(self);
|
||||
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static struct PyMethodDef Set_methods[] = {
|
||||
{"__getstate__", (PyCFunction) bucket_getstate, METH_VARARGS,
|
||||
"__getstate__() -- Return the picklable state of the object"},
|
||||
|
||||
{"__setstate__", (PyCFunction) set_setstate, METH_VARARGS,
|
||||
"__setstate__() -- Set the state of the object"},
|
||||
|
||||
{"keys", (PyCFunction) bucket_keys, METH_VARARGS | METH_KEYWORDS,
|
||||
"keys() -- Return the keys"},
|
||||
|
||||
{"has_key", (PyCFunction) bucket_has_key, METH_O,
|
||||
"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]) -- Find the maximum key\n\n"
|
||||
"If an argument is given, find the maximum <= the argument"},
|
||||
|
||||
{"minKey", (PyCFunction) Bucket_minKey, METH_VARARGS,
|
||||
"minKey([key]) -- Find the minimum key\n\n"
|
||||
"If an argument is given, find the minimum >= the argument"},
|
||||
|
||||
#ifdef PERSISTENT
|
||||
{"_p_resolveConflict",
|
||||
(PyCFunction) bucket__p_resolveConflict, METH_VARARGS,
|
||||
"_p_resolveConflict() -- Reinitialize from a newly created copy"},
|
||||
|
||||
{"_p_deactivate",
|
||||
(PyCFunction) bucket__p_deactivate, METH_VARARGS | METH_KEYWORDS,
|
||||
"_p_deactivate() -- Reinitialize from a newly created copy"},
|
||||
#endif
|
||||
|
||||
{"add", (PyCFunction)Set_insert, METH_VARARGS,
|
||||
"add(id) -- Add a key to the set"},
|
||||
|
||||
{"insert", (PyCFunction)Set_insert, METH_VARARGS,
|
||||
"insert(id) -- Add a key to the set"},
|
||||
|
||||
{"update", (PyCFunction)Set_update, METH_VARARGS,
|
||||
"update(seq) -- Add the items from the given sequence to the set"},
|
||||
|
||||
{"remove", (PyCFunction)Set_remove, METH_VARARGS,
|
||||
"remove(id) -- Remove an id from the set"},
|
||||
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
static int
|
||||
Set_init(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
PyObject *v = NULL;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|O:" MOD_NAME_PREFIX "Set", &v))
|
||||
return -1;
|
||||
|
||||
if (v)
|
||||
return _Set_update((Bucket *)self, v);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static PyObject *
|
||||
set_repr(Bucket *self)
|
||||
{
|
||||
static PyObject *format;
|
||||
PyObject *r, *t;
|
||||
|
||||
if (!format)
|
||||
format = TEXT_FROM_STRING(MOD_NAME_PREFIX "Set(%s)");
|
||||
UNLESS (t = PyTuple_New(1))
|
||||
return NULL;
|
||||
UNLESS (r = bucket_keys(self, NULL, NULL))
|
||||
goto err;
|
||||
PyTuple_SET_ITEM(t, 0, r);
|
||||
r = t;
|
||||
ASSIGN(r, TEXT_FORMAT(format, r));
|
||||
return r;
|
||||
err:
|
||||
Py_DECREF(t);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static Py_ssize_t
|
||||
set_length(Bucket *self)
|
||||
{
|
||||
int r;
|
||||
|
||||
PER_USE_OR_RETURN(self, -1);
|
||||
r = self->len;
|
||||
PER_UNUSE(self);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
set_item(Bucket *self, Py_ssize_t index)
|
||||
{
|
||||
PyObject *r=0;
|
||||
|
||||
PER_USE_OR_RETURN(self, NULL);
|
||||
if (index >= 0 && index < self->len)
|
||||
{
|
||||
COPY_KEY_TO_OBJECT(r, self->keys[index]);
|
||||
}
|
||||
else
|
||||
IndexError(index);
|
||||
|
||||
PER_UNUSE(self);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static PySequenceMethods set_as_sequence = {
|
||||
(lenfunc)set_length, /* sq_length */
|
||||
(binaryfunc)0, /* sq_concat */
|
||||
(ssizeargfunc)0, /* sq_repeat */
|
||||
(ssizeargfunc)set_item, /* sq_item */
|
||||
(ssizessizeargfunc)0, /* sq_slice */
|
||||
(ssizeobjargproc)0, /* sq_ass_item */
|
||||
(ssizessizeobjargproc)0, /* sq_ass_slice */
|
||||
(objobjproc)bucket_contains, /* sq_contains */
|
||||
0, /* sq_inplace_concat */
|
||||
0, /* sq_inplace_repeat */
|
||||
};
|
||||
|
||||
static PyTypeObject SetType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0) /* PyPersist_Type */
|
||||
MODULE_NAME MOD_NAME_PREFIX "Set", /* tp_name */
|
||||
sizeof(Bucket), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
(destructor)bucket_dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_compare */
|
||||
(reprfunc)set_repr, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
&set_as_sequence, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT |
|
||||
Py_TPFLAGS_HAVE_GC |
|
||||
Py_TPFLAGS_BASETYPE, /* tp_flags */
|
||||
0, /* tp_doc */
|
||||
(traverseproc)bucket_traverse, /* tp_traverse */
|
||||
(inquiry)bucket_tp_clear, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
(getiterfunc)Bucket_getiter, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
Set_methods, /* tp_methods */
|
||||
Bucket_members, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
Set_init, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
0, /*PyType_GenericNew,*/ /* tp_new */
|
||||
};
|
||||
|
||||
static int
|
||||
nextSet(SetIteration *i)
|
||||
{
|
||||
|
||||
if (i->position >= 0)
|
||||
{
|
||||
UNLESS(PER_USE(BUCKET(i->set)))
|
||||
return -1;
|
||||
|
||||
if (i->position)
|
||||
{
|
||||
DECREF_KEY(i->key);
|
||||
}
|
||||
|
||||
if (i->position < BUCKET(i->set)->len)
|
||||
{
|
||||
COPY_KEY(i->key, BUCKET(i->set)->keys[i->position]);
|
||||
INCREF_KEY(i->key);
|
||||
i->position ++;
|
||||
}
|
||||
else
|
||||
{
|
||||
i->position = -1;
|
||||
PER_ACCESSED(BUCKET(i->set));
|
||||
}
|
||||
|
||||
PER_ALLOW_DEACTIVATION(BUCKET(i->set));
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
All Rights Reserved.
|
||||
|
||||
This software is subject to the provisions of the Zope Public License,
|
||||
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE
|
||||
|
||||
****************************************************************************/
|
||||
#include "_compat.h"
|
||||
|
||||
#define TREESETTEMPLATE_C "$Id$\n"
|
||||
|
||||
static PyObject *
|
||||
TreeSet_insert(BTree *self, PyObject *args)
|
||||
{
|
||||
PyObject *key;
|
||||
int i;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O:insert", &key))
|
||||
return NULL;
|
||||
i = _BTree_set(self, key, Py_None, 1, 1);
|
||||
if (i < 0)
|
||||
return NULL;
|
||||
return INT_FROM_LONG(i);
|
||||
}
|
||||
|
||||
/* _Set_update and _TreeSet_update are identical except for the
|
||||
function they call to add the element to the set.
|
||||
*/
|
||||
|
||||
static int
|
||||
_TreeSet_update(BTree *self, PyObject *seq)
|
||||
{
|
||||
int n=0, ind=0;
|
||||
PyObject *iter, *v;
|
||||
|
||||
iter = PyObject_GetIter(seq);
|
||||
if (iter == NULL)
|
||||
return -1;
|
||||
|
||||
while (1)
|
||||
{
|
||||
v = PyIter_Next(iter);
|
||||
if (v == NULL)
|
||||
{
|
||||
if (PyErr_Occurred())
|
||||
goto err;
|
||||
else
|
||||
break;
|
||||
}
|
||||
ind = _BTree_set(self, v, Py_None, 1, 1);
|
||||
Py_DECREF(v);
|
||||
if (ind < 0)
|
||||
goto err;
|
||||
else
|
||||
n += ind;
|
||||
}
|
||||
|
||||
err:
|
||||
Py_DECREF(iter);
|
||||
if (ind < 0)
|
||||
return -1;
|
||||
return n;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
TreeSet_update(BTree *self, PyObject *args)
|
||||
{
|
||||
PyObject *seq = NULL;
|
||||
int n = 0;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|O:update", &seq))
|
||||
return NULL;
|
||||
|
||||
if (seq)
|
||||
{
|
||||
n = _TreeSet_update(self, seq);
|
||||
if (n < 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return INT_FROM_LONG(n);
|
||||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
TreeSet_remove(BTree *self, PyObject *args)
|
||||
{
|
||||
PyObject *key;
|
||||
|
||||
UNLESS (PyArg_ParseTuple(args, "O", &key))
|
||||
return NULL;
|
||||
if (_BTree_set(self, key, NULL, 0, 1) < 0)
|
||||
return NULL;
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
TreeSet_setstate(BTree *self, PyObject *args)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (!PyArg_ParseTuple(args,"O",&args))
|
||||
return NULL;
|
||||
|
||||
PER_PREVENT_DEACTIVATION(self);
|
||||
r=_BTree_setstate(self, args, 1);
|
||||
PER_UNUSE(self);
|
||||
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static struct PyMethodDef TreeSet_methods[] =
|
||||
{
|
||||
{"__getstate__", (PyCFunction) BTree_getstate, METH_NOARGS,
|
||||
"__getstate__() -> state\n\n"
|
||||
"Return the picklable state of the TreeSet."},
|
||||
|
||||
{"__setstate__", (PyCFunction) TreeSet_setstate, METH_VARARGS,
|
||||
"__setstate__(state)\n\n"
|
||||
"Set the state of the TreeSet."},
|
||||
|
||||
{"has_key", (PyCFunction) BTree_has_key, METH_O,
|
||||
"has_key(key)\n\n"
|
||||
"Return true if the TreeSet contains the given key."},
|
||||
|
||||
{"keys", (PyCFunction) BTree_keys, METH_VARARGS | METH_KEYWORDS,
|
||||
"keys([min, max]) -> list of keys\n\n"
|
||||
"Returns the keys of the TreeSet. If min and max are supplied, only\n"
|
||||
"keys greater than min and less than max are returned."},
|
||||
|
||||
{"maxKey", (PyCFunction) BTree_maxKey, METH_VARARGS,
|
||||
"maxKey([max]) -> key\n\n"
|
||||
"Return the largest key in the BTree. If max is specified, return\n"
|
||||
"the largest key <= max."},
|
||||
|
||||
{"minKey", (PyCFunction) BTree_minKey, METH_VARARGS,
|
||||
"minKey([mi]) -> key\n\n"
|
||||
"Return the smallest key in the BTree. If min is specified, return\n"
|
||||
"the smallest key >= min."},
|
||||
|
||||
{"clear", (PyCFunction) BTree_clear, METH_NOARGS,
|
||||
"clear()\n\nRemove all of the items from the BTree."},
|
||||
|
||||
{"add", (PyCFunction)TreeSet_insert, METH_VARARGS,
|
||||
"add(id) -- Add an item to the set"},
|
||||
|
||||
{"insert", (PyCFunction)TreeSet_insert, METH_VARARGS,
|
||||
"insert(id) -- Add an item to the set"},
|
||||
|
||||
{"update", (PyCFunction)TreeSet_update, METH_VARARGS,
|
||||
"update(collection)\n\n Add the items from the given collection."},
|
||||
|
||||
{"remove", (PyCFunction)TreeSet_remove, METH_VARARGS,
|
||||
"remove(id) -- Remove a key from the set"},
|
||||
|
||||
{"_check", (PyCFunction) BTree_check, METH_NOARGS,
|
||||
"Perform sanity check on TreeSet, and raise exception if flawed."},
|
||||
|
||||
#ifdef PERSISTENT
|
||||
{"_p_resolveConflict",
|
||||
(PyCFunction) BTree__p_resolveConflict, METH_VARARGS,
|
||||
"_p_resolveConflict() -- Reinitialize from a newly created copy"},
|
||||
|
||||
{"_p_deactivate",
|
||||
(PyCFunction) BTree__p_deactivate, METH_VARARGS | METH_KEYWORDS,
|
||||
"_p_deactivate()\n\nReinitialize from a newly created copy."},
|
||||
#endif
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
static PyMappingMethods TreeSet_as_mapping = {
|
||||
(lenfunc)BTree_length, /*mp_length*/
|
||||
};
|
||||
|
||||
static PySequenceMethods TreeSet_as_sequence = {
|
||||
(lenfunc)0, /* sq_length */
|
||||
(binaryfunc)0, /* sq_concat */
|
||||
(ssizeargfunc)0, /* sq_repeat */
|
||||
(ssizeargfunc)0, /* sq_item */
|
||||
(ssizessizeargfunc)0, /* sq_slice */
|
||||
(ssizeobjargproc)0, /* sq_ass_item */
|
||||
(ssizessizeobjargproc)0, /* sq_ass_slice */
|
||||
(objobjproc)BTree_contains, /* sq_contains */
|
||||
0, /* sq_inplace_concat */
|
||||
0, /* sq_inplace_repeat */
|
||||
};
|
||||
|
||||
static int
|
||||
TreeSet_init(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
PyObject *v = NULL;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|O:" MOD_NAME_PREFIX "TreeSet", &v))
|
||||
return -1;
|
||||
|
||||
if (v)
|
||||
return _TreeSet_update((BTree *)self, v);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyTypeObject TreeSetType =
|
||||
{
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
MODULE_NAME MOD_NAME_PREFIX "TreeSet", /* tp_name */
|
||||
sizeof(BTree), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
(destructor)BTree_dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_compare */
|
||||
0, /* tp_repr */
|
||||
&BTree_as_number_for_nonzero, /* tp_as_number */
|
||||
&TreeSet_as_sequence, /* tp_as_sequence */
|
||||
&TreeSet_as_mapping, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT |
|
||||
Py_TPFLAGS_HAVE_GC |
|
||||
Py_TPFLAGS_BASETYPE, /* tp_flags */
|
||||
0, /* tp_doc */
|
||||
(traverseproc)BTree_traverse, /* tp_traverse */
|
||||
(inquiry)BTree_tp_clear, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
(getiterfunc)BTree_getiter, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
TreeSet_methods, /* tp_methods */
|
||||
BTree_members, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
TreeSet_init, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
0, /*PyType_GenericNew,*/ /* tp_new */
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
/*############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
############################################################################*/
|
||||
|
||||
#define MASTER_ID "$Id$\n"
|
||||
|
||||
/* IFBTree - int key, float value BTree
|
||||
|
||||
Implements a collection using int type keys
|
||||
and float type values
|
||||
*/
|
||||
|
||||
/* Setup template macros */
|
||||
|
||||
#define PERSISTENT
|
||||
|
||||
#define MOD_NAME_PREFIX "IF"
|
||||
|
||||
#define DEFAULT_MAX_BUCKET_SIZE 120
|
||||
#define DEFAULT_MAX_BTREE_SIZE 500
|
||||
|
||||
#include "_compat.h"
|
||||
#include "intkeymacros.h"
|
||||
#include "floatvaluemacros.h"
|
||||
|
||||
#ifdef PY3K
|
||||
#define INITMODULE PyInit__IFBTree
|
||||
#else
|
||||
#define INITMODULE init_IFBTree
|
||||
#endif
|
||||
#include "BTreeModuleTemplate.c"
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,41 @@
|
||||
/*############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
############################################################################*/
|
||||
|
||||
#define MASTER_ID "$Id$\n"
|
||||
|
||||
/* IIBTree - int key, int value BTree
|
||||
|
||||
Implements a collection using int type keys
|
||||
and int type values
|
||||
*/
|
||||
|
||||
/* Setup template macros */
|
||||
|
||||
#define PERSISTENT
|
||||
|
||||
#define MOD_NAME_PREFIX "II"
|
||||
|
||||
#define DEFAULT_MAX_BUCKET_SIZE 120
|
||||
#define DEFAULT_MAX_BTREE_SIZE 500
|
||||
|
||||
#include "_compat.h"
|
||||
#include "intkeymacros.h"
|
||||
#include "intvaluemacros.h"
|
||||
|
||||
#ifdef PY3K
|
||||
#define INITMODULE PyInit__IIBTree
|
||||
#else
|
||||
#define INITMODULE init_IIBTree
|
||||
#endif
|
||||
#include "BTreeModuleTemplate.c"
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,39 @@
|
||||
/*############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
############################################################################*/
|
||||
|
||||
#define MASTER_ID "$Id$\n"
|
||||
|
||||
/* IOBTree - int key, object value BTree
|
||||
|
||||
Implements a collection using int type keys
|
||||
and object type values
|
||||
*/
|
||||
|
||||
#define PERSISTENT
|
||||
|
||||
#define MOD_NAME_PREFIX "IO"
|
||||
|
||||
#define DEFAULT_MAX_BUCKET_SIZE 60
|
||||
#define DEFAULT_MAX_BTREE_SIZE 500
|
||||
|
||||
#include "_compat.h"
|
||||
#include "intkeymacros.h"
|
||||
#include "objectvaluemacros.h"
|
||||
|
||||
#ifdef PY3K
|
||||
#define INITMODULE PyInit__IOBTree
|
||||
#else
|
||||
#define INITMODULE init_IOBTree
|
||||
#endif
|
||||
#include "BTreeModuleTemplate.c"
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,43 @@
|
||||
/*############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
############################################################################*/
|
||||
|
||||
#define MASTER_ID "$Id: _IFBTree.c 67074 2006-04-17 19:13:39Z fdrake $\n"
|
||||
|
||||
/* IFBTree - int key, float value BTree
|
||||
|
||||
Implements a collection using int type keys
|
||||
and float type values
|
||||
*/
|
||||
|
||||
/* Setup template macros */
|
||||
|
||||
#define PERSISTENT
|
||||
|
||||
#define MOD_NAME_PREFIX "LF"
|
||||
|
||||
#define DEFAULT_MAX_BUCKET_SIZE 120
|
||||
#define DEFAULT_MAX_BTREE_SIZE 500
|
||||
|
||||
#define ZODB_64BIT_INTS
|
||||
|
||||
#include "_compat.h"
|
||||
#include "intkeymacros.h"
|
||||
#include "floatvaluemacros.h"
|
||||
|
||||
#ifdef PY3K
|
||||
#define INITMODULE PyInit__LFBTree
|
||||
#else
|
||||
#define INITMODULE init_LFBTree
|
||||
#endif
|
||||
#include "BTreeModuleTemplate.c"
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,43 @@
|
||||
/*############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
############################################################################*/
|
||||
|
||||
#define MASTER_ID "$Id: _IIBTree.c 25186 2004-06-02 15:07:33Z jim $\n"
|
||||
|
||||
/* IIBTree - int key, int value BTree
|
||||
|
||||
Implements a collection using int type keys
|
||||
and int type values
|
||||
*/
|
||||
|
||||
/* Setup template macros */
|
||||
|
||||
#define PERSISTENT
|
||||
|
||||
#define MOD_NAME_PREFIX "LL"
|
||||
|
||||
#define DEFAULT_MAX_BUCKET_SIZE 120
|
||||
#define DEFAULT_MAX_BTREE_SIZE 500
|
||||
|
||||
#define ZODB_64BIT_INTS
|
||||
|
||||
#include "_compat.h"
|
||||
#include "intkeymacros.h"
|
||||
#include "intvaluemacros.h"
|
||||
|
||||
#ifdef PY3K
|
||||
#define INITMODULE PyInit__LLBTree
|
||||
#else
|
||||
#define INITMODULE init_LLBTree
|
||||
#endif
|
||||
#include "BTreeModuleTemplate.c"
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,41 @@
|
||||
/*############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
############################################################################*/
|
||||
|
||||
#define MASTER_ID "$Id: _IOBTree.c 25186 2004-06-02 15:07:33Z jim $\n"
|
||||
|
||||
/* IOBTree - int key, object value BTree
|
||||
|
||||
Implements a collection using int type keys
|
||||
and object type values
|
||||
*/
|
||||
|
||||
#define PERSISTENT
|
||||
|
||||
#define MOD_NAME_PREFIX "LO"
|
||||
|
||||
#define DEFAULT_MAX_BUCKET_SIZE 60
|
||||
#define DEFAULT_MAX_BTREE_SIZE 500
|
||||
|
||||
#define ZODB_64BIT_INTS
|
||||
|
||||
#include "_compat.h"
|
||||
#include "intkeymacros.h"
|
||||
#include "objectvaluemacros.h"
|
||||
|
||||
#ifdef PY3K
|
||||
#define INITMODULE PyInit__LOBTree
|
||||
#else
|
||||
#define INITMODULE init_LOBTree
|
||||
#endif
|
||||
#include "BTreeModuleTemplate.c"
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,39 @@
|
||||
/*############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
############################################################################*/
|
||||
|
||||
#define MASTER_ID "$Id$\n"
|
||||
|
||||
/* OIBTree - object key, int value BTree
|
||||
|
||||
Implements a collection using object type keys
|
||||
and int type values
|
||||
*/
|
||||
|
||||
#define PERSISTENT
|
||||
|
||||
#define MOD_NAME_PREFIX "OI"
|
||||
|
||||
#define DEFAULT_MAX_BUCKET_SIZE 60
|
||||
#define DEFAULT_MAX_BTREE_SIZE 250
|
||||
|
||||
#include "_compat.h"
|
||||
#include "objectkeymacros.h"
|
||||
#include "intvaluemacros.h"
|
||||
|
||||
#ifdef PY3K
|
||||
#define INITMODULE PyInit__OIBTree
|
||||
#else
|
||||
#define INITMODULE init_OIBTree
|
||||
#endif
|
||||
#include "BTreeModuleTemplate.c"
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,41 @@
|
||||
/*############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
############################################################################*/
|
||||
|
||||
#define MASTER_ID "$Id: _OIBTree.c 25186 2004-06-02 15:07:33Z jim $\n"
|
||||
|
||||
/* OIBTree - object key, int value BTree
|
||||
|
||||
Implements a collection using object type keys
|
||||
and int type values
|
||||
*/
|
||||
|
||||
#define PERSISTENT
|
||||
|
||||
#define MOD_NAME_PREFIX "OL"
|
||||
|
||||
#define DEFAULT_MAX_BUCKET_SIZE 60
|
||||
#define DEFAULT_MAX_BTREE_SIZE 250
|
||||
|
||||
#define ZODB_64BIT_INTS
|
||||
|
||||
#include "_compat.h"
|
||||
#include "objectkeymacros.h"
|
||||
#include "intvaluemacros.h"
|
||||
|
||||
#ifdef PY3K
|
||||
#define INITMODULE PyInit__OLBTree
|
||||
#else
|
||||
#define INITMODULE init_OLBTree
|
||||
#endif
|
||||
#include "BTreeModuleTemplate.c"
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,39 @@
|
||||
/*############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
############################################################################*/
|
||||
|
||||
#define MASTER_ID "$Id$\n"
|
||||
|
||||
/* OOBTree - object key, object value BTree
|
||||
|
||||
Implements a collection using object type keys
|
||||
and object type values
|
||||
*/
|
||||
|
||||
#define PERSISTENT
|
||||
|
||||
#define MOD_NAME_PREFIX "OO"
|
||||
|
||||
#define DEFAULT_MAX_BUCKET_SIZE 30
|
||||
#define DEFAULT_MAX_BTREE_SIZE 250
|
||||
|
||||
#include "_compat.h"
|
||||
#include "objectkeymacros.h"
|
||||
#include "objectvaluemacros.h"
|
||||
|
||||
#ifdef PY3K
|
||||
#define INITMODULE PyInit__OOBTree
|
||||
#else
|
||||
#define INITMODULE init_OOBTree
|
||||
#endif
|
||||
#include "BTreeModuleTemplate.c"
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,69 @@
|
||||
#############################################################################
|
||||
#
|
||||
# Copyright (c) 2007 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
import zope.interface
|
||||
import BTrees.Interfaces
|
||||
|
||||
|
||||
@zope.interface.implementer(BTrees.Interfaces.IBTreeFamily)
|
||||
class _Family(object):
|
||||
|
||||
from BTrees import OOBTree as OO
|
||||
|
||||
class _Family32(_Family):
|
||||
from BTrees import OIBTree as OI
|
||||
from BTrees import IIBTree as II
|
||||
from BTrees import IOBTree as IO
|
||||
from BTrees import IFBTree as IF
|
||||
|
||||
maxint = int(2**31-1)
|
||||
minint = -maxint - 1
|
||||
|
||||
def __reduce__(self):
|
||||
return _family32, ()
|
||||
|
||||
class _Family64(_Family):
|
||||
from BTrees import OLBTree as OI
|
||||
from BTrees import LLBTree as II
|
||||
from BTrees import LOBTree as IO
|
||||
from BTrees import LFBTree as IF
|
||||
|
||||
maxint = 2**63-1
|
||||
minint = -maxint - 1
|
||||
|
||||
def __reduce__(self):
|
||||
return _family64, ()
|
||||
|
||||
def _family32():
|
||||
return family32
|
||||
_family32.__safe_for_unpickling__ = True
|
||||
|
||||
def _family64():
|
||||
return family64
|
||||
_family64.__safe_for_unpickling__ = True
|
||||
|
||||
|
||||
family32 = _Family32()
|
||||
family64 = _Family64()
|
||||
|
||||
|
||||
BTrees.family64.IO.family = family64
|
||||
BTrees.family64.OI.family = family64
|
||||
BTrees.family64.IF.family = family64
|
||||
BTrees.family64.II.family = family64
|
||||
|
||||
BTrees.family32.IO.family = family32
|
||||
BTrees.family32.OI.family = family32
|
||||
BTrees.family32.IF.family = family32
|
||||
BTrees.family32.II.family = family32
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,55 @@
|
||||
/* Straddle Python 2 / 3 */
|
||||
#ifndef BTREES__COMPAT_H
|
||||
#define BTREES__COMPAT_H
|
||||
|
||||
#include "Python.h"
|
||||
|
||||
#ifdef INTERN
|
||||
#undef INTERN
|
||||
#endif
|
||||
|
||||
#ifdef INT_FROM_LONG
|
||||
#undef INT_FROM_LONG
|
||||
#endif
|
||||
|
||||
#ifdef INT_CHECK
|
||||
#undef INT_CHECK
|
||||
#endif
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
|
||||
#define PY3K
|
||||
|
||||
#define INTERN PyUnicode_InternFromString
|
||||
#define INT_FROM_LONG(x) PyLong_FromLong(x)
|
||||
#define INT_CHECK(x) PyLong_Check(x)
|
||||
#define INT_AS_LONG(x) PyLong_AS_LONG(x)
|
||||
#define TEXT_FROM_STRING PyUnicode_FromString
|
||||
#define TEXT_FORMAT PyUnicode_Format
|
||||
|
||||
/* Note that the second comparison is skipped if the first comparison returns:
|
||||
|
||||
1 -> There was no error and the answer is -1
|
||||
-1 -> There was an error, which the caller will detect with PyError_Occurred.
|
||||
*/
|
||||
#define COMPARE(lhs, rhs) \
|
||||
(lhs == Py_None ? (rhs == Py_None ? 0 : -1) : (rhs == Py_None ? 1 : \
|
||||
(PyObject_RichCompareBool((lhs), (rhs), Py_LT) != 0 ? -1 : \
|
||||
(PyObject_RichCompareBool((lhs), (rhs), Py_EQ) > 0 ? 0 : 1))))
|
||||
|
||||
#else
|
||||
|
||||
#define INTERN PyString_InternFromString
|
||||
#define INT_FROM_LONG(x) PyInt_FromLong(x)
|
||||
#define INT_CHECK(x) PyInt_Check(x)
|
||||
#define INT_AS_LONG(x) PyInt_AS_LONG(x)
|
||||
#define TEXT_FROM_STRING PyString_FromString
|
||||
#define TEXT_FORMAT PyString_Format
|
||||
|
||||
#define COMPARE(lhs, rhs) \
|
||||
(lhs == Py_None ? (rhs == Py_None ? 0 : -1) : (rhs == Py_None ? 1 : \
|
||||
PyObject_Compare((lhs), (rhs))))
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* BTREES__COMPAT_H */
|
||||
@@ -0,0 +1,100 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import os
|
||||
import sys
|
||||
|
||||
PYPY = hasattr(sys, 'pypy_version_info')
|
||||
# We can and do build the C extensions on PyPy, but
|
||||
# as of Persistent 4.2.5 the persistent C extension is not
|
||||
# built on PyPy, so importing our C extension will fail anyway.
|
||||
PURE_PYTHON = os.environ.get('PURE_PYTHON', PYPY)
|
||||
|
||||
|
||||
if sys.version_info[0] < 3: #pragma NO COVER Python2
|
||||
|
||||
PY2 = True
|
||||
PY3 = False
|
||||
|
||||
int_types = int, long
|
||||
xrange = xrange
|
||||
def compare(x, y):
|
||||
if x is None:
|
||||
if y is None:
|
||||
return 0
|
||||
else:
|
||||
return -1
|
||||
elif y is None:
|
||||
return 1
|
||||
else:
|
||||
return cmp(x, y)
|
||||
|
||||
_bytes = str
|
||||
def _ascii(x):
|
||||
return bytes(x)
|
||||
|
||||
else: #pragma NO COVER Python3
|
||||
|
||||
PY2 = False
|
||||
PY3 = True
|
||||
|
||||
int_types = int,
|
||||
xrange = range
|
||||
|
||||
def compare(x, y):
|
||||
if x is None:
|
||||
if y is None:
|
||||
return 0
|
||||
else:
|
||||
return -1
|
||||
elif y is None:
|
||||
return 1
|
||||
else:
|
||||
return (x > y) - (y > x)
|
||||
|
||||
_bytes = bytes
|
||||
def _ascii(x):
|
||||
return bytes(x, 'ascii')
|
||||
|
||||
|
||||
def import_c_extension(mod_globals):
|
||||
import importlib
|
||||
c_module = None
|
||||
module_name = mod_globals['__name__']
|
||||
assert module_name.startswith('BTrees.')
|
||||
module_name = module_name.split('.')[1]
|
||||
if not PURE_PYTHON:
|
||||
try:
|
||||
c_module = importlib.import_module('BTrees._' + module_name)
|
||||
except ImportError:
|
||||
pass
|
||||
if c_module is not None:
|
||||
new_values = dict(c_module.__dict__)
|
||||
new_values.pop("__name__", None)
|
||||
new_values.pop('__file__', None)
|
||||
new_values.pop('__doc__', None)
|
||||
mod_globals.update(new_values)
|
||||
else:
|
||||
# No C extension, make the Py versions available without that
|
||||
# extension. The list comprehension both filters and prevents
|
||||
# concurrent modification errors.
|
||||
for py in [k for k in mod_globals if k.endswith('Py')]:
|
||||
mod_globals[py[:-2]] = mod_globals[py]
|
||||
|
||||
# Assign the global aliases
|
||||
prefix = module_name[:2]
|
||||
for name in ('Bucket', 'Set', 'BTree', 'TreeSet'):
|
||||
mod_globals[name] = mod_globals[prefix + name]
|
||||
|
||||
# Cleanup
|
||||
del mod_globals['import_c_extension']
|
||||
@@ -0,0 +1,164 @@
|
||||
/*############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
############################################################################*/
|
||||
|
||||
#define MASTER_ID "$Id$\n"
|
||||
|
||||
/* fsBTree - FileStorage index BTree
|
||||
|
||||
This BTree implements a mapping from 2-character strings
|
||||
to six-character strings. This allows us to efficiently store
|
||||
a FileStorage index as a nested mapping of 6-character oid prefix
|
||||
to mapping of 2-character oid suffix to 6-character (byte) file
|
||||
positions.
|
||||
*/
|
||||
|
||||
typedef unsigned char char2[2];
|
||||
typedef unsigned char char6[6];
|
||||
|
||||
/* Setup template macros */
|
||||
|
||||
#define PERSISTENT
|
||||
|
||||
#define MOD_NAME_PREFIX "fs"
|
||||
|
||||
#define DEFAULT_MAX_BUCKET_SIZE 500
|
||||
#define DEFAULT_MAX_BTREE_SIZE 500
|
||||
|
||||
#include "_compat.h"
|
||||
/*#include "intkeymacros.h"*/
|
||||
|
||||
#define KEYMACROS_H "$Id$\n"
|
||||
#define KEY_TYPE char2
|
||||
#undef KEY_TYPE_IS_PYOBJECT
|
||||
#define KEY_CHECK(K) (PyBytes_Check(K) && PyBytes_GET_SIZE(K)==2)
|
||||
#define TEST_KEY_SET_OR(V, K, T) if ( ( (V) = ((*(K) < *(T) || (*(K) == *(T) && (K)[1] < (T)[1])) ? -1 : ((*(K) == *(T) && (K)[1] == (T)[1]) ? 0 : 1)) ), 0 )
|
||||
#define DECREF_KEY(KEY)
|
||||
#define INCREF_KEY(k)
|
||||
#define COPY_KEY(KEY, E) (*(KEY)=*(E), (KEY)[1]=(E)[1])
|
||||
#define COPY_KEY_TO_OBJECT(O, K) O=PyBytes_FromStringAndSize((const char*)K,2)
|
||||
#define COPY_KEY_FROM_ARG(TARGET, ARG, STATUS) \
|
||||
if (KEY_CHECK(ARG)) memcpy(TARGET, PyBytes_AS_STRING(ARG), 2); else { \
|
||||
PyErr_SetString(PyExc_TypeError, "expected two-character string key"); \
|
||||
(STATUS)=0; }
|
||||
|
||||
/*#include "intvaluemacros.h"*/
|
||||
#define VALUEMACROS_H "$Id$\n"
|
||||
#define VALUE_TYPE char6
|
||||
#undef VALUE_TYPE_IS_PYOBJECT
|
||||
#define TEST_VALUE(K, T) memcmp(K,T,6)
|
||||
#define DECREF_VALUE(k)
|
||||
#define INCREF_VALUE(k)
|
||||
#define COPY_VALUE(V, E) (memcpy(V, E, 6))
|
||||
#define COPY_VALUE_TO_OBJECT(O, K) O=PyBytes_FromStringAndSize((const char*)K,6)
|
||||
#define COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS) \
|
||||
if ((PyBytes_Check(ARG) && PyBytes_GET_SIZE(ARG)==6)) \
|
||||
memcpy(TARGET, PyBytes_AS_STRING(ARG), 6); else { \
|
||||
PyErr_SetString(PyExc_TypeError, "expected six-character string key"); \
|
||||
(STATUS)=0; }
|
||||
|
||||
#define NORMALIZE_VALUE(V, MIN)
|
||||
|
||||
#include "Python.h"
|
||||
|
||||
static PyObject *bucket_toBytes(PyObject *self);
|
||||
|
||||
static PyObject *bucket_fromBytes(PyObject *self, PyObject *state);
|
||||
|
||||
#define EXTRA_BUCKET_METHODS \
|
||||
{"toBytes", (PyCFunction) bucket_toBytes, METH_NOARGS, \
|
||||
"toBytes() -- Return the state as a bytes array"}, \
|
||||
{"fromBytes", (PyCFunction) bucket_fromBytes, METH_O, \
|
||||
"fromSBytes(s) -- Set the state of the object from a bytes array"}, \
|
||||
{"toString", (PyCFunction) bucket_toBytes, METH_NOARGS, \
|
||||
"toString() -- Deprecated alias for 'toBytes'"}, \
|
||||
{"fromString", (PyCFunction) bucket_fromBytes, METH_O, \
|
||||
"fromString(s) -- Deprecated alias for 'fromBytes'"}, \
|
||||
|
||||
#ifdef PY3K
|
||||
#define INITMODULE PyInit__fsBTree
|
||||
#else
|
||||
#define INITMODULE init_fsBTree
|
||||
#endif
|
||||
#include "BTreeModuleTemplate.c"
|
||||
|
||||
static PyObject *
|
||||
bucket_toBytes(PyObject *oself)
|
||||
{
|
||||
Bucket *self = (Bucket *)oself;
|
||||
PyObject *items = NULL;
|
||||
int len;
|
||||
|
||||
PER_USE_OR_RETURN(self, NULL);
|
||||
|
||||
len = self->len;
|
||||
|
||||
items = PyBytes_FromStringAndSize(NULL, len*8);
|
||||
if (items == NULL)
|
||||
goto err;
|
||||
memcpy(PyBytes_AS_STRING(items), self->keys, len*2);
|
||||
memcpy(PyBytes_AS_STRING(items)+len*2, self->values, len*6);
|
||||
|
||||
PER_UNUSE(self);
|
||||
return items;
|
||||
|
||||
err:
|
||||
PER_UNUSE(self);
|
||||
Py_XDECREF(items);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
bucket_fromBytes(PyObject *oself, PyObject *state)
|
||||
{
|
||||
Bucket *self = (Bucket *)oself;
|
||||
int len;
|
||||
KEY_TYPE *keys;
|
||||
VALUE_TYPE *values;
|
||||
|
||||
len = PyBytes_Size(state);
|
||||
if (len < 0)
|
||||
return NULL;
|
||||
|
||||
if (len%8)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "state string of wrong size");
|
||||
return NULL;
|
||||
}
|
||||
len /= 8;
|
||||
|
||||
if (self->next) {
|
||||
Py_DECREF(self->next);
|
||||
self->next = NULL;
|
||||
}
|
||||
|
||||
if (len > self->size) {
|
||||
keys = BTree_Realloc(self->keys, sizeof(KEY_TYPE)*len);
|
||||
if (keys == NULL)
|
||||
return NULL;
|
||||
values = BTree_Realloc(self->values, sizeof(VALUE_TYPE)*len);
|
||||
if (values == NULL)
|
||||
return NULL;
|
||||
self->keys = keys;
|
||||
self->values = values;
|
||||
self->size = len;
|
||||
}
|
||||
|
||||
memcpy(self->keys, PyBytes_AS_STRING(state), len*2);
|
||||
memcpy(self->values, PyBytes_AS_STRING(state)+len*2, len*6);
|
||||
|
||||
self->len = len;
|
||||
|
||||
Py_INCREF(self);
|
||||
return (PyObject *)self;
|
||||
}
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,428 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2003 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
"""
|
||||
Utilities for working with BTrees (TreeSets, Buckets, and Sets) at a low
|
||||
level.
|
||||
|
||||
The primary function is check(btree), which performs value-based consistency
|
||||
checks of a kind btree._check() does not perform. See the function docstring
|
||||
for details.
|
||||
|
||||
display(btree) displays the internal structure of a BTree (TreeSet, etc) to
|
||||
stdout.
|
||||
|
||||
CAUTION: When a BTree node has only a single bucket child, it can be
|
||||
impossible to get at the bucket from Python code (__getstate__() may squash
|
||||
the bucket object out of existence, as a pickling storage optimization). In
|
||||
such a case, the code here synthesizes a temporary bucket with the same keys
|
||||
(and values, if the bucket is of a mapping type). This has no first-order
|
||||
consequences, but can mislead if you pay close attention to reported object
|
||||
addresses and/or object identity (the synthesized bucket has an address
|
||||
that doesn't exist in the actual BTree).
|
||||
"""
|
||||
|
||||
from BTrees.IFBTree import IFBTree, IFBucket, IFSet, IFTreeSet
|
||||
from BTrees.IFBTree import IFBTreePy, IFBucketPy, IFSetPy, IFTreeSetPy
|
||||
from BTrees.IIBTree import IIBTree, IIBucket, IISet, IITreeSet
|
||||
from BTrees.IIBTree import IIBTreePy, IIBucketPy, IISetPy, IITreeSetPy
|
||||
from BTrees.IOBTree import IOBTree, IOBucket, IOSet, IOTreeSet
|
||||
from BTrees.IOBTree import IOBTreePy, IOBucketPy, IOSetPy, IOTreeSetPy
|
||||
from BTrees.LFBTree import LFBTree, LFBucket, LFSet, LFTreeSet
|
||||
from BTrees.LFBTree import LFBTreePy, LFBucketPy, LFSetPy, LFTreeSetPy
|
||||
from BTrees.LLBTree import LLBTree, LLBucket, LLSet, LLTreeSet
|
||||
from BTrees.LLBTree import LLBTreePy, LLBucketPy, LLSetPy, LLTreeSetPy
|
||||
from BTrees.LOBTree import LOBTree, LOBucket, LOSet, LOTreeSet
|
||||
from BTrees.LOBTree import LOBTreePy, LOBucketPy, LOSetPy, LOTreeSetPy
|
||||
from BTrees.OIBTree import OIBTree, OIBucket, OISet, OITreeSet
|
||||
from BTrees.OIBTree import OIBTreePy, OIBucketPy, OISetPy, OITreeSetPy
|
||||
from BTrees.OLBTree import OLBTree, OLBucket, OLSet, OLTreeSet
|
||||
from BTrees.OLBTree import OLBTreePy, OLBucketPy, OLSetPy, OLTreeSetPy
|
||||
from BTrees.OOBTree import OOBTree, OOBucket, OOSet, OOTreeSet
|
||||
from BTrees.OOBTree import OOBTreePy, OOBucketPy, OOSetPy, OOTreeSetPy
|
||||
|
||||
from BTrees.utils import positive_id
|
||||
from BTrees.utils import oid_repr
|
||||
|
||||
TYPE_UNKNOWN, TYPE_BTREE, TYPE_BUCKET = range(3)
|
||||
|
||||
from ._compat import compare
|
||||
|
||||
_type2kind = {}
|
||||
for kv in ('OO',
|
||||
'II', 'IO', 'OI', 'IF',
|
||||
'LL', 'LO', 'OL', 'LF',
|
||||
):
|
||||
for name, kind in (
|
||||
('BTree', (TYPE_BTREE, True)),
|
||||
('Bucket', (TYPE_BUCKET, True)),
|
||||
('TreeSet', (TYPE_BTREE, False)),
|
||||
('Set', (TYPE_BUCKET, False)),
|
||||
):
|
||||
_type2kind[globals()[kv+name]] = kind
|
||||
py = kv + name + 'Py'
|
||||
_type2kind[globals()[py]] = kind
|
||||
|
||||
# Return pair
|
||||
#
|
||||
# TYPE_BTREE or TYPE_BUCKET, is_mapping
|
||||
|
||||
def classify(obj):
|
||||
return _type2kind[type(obj)]
|
||||
|
||||
|
||||
BTREE_EMPTY, BTREE_ONE, BTREE_NORMAL = range(3)
|
||||
|
||||
# If the BTree is empty, returns
|
||||
#
|
||||
# BTREE_EMPTY, [], []
|
||||
#
|
||||
# If the BTree has only one bucket, sometimes returns
|
||||
#
|
||||
# BTREE_ONE, bucket_state, None
|
||||
#
|
||||
# Else returns
|
||||
#
|
||||
# BTREE_NORMAL, list of keys, list of kids
|
||||
#
|
||||
# and the list of kids has one more entry than the list of keys.
|
||||
#
|
||||
# BTree.__getstate__() docs:
|
||||
#
|
||||
# For an empty BTree (self->len == 0), None.
|
||||
#
|
||||
# For a BTree with one child (self->len == 1), and that child is a bucket,
|
||||
# and that bucket has a NULL oid, a one-tuple containing a one-tuple
|
||||
# containing the bucket's state:
|
||||
#
|
||||
# (
|
||||
# (
|
||||
# child[0].__getstate__(),
|
||||
# ),
|
||||
# )
|
||||
#
|
||||
# Else a two-tuple. The first element is a tuple interleaving the BTree's
|
||||
# keys and direct children, of size 2*self->len - 1 (key[0] is unused and
|
||||
# is not saved). The second element is the firstbucket:
|
||||
#
|
||||
# (
|
||||
# (child[0], key[1], child[1], key[2], child[2], ...,
|
||||
# key[len-1], child[len-1]),
|
||||
# self->firstbucket
|
||||
# )
|
||||
|
||||
_btree2bucket = {}
|
||||
for kv in ('OO',
|
||||
'II', 'IO', 'OI', 'IF',
|
||||
'LL', 'LO', 'OL', 'LF',
|
||||
):
|
||||
_btree2bucket[globals()[kv+'BTree']] = globals()[kv+'Bucket']
|
||||
py = kv + 'BTreePy'
|
||||
_btree2bucket[globals()[py]] = globals()[kv+'BucketPy']
|
||||
_btree2bucket[globals()[kv+'TreeSet']] = globals()[kv+'Set']
|
||||
py = kv + 'TreeSetPy'
|
||||
_btree2bucket[globals()[kv+'TreeSetPy']] = globals()[kv+'SetPy']
|
||||
|
||||
|
||||
def crack_btree(t, is_mapping):
|
||||
state = t.__getstate__()
|
||||
if state is None:
|
||||
return BTREE_EMPTY, [], []
|
||||
|
||||
assert isinstance(state, tuple)
|
||||
if len(state) == 1:
|
||||
state = state[0]
|
||||
assert isinstance(state, tuple) and len(state) == 1
|
||||
state = state[0]
|
||||
return BTREE_ONE, state, None
|
||||
|
||||
assert len(state) == 2
|
||||
data, firstbucket = state
|
||||
n = len(data)
|
||||
assert n & 1
|
||||
kids = []
|
||||
keys = []
|
||||
i = 0
|
||||
for x in data:
|
||||
if i & 1:
|
||||
keys.append(x)
|
||||
else:
|
||||
kids.append(x)
|
||||
i += 1
|
||||
return BTREE_NORMAL, keys, kids
|
||||
|
||||
# Returns
|
||||
#
|
||||
# keys, values # for a mapping; len(keys) == len(values) in this case
|
||||
# or
|
||||
# keys, [] # for a set
|
||||
#
|
||||
# bucket.__getstate__() docs:
|
||||
#
|
||||
# For a set bucket (self->values is NULL), a one-tuple or two-tuple. The
|
||||
# first element is a tuple of keys, of length self->len. The second element
|
||||
# is the next bucket, present if and only if next is non-NULL:
|
||||
#
|
||||
# (
|
||||
# (keys[0], keys[1], ..., keys[len-1]),
|
||||
# <self->next iff non-NULL>
|
||||
# )
|
||||
#
|
||||
# For a mapping bucket (self->values is not NULL), a one-tuple or two-tuple.
|
||||
# The first element is a tuple interleaving keys and values, of length
|
||||
# 2 * self->len. The second element is the next bucket, present iff next is
|
||||
# non-NULL:
|
||||
#
|
||||
# (
|
||||
# (keys[0], values[0], keys[1], values[1], ...,
|
||||
# keys[len-1], values[len-1]),
|
||||
# <self->next iff non-NULL>
|
||||
# )
|
||||
|
||||
def crack_bucket(b, is_mapping):
|
||||
state = b.__getstate__()
|
||||
assert isinstance(state, tuple)
|
||||
assert 1 <= len(state) <= 2
|
||||
data = state[0]
|
||||
if not is_mapping:
|
||||
return data, []
|
||||
keys = []
|
||||
values = []
|
||||
n = len(data)
|
||||
assert n & 1 == 0
|
||||
i = 0
|
||||
for x in data:
|
||||
if i & 1:
|
||||
values.append(x)
|
||||
else:
|
||||
keys.append(x)
|
||||
i += 1
|
||||
return keys, values
|
||||
|
||||
def type_and_adr(obj):
|
||||
if hasattr(obj, '_p_oid'):
|
||||
oid = oid_repr(obj._p_oid)
|
||||
else:
|
||||
oid = 'None'
|
||||
return "%s (0x%x oid=%s)" % (type(obj).__name__, positive_id(obj), oid)
|
||||
|
||||
# Walker implements a depth-first search of a BTree (or TreeSet or Set or
|
||||
# Bucket). Subclasses must implement the visit_btree() and visit_bucket()
|
||||
# methods, and arrange to call the walk() method. walk() calls the
|
||||
# visit_XYZ() methods once for each node in the tree, in depth-first
|
||||
# left-to-right order.
|
||||
|
||||
class Walker:
|
||||
def __init__(self, obj):
|
||||
self.obj = obj
|
||||
|
||||
# obj is the BTree (BTree or TreeSet).
|
||||
# path is a list of indices, from the root. For example, if a BTree node
|
||||
# is child[5] of child[3] of the root BTree, [3, 5].
|
||||
# parent is the parent BTree object, or None if this is the root BTree.
|
||||
# is_mapping is True for a BTree and False for a TreeSet.
|
||||
# keys is a list of the BTree's internal keys.
|
||||
# kids is a list of the BTree's children.
|
||||
# If the BTree is an empty root node, keys == kids == [].
|
||||
# Else len(kids) == len(keys) + 1.
|
||||
# lo and hi are slice bounds on the values the elements of keys *should*
|
||||
# lie in (lo inclusive, hi exclusive). lo is None if there is no lower
|
||||
# bound known, and hi is None if no upper bound is known.
|
||||
|
||||
def visit_btree(self, obj, path, parent, is_mapping,
|
||||
keys, kids, lo, hi):
|
||||
raise NotImplementedError
|
||||
|
||||
# obj is the bucket (Bucket or Set).
|
||||
# path is a list of indices, from the root. For example, if a bucket
|
||||
# node is child[5] of child[3] of the root BTree, [3, 5].
|
||||
# parent is the parent BTree object.
|
||||
# is_mapping is True for a Bucket and False for a Set.
|
||||
# keys is a list of the bucket's keys.
|
||||
# values is a list of the bucket's values.
|
||||
# If is_mapping is false, values == []. Else len(keys) == len(values).
|
||||
# lo and hi are slice bounds on the values the elements of keys *should*
|
||||
# lie in (lo inclusive, hi exclusive). lo is None if there is no lower
|
||||
# bound known, and hi is None if no upper bound is known.
|
||||
|
||||
def visit_bucket(self, obj, path, parent, is_mapping,
|
||||
keys, values, lo, hi):
|
||||
raise NotImplementedError
|
||||
|
||||
def walk(self):
|
||||
obj = self.obj
|
||||
path = []
|
||||
stack = [(obj, path, None, None, None)]
|
||||
while stack:
|
||||
obj, path, parent, lo, hi = stack.pop()
|
||||
kind, is_mapping = classify(obj)
|
||||
if kind is TYPE_BTREE:
|
||||
bkind, keys, kids = crack_btree(obj, is_mapping)
|
||||
if bkind is BTREE_NORMAL:
|
||||
# push the kids, in reverse order (so they're popped off
|
||||
# the stack in forward order)
|
||||
n = len(kids)
|
||||
for i in range(len(kids)-1, -1, -1):
|
||||
newlo, newhi = lo, hi
|
||||
if i < n-1:
|
||||
newhi = keys[i]
|
||||
if i > 0:
|
||||
newlo = keys[i-1]
|
||||
stack.append((kids[i],
|
||||
path + [i],
|
||||
obj,
|
||||
newlo,
|
||||
newhi))
|
||||
|
||||
elif bkind is BTREE_EMPTY:
|
||||
pass
|
||||
else:
|
||||
assert bkind is BTREE_ONE
|
||||
# Yuck. There isn't a bucket object to pass on, as
|
||||
# the bucket state is embedded directly in the BTree
|
||||
# state. Synthesize a bucket.
|
||||
assert kids is None # and "keys" is really the bucket
|
||||
# state
|
||||
bucket = _btree2bucket[type(obj)]()
|
||||
bucket.__setstate__(keys)
|
||||
stack.append((bucket,
|
||||
path + [0],
|
||||
obj,
|
||||
lo,
|
||||
hi))
|
||||
keys = []
|
||||
kids = [bucket]
|
||||
|
||||
self.visit_btree(obj,
|
||||
path,
|
||||
parent,
|
||||
is_mapping,
|
||||
keys,
|
||||
kids,
|
||||
lo,
|
||||
hi)
|
||||
else:
|
||||
assert kind is TYPE_BUCKET
|
||||
keys, values = crack_bucket(obj, is_mapping)
|
||||
self.visit_bucket(obj,
|
||||
path,
|
||||
parent,
|
||||
is_mapping,
|
||||
keys,
|
||||
values,
|
||||
lo,
|
||||
hi)
|
||||
|
||||
|
||||
class Checker(Walker):
|
||||
def __init__(self, obj):
|
||||
Walker.__init__(self, obj)
|
||||
self.errors = []
|
||||
|
||||
def check(self):
|
||||
self.walk()
|
||||
if self.errors:
|
||||
s = "Errors found in %s:" % type_and_adr(self.obj)
|
||||
self.errors.insert(0, s)
|
||||
s = "\n".join(self.errors)
|
||||
raise AssertionError(s)
|
||||
|
||||
def visit_btree(self, obj, path, parent, is_mapping,
|
||||
keys, kids, lo, hi):
|
||||
self.check_sorted(obj, path, keys, lo, hi)
|
||||
|
||||
def visit_bucket(self, obj, path, parent, is_mapping,
|
||||
keys, values, lo, hi):
|
||||
self.check_sorted(obj, path, keys, lo, hi)
|
||||
|
||||
def check_sorted(self, obj, path, keys, lo, hi):
|
||||
i, n = 0, len(keys)
|
||||
for x in keys:
|
||||
# lo or hi are ommitted by supplying None. Thus the not
|
||||
# None checkes below.
|
||||
if lo is not None and not compare(lo, x) <= 0:
|
||||
s = "key %r < lower bound %r at index %d" % (x, lo, i)
|
||||
self.complain(s, obj, path)
|
||||
if hi is not None and not compare(x, hi) < 0:
|
||||
s = "key %r >= upper bound %r at index %d" % (x, hi, i)
|
||||
self.complain(s, obj, path)
|
||||
if i < n-1 and not compare(x, keys[i+1]) < 0:
|
||||
s = "key %r at index %d >= key %r at index %d" % (
|
||||
x, i, keys[i+1], i+1)
|
||||
self.complain(s, obj, path)
|
||||
i += 1
|
||||
|
||||
def complain(self, msg, obj, path):
|
||||
s = "%s, in %s, path from root %s" % (
|
||||
msg,
|
||||
type_and_adr(obj),
|
||||
".".join(map(str, path)))
|
||||
self.errors.append(s)
|
||||
|
||||
class Printer(Walker): #pragma NO COVER
|
||||
def __init__(self, obj):
|
||||
Walker.__init__(self, obj)
|
||||
|
||||
def display(self):
|
||||
self.walk()
|
||||
|
||||
def visit_btree(self, obj, path, parent, is_mapping,
|
||||
keys, kids, lo, hi):
|
||||
indent = " " * len(path)
|
||||
print("%s%s %s with %d children" % (
|
||||
indent,
|
||||
".".join(map(str, path)),
|
||||
type_and_adr(obj),
|
||||
len(kids)))
|
||||
indent += " "
|
||||
n = len(keys)
|
||||
for i in range(n):
|
||||
print("%skey %d: %r" % (indent, i, keys[i]))
|
||||
|
||||
def visit_bucket(self, obj, path, parent, is_mapping,
|
||||
keys, values, lo, hi):
|
||||
indent = " " * len(path)
|
||||
print("%s%s %s with %d keys" % (
|
||||
indent,
|
||||
".".join(map(str, path)),
|
||||
type_and_adr(obj),
|
||||
len(keys)))
|
||||
indent += " "
|
||||
n = len(keys)
|
||||
for i in range(n):
|
||||
print("%skey %d: %r" % (indent, i, keys[i]),)
|
||||
if is_mapping:
|
||||
print("value %r" % (values[i],))
|
||||
|
||||
def check(btree):
|
||||
"""Check internal value-based invariants in a BTree or TreeSet.
|
||||
|
||||
The btree._check() method checks internal C-level pointer consistency.
|
||||
The check() function here checks value-based invariants: whether the
|
||||
keys in leaf bucket and internal nodes are in strictly increasing order,
|
||||
and whether they all lie in their expected range. The latter is a subtle
|
||||
invariant that can't be checked locally -- it requires propagating
|
||||
range info down from the root of the tree, and modifying it at each
|
||||
level for each child.
|
||||
|
||||
Raises AssertionError if anything is wrong, with a string detail
|
||||
explaining the problems. The entire tree is checked before
|
||||
AssertionError is raised, and the string detail may be large (depending
|
||||
on how much went wrong).
|
||||
"""
|
||||
|
||||
Checker(btree).check()
|
||||
|
||||
def display(btree): #pragma NO COVER
|
||||
"Display the internal structure of a BTree, Bucket, TreeSet or Set."
|
||||
Printer(btree).display()
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
#define VALUEMACROS_H "$Id$\n"
|
||||
|
||||
#define VALUE_TYPE float
|
||||
#undef VALUE_TYPE_IS_PYOBJECT
|
||||
#define TEST_VALUE(K, T) (((K) < (T)) ? -1 : (((K) > (T)) ? 1: 0))
|
||||
#define VALUE_SAME(VALUE, TARGET) ( (VALUE) == (TARGET) )
|
||||
#define DECLARE_VALUE(NAME) VALUE_TYPE NAME
|
||||
#define VALUE_PARSE "f"
|
||||
#define DECREF_VALUE(k)
|
||||
#define INCREF_VALUE(k)
|
||||
#define COPY_VALUE(V, E) (V=(E))
|
||||
#define COPY_VALUE_TO_OBJECT(O, K) O=PyFloat_FromDouble(K)
|
||||
#define COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS) \
|
||||
if (PyFloat_Check(ARG)) TARGET = (float)PyFloat_AsDouble(ARG); \
|
||||
else if (INT_CHECK(ARG)) TARGET = (float)INT_AS_LONG(ARG); \
|
||||
else { \
|
||||
PyErr_SetString(PyExc_TypeError, "expected float or int value"); \
|
||||
(STATUS)=0; (TARGET)=0; }
|
||||
|
||||
#define NORMALIZE_VALUE(V, MIN) ((MIN) > 0) ? ((V)/=(MIN)) : 0
|
||||
|
||||
#define MERGE_DEFAULT 1.0f
|
||||
#define MERGE(O1, w1, O2, w2) ((O1)*(w1)+(O2)*(w2))
|
||||
#define MERGE_WEIGHT(O, w) ((O)*(w))
|
||||
@@ -0,0 +1,110 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# fsBTrees are data structures used for ZODB FileStorage. They are not
|
||||
# expected to be "public" excpect to FileStorage.
|
||||
# Each item in an fsBTree maps a two-byte key to a six-byte value.
|
||||
|
||||
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
|
||||
'fsBucket', 'fsSet', 'fsBTree', 'fsTreeSet',
|
||||
'union', 'intersection', 'difference',
|
||||
)
|
||||
|
||||
|
||||
from zope.interface import moduleProvides
|
||||
|
||||
from .Interfaces import IIntegerObjectBTreeModule
|
||||
from ._base import Bucket
|
||||
from ._base import Set
|
||||
from ._base import Tree as BTree
|
||||
from ._base import TreeSet
|
||||
from ._base import difference as _difference
|
||||
from ._base import intersection as _intersection
|
||||
from ._base import set_operation as _set_operation
|
||||
from ._base import to_bytes as _to_bytes
|
||||
from ._base import union as _union
|
||||
from ._base import _fix_pickle
|
||||
from ._compat import import_c_extension
|
||||
|
||||
|
||||
_BUCKET_SIZE = 500
|
||||
_TREE_SIZE = 500
|
||||
using64bits = False
|
||||
_to_key = _to_bytes(2)
|
||||
_to_value = _to_bytes(6)
|
||||
|
||||
|
||||
class fsBucketPy(Bucket):
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
|
||||
def toString(self):
|
||||
return b''.join(self._keys) + b''.join(self._values)
|
||||
|
||||
def fromString(self, v):
|
||||
length = len(v)
|
||||
if length % 8 != 0:
|
||||
raise ValueError()
|
||||
count = length // 8
|
||||
keys, values = v[:count*2], v[count*2:]
|
||||
self.clear()
|
||||
while keys and values:
|
||||
key, keys = keys[:2], keys[2:]
|
||||
value, values = values[:6], values[6:]
|
||||
self._keys.append(key)
|
||||
self._values.append(value)
|
||||
return self
|
||||
|
||||
|
||||
class fsSetPy(Set):
|
||||
_to_key = _to_key
|
||||
|
||||
|
||||
class fsBTreePy(BTree):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
_to_value = _to_value
|
||||
|
||||
|
||||
class fsTreeSetPy(TreeSet):
|
||||
max_leaf_size = _BUCKET_SIZE
|
||||
max_internal_size = _TREE_SIZE
|
||||
_to_key = _to_key
|
||||
|
||||
|
||||
# Can't declare forward refs, so fix up afterwards:
|
||||
|
||||
fsBucketPy._mapping_type = fsBucketPy._bucket_type = fsBucketPy
|
||||
fsBucketPy._set_type = fsSetPy
|
||||
|
||||
fsSetPy._mapping_type = fsBucketPy
|
||||
fsSetPy._set_type = fsSetPy._bucket_type = fsSetPy
|
||||
|
||||
fsBTreePy._mapping_type = fsBTreePy._bucket_type = fsBucketPy
|
||||
fsBTreePy._set_type = fsSetPy
|
||||
|
||||
fsTreeSetPy._mapping_type = fsBucketPy
|
||||
fsTreeSetPy._set_type = fsTreeSetPy._bucket_type = fsSetPy
|
||||
|
||||
|
||||
differencePy = _set_operation(_difference, fsSetPy)
|
||||
unionPy = _set_operation(_union, fsSetPy)
|
||||
intersectionPy = _set_operation(_intersection, fsSetPy)
|
||||
|
||||
import_c_extension(globals())
|
||||
|
||||
_fix_pickle(globals(), __name__)
|
||||
|
||||
moduleProvides(IIntegerObjectBTreeModule)
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
#define KEYMACROS_H "$Id$\n"
|
||||
|
||||
#ifdef ZODB_64BIT_INTS
|
||||
/* PY_LONG_LONG as key */
|
||||
#define NEED_LONG_LONG_SUPPORT
|
||||
#define NEED_LONG_LONG_KEYS
|
||||
#define KEY_TYPE PY_LONG_LONG
|
||||
#define KEY_CHECK longlong_check
|
||||
#define COPY_KEY_TO_OBJECT(O, K) O=longlong_as_object(K)
|
||||
#define COPY_KEY_FROM_ARG(TARGET, ARG, STATUS) \
|
||||
if (!longlong_convert((ARG), &TARGET)) \
|
||||
{ \
|
||||
(STATUS)=0; (TARGET)=0; \
|
||||
}
|
||||
#else
|
||||
/* C int as key */
|
||||
#define KEY_TYPE int
|
||||
#define KEY_CHECK INT_CHECK
|
||||
#define COPY_KEY_TO_OBJECT(O, K) O=INT_FROM_LONG(K)
|
||||
#define COPY_KEY_FROM_ARG(TARGET, ARG, STATUS) \
|
||||
if (INT_CHECK(ARG)) { \
|
||||
long vcopy = INT_AS_LONG(ARG); \
|
||||
if (PyErr_Occurred()) { (STATUS)=0; (TARGET)=0; } \
|
||||
else if ((int)vcopy != vcopy) { \
|
||||
PyErr_SetString(PyExc_TypeError, "integer out of range"); \
|
||||
(STATUS)=0; (TARGET)=0; \
|
||||
} \
|
||||
else TARGET = vcopy; \
|
||||
} else { \
|
||||
PyErr_SetString(PyExc_TypeError, "expected integer key"); \
|
||||
(STATUS)=0; (TARGET)=0; }
|
||||
#endif
|
||||
|
||||
#undef KEY_TYPE_IS_PYOBJECT
|
||||
#define TEST_KEY_SET_OR(V, K, T) if ( ( (V) = (((K) < (T)) ? -1 : (((K) > (T)) ? 1: 0)) ) , 0 )
|
||||
#define DECREF_KEY(KEY)
|
||||
#define INCREF_KEY(k)
|
||||
#define COPY_KEY(KEY, E) (KEY=(E))
|
||||
#define MULTI_INT_UNION 1
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
#define VALUEMACROS_H "$Id$\n"
|
||||
|
||||
#ifdef ZODB_64BIT_INTS
|
||||
#define NEED_LONG_LONG_SUPPORT
|
||||
#define VALUE_TYPE PY_LONG_LONG
|
||||
#define VALUE_PARSE "L"
|
||||
#define COPY_VALUE_TO_OBJECT(O, K) O=longlong_as_object(K)
|
||||
#define COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS) \
|
||||
if (!longlong_convert((ARG), &TARGET)) \
|
||||
{ \
|
||||
(STATUS)=0; (TARGET)=0; \
|
||||
}
|
||||
#else
|
||||
#define VALUE_TYPE int
|
||||
#define VALUE_PARSE "i"
|
||||
#define COPY_VALUE_TO_OBJECT(O, K) O=INT_FROM_LONG(K)
|
||||
|
||||
#define COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS) \
|
||||
if (INT_CHECK(ARG)) { \
|
||||
long vcopy = INT_AS_LONG(ARG); \
|
||||
if (PyErr_Occurred()) { (STATUS)=0; (TARGET)=0; } \
|
||||
else if ((int)vcopy != vcopy) { \
|
||||
PyErr_SetString(PyExc_TypeError, "integer out of range"); \
|
||||
(STATUS)=0; (TARGET)=0; \
|
||||
} \
|
||||
else TARGET = vcopy; \
|
||||
} else { \
|
||||
PyErr_SetString(PyExc_TypeError, "expected integer key"); \
|
||||
(STATUS)=0; (TARGET)=0; }
|
||||
|
||||
#endif
|
||||
|
||||
#undef VALUE_TYPE_IS_PYOBJECT
|
||||
#define TEST_VALUE(K, T) (((K) < (T)) ? -1 : (((K) > (T)) ? 1: 0))
|
||||
#define VALUE_SAME(VALUE, TARGET) ( (VALUE) == (TARGET) )
|
||||
#define DECLARE_VALUE(NAME) VALUE_TYPE NAME
|
||||
#define DECREF_VALUE(k)
|
||||
#define INCREF_VALUE(k)
|
||||
#define COPY_VALUE(V, E) (V=(E))
|
||||
|
||||
#define NORMALIZE_VALUE(V, MIN) ((MIN) > 0) ? ((V)/=(MIN)) : 0
|
||||
|
||||
#define MERGE_DEFAULT 1
|
||||
#define MERGE(O1, w1, O2, w2) ((O1)*(w1)+(O2)*(w2))
|
||||
#define MERGE_WEIGHT(O, w) ((O)*(w))
|
||||
@@ -0,0 +1,44 @@
|
||||
#define KEYMACROS_H "$Id$\n"
|
||||
#define KEY_TYPE PyObject *
|
||||
#define KEY_TYPE_IS_PYOBJECT
|
||||
|
||||
#include "Python.h"
|
||||
#include "_compat.h"
|
||||
|
||||
static PyObject *object_; /* initialized in BTreeModuleTemplate init */
|
||||
|
||||
static int
|
||||
check_argument_cmp(PyObject *arg)
|
||||
{
|
||||
/* printf("check cmp %p %p %p %p\n", */
|
||||
/* arg->ob_type->tp_richcompare, */
|
||||
/* ((PyTypeObject *)object_)->ob_type->tp_richcompare, */
|
||||
/* arg->ob_type->tp_compare, */
|
||||
/* ((PyTypeObject *)object_)->ob_type->tp_compare); */
|
||||
if (arg == Py_None) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef PY3K
|
||||
if (Py_TYPE(arg)->tp_richcompare == Py_TYPE(object_)->tp_richcompare)
|
||||
#else
|
||||
if (Py_TYPE(arg)->tp_richcompare == NULL
|
||||
&& Py_TYPE(arg)->tp_compare == Py_TYPE(object_)->tp_compare)
|
||||
#endif
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "Object has default comparison");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define TEST_KEY_SET_OR(V, KEY, TARGET) \
|
||||
if ( ( (V) = COMPARE((KEY),(TARGET)) ), PyErr_Occurred() )
|
||||
#define INCREF_KEY(k) Py_INCREF(k)
|
||||
#define DECREF_KEY(KEY) Py_DECREF(KEY)
|
||||
#define COPY_KEY(KEY, E) KEY=(E)
|
||||
#define COPY_KEY_TO_OBJECT(O, K) O=(K); Py_INCREF(O)
|
||||
#define COPY_KEY_FROM_ARG(TARGET, ARG, S) \
|
||||
TARGET=(ARG); \
|
||||
(S) = 1;
|
||||
#define KEY_CHECK_ON_SET check_argument_cmp
|
||||
@@ -0,0 +1,12 @@
|
||||
#define VALUEMACROS_H "$Id$\n"
|
||||
|
||||
#define VALUE_TYPE PyObject *
|
||||
#define VALUE_TYPE_IS_PYOBJECT
|
||||
#define TEST_VALUE(VALUE, TARGET) (COMPARE((VALUE),(TARGET)))
|
||||
#define DECLARE_VALUE(NAME) VALUE_TYPE NAME
|
||||
#define INCREF_VALUE(k) Py_INCREF(k)
|
||||
#define DECREF_VALUE(k) Py_DECREF(k)
|
||||
#define COPY_VALUE(k,e) k=(e)
|
||||
#define COPY_VALUE_TO_OBJECT(O, K) O=(K); Py_INCREF(O)
|
||||
#define COPY_VALUE_FROM_ARG(TARGET, ARG, S) TARGET=(ARG)
|
||||
#define NORMALIZE_VALUE(V, MIN) Py_INCREF(V)
|
||||
@@ -0,0 +1,542 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 2002 Zope Foundation and Contributors.
|
||||
All Rights Reserved.
|
||||
|
||||
This software is subject to the provisions of the Zope Public License,
|
||||
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
/* Revision information: $Id$ */
|
||||
|
||||
/* The only routine here intended to be used outside the file is
|
||||
size_t sort_int_nodups(int *p, size_t n)
|
||||
|
||||
Sort the array of n ints pointed at by p, in place, and also remove
|
||||
duplicates. Return the number of unique elements remaining, which occupy
|
||||
a contiguous and monotonically increasing slice of the array starting at p.
|
||||
|
||||
Example: If the input array is [3, 1, 2, 3, 1, 5, 2], sort_int_nodups
|
||||
returns 4, and the first 4 elements of the array are changed to
|
||||
[1, 2, 3, 5]. The content of the remaining array positions is not defined.
|
||||
|
||||
Notes:
|
||||
|
||||
+ This is specific to n-byte signed ints, with endianness natural to the
|
||||
platform. `n` is determined based on ZODB_64BIT_INTS.
|
||||
|
||||
+ 4*n bytes of available heap memory are required for best speed
|
||||
(8*n when ZODB_64BIT_INTS is defined).
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <memory.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* The type of array elements to be sorted. Most of the routines don't
|
||||
care about the type, and will work fine for any scalar C type (provided
|
||||
they're recompiled with element_type appropriately redefined). However,
|
||||
the radix sort has to know everything about the type's internal
|
||||
representation.
|
||||
*/
|
||||
typedef KEY_TYPE element_type;
|
||||
|
||||
/* The radixsort is faster than the quicksort for large arrays, but radixsort
|
||||
has high fixed overhead, making it a poor choice for small arrays. The
|
||||
crossover point isn't critical, and is sensitive to things like compiler
|
||||
and machine cache structure, so don't worry much about this.
|
||||
*/
|
||||
#define QUICKSORT_BEATS_RADIXSORT 800U
|
||||
|
||||
/* In turn, the quicksort backs off to an insertion sort for very small
|
||||
slices. MAX_INSERTION is the largest slice quicksort leaves entirely to
|
||||
insertion. Because this version of quicksort uses a median-of-3 rule for
|
||||
selecting a pivot, MAX_INSERTION must be at least 2 (so that quicksort
|
||||
has at least 3 values to look at in a slice). Again, the exact value here
|
||||
isn't critical.
|
||||
*/
|
||||
#define MAX_INSERTION 25U
|
||||
|
||||
#if MAX_INSERTION < 2U
|
||||
# error "MAX_INSERTION must be >= 2"
|
||||
#endif
|
||||
|
||||
/* LSB-first radix sort of the n elements in 'in'.
|
||||
'work' is work storage at least as large as 'in'. Depending on how many
|
||||
swaps are done internally, the final result may come back in 'in' or 'work';
|
||||
and that pointer is returned.
|
||||
|
||||
radixsort_int is specific to signed n-byte ints, with natural machine
|
||||
endianness. `n` is determined based on ZODB_64BIT_INTS.
|
||||
*/
|
||||
static element_type*
|
||||
radixsort_int(element_type *in, element_type *work, size_t n)
|
||||
{
|
||||
/* count[i][j] is the number of input elements that have byte value j
|
||||
in byte position i, where byte position 0 is the LSB. Note that
|
||||
holding i fixed, the sum of count[i][j] over all j in range(256)
|
||||
is n.
|
||||
*/
|
||||
#ifdef ZODB_64BIT_INTS
|
||||
size_t count[8][256];
|
||||
#else
|
||||
size_t count[4][256];
|
||||
#endif
|
||||
size_t i;
|
||||
int offset, offsetinc;
|
||||
|
||||
/* Which byte position are we working on now? 0=LSB, 1, 2, ... */
|
||||
size_t bytenum;
|
||||
|
||||
#ifdef ZODB_64BIT_INTS
|
||||
assert(sizeof(element_type) == 8);
|
||||
#else
|
||||
assert(sizeof(element_type) == 4);
|
||||
#endif
|
||||
assert(in);
|
||||
assert(work);
|
||||
|
||||
/* Compute all of count in one pass. */
|
||||
memset(count, 0, sizeof(count));
|
||||
for (i = 0; i < n; ++i) {
|
||||
element_type const x = in[i];
|
||||
++count[0][(x ) & 0xff];
|
||||
++count[1][(x >> 8) & 0xff];
|
||||
++count[2][(x >> 16) & 0xff];
|
||||
++count[3][(x >> 24) & 0xff];
|
||||
#ifdef ZODB_64BIT_INTS
|
||||
++count[4][(x >> 32) & 0xff];
|
||||
++count[5][(x >> 40) & 0xff];
|
||||
++count[6][(x >> 48) & 0xff];
|
||||
++count[7][(x >> 56) & 0xff];
|
||||
#endif
|
||||
}
|
||||
|
||||
/* For p an element_type* cast to char*, offset is how much farther we
|
||||
have to go to get to the LSB of the element; this is 0 for little-
|
||||
endian boxes and sizeof(element_type)-1 for big-endian.
|
||||
offsetinc is 1 or -1, respectively, telling us which direction to go
|
||||
from p+offset to get to the element's more-significant bytes.
|
||||
*/
|
||||
{
|
||||
element_type one = 1;
|
||||
if (*(char*)&one) {
|
||||
/* Little endian. */
|
||||
offset = 0;
|
||||
offsetinc = 1;
|
||||
}
|
||||
else {
|
||||
/* Big endian. */
|
||||
offset = sizeof(element_type) - 1;
|
||||
offsetinc = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* The radix sort. */
|
||||
for (bytenum = 0;
|
||||
bytenum < sizeof(element_type);
|
||||
++bytenum, offset += offsetinc) {
|
||||
|
||||
/* Do a stable distribution sort on byte position bytenum,
|
||||
from in to work. index[i] tells us the work index at which
|
||||
to store the next in element with byte value i. pinbyte
|
||||
points to the correct byte in the input array.
|
||||
*/
|
||||
size_t index[256];
|
||||
unsigned char* pinbyte;
|
||||
size_t total = 0;
|
||||
size_t *pcount = count[bytenum];
|
||||
|
||||
/* Compute the correct output starting index for each possible
|
||||
byte value.
|
||||
*/
|
||||
if (bytenum < sizeof(element_type) - 1) {
|
||||
for (i = 0; i < 256; ++i) {
|
||||
const size_t icount = pcount[i];
|
||||
index[i] = total;
|
||||
total += icount;
|
||||
if (icount == n)
|
||||
break;
|
||||
}
|
||||
if (i < 256) {
|
||||
/* All bytes in the current position have value
|
||||
i, so there's nothing to do on this pass.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* The MSB of signed ints needs to be distributed
|
||||
differently than the other bytes, in order
|
||||
0x80, 0x81, ... 0xff, 0x00, 0x01, ... 0x7f
|
||||
*/
|
||||
for (i = 128; i < 256; ++i) {
|
||||
const size_t icount = pcount[i];
|
||||
index[i] = total;
|
||||
total += icount;
|
||||
if (icount == n)
|
||||
break;
|
||||
}
|
||||
if (i < 256)
|
||||
continue;
|
||||
for (i = 0; i < 128; ++i) {
|
||||
const size_t icount = pcount[i];
|
||||
index[i] = total;
|
||||
total += icount;
|
||||
if (icount == n)
|
||||
break;
|
||||
}
|
||||
if (i < 128)
|
||||
continue;
|
||||
}
|
||||
assert(total == n);
|
||||
|
||||
/* Distribute the elements according to byte value. Note that
|
||||
this is where most of the time is spent.
|
||||
Note: The loop is unrolled 4x by hand, for speed. This
|
||||
may be a pessimization someday, but was a significant win
|
||||
on my MSVC 6.0 timing tests.
|
||||
*/
|
||||
pinbyte = (unsigned char *)in + offset;
|
||||
i = 0;
|
||||
/* Reduce number of elements to copy to a multiple of 4. */
|
||||
while ((n - i) & 0x3) {
|
||||
unsigned char byte = *pinbyte;
|
||||
work[index[byte]++] = in[i];
|
||||
++i;
|
||||
pinbyte += sizeof(element_type);
|
||||
}
|
||||
for (; i < n; i += 4, pinbyte += 4 * sizeof(element_type)) {
|
||||
unsigned char byte1 = *(pinbyte );
|
||||
unsigned char byte2 = *(pinbyte + sizeof(element_type));
|
||||
unsigned char byte3 = *(pinbyte + 2 * sizeof(element_type));
|
||||
unsigned char byte4 = *(pinbyte + 3 * sizeof(element_type));
|
||||
|
||||
element_type in1 = in[i ];
|
||||
element_type in2 = in[i+1];
|
||||
element_type in3 = in[i+2];
|
||||
element_type in4 = in[i+3];
|
||||
|
||||
work[index[byte1]++] = in1;
|
||||
work[index[byte2]++] = in2;
|
||||
work[index[byte3]++] = in3;
|
||||
work[index[byte4]++] = in4;
|
||||
}
|
||||
/* Swap in and work (just a pointer swap). */
|
||||
{
|
||||
element_type *temp = in;
|
||||
in = work;
|
||||
work = temp;
|
||||
}
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
/* Remove duplicates from sorted array in, storing exactly one of each distinct
|
||||
element value into sorted array out. It's OK (and expected!) for in == out,
|
||||
but otherwise the n elements beginning at in must not overlap with the n
|
||||
beginning at out.
|
||||
Return the number of elements in out.
|
||||
*/
|
||||
static size_t
|
||||
uniq(element_type *out, element_type *in, size_t n)
|
||||
{
|
||||
size_t i;
|
||||
element_type lastelt;
|
||||
element_type *pout;
|
||||
|
||||
assert(out);
|
||||
assert(in);
|
||||
if (n == 0)
|
||||
return 0;
|
||||
|
||||
/* i <- first index in 'in' that contains a duplicate.
|
||||
in[0], in[1], ... in[i-1] are unique, but in[i-1] == in[i].
|
||||
Set i to n if everything is unique.
|
||||
*/
|
||||
for (i = 1; i < n; ++i) {
|
||||
if (in[i-1] == in[i])
|
||||
break;
|
||||
}
|
||||
|
||||
/* in[:i] is unique; copy to out[:i] if needed. */
|
||||
assert(i > 0);
|
||||
if (in != out)
|
||||
memcpy(out, in, i * sizeof(element_type));
|
||||
|
||||
pout = out + i;
|
||||
lastelt = in[i-1]; /* safe even when i == n */
|
||||
for (++i; i < n; ++i) {
|
||||
element_type elt = in[i];
|
||||
if (elt != lastelt)
|
||||
*pout++ = lastelt = elt;
|
||||
}
|
||||
return pout - out;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* insertionsort is no longer referenced directly, but I'd like to keep
|
||||
* the code here just in case.
|
||||
*/
|
||||
|
||||
/* Straight insertion sort of the n elements starting at 'in'. */
|
||||
static void
|
||||
insertionsort(element_type *in, size_t n)
|
||||
{
|
||||
element_type *p, *q;
|
||||
element_type minimum; /* smallest seen so far */
|
||||
element_type *plimit = in + n;
|
||||
|
||||
assert(in);
|
||||
if (n < 2)
|
||||
return;
|
||||
|
||||
minimum = *in;
|
||||
for (p = in+1; p < plimit; ++p) {
|
||||
/* *in <= *(in+1) <= ... <= *(p-1). Slide *p into place. */
|
||||
element_type thiselt = *p;
|
||||
if (thiselt < minimum) {
|
||||
/* This is a new minimum. This saves p-in compares
|
||||
when it happens, but should happen so rarely that
|
||||
it's not worth checking for its own sake: the
|
||||
point is that the far more popular 'else' branch can
|
||||
exploit that thiselt is *not* the smallest so far.
|
||||
*/
|
||||
memmove(in+1, in, (p - in) * sizeof(*in));
|
||||
*in = minimum = thiselt;
|
||||
}
|
||||
else {
|
||||
/* thiselt >= minimum, so the loop will find a q
|
||||
with *q <= thiselt. This saves testing q >= in
|
||||
on each trip. It's such a simple loop that saving
|
||||
a per-trip test is a major speed win.
|
||||
*/
|
||||
for (q = p-1; *q > thiselt; --q)
|
||||
*(q+1) = *q;
|
||||
*(q+1) = thiselt;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The maximum number of elements in the pending-work stack quicksort
|
||||
maintains. The maximum stack depth is approximately log2(n), so
|
||||
arrays of size up to approximately MAX_INSERTION * 2**STACKSIZE can be
|
||||
sorted. The memory burden for the stack is small, so better safe than
|
||||
sorry.
|
||||
*/
|
||||
#define STACKSIZE 60
|
||||
|
||||
/* A _stacknode remembers a contiguous slice of an array that needs to sorted.
|
||||
lo must be <= hi, and, unlike Python array slices, this includes both ends.
|
||||
*/
|
||||
struct _stacknode {
|
||||
element_type *lo;
|
||||
element_type *hi;
|
||||
};
|
||||
|
||||
static void
|
||||
quicksort(element_type *plo, size_t n)
|
||||
{
|
||||
element_type *phi;
|
||||
|
||||
/* Swap two array elements. */
|
||||
element_type _temp;
|
||||
#define SWAP(P, Q) (_temp = *(P), *(P) = *(Q), *(Q) = _temp)
|
||||
|
||||
/* Stack of pending array slices to be sorted. */
|
||||
struct _stacknode stack[STACKSIZE];
|
||||
struct _stacknode *stackfree = stack; /* available stack slot */
|
||||
|
||||
/* Push an array slice on the pending-work stack. */
|
||||
#define PUSH(PLO, PHI) \
|
||||
do { \
|
||||
assert(stackfree - stack < STACKSIZE); \
|
||||
assert((PLO) <= (PHI)); \
|
||||
stackfree->lo = (PLO); \
|
||||
stackfree->hi = (PHI); \
|
||||
++stackfree; \
|
||||
} while(0)
|
||||
|
||||
assert(plo);
|
||||
phi = plo + n - 1;
|
||||
|
||||
for (;;) {
|
||||
element_type pivot;
|
||||
element_type *pi, *pj;
|
||||
|
||||
assert(plo <= phi);
|
||||
n = phi - plo + 1;
|
||||
if (n <= MAX_INSERTION) {
|
||||
/* Do a small insertion sort. Contra Knuth, we do
|
||||
this now instead of waiting until the end, because
|
||||
this little slice is likely still in cache now.
|
||||
*/
|
||||
element_type *p, *q;
|
||||
element_type minimum = *plo;
|
||||
|
||||
for (p = plo+1; p <= phi; ++p) {
|
||||
/* *plo <= *(plo+1) <= ... <= *(p-1).
|
||||
Slide *p into place. */
|
||||
element_type thiselt = *p;
|
||||
if (thiselt < minimum) {
|
||||
/* New minimum. */
|
||||
memmove(plo+1,
|
||||
plo,
|
||||
(p - plo) * sizeof(*p));
|
||||
*plo = minimum = thiselt;
|
||||
}
|
||||
else {
|
||||
/* thiselt >= minimum, so the loop will
|
||||
find a q with *q <= thiselt.
|
||||
*/
|
||||
for (q = p-1; *q > thiselt; --q)
|
||||
*(q+1) = *q;
|
||||
*(q+1) = thiselt;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pop another slice off the stack. */
|
||||
if (stack == stackfree)
|
||||
break; /* no more slices -- we're done */
|
||||
--stackfree;
|
||||
plo = stackfree->lo;
|
||||
phi = stackfree->hi;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Parition the slice.
|
||||
For pivot, take the median of the leftmost, rightmost, and
|
||||
middle elements. First sort those three; then the median
|
||||
is the middle one. For technical reasons, the middle
|
||||
element is swapped to plo+1 first (see Knuth Vol 3 Ed 2
|
||||
section 5.2.2 exercise 55 -- reverse-sorted arrays can
|
||||
take quadratic time otherwise!).
|
||||
*/
|
||||
{
|
||||
element_type *plop1 = plo + 1;
|
||||
element_type *pmid = plo + (n >> 1);
|
||||
|
||||
assert(plo < pmid && pmid < phi);
|
||||
SWAP(plop1, pmid);
|
||||
|
||||
/* Sort plo, plop1, phi. */
|
||||
/* Smaller of rightmost two -> middle. */
|
||||
if (*plop1 > *phi)
|
||||
SWAP(plop1, phi);
|
||||
/* Smallest of all -> left; if plo is already the
|
||||
smallest, the sort is complete.
|
||||
*/
|
||||
if (*plo > *plop1) {
|
||||
SWAP(plo, plop1);
|
||||
/* Largest of all -> right. */
|
||||
if (*plop1 > *phi)
|
||||
SWAP(plop1, phi);
|
||||
}
|
||||
pivot = *plop1;
|
||||
pi = plop1;
|
||||
}
|
||||
assert(*plo <= pivot);
|
||||
assert(*pi == pivot);
|
||||
assert(*phi >= pivot);
|
||||
pj = phi;
|
||||
|
||||
/* Partition wrt pivot. This is the time-critical part, and
|
||||
nearly every decision in the routine aims at making this
|
||||
loop as fast as possible -- even small points like
|
||||
arranging that all loop tests can be done correctly at the
|
||||
bottoms of loops instead of the tops, and that pointers can
|
||||
be derefenced directly as-is (without fiddly +1 or -1).
|
||||
The aim is to make the C here so simple that a compiler
|
||||
has a good shot at doing as well as hand-crafted assembler.
|
||||
*/
|
||||
for (;;) {
|
||||
/* Invariants:
|
||||
1. pi < pj.
|
||||
2. All elements at plo, plo+1 .. pi are <= pivot.
|
||||
3. All elements at pj, pj+1 .. phi are >= pivot.
|
||||
4. There is an element >= pivot to the right of pi.
|
||||
5. There is an element <= pivot to the left of pj.
|
||||
|
||||
Note that #4 and #5 save us from needing to check
|
||||
that the pointers stay in bounds.
|
||||
*/
|
||||
assert(pi < pj);
|
||||
|
||||
do { ++pi; } while (*pi < pivot);
|
||||
assert(pi <= pj);
|
||||
|
||||
do { --pj; } while (*pj > pivot);
|
||||
assert(pj >= pi - 1);
|
||||
|
||||
if (pi < pj)
|
||||
SWAP(pi, pj);
|
||||
else
|
||||
break;
|
||||
}
|
||||
assert(plo+1 < pi && pi <= phi);
|
||||
assert(plo < pj && pj < phi);
|
||||
assert(*pi >= pivot);
|
||||
assert( (pi == pj && *pj == pivot) ||
|
||||
(pj + 1 == pi && *pj <= pivot) );
|
||||
|
||||
/* Swap pivot into its final position, pj. */
|
||||
assert(plo[1] == pivot);
|
||||
plo[1] = *pj;
|
||||
*pj = pivot;
|
||||
|
||||
/* Subfiles are from plo to pj-1 inclusive, and pj+1 to phi
|
||||
inclusive. Push the larger one, and loop back to do the
|
||||
smaller one directly.
|
||||
*/
|
||||
if (pj - plo >= phi - pj) {
|
||||
PUSH(plo, pj-1);
|
||||
plo = pj+1;
|
||||
}
|
||||
else {
|
||||
PUSH(pj+1, phi);
|
||||
phi = pj-1;
|
||||
}
|
||||
}
|
||||
|
||||
#undef PUSH
|
||||
#undef SWAP
|
||||
}
|
||||
|
||||
/* Sort p and remove duplicates, as fast as we can. */
|
||||
static size_t
|
||||
sort_int_nodups(KEY_TYPE *p, size_t n)
|
||||
{
|
||||
size_t nunique;
|
||||
element_type *work;
|
||||
|
||||
assert(sizeof(KEY_TYPE) == sizeof(element_type));
|
||||
assert(p);
|
||||
|
||||
/* Use quicksort if the array is small, OR if malloc can't find
|
||||
enough temp memory for radixsort.
|
||||
*/
|
||||
work = NULL;
|
||||
if (n > QUICKSORT_BEATS_RADIXSORT)
|
||||
work = (element_type *)malloc(n * sizeof(element_type));
|
||||
|
||||
if (work) {
|
||||
element_type *out = radixsort_int(p, work, n);
|
||||
nunique = uniq(p, out, n);
|
||||
free(work);
|
||||
}
|
||||
else {
|
||||
quicksort(p, n);
|
||||
nunique = uniq(p, p, n);
|
||||
}
|
||||
|
||||
return nunique;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
# If tests is a package, debugging is a bit easier.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,534 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from BTrees.tests.common import permutations
|
||||
|
||||
|
||||
class DegenerateBTree(unittest.TestCase):
|
||||
# Build a degenerate tree (set). Boxes are BTree nodes. There are
|
||||
# 5 leaf buckets, each containing a single int. Keys in the BTree
|
||||
# nodes don't appear in the buckets. Seven BTree nodes are purely
|
||||
# indirection nodes (no keys). Buckets aren't all at the same depth:
|
||||
#
|
||||
# +------------------------+
|
||||
# | 4 |
|
||||
# +------------------------+
|
||||
# | |
|
||||
# | v
|
||||
# | +-+
|
||||
# | | |
|
||||
# | +-+
|
||||
# | |
|
||||
# v v
|
||||
# +-------+ +-------------+
|
||||
# | 2 | | 6 10 |
|
||||
# +-------+ +-------------+
|
||||
# | | | | |
|
||||
# v v v v v
|
||||
# +-+ +-+ +-+ +-+ +-+
|
||||
# | | | | | | | | | |
|
||||
# +-+ +-+ +-+ +-+ +-+
|
||||
# | | | | |
|
||||
# v v v v v
|
||||
# 1 3 +-+ 7 11
|
||||
# | |
|
||||
# +-+
|
||||
# |
|
||||
# v
|
||||
# 5
|
||||
#
|
||||
# This is nasty for many algorithms. Consider a high-end range search
|
||||
# for 4. The BTree nodes direct it to the 5 bucket, but the correct
|
||||
# answer is the 3 bucket, which requires going in a different direction
|
||||
# at the very top node already. Consider a low-end range search for
|
||||
# 9. The BTree nodes direct it to the 7 bucket, but the correct answer
|
||||
# is the 11 bucket. This is also a nasty-case tree for deletions.
|
||||
|
||||
def _build_degenerate_tree(self):
|
||||
# Build the buckets and chain them together.
|
||||
from BTrees.IIBTree import IISet
|
||||
from BTrees.IIBTree import IITreeSet
|
||||
from BTrees.check import check
|
||||
bucket11 = IISet([11])
|
||||
|
||||
bucket7 = IISet()
|
||||
bucket7.__setstate__(((7,), bucket11))
|
||||
|
||||
bucket5 = IISet()
|
||||
bucket5.__setstate__(((5,), bucket7))
|
||||
|
||||
bucket3 = IISet()
|
||||
bucket3.__setstate__(((3,), bucket5))
|
||||
|
||||
bucket1 = IISet()
|
||||
bucket1.__setstate__(((1,), bucket3))
|
||||
|
||||
# Build the deepest layers of indirection nodes.
|
||||
ts = IITreeSet
|
||||
tree1 = ts()
|
||||
tree1.__setstate__(((bucket1,), bucket1))
|
||||
|
||||
tree3 = ts()
|
||||
tree3.__setstate__(((bucket3,), bucket3))
|
||||
|
||||
tree5lower = ts()
|
||||
tree5lower.__setstate__(((bucket5,), bucket5))
|
||||
tree5 = ts()
|
||||
tree5.__setstate__(((tree5lower,), bucket5))
|
||||
|
||||
tree7 = ts()
|
||||
tree7.__setstate__(((bucket7,), bucket7))
|
||||
|
||||
tree11 = ts()
|
||||
tree11.__setstate__(((bucket11,), bucket11))
|
||||
|
||||
# Paste together the middle layers.
|
||||
tree13 = ts()
|
||||
tree13.__setstate__(((tree1, 2, tree3), bucket1))
|
||||
|
||||
tree5711lower = ts()
|
||||
tree5711lower.__setstate__(((tree5, 6, tree7, 10, tree11), bucket5))
|
||||
tree5711 = ts()
|
||||
tree5711.__setstate__(((tree5711lower,), bucket5))
|
||||
|
||||
# One more.
|
||||
t = ts()
|
||||
t.__setstate__(((tree13, 4, tree5711), bucket1))
|
||||
t._check()
|
||||
check(t)
|
||||
return t, [1, 3, 5, 7, 11]
|
||||
|
||||
def testBasicOps(self):
|
||||
t, keys = self._build_degenerate_tree()
|
||||
self.assertEqual(len(t), len(keys))
|
||||
self.assertEqual(list(t.keys()), keys)
|
||||
# has_key actually returns the depth of a bucket.
|
||||
self.assertEqual(t.has_key(1), 4)
|
||||
self.assertEqual(t.has_key(3), 4)
|
||||
self.assertEqual(t.has_key(5), 6)
|
||||
self.assertEqual(t.has_key(7), 5)
|
||||
self.assertEqual(t.has_key(11), 5)
|
||||
for i in 0, 2, 4, 6, 8, 9, 10, 12:
|
||||
self.assertTrue(i not in t)
|
||||
|
||||
def _checkRanges(self, tree, keys):
|
||||
self.assertEqual(len(tree), len(keys))
|
||||
sorted_keys = keys[:]
|
||||
sorted_keys.sort()
|
||||
self.assertEqual(list(tree.keys()), sorted_keys)
|
||||
for k in keys:
|
||||
self.assertTrue(k in tree)
|
||||
if keys:
|
||||
lokey = sorted_keys[0]
|
||||
hikey = sorted_keys[-1]
|
||||
self.assertEqual(lokey, tree.minKey())
|
||||
self.assertEqual(hikey, tree.maxKey())
|
||||
else:
|
||||
lokey = hikey = 42
|
||||
|
||||
# Try all range searches.
|
||||
for lo in range(lokey - 1, hikey + 2):
|
||||
for hi in range(lo - 1, hikey + 2):
|
||||
for skipmin in False, True:
|
||||
for skipmax in False, True:
|
||||
wantlo, wanthi = lo, hi
|
||||
if skipmin:
|
||||
wantlo += 1
|
||||
if skipmax:
|
||||
wanthi -= 1
|
||||
want = [k for k in keys if wantlo <= k <= wanthi]
|
||||
got = list(tree.keys(lo, hi, skipmin, skipmax))
|
||||
self.assertEqual(want, got)
|
||||
|
||||
def testRanges(self):
|
||||
t, keys = self._build_degenerate_tree()
|
||||
self._checkRanges(t, keys)
|
||||
|
||||
def testDeletes(self):
|
||||
# Delete keys in all possible orders, checking each tree along
|
||||
# the way.
|
||||
|
||||
# This is a tough test. Previous failure modes included:
|
||||
# 1. A variety of assertion failures in _checkRanges.
|
||||
# 2. Assorted "Invalid firstbucket pointer" failures at
|
||||
# seemingly random times, coming out of the BTree destructor.
|
||||
# 3. Under Python 2.3 CVS, some baffling
|
||||
# RuntimeWarning: tp_compare didn't return -1 or -2 for exception
|
||||
# warnings, possibly due to memory corruption after a BTree
|
||||
# goes insane.
|
||||
# On CPython in PURE_PYTHON mode, this is a *slow* test, taking 15+s
|
||||
# on a 2015 laptop.
|
||||
from BTrees.check import check
|
||||
t, keys = self._build_degenerate_tree()
|
||||
for oneperm in permutations(keys):
|
||||
t, keys = self._build_degenerate_tree()
|
||||
for key in oneperm:
|
||||
t.remove(key)
|
||||
keys.remove(key)
|
||||
t._check()
|
||||
check(t)
|
||||
self._checkRanges(t, keys)
|
||||
# We removed all the keys, so the tree should be empty now.
|
||||
self.assertEqual(t.__getstate__(), None)
|
||||
|
||||
# A damaged tree may trigger an "invalid firstbucket pointer"
|
||||
# failure at the time its destructor is invoked. Try to force
|
||||
# that to happen now, so it doesn't look like a baffling failure
|
||||
# at some unrelated line.
|
||||
del t # trigger destructor
|
||||
|
||||
|
||||
LP294788_ids = {}
|
||||
|
||||
|
||||
class ToBeDeleted(object):
|
||||
def __init__(self, id):
|
||||
assert isinstance(id, int) #we don't want to store any object ref here
|
||||
self.id = id
|
||||
|
||||
global LP294788_ids
|
||||
LP294788_ids[id] = 1
|
||||
|
||||
def __del__(self):
|
||||
global LP294788_ids
|
||||
LP294788_ids.pop(self.id, None)
|
||||
|
||||
def __cmp__(self, other):
|
||||
return cmp(self.id, other.id)
|
||||
|
||||
def __le__(self, other):
|
||||
return self.id <= other.id
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.id < other.id
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.id == other.id
|
||||
|
||||
def __ne__(self, other):
|
||||
return self.id != other.id
|
||||
|
||||
def __gt__(self, other):
|
||||
return self.id > other.id
|
||||
|
||||
def __ge__(self, other):
|
||||
return self.id >= other.id
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.id)
|
||||
|
||||
|
||||
class BugFixes(unittest.TestCase):
|
||||
|
||||
# Collector 1843. Error returns were effectively ignored in
|
||||
# Bucket_rangeSearch(), leading to "delayed" errors, or worse.
|
||||
def testFixed1843(self):
|
||||
from BTrees.IIBTree import IISet
|
||||
t = IISet()
|
||||
t.insert(1)
|
||||
# This one used to fail to raise the TypeError when it occurred.
|
||||
self.assertRaises(TypeError, t.keys, "")
|
||||
# This one used to segfault.
|
||||
self.assertRaises(TypeError, t.keys, 0, "")
|
||||
|
||||
def test_LP294788(self):
|
||||
# https://bugs.launchpad.net/bugs/294788
|
||||
# BTree keeps some deleted objects referenced
|
||||
|
||||
# The logic here together with the ToBeDeleted class is that
|
||||
# a separate reference dict is populated on object creation
|
||||
# and removed in __del__
|
||||
# That means what's left in the reference dict is never GC'ed
|
||||
# therefore referenced somewhere
|
||||
# To simulate real life, some random data is used to exercise the tree
|
||||
import gc
|
||||
import random
|
||||
from BTrees.OOBTree import OOBTree
|
||||
|
||||
t = OOBTree()
|
||||
|
||||
trandom = random.Random('OOBTree')
|
||||
|
||||
global LP294788_ids
|
||||
|
||||
# /// BTree keys are integers, value is an object
|
||||
LP294788_ids = {}
|
||||
ids = {}
|
||||
for i in range(1024):
|
||||
if trandom.random() > 0.1 or not ids:
|
||||
#add
|
||||
id = None
|
||||
while id is None or id in ids:
|
||||
id = trandom.randint(0, 1000000)
|
||||
|
||||
ids[id] = 1
|
||||
t[id] = ToBeDeleted(id)
|
||||
else:
|
||||
#del
|
||||
keys = list(ids.keys())
|
||||
if keys:
|
||||
id = trandom.choice(list(ids.keys()))
|
||||
del t[id]
|
||||
del ids[id]
|
||||
|
||||
ids = ids.keys()
|
||||
trandom.shuffle(list(ids))
|
||||
for id in ids:
|
||||
del t[id]
|
||||
ids = None
|
||||
|
||||
#to be on the safe side run a full GC
|
||||
gc.collect()
|
||||
|
||||
#print LP294788_ids
|
||||
|
||||
self.assertEqual(len(t), 0)
|
||||
self.assertEqual(len(LP294788_ids), 0)
|
||||
# \\\
|
||||
|
||||
# /// BTree keys are integers, value is a tuple having an object
|
||||
LP294788_ids = {}
|
||||
ids = {}
|
||||
for i in range(1024):
|
||||
if trandom.random() > 0.1 or not ids:
|
||||
#add
|
||||
id = None
|
||||
while id is None or id in ids:
|
||||
id = trandom.randint(0, 1000000)
|
||||
|
||||
ids[id] = 1
|
||||
t[id] = (id, ToBeDeleted(id), u'somename')
|
||||
else:
|
||||
#del
|
||||
keys = list(ids.keys())
|
||||
if keys:
|
||||
id = trandom.choice(keys)
|
||||
del t[id]
|
||||
del ids[id]
|
||||
|
||||
ids = ids.keys()
|
||||
trandom.shuffle(list(ids))
|
||||
for id in ids:
|
||||
del t[id]
|
||||
ids = None
|
||||
|
||||
#to be on the safe side run a full GC
|
||||
gc.collect()
|
||||
|
||||
#print LP294788_ids
|
||||
|
||||
self.assertEqual(len(t), 0)
|
||||
self.assertEqual(len(LP294788_ids), 0)
|
||||
# \\\
|
||||
|
||||
|
||||
# /// BTree keys are objects, value is an int
|
||||
t = OOBTree()
|
||||
LP294788_ids = {}
|
||||
ids = {}
|
||||
for i in range(1024):
|
||||
if trandom.random() > 0.1 or not ids:
|
||||
#add
|
||||
id = None
|
||||
while id is None or id in ids:
|
||||
id = ToBeDeleted(trandom.randint(0, 1000000))
|
||||
|
||||
ids[id] = 1
|
||||
t[id] = 1
|
||||
else:
|
||||
#del
|
||||
id = trandom.choice(list(ids.keys()))
|
||||
del ids[id]
|
||||
del t[id]
|
||||
|
||||
ids = ids.keys()
|
||||
trandom.shuffle(list(ids))
|
||||
for id in ids:
|
||||
del t[id]
|
||||
#release all refs
|
||||
ids = id = None
|
||||
|
||||
#to be on the safe side run a full GC
|
||||
gc.collect()
|
||||
|
||||
#print LP294788_ids
|
||||
|
||||
self.assertEqual(len(t), 0)
|
||||
self.assertEqual(len(LP294788_ids), 0)
|
||||
|
||||
# /// BTree keys are tuples having objects, value is an int
|
||||
t = OOBTree()
|
||||
LP294788_ids = {}
|
||||
ids = {}
|
||||
for i in range(1024):
|
||||
if trandom.random() > 0.1 or not ids:
|
||||
#add
|
||||
id = None
|
||||
while id is None or id in ids:
|
||||
id = trandom.randint(0, 1000000)
|
||||
id = (id, ToBeDeleted(id), u'somename')
|
||||
|
||||
ids[id] = 1
|
||||
t[id] = 1
|
||||
else:
|
||||
#del
|
||||
id = trandom.choice(list(ids.keys()))
|
||||
del ids[id]
|
||||
del t[id]
|
||||
|
||||
ids = ids.keys()
|
||||
trandom.shuffle(list(ids))
|
||||
for id in ids:
|
||||
del t[id]
|
||||
#release all refs
|
||||
ids = id = key = None
|
||||
|
||||
#to be on the safe side run a full GC
|
||||
gc.collect()
|
||||
|
||||
#print LP294788_ids
|
||||
|
||||
self.assertEqual(len(t), 0)
|
||||
self.assertEqual(len(LP294788_ids), 0)
|
||||
|
||||
|
||||
# cmp error propagation tests
|
||||
|
||||
|
||||
class DoesntLikeBeingCompared:
|
||||
|
||||
def __cmp__(self, other):
|
||||
raise ValueError('incomparable')
|
||||
__lt__ = __le__ = __eq__ = __ne__ = __ge__ = __gt__ = __cmp__
|
||||
|
||||
class TestCmpError(unittest.TestCase):
|
||||
|
||||
def testFoo(self):
|
||||
from BTrees.OOBTree import OOBTree
|
||||
t = OOBTree()
|
||||
t['hello world'] = None
|
||||
try:
|
||||
t[DoesntLikeBeingCompared()] = None
|
||||
except ValueError as e:
|
||||
self.assertEqual(str(e), 'incomparable')
|
||||
else:
|
||||
self.fail('incomarable objects should not be allowed into '
|
||||
'the tree')
|
||||
|
||||
|
||||
class FamilyTest(unittest.TestCase):
|
||||
def test32(self):
|
||||
from zope.interface.verify import verifyObject
|
||||
import BTrees
|
||||
from BTrees.IOBTree import IOTreeSet
|
||||
verifyObject(BTrees.Interfaces.IBTreeFamily, BTrees.family32)
|
||||
self.assertEqual(
|
||||
BTrees.family32.IO, BTrees.IOBTree)
|
||||
self.assertEqual(
|
||||
BTrees.family32.OI, BTrees.OIBTree)
|
||||
self.assertEqual(
|
||||
BTrees.family32.II, BTrees.IIBTree)
|
||||
self.assertEqual(
|
||||
BTrees.family32.IF, BTrees.IFBTree)
|
||||
self.assertEqual(
|
||||
BTrees.family32.OO, BTrees.OOBTree)
|
||||
s = IOTreeSet()
|
||||
s.insert(BTrees.family32.maxint)
|
||||
self.assertTrue(BTrees.family32.maxint in s)
|
||||
s = IOTreeSet()
|
||||
s.insert(BTrees.family32.minint)
|
||||
self.assertTrue(BTrees.family32.minint in s)
|
||||
s = IOTreeSet()
|
||||
# this next bit illustrates an, um, "interesting feature". If
|
||||
# the characteristics change to match the 64 bit version, please
|
||||
# feel free to change.
|
||||
with self.assertRaises((TypeError, OverflowError)):
|
||||
s.insert(BTrees.family32.maxint + 1)
|
||||
|
||||
with self.assertRaises((TypeError, OverflowError)):
|
||||
s.insert(BTrees.family32.minint - 1)
|
||||
self.check_pickling(BTrees.family32)
|
||||
|
||||
def test64(self):
|
||||
from zope.interface.verify import verifyObject
|
||||
import BTrees
|
||||
from BTrees.LOBTree import LOTreeSet
|
||||
verifyObject(BTrees.Interfaces.IBTreeFamily, BTrees.family64)
|
||||
self.assertEqual(
|
||||
BTrees.family64.IO, BTrees.LOBTree)
|
||||
self.assertEqual(
|
||||
BTrees.family64.OI, BTrees.OLBTree)
|
||||
self.assertEqual(
|
||||
BTrees.family64.II, BTrees.LLBTree)
|
||||
self.assertEqual(
|
||||
BTrees.family64.IF, BTrees.LFBTree)
|
||||
self.assertEqual(
|
||||
BTrees.family64.OO, BTrees.OOBTree)
|
||||
s = LOTreeSet()
|
||||
s.insert(BTrees.family64.maxint)
|
||||
self.assertTrue(BTrees.family64.maxint in s)
|
||||
s = LOTreeSet()
|
||||
s.insert(BTrees.family64.minint)
|
||||
self.assertTrue(BTrees.family64.minint in s)
|
||||
s = LOTreeSet()
|
||||
# XXX why oh why do we expect ValueError here, but TypeError in test32?
|
||||
self.assertRaises(ValueError, s.insert, BTrees.family64.maxint + 1)
|
||||
self.assertRaises(ValueError, s.insert, BTrees.family64.minint - 1)
|
||||
self.check_pickling(BTrees.family64)
|
||||
|
||||
def check_pickling(self, family):
|
||||
# The "family" objects are singletons; they can be pickled and
|
||||
# unpickled, and the same instances will always be returned on
|
||||
# unpickling, whether from the same unpickler or different
|
||||
# unpicklers.
|
||||
import pickle
|
||||
from io import BytesIO
|
||||
|
||||
s = pickle.dumps((family, family))
|
||||
(f1, f2) = pickle.loads(s)
|
||||
self.assertIs(f1, family)
|
||||
self.assertIs(f2, family)
|
||||
|
||||
# Using a single memo across multiple pickles:
|
||||
sio = BytesIO()
|
||||
p = pickle.Pickler(sio)
|
||||
p.dump(family)
|
||||
p.dump([family])
|
||||
u = pickle.Unpickler(BytesIO(sio.getvalue()))
|
||||
f1 = u.load()
|
||||
f2, = u.load()
|
||||
self.assertTrue(f1 is family)
|
||||
self.assertTrue(f2 is family)
|
||||
|
||||
# Using separate memos for each pickle:
|
||||
sio = BytesIO()
|
||||
p = pickle.Pickler(sio)
|
||||
p.dump(family)
|
||||
p.clear_memo()
|
||||
p.dump([family])
|
||||
u = pickle.Unpickler(BytesIO(sio.getvalue()))
|
||||
f1 = u.load()
|
||||
f2, = u.load()
|
||||
self.assertTrue(f1 is family)
|
||||
self.assertTrue(f2 is family)
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(DegenerateBTree),
|
||||
unittest.makeSuite(BugFixes),
|
||||
unittest.makeSuite(TestCmpError),
|
||||
unittest.makeSuite(FamilyTest),
|
||||
))
|
||||
@@ -0,0 +1,76 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import unittest
|
||||
from .common import _skip_under_Py3k
|
||||
|
||||
# When an OOBtree contains unicode strings as keys,
|
||||
# it is neccessary accessing non-unicode strings are
|
||||
# either ascii strings or encoded as unicoded using the
|
||||
# corresponding encoding
|
||||
|
||||
encoding = 'ISO-8859-1'
|
||||
|
||||
class TestBTreesUnicode(unittest.TestCase):
|
||||
""" test unicode"""
|
||||
|
||||
def setUp(self):
|
||||
#setup an OOBTree with some unicode strings
|
||||
from BTrees.OOBTree import OOBTree
|
||||
from BTrees._compat import _bytes
|
||||
|
||||
self.s = b'dreit\xe4gigen'.decode('latin1')
|
||||
|
||||
self.data = [(b'alien', 1),
|
||||
(b'k\xf6nnten', 2),
|
||||
(b'fox', 3),
|
||||
(b'future', 4),
|
||||
(b'quick', 5),
|
||||
(b'zerst\xf6rt', 6),
|
||||
(u'dreit\xe4gigen', 7),
|
||||
]
|
||||
|
||||
self.tree = OOBTree()
|
||||
for k, v in self.data:
|
||||
if isinstance(k, _bytes):
|
||||
k = k.decode('latin1')
|
||||
self.tree[k] = v
|
||||
|
||||
@_skip_under_Py3k
|
||||
def testAllKeys(self):
|
||||
# check every item of the tree
|
||||
from BTrees._compat import _bytes
|
||||
for k, v in self.data:
|
||||
if isinstance(k, _bytes):
|
||||
k = k.decode(encoding)
|
||||
self.assertTrue(k in self.tree)
|
||||
self.assertEqual(self.tree[k], v)
|
||||
|
||||
@_skip_under_Py3k
|
||||
def testUnicodeKeys(self):
|
||||
# try to access unicode keys in tree
|
||||
k, v = self.data[-1]
|
||||
self.assertEqual(k, self.s)
|
||||
self.assertEqual(self.tree[k], v)
|
||||
self.assertEqual(self.tree[self.s], v)
|
||||
|
||||
@_skip_under_Py3k
|
||||
def testAsciiKeys(self):
|
||||
# try to access some "plain ASCII" keys in the tree
|
||||
for k, v in self.data[0], self.data[2]:
|
||||
self.assertTrue(isinstance(k, str))
|
||||
self.assertEqual(self.tree[k], v)
|
||||
|
||||
def test_suite():
|
||||
return unittest.makeSuite(TestBTreesUnicode)
|
||||
@@ -0,0 +1,573 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from .common import _skip_wo_ZODB
|
||||
from .common import ConflictTestBase
|
||||
|
||||
|
||||
class NastyConfictFunctionalTests(ConflictTestBase, unittest.TestCase):
|
||||
# FUNCTESTS: Provoke various conflict scenarios using ZODB + transaction
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOBTree
|
||||
return OOBTree
|
||||
|
||||
def openDB(self):
|
||||
# The conflict tests tend to open two or more connections
|
||||
# and then try to commit them. A standard FileStorage
|
||||
# is not MVCC aware, and so each connection would have the same
|
||||
# instance of the storage, leading to the error
|
||||
# "Duplicate tpc_begin calls for same transaction" on commit;
|
||||
# thus we use a MVCCMappingStorage for these tests, ensuring each
|
||||
# connection has its own storage.
|
||||
# Unfortunately, it wants to acquire the identically same
|
||||
# non-recursive lock in each of its *its* tpc_* methods, which deadlocks.
|
||||
# The solution is to give each instance its own lock, and trust in the
|
||||
# serialization (ordering) of the datamanager, and the fact that these tests are
|
||||
# single-threaded.
|
||||
import threading
|
||||
from ZODB.tests.MVCCMappingStorage import MVCCMappingStorage
|
||||
class _MVCCMappingStorage(MVCCMappingStorage):
|
||||
def new_instance(self):
|
||||
inst = MVCCMappingStorage.new_instance(self)
|
||||
inst._commit_lock = threading.Lock()
|
||||
return inst
|
||||
from ZODB.DB import DB
|
||||
self.storage = _MVCCMappingStorage()
|
||||
self.db = DB(self.storage)
|
||||
return self.db
|
||||
|
||||
|
||||
@_skip_wo_ZODB
|
||||
def testSimpleConflict(self):
|
||||
# Invoke conflict resolution by committing a transaction and
|
||||
# catching a conflict in the storage.
|
||||
import transaction
|
||||
self.openDB()
|
||||
|
||||
r1 = self.db.open().root()
|
||||
r1["t"] = t = self._makeOne()
|
||||
transaction.commit()
|
||||
|
||||
r2 = self.db.open().root()
|
||||
copy = r2["t"]
|
||||
list(copy) # unghostify
|
||||
|
||||
self.assertEqual(t._p_serial, copy._p_serial)
|
||||
|
||||
t.update({1:2, 2:3})
|
||||
transaction.commit()
|
||||
|
||||
copy.update({3:4})
|
||||
transaction.commit()
|
||||
|
||||
# This tests a problem that cropped up while trying to write
|
||||
# testBucketSplitConflict (below): conflict resolution wasn't
|
||||
# working at all in non-trivial cases. Symptoms varied from
|
||||
# strange complaints about pickling (despite that the test isn't
|
||||
# doing any *directly*), thru SystemErrors from Python and
|
||||
# AssertionErrors inside the BTree code.
|
||||
@_skip_wo_ZODB
|
||||
def testResolutionBlowsUp(self):
|
||||
import transaction
|
||||
b = self._makeOne()
|
||||
for i in range(0, 200, 4):
|
||||
b[i] = i
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 15 values: 60, 64 .. 116
|
||||
# bucket 2 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
|
||||
# If these fail, the *preconditions* for running the test aren't
|
||||
# satisfied -- the test itself hasn't been run yet.
|
||||
self.assertEqual(len(state), 2)
|
||||
self.assertEqual(len(state[0]), 5)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
self.assertEqual(state[0][3], 120)
|
||||
|
||||
# Invoke conflict resolution by committing a transaction.
|
||||
self.openDB()
|
||||
|
||||
r1 = self.db.open().root()
|
||||
r1["t"] = b
|
||||
transaction.commit()
|
||||
|
||||
r2 = self.db.open().root()
|
||||
copy = r2["t"]
|
||||
# Make sure all of copy is loaded.
|
||||
list(copy.values())
|
||||
|
||||
self.assertEqual(b._p_serial, copy._p_serial)
|
||||
|
||||
b.update({1:2, 2:3})
|
||||
transaction.commit()
|
||||
|
||||
copy.update({3:4})
|
||||
transaction.commit() # if this doesn't blow up
|
||||
list(copy.values()) # and this doesn't either, then fine
|
||||
|
||||
@_skip_wo_ZODB
|
||||
def testBucketSplitConflict(self):
|
||||
# Tests that a bucket split is viewed as a conflict.
|
||||
# It's (almost necessarily) a white-box test, and sensitive to
|
||||
# implementation details.
|
||||
import transaction
|
||||
from ZODB.POSException import ConflictError
|
||||
b = orig = self._makeOne()
|
||||
for i in range(0, 200, 4):
|
||||
b[i] = i
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 15 values: 60, 64 .. 116
|
||||
# bucket 2 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
|
||||
# If these fail, the *preconditions* for running the test aren't
|
||||
# satisfied -- the test itself hasn't been run yet.
|
||||
self.assertEqual(len(state), 2)
|
||||
self.assertEqual(len(state[0]), 5)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
self.assertEqual(state[0][3], 120)
|
||||
|
||||
# Invoke conflict resolution by committing a transaction.
|
||||
self.openDB()
|
||||
|
||||
tm1 = transaction.TransactionManager()
|
||||
r1 = self.db.open(transaction_manager=tm1).root()
|
||||
r1["t"] = b
|
||||
tm1.commit()
|
||||
|
||||
tm2 = transaction.TransactionManager()
|
||||
r2 = self.db.open(transaction_manager=tm2).root()
|
||||
copy = r2["t"]
|
||||
# Make sure all of copy is loaded.
|
||||
list(copy.values())
|
||||
|
||||
self.assertEqual(orig._p_serial, copy._p_serial)
|
||||
|
||||
# In one transaction, add 16 new keys to bucket1, to force a bucket
|
||||
# split.
|
||||
b = orig
|
||||
numtoadd = 16
|
||||
candidate = 60
|
||||
while numtoadd:
|
||||
if candidate not in b:
|
||||
b[candidate] = candidate
|
||||
numtoadd -= 1
|
||||
candidate += 1
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 15 values: 60, 61 .. 74
|
||||
# bucket 2 has 16 values: [75, 76 .. 81] + [84, 88 ..116]
|
||||
# bucket 3 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((b0, 60, b1, 75, b2, 120, b3), firstbucket)
|
||||
# The next block is still verifying preconditions.
|
||||
self.assertEqual(len(state) , 2)
|
||||
self.assertEqual(len(state[0]), 7)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
self.assertEqual(state[0][3], 75)
|
||||
self.assertEqual(state[0][5], 120)
|
||||
|
||||
tm1.commit()
|
||||
|
||||
# In the other transaction, add 3 values near the tail end of bucket1.
|
||||
# This doesn't cause a split.
|
||||
b = copy
|
||||
for i in range(112, 116):
|
||||
b[i] = i
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 18 values: 60, 64 .. 112, 113, 114, 115, 116
|
||||
# bucket 2 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
|
||||
# The next block is still verifying preconditions.
|
||||
self.assertEqual(len(state), 2)
|
||||
self.assertEqual(len(state[0]), 5)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
self.assertEqual(state[0][3], 120)
|
||||
|
||||
self.assertRaises(ConflictError, tm2.commit)
|
||||
|
||||
@_skip_wo_ZODB
|
||||
def testEmptyBucketConflict(self):
|
||||
# Tests that an emptied bucket *created by* conflict resolution is
|
||||
# viewed as a conflict: conflict resolution doesn't have enough
|
||||
# info to unlink the empty bucket from the BTree correctly.
|
||||
import transaction
|
||||
from ZODB.POSException import ConflictError
|
||||
b = orig = self._makeOne()
|
||||
for i in range(0, 200, 4):
|
||||
b[i] = i
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 15 values: 60, 64 .. 116
|
||||
# bucket 2 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
|
||||
# If these fail, the *preconditions* for running the test aren't
|
||||
# satisfied -- the test itself hasn't been run yet.
|
||||
self.assertEqual(len(state), 2)
|
||||
self.assertEqual(len(state[0]), 5)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
self.assertEqual(state[0][3], 120)
|
||||
|
||||
# Invoke conflict resolution by committing a transaction.
|
||||
self.openDB()
|
||||
|
||||
tm1 = transaction.TransactionManager()
|
||||
r1 = self.db.open(transaction_manager=tm1).root()
|
||||
r1["t"] = b
|
||||
tm1.commit()
|
||||
|
||||
tm2 = transaction.TransactionManager()
|
||||
r2 = self.db.open(transaction_manager=tm2).root()
|
||||
copy = r2["t"]
|
||||
# Make sure all of copy is loaded.
|
||||
list(copy.values())
|
||||
|
||||
self.assertEqual(orig._p_serial, copy._p_serial)
|
||||
|
||||
# In one transaction, delete half of bucket 1.
|
||||
b = orig
|
||||
for k in 60, 64, 68, 72, 76, 80, 84, 88:
|
||||
del b[k]
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 7 values: 92, 96, 100, 104, 108, 112, 116
|
||||
# bucket 2 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
|
||||
# The next block is still verifying preconditions.
|
||||
self.assertEqual(len(state) , 2)
|
||||
self.assertEqual(len(state[0]), 5)
|
||||
self.assertEqual(state[0][1], 92)
|
||||
self.assertEqual(state[0][3], 120)
|
||||
|
||||
tm1.commit()
|
||||
|
||||
# In the other transaction, delete the other half of bucket 1.
|
||||
b = copy
|
||||
for k in 92, 96, 100, 104, 108, 112, 116:
|
||||
del b[k]
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 8 values: 60, 64, 68, 72, 76, 80, 84, 88
|
||||
# bucket 2 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
|
||||
# The next block is still verifying preconditions.
|
||||
self.assertEqual(len(state), 2)
|
||||
self.assertEqual(len(state[0]), 5)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
self.assertEqual(state[0][3], 120)
|
||||
|
||||
# Conflict resolution empties bucket1 entirely. This used to
|
||||
# create an "insane" BTree (a legit BTree cannot contain an empty
|
||||
# bucket -- it contains NULL pointers the BTree code doesn't
|
||||
# expect, and segfaults result).
|
||||
self.assertRaises(ConflictError, tm2.commit)
|
||||
|
||||
@_skip_wo_ZODB
|
||||
def testEmptyBucketNoConflict(self):
|
||||
# Tests that a plain empty bucket (on input) is not viewed as a
|
||||
# conflict.
|
||||
import transaction
|
||||
b = orig = self._makeOne()
|
||||
for i in range(0, 200, 4):
|
||||
b[i] = i
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 15 values: 60, 64 .. 116
|
||||
# bucket 2 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
|
||||
# If these fail, the *preconditions* for running the test aren't
|
||||
# satisfied -- the test itself hasn't been run yet.
|
||||
self.assertEqual(len(state), 2)
|
||||
self.assertEqual(len(state[0]), 5)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
self.assertEqual(state[0][3], 120)
|
||||
|
||||
# Invoke conflict resolution by committing a transaction.
|
||||
self.openDB()
|
||||
|
||||
r1 = self.db.open().root()
|
||||
r1["t"] = orig
|
||||
transaction.commit()
|
||||
|
||||
r2 = self.db.open().root()
|
||||
copy = r2["t"]
|
||||
# Make sure all of copy is loaded.
|
||||
list(copy.values())
|
||||
|
||||
self.assertEqual(orig._p_serial, copy._p_serial)
|
||||
|
||||
# In one transaction, just add a key.
|
||||
b = orig
|
||||
b[1] = 1
|
||||
# bucket 0 has 16 values: [0, 1] + [4, 8 .. 56]
|
||||
# bucket 1 has 15 values: 60, 64 .. 116
|
||||
# bucket 2 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
|
||||
# The next block is still verifying preconditions.
|
||||
self.assertEqual(len(state), 2)
|
||||
self.assertEqual(len(state[0]), 5)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
self.assertEqual(state[0][3], 120)
|
||||
|
||||
transaction.commit()
|
||||
|
||||
# In the other transaction, delete bucket 2.
|
||||
b = copy
|
||||
for k in range(120, 200, 4):
|
||||
del b[k]
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 15 values: 60, 64 .. 116
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1), firstbucket)
|
||||
# The next block is still verifying preconditions.
|
||||
self.assertEqual(len(state), 2)
|
||||
self.assertEqual(len(state[0]), 3)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
|
||||
# This shouldn't create a ConflictError.
|
||||
transaction.commit()
|
||||
# And the resulting BTree shouldn't have internal damage.
|
||||
b._check()
|
||||
|
||||
# The snaky control flow in _bucket__p_resolveConflict ended up trying
|
||||
# to decref a NULL pointer if conflict resolution was fed 3 empty
|
||||
# buckets. http://collector.zope.org/Zope/553
|
||||
def testThreeEmptyBucketsNoSegfault(self):
|
||||
# Note that the conflict is raised by our C extension, rather than
|
||||
# indirectly via the storage, and hence is a more specialized type.
|
||||
# This test therefore does not require ZODB.
|
||||
from BTrees.Interfaces import BTreesConflictError
|
||||
t = self._makeOne()
|
||||
t[1] = 1
|
||||
bucket = t._firstbucket
|
||||
del t[1]
|
||||
state1 = bucket.__getstate__()
|
||||
state2 = bucket.__getstate__()
|
||||
state3 = bucket.__getstate__()
|
||||
self.assertTrue(state2 is not state1 and
|
||||
state2 is not state3 and
|
||||
state3 is not state1)
|
||||
self.assertTrue(state2 == state1 and
|
||||
state3 == state1)
|
||||
self.assertRaises(BTreesConflictError, bucket._p_resolveConflict,
|
||||
state1, state2, state3)
|
||||
# When an empty BTree resolves conflicts, it computes the
|
||||
# bucket state as None, so...
|
||||
self.assertRaises(BTreesConflictError, bucket._p_resolveConflict,
|
||||
None, None, None)
|
||||
|
||||
@_skip_wo_ZODB
|
||||
def testCantResolveBTreeConflict(self):
|
||||
# Test that a conflict involving two different changes to
|
||||
# an internal BTree node is unresolvable. An internal node
|
||||
# only changes when there are enough additions or deletions
|
||||
# to a child bucket that the bucket is split or removed.
|
||||
# It's (almost necessarily) a white-box test, and sensitive to
|
||||
# implementation details.
|
||||
import transaction
|
||||
from ZODB.POSException import ConflictError
|
||||
b = orig = self._makeOne()
|
||||
for i in range(0, 200, 4):
|
||||
b[i] = i
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 15 values: 60, 64 .. 116
|
||||
# bucket 2 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
|
||||
# If these fail, the *preconditions* for running the test aren't
|
||||
# satisfied -- the test itself hasn't been run yet.
|
||||
self.assertEqual(len(state), 2)
|
||||
self.assertEqual(len(state[0]), 5)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
self.assertEqual(state[0][3], 120)
|
||||
|
||||
# Set up database connections to provoke conflict.
|
||||
self.openDB()
|
||||
tm1 = transaction.TransactionManager()
|
||||
r1 = self.db.open(transaction_manager=tm1).root()
|
||||
r1["t"] = orig
|
||||
tm1.commit()
|
||||
|
||||
tm2 = transaction.TransactionManager()
|
||||
r2 = self.db.open(transaction_manager=tm2).root()
|
||||
copy = r2["t"]
|
||||
# Make sure all of copy is loaded.
|
||||
list(copy.values())
|
||||
|
||||
self.assertEqual(orig._p_serial, copy._p_serial)
|
||||
|
||||
# Now one transaction should add enough keys to cause a split,
|
||||
# and another should remove all the keys in one bucket.
|
||||
|
||||
for k in range(200, 300, 4):
|
||||
orig[k] = k
|
||||
tm1.commit()
|
||||
|
||||
for k in range(0, 60, 4):
|
||||
del copy[k]
|
||||
|
||||
self.assertRaises(ConflictError, tm2.commit)
|
||||
|
||||
@_skip_wo_ZODB
|
||||
def testConflictWithOneEmptyBucket(self):
|
||||
# If one transaction empties a bucket, while another adds an item
|
||||
# to the bucket, all the changes "look resolvable": bucket conflict
|
||||
# resolution returns a bucket containing (only) the item added by
|
||||
# the latter transaction, but changes from the former transaction
|
||||
# removing the bucket are uncontested: the bucket is removed from
|
||||
# the BTree despite that resolution thinks it's non-empty! This
|
||||
# was first reported by Dieter Maurer, to zodb-dev on 22 Mar 2005.
|
||||
import transaction
|
||||
from ZODB.POSException import ConflictError
|
||||
b = orig = self._makeOne()
|
||||
for i in range(0, 200, 4):
|
||||
b[i] = i
|
||||
# bucket 0 has 15 values: 0, 4 .. 56
|
||||
# bucket 1 has 15 values: 60, 64 .. 116
|
||||
# bucket 2 has 20 values: 120, 124 .. 196
|
||||
state = b.__getstate__()
|
||||
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
|
||||
# If these fail, the *preconditions* for running the test aren't
|
||||
# satisfied -- the test itself hasn't been run yet.
|
||||
self.assertEqual(len(state), 2)
|
||||
self.assertEqual(len(state[0]), 5)
|
||||
self.assertEqual(state[0][1], 60)
|
||||
self.assertEqual(state[0][3], 120)
|
||||
|
||||
# Set up database connections to provoke conflict.
|
||||
self.openDB()
|
||||
tm1 = transaction.TransactionManager()
|
||||
r1 = self.db.open(transaction_manager=tm1).root()
|
||||
r1["t"] = orig
|
||||
tm1.commit()
|
||||
|
||||
tm2 = transaction.TransactionManager()
|
||||
r2 = self.db.open(transaction_manager=tm2).root()
|
||||
copy = r2["t"]
|
||||
# Make sure all of copy is loaded.
|
||||
list(copy.values())
|
||||
|
||||
self.assertEqual(orig._p_serial, copy._p_serial)
|
||||
|
||||
# Now one transaction empties the first bucket, and another adds a
|
||||
# key to the first bucket.
|
||||
|
||||
for k in range(0, 60, 4):
|
||||
del orig[k]
|
||||
tm1.commit()
|
||||
|
||||
copy[1] = 1
|
||||
|
||||
self.assertRaises(ConflictError, tm2.commit)
|
||||
|
||||
# Same thing, except commit the transactions in the opposite order.
|
||||
b = self._makeOne()
|
||||
for i in range(0, 200, 4):
|
||||
b[i] = i
|
||||
|
||||
tm1 = transaction.TransactionManager()
|
||||
r1 = self.db.open(transaction_manager=tm1).root()
|
||||
r1["t"] = b
|
||||
tm1.commit()
|
||||
|
||||
tm2 = transaction.TransactionManager()
|
||||
r2 = self.db.open(transaction_manager=tm2).root()
|
||||
copy = r2["t"]
|
||||
# Make sure all of copy is loaded.
|
||||
list(copy.values())
|
||||
|
||||
self.assertEqual(b._p_serial, copy._p_serial)
|
||||
|
||||
# Now one transaction empties the first bucket, and another adds a
|
||||
# key to the first bucket.
|
||||
b[1] = 1
|
||||
tm1.commit()
|
||||
|
||||
for k in range(0, 60, 4):
|
||||
del copy[k]
|
||||
|
||||
self.assertRaises(ConflictError, tm2.commit)
|
||||
|
||||
@_skip_wo_ZODB
|
||||
def testConflictOfInsertAndDeleteOfFirstBucketItem(self):
|
||||
# Recently, BTrees became careful about removing internal keys
|
||||
# (keys in internal aka BTree nodes) when they were deleted from
|
||||
# buckets. This poses a problem for conflict resolution.
|
||||
|
||||
# We want to guard against a case in which the first key in a
|
||||
# bucket is removed in one transaction while a key is added
|
||||
# after that key but before the next key in another transaction
|
||||
# with the result that the added key is unreachable.
|
||||
|
||||
# original:
|
||||
|
||||
# Bucket(...), k1, Bucket((k1, v1), (k3, v3), ...)
|
||||
|
||||
# tran1
|
||||
|
||||
# Bucket(...), k3, Bucket(k3, v3), ...)
|
||||
|
||||
# tran2
|
||||
|
||||
# Bucket(...), k1, Bucket((k1, v1), (k2, v2), (k3, v3), ...)
|
||||
|
||||
# where k1 < k2 < k3
|
||||
|
||||
# We don't want:
|
||||
|
||||
# Bucket(...), k3, Bucket((k2, v2), (k3, v3), ...)
|
||||
|
||||
# as k2 would be unfindable, so we want a conflict.
|
||||
|
||||
import transaction
|
||||
from ZODB.POSException import ConflictError
|
||||
mytype = self._getTargetClass()
|
||||
db = self.openDB()
|
||||
tm1 = transaction.TransactionManager()
|
||||
conn1 = db.open(tm1)
|
||||
conn1.root.t = t = mytype()
|
||||
for i in range(0, 200, 2):
|
||||
t[i] = i
|
||||
tm1.commit()
|
||||
k = t.__getstate__()[0][1]
|
||||
assert t.__getstate__()[0][2].keys()[0] == k
|
||||
|
||||
tm2 = transaction.TransactionManager()
|
||||
conn2 = db.open(tm2)
|
||||
|
||||
t[k+1] = k+1
|
||||
del conn2.root.t[k]
|
||||
for i in range(200,300):
|
||||
conn2.root.t[i] = i
|
||||
|
||||
tm1.commit()
|
||||
self.assertRaises(ConflictError, tm2.commit)
|
||||
tm2.abort()
|
||||
|
||||
k = t.__getstate__()[0][1]
|
||||
t[k+1] = k+1
|
||||
del conn2.root.t[k]
|
||||
|
||||
tm2.commit()
|
||||
self.assertRaises(ConflictError, tm1.commit)
|
||||
tm1.abort()
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(NastyConfictFunctionalTests),
|
||||
))
|
||||
@@ -0,0 +1,378 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from .common import BTreeTests
|
||||
from .common import ExtendedSetTests
|
||||
from .common import InternalKeysMappingTest
|
||||
from .common import InternalKeysSetTest
|
||||
from .common import MappingBase
|
||||
from .common import MappingConflictTestBase
|
||||
from .common import ModuleTest
|
||||
from .common import MultiUnion
|
||||
from .common import NormalSetTests
|
||||
from .common import SetConflictTestBase
|
||||
from .common import SetResult
|
||||
from .common import TestLongIntKeys
|
||||
from .common import makeBuilder
|
||||
from BTrees.IIBTree import using64bits #XXX Ugly, but unavoidable
|
||||
|
||||
|
||||
class IFBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFBTree
|
||||
return IFBTree
|
||||
|
||||
|
||||
class IFBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFBTreePy
|
||||
return IFBTreePy
|
||||
|
||||
|
||||
class IFTreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFTreeSet
|
||||
return IFTreeSet
|
||||
|
||||
|
||||
class IFTreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFTreeSetPy
|
||||
return IFTreeSetPy
|
||||
|
||||
|
||||
class IFBucketTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFBucket
|
||||
return IFBucket
|
||||
|
||||
|
||||
class IFBucketPyTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFBucketPy
|
||||
return IFBucketPy
|
||||
|
||||
|
||||
class IFTreeSetTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFTreeSet
|
||||
return IFTreeSet
|
||||
|
||||
|
||||
class IFTreeSetPyTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFTreeSetPy
|
||||
return IFTreeSetPy
|
||||
|
||||
|
||||
class IFSetTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFSet
|
||||
return IFSet
|
||||
|
||||
|
||||
class IFSetPyTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFSetPy
|
||||
return IFSetPy
|
||||
|
||||
|
||||
class IFBTreeTest(BTreeTests, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IFBTree import IFBTree
|
||||
return IFBTree()
|
||||
|
||||
|
||||
class IFBTreePyTest(BTreeTests, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IFBTree import IFBTreePy
|
||||
return IFBTreePy()
|
||||
|
||||
if using64bits:
|
||||
|
||||
class IFBTreeTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IFBTree import IFBTree
|
||||
return IFBTree()
|
||||
|
||||
def getTwoValues(self):
|
||||
return 0.5, 1.5
|
||||
|
||||
class IFBTreePyTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IFBTree import IFBTreePy
|
||||
return IFBTreePy()
|
||||
|
||||
def getTwoValues(self):
|
||||
return 0.5, 1.5
|
||||
|
||||
|
||||
class _TestIFBTreesBase(object):
|
||||
|
||||
def testNonIntegerKeyRaises(self):
|
||||
self.assertRaises(TypeError, self._stringraiseskey)
|
||||
self.assertRaises(TypeError, self._floatraiseskey)
|
||||
self.assertRaises(TypeError, self._noneraiseskey)
|
||||
|
||||
def testNonNumericValueRaises(self):
|
||||
self.assertRaises(TypeError, self._stringraisesvalue)
|
||||
self.assertRaises(TypeError, self._noneraisesvalue)
|
||||
self._makeOne()[1] = 1
|
||||
self._makeOne()[1] = 1.0
|
||||
|
||||
def _stringraiseskey(self):
|
||||
self._makeOne()['c'] = 1
|
||||
|
||||
def _floatraiseskey(self):
|
||||
self._makeOne()[2.5] = 1
|
||||
|
||||
def _noneraiseskey(self):
|
||||
self._makeOne()[None] = 1
|
||||
|
||||
def _stringraisesvalue(self):
|
||||
self._makeOne()[1] = 'c'
|
||||
|
||||
def _floatraisesvalue(self):
|
||||
self._makeOne()[1] = 1.4
|
||||
|
||||
def _noneraisesvalue(self):
|
||||
self._makeOne()[1] = None
|
||||
|
||||
|
||||
class TestIFBTrees(_TestIFBTreesBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IFBTree import IFBTree
|
||||
return IFBTree()
|
||||
|
||||
|
||||
class TestIFBTreesPy(_TestIFBTreesBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IFBTree import IFBTreePy
|
||||
return IFBTreePy()
|
||||
|
||||
|
||||
class TestIFMultiUnion(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.IFBTree import multiunion
|
||||
return multiunion(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IFBTree import union
|
||||
return union(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.IFBTree import IFSet as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.IFBTree import IFTreeSet as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.IFBTree import IFBucket as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.IFBTree import IFBTree as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class TestIFMultiUnionPy(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.IFBTree import multiunionPy
|
||||
return multiunionPy(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IFBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.IFBTree import IFSetPy as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.IFBTree import IFTreeSetPy as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.IFBTree import IFBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.IFBTree import IFBTreePy as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class PureIF(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IFBTree import union
|
||||
return union(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.IFBTree import intersection
|
||||
return intersection(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.IFBTree import difference
|
||||
return difference(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.IFBTree import IFBTree
|
||||
from BTrees.IFBTree import IFBucket
|
||||
from BTrees.IFBTree import IFTreeSet
|
||||
from BTrees.IFBTree import IFSet
|
||||
return IFSet, IFTreeSet, makeBuilder(IFBTree), makeBuilder(IFBucket)
|
||||
|
||||
|
||||
class PureIFPy(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IFBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.IFBTree import intersectionPy
|
||||
return intersectionPy(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.IFBTree import differencePy
|
||||
return differencePy(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.IFBTree import IFBTreePy
|
||||
from BTrees.IFBTree import IFBucketPy
|
||||
from BTrees.IFBTree import IFTreeSetPy
|
||||
from BTrees.IFBTree import IFSetPy
|
||||
return (IFSetPy, IFTreeSetPy,
|
||||
makeBuilder(IFBTreePy), makeBuilder(IFBucketPy))
|
||||
|
||||
|
||||
class IFBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFBTree
|
||||
return IFBTree
|
||||
|
||||
|
||||
class IFBTreePyConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFBTreePy
|
||||
return IFBTreePy
|
||||
|
||||
|
||||
class IFBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFBucket
|
||||
return IFBucket
|
||||
|
||||
|
||||
class IFBucketPyConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFBucketPy
|
||||
return IFBucketPy
|
||||
|
||||
|
||||
class IFTreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFTreeSet
|
||||
return IFTreeSet
|
||||
|
||||
|
||||
class IFTreeSetPyConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFTreeSetPy
|
||||
return IFTreeSetPy
|
||||
|
||||
|
||||
class IFSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFSet
|
||||
return IFSet
|
||||
|
||||
|
||||
class IFSetPyConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IFBTree import IFSetPy
|
||||
return IFSetPy
|
||||
|
||||
|
||||
class IFModuleTest(ModuleTest, unittest.TestCase):
|
||||
|
||||
prefix = 'IF'
|
||||
|
||||
def _getModule(self):
|
||||
import BTrees
|
||||
return BTrees.IFBTree
|
||||
|
||||
def _getInterface(self):
|
||||
import BTrees.Interfaces
|
||||
return BTrees.Interfaces.IIntegerFloatBTreeModule
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(IFBTreeInternalKeyTest),
|
||||
unittest.makeSuite(IFBTreePyInternalKeyTest),
|
||||
unittest.makeSuite(IFTreeSetInternalKeyTest),
|
||||
unittest.makeSuite(IFTreeSetPyInternalKeyTest),
|
||||
unittest.makeSuite(IFBucketTest),
|
||||
unittest.makeSuite(IFBucketPyTest),
|
||||
unittest.makeSuite(IFTreeSetTest),
|
||||
unittest.makeSuite(IFTreeSetPyTest),
|
||||
unittest.makeSuite(IFSetTest),
|
||||
unittest.makeSuite(IFSetPyTest),
|
||||
unittest.makeSuite(IFBTreeTest),
|
||||
unittest.makeSuite(IFBTreePyTest),
|
||||
unittest.makeSuite(TestIFBTrees),
|
||||
unittest.makeSuite(TestIFBTreesPy),
|
||||
unittest.makeSuite(TestIFMultiUnion),
|
||||
unittest.makeSuite(TestIFMultiUnionPy),
|
||||
unittest.makeSuite(PureIF),
|
||||
unittest.makeSuite(PureIFPy),
|
||||
unittest.makeSuite(IFBTreeConflictTests),
|
||||
unittest.makeSuite(IFBTreePyConflictTests),
|
||||
unittest.makeSuite(IFBucketConflictTests),
|
||||
unittest.makeSuite(IFBucketPyConflictTests),
|
||||
unittest.makeSuite(IFTreeSetConflictTests),
|
||||
unittest.makeSuite(IFTreeSetPyConflictTests),
|
||||
unittest.makeSuite(IFSetConflictTests),
|
||||
unittest.makeSuite(IFSetPyConflictTests),
|
||||
unittest.makeSuite(IFModuleTest),
|
||||
))
|
||||
@@ -0,0 +1,507 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from .common import BTreeTests
|
||||
from .common import ExtendedSetTests
|
||||
from .common import I_SetsBase
|
||||
from .common import InternalKeysMappingTest
|
||||
from .common import InternalKeysSetTest
|
||||
from .common import MappingBase
|
||||
from .common import MappingConflictTestBase
|
||||
from .common import ModuleTest
|
||||
from .common import MultiUnion
|
||||
from .common import NormalSetTests
|
||||
from .common import SetConflictTestBase
|
||||
from .common import SetResult
|
||||
from .common import TestLongIntKeys
|
||||
from .common import TestLongIntValues
|
||||
from .common import Weighted
|
||||
from .common import itemsToSet
|
||||
from .common import makeBuilder
|
||||
from BTrees.IIBTree import using64bits #XXX Ugly, but unavoidable
|
||||
|
||||
|
||||
class IIBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IIBTree
|
||||
return IIBTree
|
||||
|
||||
|
||||
class IIBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IIBTreePy
|
||||
return IIBTreePy
|
||||
|
||||
|
||||
class IITreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IITreeSet
|
||||
return IITreeSet
|
||||
|
||||
|
||||
class IITreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IITreeSetPy
|
||||
return IITreeSetPy
|
||||
|
||||
|
||||
class IIBucketTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IIBucket
|
||||
return IIBucket
|
||||
|
||||
|
||||
class IIBucketPyTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IIBucketPy
|
||||
return IIBucketPy
|
||||
|
||||
|
||||
class IITreeSetTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IITreeSet
|
||||
return IITreeSet
|
||||
|
||||
|
||||
class IITreeSetPyTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IITreeSetPy
|
||||
return IITreeSetPy
|
||||
|
||||
|
||||
class IISetTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IISet
|
||||
return IISet
|
||||
|
||||
|
||||
class IISetPyTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IISetPy
|
||||
return IISetPy
|
||||
|
||||
|
||||
class _IIBTreeTestBase(BTreeTests):
|
||||
|
||||
def testIIBTreeOverflow(self):
|
||||
good = set()
|
||||
b = self._makeOne()
|
||||
|
||||
def trial(i):
|
||||
i = int(i)
|
||||
try:
|
||||
b[i] = 0
|
||||
except OverflowError:
|
||||
self.assertRaises(OverflowError, b.__setitem__, 0, i)
|
||||
except TypeError:
|
||||
self.assertRaises(TypeError, b.__setitem__, 0, i)
|
||||
else:
|
||||
good.add(i)
|
||||
b[0] = i
|
||||
self.assertEqual(b[0], i)
|
||||
|
||||
for i in range((1<<31) - 3, (1<<31) + 3):
|
||||
trial(i)
|
||||
trial(-i)
|
||||
|
||||
del b[0]
|
||||
self.assertEqual(sorted(good), sorted(b))
|
||||
|
||||
|
||||
class IIBTreeTest(_IIBTreeTestBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IIBTree import IIBTree
|
||||
return IIBTree()
|
||||
|
||||
|
||||
class IIBTreeTestPy(_IIBTreeTestBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IIBTree import IIBTreePy
|
||||
return IIBTreePy()
|
||||
|
||||
|
||||
if using64bits:
|
||||
|
||||
class IIBTreeTest(BTreeTests, TestLongIntKeys, TestLongIntValues,
|
||||
unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IIBTree import IIBTree
|
||||
return IIBTree()
|
||||
|
||||
def getTwoValues(self):
|
||||
return 1, 2
|
||||
|
||||
class IIBTreeTest(BTreeTests, TestLongIntKeys, TestLongIntValues,
|
||||
unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IIBTree import IIBTreePy
|
||||
return IIBTreePy()
|
||||
|
||||
def getTwoValues(self):
|
||||
return 1, 2
|
||||
|
||||
|
||||
class _TestIIBTreesBase(object):
|
||||
|
||||
def testNonIntegerKeyRaises(self):
|
||||
self.assertRaises(TypeError, self._stringraiseskey)
|
||||
self.assertRaises(TypeError, self._floatraiseskey)
|
||||
self.assertRaises(TypeError, self._noneraiseskey)
|
||||
|
||||
def testNonIntegerValueRaises(self):
|
||||
self.assertRaises(TypeError, self._stringraisesvalue)
|
||||
self.assertRaises(TypeError, self._floatraisesvalue)
|
||||
self.assertRaises(TypeError, self._noneraisesvalue)
|
||||
|
||||
def _stringraiseskey(self):
|
||||
self._makeOne()['c'] = 1
|
||||
|
||||
def _floatraiseskey(self):
|
||||
self._makeOne()[2.5] = 1
|
||||
|
||||
def _noneraiseskey(self):
|
||||
self._makeOne()[None] = 1
|
||||
|
||||
def _stringraisesvalue(self):
|
||||
self._makeOne()[1] = 'c'
|
||||
|
||||
def _floatraisesvalue(self):
|
||||
self._makeOne()[1] = 1.4
|
||||
|
||||
def _noneraisesvalue(self):
|
||||
self._makeOne()[1] = None
|
||||
|
||||
|
||||
class TestIIBTrees(_TestIIBTreesBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IIBTree import IIBTree
|
||||
return IIBTree()
|
||||
|
||||
|
||||
class TestIIBTreesPy(_TestIIBTreesBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IIBTree import IIBTreePy
|
||||
return IIBTreePy()
|
||||
|
||||
|
||||
class TestIISets(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IIBTree import IISet
|
||||
return IISet()
|
||||
|
||||
|
||||
class TestIISetsPy(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IIBTree import IISetPy
|
||||
return IISetPy()
|
||||
|
||||
|
||||
class TestIITreeSets(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IIBTree import IITreeSet
|
||||
return IITreeSet()
|
||||
|
||||
|
||||
class TestIITreeSetsPy(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IIBTree import IITreeSetPy
|
||||
return IITreeSetPy()
|
||||
|
||||
|
||||
class PureII(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IIBTree import union
|
||||
return union(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.IIBTree import intersection
|
||||
return intersection(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.IIBTree import difference
|
||||
return difference(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.IIBTree import IIBTree
|
||||
from BTrees.IIBTree import IIBucket
|
||||
from BTrees.IIBTree import IITreeSet
|
||||
from BTrees.IIBTree import IISet
|
||||
return IISet, IITreeSet, makeBuilder(IIBTree), makeBuilder(IIBucket)
|
||||
|
||||
|
||||
class PureIIPy(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IIBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.IIBTree import intersectionPy
|
||||
return intersectionPy(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.IIBTree import differencePy
|
||||
return differencePy(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.IIBTree import IIBTreePy
|
||||
from BTrees.IIBTree import IIBucketPy
|
||||
from BTrees.IIBTree import IITreeSetPy
|
||||
from BTrees.IIBTree import IISetPy
|
||||
return (IISetPy, IITreeSetPy,
|
||||
makeBuilder(IIBTreePy), makeBuilder(IIBucketPy))
|
||||
|
||||
|
||||
class TestIIMultiUnion(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.IIBTree import multiunion
|
||||
return multiunion(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IIBTree import union
|
||||
return union(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.IIBTree import IISet as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.IIBTree import IITreeSet as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.IIBTree import IIBucket as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.IIBTree import IIBTree as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class TestIIMultiUnionPy(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.IIBTree import multiunionPy
|
||||
return multiunionPy(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IIBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.IIBTree import IISetPy as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.IIBTree import IITreeSetPy as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.IIBTree import IIBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.IIBTree import IIBTreePy as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class TestWeightedII(Weighted, unittest.TestCase):
|
||||
|
||||
def weightedUnion(self):
|
||||
from BTrees.IIBTree import weightedUnion
|
||||
return weightedUnion
|
||||
|
||||
def weightedIntersection(self):
|
||||
from BTrees.IIBTree import weightedIntersection
|
||||
return weightedIntersection
|
||||
|
||||
def union(self):
|
||||
from BTrees.IIBTree import union
|
||||
return union
|
||||
|
||||
def intersection(self):
|
||||
from BTrees.IIBTree import intersection
|
||||
return intersection
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.IIBTree import IIBucket as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.IIBTree import IIBTree
|
||||
from BTrees.IIBTree import IIBucket
|
||||
from BTrees.IIBTree import IITreeSet
|
||||
from BTrees.IIBTree import IISet
|
||||
return IIBucket, IIBTree, itemsToSet(IISet), itemsToSet(IITreeSet)
|
||||
|
||||
|
||||
class TestWeightedIIPy(Weighted, unittest.TestCase):
|
||||
|
||||
def weightedUnion(self):
|
||||
from BTrees.IIBTree import weightedUnionPy
|
||||
return weightedUnionPy
|
||||
|
||||
def weightedIntersection(self):
|
||||
from BTrees.IIBTree import weightedIntersectionPy
|
||||
return weightedIntersectionPy
|
||||
|
||||
def union(self):
|
||||
from BTrees.IIBTree import unionPy
|
||||
return unionPy
|
||||
|
||||
def intersection(self):
|
||||
from BTrees.IIBTree import intersectionPy
|
||||
return intersectionPy
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.IIBTree import IIBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.IIBTree import IIBTreePy
|
||||
from BTrees.IIBTree import IIBucketPy
|
||||
from BTrees.IIBTree import IITreeSetPy
|
||||
from BTrees.IIBTree import IISetPy
|
||||
return (IIBucketPy, IIBTreePy,
|
||||
itemsToSet(IISetPy), itemsToSet(IITreeSetPy))
|
||||
|
||||
|
||||
class IIBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IIBTree
|
||||
return IIBTree
|
||||
|
||||
|
||||
class IIBTreeConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IIBTreePy
|
||||
return IIBTreePy
|
||||
|
||||
|
||||
class IIBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IIBucket
|
||||
return IIBucket
|
||||
|
||||
|
||||
class IIBucketConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IIBucketPy
|
||||
return IIBucketPy
|
||||
|
||||
|
||||
class IITreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IITreeSet
|
||||
return IITreeSet
|
||||
|
||||
|
||||
class IITreeSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IITreeSetPy
|
||||
return IITreeSetPy
|
||||
|
||||
|
||||
class IISetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IISet
|
||||
return IISet
|
||||
|
||||
|
||||
class IISetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IIBTree import IISetPy
|
||||
return IISetPy
|
||||
|
||||
|
||||
class IIModuleTest(ModuleTest, unittest.TestCase):
|
||||
|
||||
prefix = 'II'
|
||||
|
||||
def _getModule(self):
|
||||
import BTrees
|
||||
return BTrees.IIBTree
|
||||
def _getInterface(self):
|
||||
import BTrees.Interfaces
|
||||
return BTrees.Interfaces.IIntegerIntegerBTreeModule
|
||||
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(IIBTreeInternalKeyTest),
|
||||
unittest.makeSuite(IIBTreePyInternalKeyTest),
|
||||
unittest.makeSuite(IITreeSetInternalKeyTest),
|
||||
unittest.makeSuite(IITreeSetPyInternalKeyTest),
|
||||
unittest.makeSuite(IIBucketTest),
|
||||
unittest.makeSuite(IIBucketPyTest),
|
||||
unittest.makeSuite(IITreeSetTest),
|
||||
unittest.makeSuite(IITreeSetPyTest),
|
||||
unittest.makeSuite(IISetTest),
|
||||
unittest.makeSuite(IISetPyTest),
|
||||
unittest.makeSuite(IIBTreeTest),
|
||||
unittest.makeSuite(IIBTreeTestPy),
|
||||
unittest.makeSuite(TestIIBTrees),
|
||||
unittest.makeSuite(TestIIBTreesPy),
|
||||
unittest.makeSuite(TestIISets),
|
||||
unittest.makeSuite(TestIISetsPy),
|
||||
unittest.makeSuite(TestIITreeSets),
|
||||
unittest.makeSuite(TestIITreeSetsPy),
|
||||
unittest.makeSuite(TestIIMultiUnion),
|
||||
unittest.makeSuite(TestIIMultiUnionPy),
|
||||
unittest.makeSuite(PureII),
|
||||
unittest.makeSuite(PureIIPy),
|
||||
unittest.makeSuite(TestWeightedII),
|
||||
unittest.makeSuite(TestWeightedIIPy),
|
||||
unittest.makeSuite(IIBTreeConflictTests),
|
||||
unittest.makeSuite(IIBTreeConflictTestsPy),
|
||||
unittest.makeSuite(IIBucketConflictTests),
|
||||
unittest.makeSuite(IIBucketConflictTestsPy),
|
||||
unittest.makeSuite(IITreeSetConflictTests),
|
||||
unittest.makeSuite(IITreeSetConflictTestsPy),
|
||||
unittest.makeSuite(IISetConflictTests),
|
||||
unittest.makeSuite(IISetConflictTestsPy),
|
||||
unittest.makeSuite(IIModuleTest),
|
||||
))
|
||||
@@ -0,0 +1,414 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from .common import BTreeTests
|
||||
from .common import ExtendedSetTests
|
||||
from .common import I_SetsBase
|
||||
from .common import InternalKeysMappingTest
|
||||
from .common import InternalKeysSetTest
|
||||
from .common import MappingBase
|
||||
from .common import MappingConflictTestBase
|
||||
from .common import ModuleTest
|
||||
from .common import MultiUnion
|
||||
from .common import NormalSetTests
|
||||
from .common import SetConflictTestBase
|
||||
from .common import SetResult
|
||||
from .common import TypeTest
|
||||
from .common import TestLongIntKeys
|
||||
from .common import makeBuilder
|
||||
from BTrees.IIBTree import using64bits #XXX Ugly, but unavoidable
|
||||
|
||||
|
||||
class IOBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOBTree
|
||||
return IOBTree
|
||||
|
||||
|
||||
class IOBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOBTreePy
|
||||
return IOBTreePy
|
||||
|
||||
|
||||
class IOTreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOTreeSet
|
||||
return IOTreeSet
|
||||
|
||||
|
||||
class IOTreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOTreeSetPy
|
||||
return IOTreeSetPy
|
||||
|
||||
|
||||
class IOBucketTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOBucket
|
||||
return IOBucket
|
||||
|
||||
|
||||
class IOBucketPyTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOBucketPy
|
||||
return IOBucketPy
|
||||
|
||||
|
||||
class IOTreeSetTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOTreeSet
|
||||
return IOTreeSet
|
||||
|
||||
|
||||
class IOTreeSetPyTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOTreeSetPy
|
||||
return IOTreeSetPy
|
||||
|
||||
|
||||
class IOSetTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOSet
|
||||
return IOSet
|
||||
|
||||
|
||||
class IOSetPyTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOSetPy
|
||||
return IOSetPy
|
||||
|
||||
|
||||
class IOBTreeTest(BTreeTests, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IOBTree import IOBTree
|
||||
return IOBTree()
|
||||
|
||||
|
||||
class IOBTreePyTest(BTreeTests, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IOBTree import IOBTreePy
|
||||
return IOBTreePy()
|
||||
|
||||
|
||||
if using64bits:
|
||||
|
||||
|
||||
class IOBTreeTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IOBTree import IOBTree
|
||||
return IOBTree()
|
||||
|
||||
|
||||
class IOBTreePyTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IOBTree import IOBTreePy
|
||||
return IOBTreePy()
|
||||
|
||||
|
||||
class _TestIOBTreesBase(TypeTest):
|
||||
|
||||
def _stringraises(self):
|
||||
self._makeOne()['c'] = 1
|
||||
|
||||
def _floatraises(self):
|
||||
self._makeOne()[2.5] = 1
|
||||
|
||||
def _noneraises(self):
|
||||
self._makeOne()[None] = 1
|
||||
|
||||
def testStringAllowedInContains(self):
|
||||
self.assertFalse('key' in self._makeOne())
|
||||
|
||||
def testStringKeyRaisesKeyErrorWhenMissing(self):
|
||||
self.assertRaises(KeyError, self._makeOne().__getitem__, 'key')
|
||||
|
||||
def testStringKeyReturnsDefaultFromGetWhenMissing(self):
|
||||
self.assertEqual(self._makeOne().get('key', 42), 42)
|
||||
|
||||
class TestIOBTrees(_TestIOBTreesBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IOBTree import IOBTree
|
||||
return IOBTree()
|
||||
|
||||
|
||||
class TestIOBTreesPy(_TestIOBTreesBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IOBTree import IOBTreePy
|
||||
return IOBTreePy()
|
||||
|
||||
|
||||
class TestIOSets(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IOBTree import IOSet
|
||||
return IOSet()
|
||||
|
||||
|
||||
class TestIOSetsPy(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IOBTree import IOSetPy
|
||||
return IOSetPy()
|
||||
|
||||
|
||||
class TestIOTreeSets(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IOBTree import IOTreeSet
|
||||
return IOTreeSet()
|
||||
|
||||
|
||||
class TestIOTreeSetsPy(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.IOBTree import IOTreeSetPy
|
||||
return IOTreeSetPy()
|
||||
|
||||
|
||||
class PureIO(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IOBTree import union
|
||||
return union(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.IOBTree import intersection
|
||||
return intersection(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.IOBTree import difference
|
||||
return difference(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.IOBTree import IOBTree
|
||||
from BTrees.IOBTree import IOBucket
|
||||
from BTrees.IOBTree import IOTreeSet
|
||||
from BTrees.IOBTree import IOSet
|
||||
return IOSet, IOTreeSet, makeBuilder(IOBTree), makeBuilder(IOBucket)
|
||||
|
||||
|
||||
class PureIOPy(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IOBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.IOBTree import intersectionPy
|
||||
return intersectionPy(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.IOBTree import differencePy
|
||||
return differencePy(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.IOBTree import IOBTreePy
|
||||
from BTrees.IOBTree import IOBucketPy
|
||||
from BTrees.IOBTree import IOTreeSetPy
|
||||
from BTrees.IOBTree import IOSetPy
|
||||
return (IOSetPy, IOTreeSetPy,
|
||||
makeBuilder(IOBTreePy), makeBuilder(IOBucketPy))
|
||||
|
||||
|
||||
class TestIOMultiUnion(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.IOBTree import multiunion
|
||||
return multiunion(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IOBTree import union
|
||||
return union(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.IOBTree import IOSet as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.IOBTree import IOTreeSet as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.IOBTree import IOBucket as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.IOBTree import IOBTree as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class TestIOMultiUnionPy(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.IOBTree import multiunionPy
|
||||
return multiunionPy(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.IOBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.IOBTree import IOSetPy as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.IOBTree import IOTreeSetPy as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.IOBTree import IOBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.IOBTree import IOBTreePy as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class IOBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOBTree
|
||||
return IOBTree
|
||||
|
||||
|
||||
class IOBTreeConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOBTreePy
|
||||
return IOBTreePy
|
||||
|
||||
|
||||
class IOBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOBucket
|
||||
return IOBucket
|
||||
|
||||
|
||||
class IOBucketConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOBucketPy
|
||||
return IOBucketPy
|
||||
|
||||
|
||||
class IOTreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOTreeSet
|
||||
return IOTreeSet
|
||||
|
||||
|
||||
class IOTreeSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOTreeSetPy
|
||||
return IOTreeSetPy
|
||||
|
||||
|
||||
class IOSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOSet
|
||||
return IOSet
|
||||
|
||||
|
||||
class IOSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.IOBTree import IOSetPy
|
||||
return IOSetPy
|
||||
|
||||
|
||||
class IOModuleTest(ModuleTest, unittest.TestCase):
|
||||
|
||||
prefix = 'IO'
|
||||
|
||||
def _getModule(self):
|
||||
import BTrees
|
||||
return BTrees.IOBTree
|
||||
|
||||
def _getInterface(self):
|
||||
import BTrees.Interfaces
|
||||
return BTrees.Interfaces.IIntegerObjectBTreeModule
|
||||
|
||||
def test_weightedUnion_not_present(self):
|
||||
try:
|
||||
from BTrees.IOBTree import weightedUnion
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
self.fail("IOBTree shouldn't have weightedUnion")
|
||||
|
||||
def test_weightedIntersection_not_present(self):
|
||||
try:
|
||||
from BTrees.IOBTree import weightedIntersection
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
self.fail("IOBTree shouldn't have weightedIntersection")
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(IOBTreeInternalKeyTest),
|
||||
unittest.makeSuite(IOBTreePyInternalKeyTest),
|
||||
unittest.makeSuite(IOTreeSetInternalKeyTest),
|
||||
unittest.makeSuite(IOTreeSetPyInternalKeyTest),
|
||||
unittest.makeSuite(IOBucketTest),
|
||||
unittest.makeSuite(IOBucketPyTest),
|
||||
unittest.makeSuite(IOTreeSetTest),
|
||||
unittest.makeSuite(IOTreeSetPyTest),
|
||||
unittest.makeSuite(IOSetTest),
|
||||
unittest.makeSuite(IOSetPyTest),
|
||||
unittest.makeSuite(IOBTreeTest),
|
||||
unittest.makeSuite(IOBTreePyTest),
|
||||
unittest.makeSuite(TestIOBTrees),
|
||||
unittest.makeSuite(TestIOBTreesPy),
|
||||
unittest.makeSuite(TestIOSets),
|
||||
unittest.makeSuite(TestIOSetsPy),
|
||||
unittest.makeSuite(TestIOTreeSets),
|
||||
unittest.makeSuite(TestIOTreeSetsPy),
|
||||
unittest.makeSuite(TestIOMultiUnion),
|
||||
unittest.makeSuite(TestIOMultiUnionPy),
|
||||
unittest.makeSuite(PureIO),
|
||||
unittest.makeSuite(PureIOPy),
|
||||
unittest.makeSuite(IOBTreeConflictTests),
|
||||
unittest.makeSuite(IOBTreeConflictTestsPy),
|
||||
unittest.makeSuite(IOBucketConflictTests),
|
||||
unittest.makeSuite(IOBucketConflictTestsPy),
|
||||
unittest.makeSuite(IOTreeSetConflictTests),
|
||||
unittest.makeSuite(IOTreeSetConflictTestsPy),
|
||||
unittest.makeSuite(IOSetConflictTests),
|
||||
unittest.makeSuite(IOSetConflictTestsPy),
|
||||
unittest.makeSuite(IOModuleTest),
|
||||
))
|
||||
@@ -0,0 +1,316 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from .common import BTreeTests
|
||||
from .common import ExtendedSetTests
|
||||
from .common import InternalKeysMappingTest
|
||||
from .common import InternalKeysSetTest
|
||||
from .common import MappingBase
|
||||
from .common import MappingConflictTestBase
|
||||
from .common import ModuleTest
|
||||
from .common import MultiUnion
|
||||
from .common import NormalSetTests
|
||||
from .common import SetConflictTestBase
|
||||
from .common import SetResult
|
||||
from .common import TestLongIntKeys
|
||||
from .common import makeBuilder
|
||||
|
||||
|
||||
class LFBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFBTree
|
||||
return LFBTree
|
||||
|
||||
|
||||
class LFBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFBTreePy
|
||||
return LFBTreePy
|
||||
|
||||
|
||||
class LFTreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFTreeSet
|
||||
return LFTreeSet
|
||||
|
||||
|
||||
class LFTreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFTreeSetPy
|
||||
return LFTreeSetPy
|
||||
|
||||
|
||||
class LFBucketTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFBucket
|
||||
return LFBucket
|
||||
|
||||
|
||||
class LFBucketPyTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFBucketPy
|
||||
return LFBucketPy
|
||||
|
||||
|
||||
class LFTreeSetTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFTreeSet
|
||||
return LFTreeSet
|
||||
|
||||
|
||||
class LFTreeSetPyTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFTreeSetPy
|
||||
return LFTreeSetPy
|
||||
|
||||
|
||||
class LFSetTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFSet
|
||||
return LFSet
|
||||
|
||||
|
||||
class LFSetPyTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFSetPy
|
||||
return LFSetPy
|
||||
|
||||
|
||||
class LFBTreeTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LFBTree import LFBTree
|
||||
return LFBTree()
|
||||
|
||||
def getTwoValues(self):
|
||||
return 0.5, 1.5
|
||||
|
||||
|
||||
class LFBTreePyTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LFBTree import LFBTreePy
|
||||
return LFBTreePy()
|
||||
|
||||
def getTwoValues(self):
|
||||
return 0.5, 1.5
|
||||
|
||||
|
||||
class TestLFMultiUnion(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.LFBTree import multiunionPy
|
||||
return multiunionPy(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LFBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.LFBTree import LFSetPy as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.LFBTree import LFTreeSetPy as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.LFBTree import LFBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.LFBTree import LFBTreePy as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class TestLFMultiUnionPy(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.LFBTree import multiunionPy
|
||||
return multiunionPy(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LFBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.LFBTree import LFSetPy as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.LFBTree import LFTreeSetPy as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.LFBTree import LFBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.LFBTree import LFBTreePy as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class PureLF(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LFBTree import union
|
||||
return union(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.LFBTree import intersection
|
||||
return intersection(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.LFBTree import difference
|
||||
return difference(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.LFBTree import LFBTree
|
||||
from BTrees.LFBTree import LFBucket
|
||||
from BTrees.LFBTree import LFTreeSet
|
||||
from BTrees.LFBTree import LFSet
|
||||
return LFSet, LFTreeSet, makeBuilder(LFBTree), makeBuilder(LFBucket)
|
||||
|
||||
|
||||
class PureLFPy(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LFBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.LFBTree import intersectionPy
|
||||
return intersectionPy(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.LFBTree import differencePy
|
||||
return differencePy(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.LFBTree import LFBTreePy
|
||||
from BTrees.LFBTree import LFBucketPy
|
||||
from BTrees.LFBTree import LFTreeSetPy
|
||||
from BTrees.LFBTree import LFSetPy
|
||||
return (LFSetPy, LFTreeSetPy,
|
||||
makeBuilder(LFBTreePy), makeBuilder(LFBucketPy))
|
||||
|
||||
|
||||
class LFBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFBTree
|
||||
return LFBTree
|
||||
|
||||
|
||||
class LFBTreeConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFBTreePy
|
||||
return LFBTreePy
|
||||
|
||||
|
||||
class LFBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFBucket
|
||||
return LFBucket
|
||||
|
||||
|
||||
class LFBucketConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFBucketPy
|
||||
return LFBucketPy
|
||||
|
||||
|
||||
class LFTreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFTreeSet
|
||||
return LFTreeSet
|
||||
|
||||
|
||||
class LFTreeSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFTreeSetPy
|
||||
return LFTreeSetPy
|
||||
|
||||
|
||||
class LFSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFSet
|
||||
return LFSet
|
||||
|
||||
|
||||
class LFSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LFBTree import LFSetPy
|
||||
return LFSetPy
|
||||
|
||||
|
||||
class LFModuleTest(ModuleTest, unittest.TestCase):
|
||||
|
||||
prefix = 'LF'
|
||||
|
||||
def _getModule(self):
|
||||
import BTrees
|
||||
return BTrees.LFBTree
|
||||
|
||||
def _getInterface(self):
|
||||
import BTrees.Interfaces
|
||||
return BTrees.Interfaces.IIntegerFloatBTreeModule
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(LFBTreeInternalKeyTest),
|
||||
unittest.makeSuite(LFBTreePyInternalKeyTest),
|
||||
unittest.makeSuite(LFTreeSetInternalKeyTest),
|
||||
unittest.makeSuite(LFTreeSetPyInternalKeyTest),
|
||||
unittest.makeSuite(LFBucketTest),
|
||||
unittest.makeSuite(LFBucketPyTest),
|
||||
unittest.makeSuite(LFTreeSetTest),
|
||||
unittest.makeSuite(LFTreeSetPyTest),
|
||||
unittest.makeSuite(LFSetTest),
|
||||
unittest.makeSuite(LFSetPyTest),
|
||||
unittest.makeSuite(LFBTreeTest),
|
||||
unittest.makeSuite(LFBTreePyTest),
|
||||
unittest.makeSuite(TestLFMultiUnion),
|
||||
unittest.makeSuite(TestLFMultiUnionPy),
|
||||
unittest.makeSuite(PureLF),
|
||||
unittest.makeSuite(PureLFPy),
|
||||
unittest.makeSuite(LFBTreeConflictTests),
|
||||
unittest.makeSuite(LFBTreeConflictTestsPy),
|
||||
unittest.makeSuite(LFBucketConflictTests),
|
||||
unittest.makeSuite(LFBucketConflictTestsPy),
|
||||
unittest.makeSuite(LFTreeSetConflictTests),
|
||||
unittest.makeSuite(LFTreeSetConflictTestsPy),
|
||||
unittest.makeSuite(LFSetConflictTests),
|
||||
unittest.makeSuite(LFSetConflictTestsPy),
|
||||
unittest.makeSuite(LFModuleTest),
|
||||
))
|
||||
@@ -0,0 +1,415 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from .common import BTreeTests
|
||||
from .common import ExtendedSetTests
|
||||
from .common import I_SetsBase
|
||||
from .common import InternalKeysMappingTest
|
||||
from .common import InternalKeysSetTest
|
||||
from .common import MappingBase
|
||||
from .common import MappingConflictTestBase
|
||||
from .common import ModuleTest
|
||||
from .common import MultiUnion
|
||||
from .common import NormalSetTests
|
||||
from .common import SetConflictTestBase
|
||||
from .common import SetResult
|
||||
from .common import TestLongIntKeys
|
||||
from .common import TestLongIntValues
|
||||
from .common import Weighted
|
||||
from .common import itemsToSet
|
||||
from .common import makeBuilder
|
||||
|
||||
|
||||
class LLBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLBTree
|
||||
return LLBTree
|
||||
|
||||
|
||||
class LLBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLBTreePy
|
||||
return LLBTreePy
|
||||
|
||||
|
||||
class LLTreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLTreeSet
|
||||
return LLTreeSet
|
||||
|
||||
|
||||
class LLTreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLTreeSetPy
|
||||
return LLTreeSetPy
|
||||
|
||||
|
||||
class LLBucketTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLBucket
|
||||
return LLBucket
|
||||
|
||||
|
||||
class LLBucketTestPy(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLBucketPy
|
||||
return LLBucketPy
|
||||
|
||||
|
||||
class LLTreeSetTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLTreeSet
|
||||
return LLTreeSet
|
||||
|
||||
|
||||
class LLTreeSetTestPy(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLTreeSetPy
|
||||
return LLTreeSetPy
|
||||
|
||||
|
||||
class LLSetTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLSet
|
||||
return LLSet
|
||||
|
||||
|
||||
class LLSetTestPy(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLSetPy
|
||||
return LLSetPy
|
||||
|
||||
|
||||
class LLBTreeTest(BTreeTests, TestLongIntKeys, TestLongIntValues,
|
||||
unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LLBTree import LLBTree
|
||||
return LLBTree()
|
||||
def getTwoValues(self):
|
||||
return 1, 2
|
||||
|
||||
|
||||
class LLBTreeTestPy(BTreeTests, TestLongIntKeys, TestLongIntValues,
|
||||
unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LLBTree import LLBTreePy
|
||||
return LLBTreePy()
|
||||
def getTwoValues(self):
|
||||
return 1, 2
|
||||
|
||||
|
||||
class TestLLSets(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LLBTree import LLSet
|
||||
return LLSet()
|
||||
|
||||
|
||||
class TestLLSetsPy(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LLBTree import LLSetPy
|
||||
return LLSetPy()
|
||||
|
||||
|
||||
class TestLLTreeSets(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LLBTree import LLTreeSet
|
||||
return LLTreeSet()
|
||||
|
||||
|
||||
class TestLLTreeSetsPy(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LLBTree import LLTreeSetPy
|
||||
return LLTreeSetPy()
|
||||
|
||||
|
||||
class PureLL(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LLBTree import union
|
||||
return union(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.LLBTree import intersection
|
||||
return intersection(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.LLBTree import difference
|
||||
return difference(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.LLBTree import LLBTree
|
||||
from BTrees.LLBTree import LLBucket
|
||||
from BTrees.LLBTree import LLTreeSet
|
||||
from BTrees.LLBTree import LLSet
|
||||
return LLSet, LLTreeSet, makeBuilder(LLBTree), makeBuilder(LLBucket)
|
||||
|
||||
|
||||
class PureLLPy(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LLBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.LLBTree import intersectionPy
|
||||
return intersectionPy(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.LLBTree import differencePy
|
||||
return differencePy(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.LLBTree import LLBTreePy
|
||||
from BTrees.LLBTree import LLBucketPy
|
||||
from BTrees.LLBTree import LLTreeSetPy
|
||||
from BTrees.LLBTree import LLSetPy
|
||||
return (LLSetPy, LLTreeSetPy,
|
||||
makeBuilder(LLBTreePy), makeBuilder(LLBucketPy))
|
||||
|
||||
|
||||
class TestLLMultiUnion(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.LLBTree import multiunion
|
||||
return multiunion(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LLBTree import union
|
||||
return union(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.LLBTree import LLSet as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.LLBTree import LLTreeSet as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.LLBTree import LLBucket as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.LLBTree import LLBTree as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class TestLLMultiUnionPy(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.LLBTree import multiunionPy
|
||||
return multiunionPy(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LLBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.LLBTree import LLSetPy as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.LLBTree import LLTreeSetPy as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.LLBTree import LLBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.LLBTree import LLBTreePy as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class TestWeightedLL(Weighted, unittest.TestCase):
|
||||
|
||||
def weightedUnion(self):
|
||||
from BTrees.LLBTree import weightedUnion
|
||||
return weightedUnion
|
||||
|
||||
def weightedIntersection(self):
|
||||
from BTrees.LLBTree import weightedIntersection
|
||||
return weightedIntersection
|
||||
|
||||
def union(self):
|
||||
from BTrees.LLBTree import union
|
||||
return union
|
||||
|
||||
def intersection(self):
|
||||
from BTrees.LLBTree import intersection
|
||||
return intersection
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.LLBTree import LLBucket as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.LLBTree import LLBTree
|
||||
from BTrees.LLBTree import LLBucket
|
||||
from BTrees.LLBTree import LLTreeSet
|
||||
from BTrees.LLBTree import LLSet
|
||||
return LLBucket, LLBTree, itemsToSet(LLSet), itemsToSet(LLTreeSet)
|
||||
|
||||
|
||||
class TestWeightedLLPy(Weighted, unittest.TestCase):
|
||||
|
||||
def weightedUnion(self):
|
||||
from BTrees.LLBTree import weightedUnionPy
|
||||
return weightedUnionPy
|
||||
|
||||
def weightedIntersection(self):
|
||||
from BTrees.LLBTree import weightedIntersectionPy
|
||||
return weightedIntersectionPy
|
||||
|
||||
def union(self):
|
||||
from BTrees.LLBTree import unionPy
|
||||
return unionPy
|
||||
|
||||
def intersection(self):
|
||||
from BTrees.LLBTree import intersectionPy
|
||||
return intersectionPy
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.LLBTree import LLBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.LLBTree import LLBTreePy
|
||||
from BTrees.LLBTree import LLBucketPy
|
||||
from BTrees.LLBTree import LLTreeSetPy
|
||||
from BTrees.LLBTree import LLSetPy
|
||||
return (LLBucketPy, LLBTreePy,
|
||||
itemsToSet(LLSetPy), itemsToSet(LLTreeSetPy))
|
||||
|
||||
|
||||
class LLBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLBTree
|
||||
return LLBTree
|
||||
|
||||
|
||||
class LLBTreeConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLBTreePy
|
||||
return LLBTreePy
|
||||
|
||||
|
||||
class LLBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLBucket
|
||||
return LLBucket
|
||||
|
||||
|
||||
class LLBucketConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLBucketPy
|
||||
return LLBucketPy
|
||||
|
||||
|
||||
class LLTreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLTreeSet
|
||||
return LLTreeSet
|
||||
|
||||
|
||||
class LLTreeSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLTreeSetPy
|
||||
return LLTreeSetPy
|
||||
|
||||
|
||||
class LLSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLSet
|
||||
return LLSet
|
||||
|
||||
|
||||
class LLSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LLBTree import LLSetPy
|
||||
return LLSetPy
|
||||
|
||||
|
||||
class LLModuleTest(ModuleTest, unittest.TestCase):
|
||||
|
||||
prefix = 'LL'
|
||||
|
||||
def _getModule(self):
|
||||
import BTrees
|
||||
return BTrees.LLBTree
|
||||
|
||||
def _getInterface(self):
|
||||
import BTrees.Interfaces
|
||||
return BTrees.Interfaces.IIntegerIntegerBTreeModule
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(LLBTreeInternalKeyTest),
|
||||
unittest.makeSuite(LLBTreeInternalKeyTest),
|
||||
unittest.makeSuite(LLTreeSetInternalKeyTest),
|
||||
unittest.makeSuite(LLTreeSetInternalKeyTest),
|
||||
unittest.makeSuite(LLBucketTest),
|
||||
unittest.makeSuite(LLBucketTest),
|
||||
unittest.makeSuite(LLTreeSetTest),
|
||||
unittest.makeSuite(LLTreeSetTest),
|
||||
unittest.makeSuite(LLSetTest),
|
||||
unittest.makeSuite(LLSetTest),
|
||||
unittest.makeSuite(LLBTreeTest),
|
||||
unittest.makeSuite(LLBTreeTest),
|
||||
unittest.makeSuite(TestLLSets),
|
||||
unittest.makeSuite(TestLLSets),
|
||||
unittest.makeSuite(TestLLTreeSets),
|
||||
unittest.makeSuite(TestLLTreeSets),
|
||||
unittest.makeSuite(TestLLMultiUnion),
|
||||
unittest.makeSuite(TestLLMultiUnion),
|
||||
unittest.makeSuite(PureLL),
|
||||
unittest.makeSuite(PureLL),
|
||||
unittest.makeSuite(TestWeightedLL),
|
||||
unittest.makeSuite(TestWeightedLL),
|
||||
unittest.makeSuite(LLBTreeConflictTests),
|
||||
unittest.makeSuite(LLBTreeConflictTests),
|
||||
unittest.makeSuite(LLBucketConflictTests),
|
||||
unittest.makeSuite(LLBucketConflictTests),
|
||||
unittest.makeSuite(LLTreeSetConflictTests),
|
||||
unittest.makeSuite(LLTreeSetConflictTests),
|
||||
unittest.makeSuite(LLSetConflictTests),
|
||||
unittest.makeSuite(LLSetConflictTests),
|
||||
unittest.makeSuite(LLModuleTest),
|
||||
))
|
||||
@@ -0,0 +1,359 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from .common import BTreeTests
|
||||
from .common import ExtendedSetTests
|
||||
from .common import I_SetsBase
|
||||
from .common import InternalKeysMappingTest
|
||||
from .common import InternalKeysSetTest
|
||||
from .common import MappingBase
|
||||
from .common import MappingConflictTestBase
|
||||
from .common import ModuleTest
|
||||
from .common import MultiUnion
|
||||
from .common import NormalSetTests
|
||||
from .common import SetConflictTestBase
|
||||
from .common import SetResult
|
||||
from .common import TestLongIntKeys
|
||||
from .common import makeBuilder
|
||||
|
||||
|
||||
class LOBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOBTree
|
||||
return LOBTree
|
||||
|
||||
|
||||
class LOBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOBTreePy
|
||||
return LOBTreePy
|
||||
|
||||
|
||||
class LOTreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOTreeSet
|
||||
return LOTreeSet
|
||||
|
||||
|
||||
class LOTreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOTreeSetPy
|
||||
return LOTreeSetPy
|
||||
|
||||
|
||||
class LOBucketTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOBucket
|
||||
return LOBucket
|
||||
|
||||
|
||||
class LOBucketPyTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOBucketPy
|
||||
return LOBucketPy
|
||||
|
||||
|
||||
class LOTreeSetTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOTreeSet
|
||||
return LOTreeSet
|
||||
|
||||
|
||||
class LOTreeSetPyTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOTreeSetPy
|
||||
return LOTreeSetPy
|
||||
|
||||
|
||||
class LOSetTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOSet
|
||||
return LOSet
|
||||
|
||||
|
||||
class LOSetPyTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOSetPy
|
||||
return LOSetPy
|
||||
|
||||
|
||||
class LOBTreeTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LOBTree import LOBTree
|
||||
return LOBTree()
|
||||
|
||||
|
||||
class LOBTreePyTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LOBTree import LOBTreePy
|
||||
return LOBTreePy()
|
||||
|
||||
|
||||
class TestLOSets(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LOBTree import LOSet
|
||||
return LOSet()
|
||||
|
||||
|
||||
class TestLOSetsPy(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LOBTree import LOSetPy
|
||||
return LOSetPy()
|
||||
|
||||
|
||||
class TestLOTreeSets(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LOBTree import LOTreeSet
|
||||
return LOTreeSet()
|
||||
|
||||
|
||||
class TestLOTreeSetsPy(I_SetsBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.LOBTree import LOTreeSetPy
|
||||
return LOTreeSetPy()
|
||||
|
||||
|
||||
class TestLOMultiUnion(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.LOBTree import multiunion
|
||||
return multiunion(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LOBTree import union
|
||||
return union(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.LOBTree import LOSet as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.LOBTree import LOTreeSet as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.LOBTree import LOBucket as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.LOBTree import LOBTree as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class TestLOMultiUnionPy(MultiUnion, unittest.TestCase):
|
||||
|
||||
def multiunion(self, *args):
|
||||
from BTrees.LOBTree import multiunionPy
|
||||
return multiunionPy(*args)
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LOBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def mkset(self, *args):
|
||||
from BTrees.LOBTree import LOSetPy as mkset
|
||||
return mkset(*args)
|
||||
|
||||
def mktreeset(self, *args):
|
||||
from BTrees.LOBTree import LOTreeSetPy as mktreeset
|
||||
return mktreeset(*args)
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.LOBTree import LOBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def mkbtree(self, *args):
|
||||
from BTrees.LOBTree import LOBTreePy as mkbtree
|
||||
return mkbtree(*args)
|
||||
|
||||
|
||||
class PureLO(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LOBTree import union
|
||||
return union(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.LOBTree import intersection
|
||||
return intersection(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.LOBTree import difference
|
||||
return difference(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.LOBTree import LOBTree
|
||||
from BTrees.LOBTree import LOBucket
|
||||
from BTrees.LOBTree import LOTreeSet
|
||||
from BTrees.LOBTree import LOSet
|
||||
return LOSet, LOTreeSet, makeBuilder(LOBTree), makeBuilder(LOBucket)
|
||||
|
||||
|
||||
class PureLOPy(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.LOBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.LOBTree import intersectionPy
|
||||
return intersectionPy(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.LOBTree import differencePy
|
||||
return differencePy(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.LOBTree import LOBTreePy
|
||||
from BTrees.LOBTree import LOBucketPy
|
||||
from BTrees.LOBTree import LOTreeSetPy
|
||||
from BTrees.LOBTree import LOSetPy
|
||||
return (LOSetPy, LOTreeSetPy,
|
||||
makeBuilder(LOBTreePy), makeBuilder(LOBucketPy))
|
||||
|
||||
|
||||
class LOBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOBTree
|
||||
return LOBTree
|
||||
|
||||
|
||||
class LOBTreeConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOBTreePy
|
||||
return LOBTreePy
|
||||
|
||||
|
||||
class LOBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOBucket
|
||||
return LOBucket
|
||||
|
||||
|
||||
class LOBucketConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOBucketPy
|
||||
return LOBucketPy
|
||||
|
||||
|
||||
class LOTreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOTreeSet
|
||||
return LOTreeSet
|
||||
|
||||
|
||||
class LOTreeSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOTreeSetPy
|
||||
return LOTreeSetPy
|
||||
|
||||
|
||||
class LOSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOSet
|
||||
return LOSet
|
||||
|
||||
|
||||
class LOSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.LOBTree import LOSetPy
|
||||
return LOSetPy
|
||||
|
||||
|
||||
class LOModuleTest(ModuleTest, unittest.TestCase):
|
||||
|
||||
prefix = 'LO'
|
||||
|
||||
def _getModule(self):
|
||||
import BTrees
|
||||
return BTrees.LOBTree
|
||||
|
||||
def _getInterface(self):
|
||||
import BTrees.Interfaces
|
||||
return BTrees.Interfaces.IIntegerObjectBTreeModule
|
||||
|
||||
def test_weightedUnion_not_present(self):
|
||||
try:
|
||||
from BTrees.LOBTree import weightedUnion
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
self.fail("LOBTree shouldn't have weightedUnion")
|
||||
|
||||
def test_weightedIntersection_not_present(self):
|
||||
try:
|
||||
from BTrees.LOBTree import weightedIntersection
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
self.fail("LOBTree shouldn't have weightedIntersection")
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(LOBTreeInternalKeyTest),
|
||||
unittest.makeSuite(LOBTreePyInternalKeyTest),
|
||||
unittest.makeSuite(LOTreeSetInternalKeyTest),
|
||||
unittest.makeSuite(LOTreeSetPyInternalKeyTest),
|
||||
unittest.makeSuite(LOBucketTest),
|
||||
unittest.makeSuite(LOBucketPyTest),
|
||||
unittest.makeSuite(LOTreeSetTest),
|
||||
unittest.makeSuite(LOTreeSetPyTest),
|
||||
unittest.makeSuite(LOSetTest),
|
||||
unittest.makeSuite(LOSetPyTest),
|
||||
unittest.makeSuite(LOBTreeTest),
|
||||
unittest.makeSuite(LOBTreePyTest),
|
||||
unittest.makeSuite(TestLOSets),
|
||||
unittest.makeSuite(TestLOSetsPy),
|
||||
unittest.makeSuite(TestLOTreeSets),
|
||||
unittest.makeSuite(TestLOTreeSetsPy),
|
||||
unittest.makeSuite(TestLOMultiUnion),
|
||||
unittest.makeSuite(TestLOMultiUnionPy),
|
||||
unittest.makeSuite(PureLO),
|
||||
unittest.makeSuite(PureLOPy),
|
||||
unittest.makeSuite(LOBTreeConflictTests),
|
||||
unittest.makeSuite(LOBTreeConflictTestsPy),
|
||||
unittest.makeSuite(LOBucketConflictTests),
|
||||
unittest.makeSuite(LOBucketConflictTestsPy),
|
||||
unittest.makeSuite(LOTreeSetConflictTests),
|
||||
unittest.makeSuite(LOTreeSetConflictTestsPy),
|
||||
unittest.makeSuite(LOSetConflictTests),
|
||||
unittest.makeSuite(LOSetConflictTestsPy),
|
||||
unittest.makeSuite(LOModuleTest),
|
||||
))
|
||||
@@ -0,0 +1,110 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2008 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
|
||||
_marker = object()
|
||||
|
||||
class LengthTestCase(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.Length import Length
|
||||
return Length
|
||||
|
||||
def _makeOne(self, value=_marker):
|
||||
if value is _marker:
|
||||
return self._getTargetClass()()
|
||||
return self._getTargetClass()(value)
|
||||
|
||||
def test_ctor_default(self):
|
||||
length = self._makeOne()
|
||||
self.assertEqual(length.value, 0)
|
||||
|
||||
def test_ctor_explict(self):
|
||||
length = self._makeOne(42)
|
||||
self.assertEqual(length.value, 42)
|
||||
|
||||
def test___getstate___(self):
|
||||
length = self._makeOne(42)
|
||||
self.assertEqual(length.__getstate__(), 42)
|
||||
|
||||
def test___setstate__(self):
|
||||
length = self._makeOne()
|
||||
length.__setstate__(42)
|
||||
self.assertEqual(length.value, 42)
|
||||
|
||||
def test_set(self):
|
||||
length = self._makeOne()
|
||||
length.set(42)
|
||||
self.assertEqual(length.value, 42)
|
||||
|
||||
def test__p_resolveConflict(self):
|
||||
length = self._makeOne()
|
||||
self.assertEqual(length._p_resolveConflict(5, 7, 9), 11)
|
||||
|
||||
def test_change_w_positive_delta(self):
|
||||
length = self._makeOne()
|
||||
length.change(3)
|
||||
self.assertEqual(length.value, 3)
|
||||
|
||||
def test_change_w_negative_delta(self):
|
||||
length = self._makeOne()
|
||||
length.change(-3)
|
||||
self.assertEqual(length.value, -3)
|
||||
|
||||
def test_change_overflows_to_long(self):
|
||||
import sys
|
||||
try:
|
||||
length = self._makeOne(sys.maxint)
|
||||
except AttributeError: #pragma NO COVER Py3k
|
||||
return
|
||||
else: #pragma NO COVER Py2
|
||||
self.assertEqual(length(), sys.maxint)
|
||||
self.assertTrue(type(length()) is int)
|
||||
length.change(+1)
|
||||
self.assertEqual(length(), sys.maxint + 1)
|
||||
self.assertTrue(type(length()) is long)
|
||||
|
||||
def test_change_underflows_to_long(self):
|
||||
import sys
|
||||
try:
|
||||
minint = (-sys.maxint) - 1
|
||||
except AttributeError: #pragma NO COVER Py3k
|
||||
return
|
||||
else: #pragma NO COVER Py2
|
||||
length = self._makeOne(minint)
|
||||
self.assertEqual(length(), minint)
|
||||
self.assertTrue(type(length()) is int)
|
||||
length.change(-1)
|
||||
self.assertEqual(length(), minint - 1)
|
||||
self.assertTrue(type(length()) is long)
|
||||
|
||||
def test___call___no_args(self):
|
||||
length = self._makeOne(42)
|
||||
self.assertEqual(length(), 42)
|
||||
|
||||
def test___call___w_args(self):
|
||||
length = self._makeOne(42)
|
||||
self.assertEqual(length(0, None, (), [], {}), 42)
|
||||
|
||||
def test_lp_516653(self):
|
||||
# Test for https://bugs.launchpad.net/zodb/+bug/516653
|
||||
import copy
|
||||
length = self._makeOne()
|
||||
other = copy.copy(length)
|
||||
self.assertEqual(other(), 0)
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.makeSuite(LengthTestCase)
|
||||
@@ -0,0 +1,383 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from .common import BTreeTests
|
||||
from .common import ExtendedSetTests
|
||||
from .common import InternalKeysMappingTest
|
||||
from .common import InternalKeysSetTest
|
||||
from .common import MappingBase
|
||||
from .common import MappingConflictTestBase
|
||||
from .common import ModuleTest
|
||||
from .common import NormalSetTests
|
||||
from .common import SetConflictTestBase
|
||||
from .common import SetResult
|
||||
from .common import TestLongIntValues
|
||||
from .common import TypeTest
|
||||
from .common import Weighted
|
||||
from .common import itemsToSet
|
||||
from .common import makeBuilder
|
||||
from BTrees.IIBTree import using64bits #XXX Ugly, but necessary
|
||||
|
||||
|
||||
class OIBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OIBTree
|
||||
return OIBTree
|
||||
|
||||
|
||||
class OIBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OIBTreePy
|
||||
return OIBTreePy
|
||||
|
||||
|
||||
class OITreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OITreeSet
|
||||
return OITreeSet
|
||||
|
||||
|
||||
class OITreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OITreeSetPy
|
||||
return OITreeSetPy
|
||||
|
||||
|
||||
class OIBucketTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OIBucket
|
||||
return OIBucket
|
||||
|
||||
|
||||
class OIBucketPyTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OIBucketPy
|
||||
return OIBucketPy
|
||||
|
||||
|
||||
class OITreeSetTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OITreeSet
|
||||
return OITreeSet
|
||||
|
||||
|
||||
class OITreeSetPyTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OITreeSetPy
|
||||
return OITreeSetPy
|
||||
|
||||
|
||||
class OISetTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OISet
|
||||
return OISet
|
||||
|
||||
|
||||
class OISetPyTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OISetPy
|
||||
return OISetPy
|
||||
|
||||
|
||||
class OIBTreeTest(BTreeTests, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.OIBTree import OIBTree
|
||||
return OIBTree()
|
||||
|
||||
|
||||
class OIBTreePyTest(BTreeTests, unittest.TestCase):
|
||||
def _makeOne(self):
|
||||
from BTrees.OIBTree import OIBTreePy
|
||||
return OIBTreePy()
|
||||
|
||||
|
||||
if using64bits:
|
||||
|
||||
class OIBTreeTest(BTreeTests, TestLongIntValues, unittest.TestCase):
|
||||
def _makeOne(self):
|
||||
from BTrees.OIBTree import OIBTree
|
||||
return OIBTree()
|
||||
def getTwoKeys(self):
|
||||
return object(), object()
|
||||
|
||||
class OIBTreePyTest(BTreeTests, TestLongIntValues, unittest.TestCase):
|
||||
def _makeOne(self):
|
||||
from BTrees.OIBTree import OIBTreePy
|
||||
return OIBTreePy()
|
||||
def getTwoKeys(self):
|
||||
return object(), object()
|
||||
|
||||
|
||||
class _TestOIBTreesBase(TypeTest):
|
||||
|
||||
def _stringraises(self):
|
||||
self._makeOne()[1] = 'c'
|
||||
|
||||
def _floatraises(self):
|
||||
self._makeOne()[1] = 1.4
|
||||
|
||||
def _noneraises(self):
|
||||
self._makeOne()[1] = None
|
||||
|
||||
def testEmptyFirstBucketReportedByGuido(self):
|
||||
from .._compat import xrange
|
||||
b = self._makeOne()
|
||||
for i in xrange(29972): # reduce to 29971 and it works
|
||||
b[i] = i
|
||||
for i in xrange(30): # reduce to 29 and it works
|
||||
del b[i]
|
||||
b[i+40000] = i
|
||||
|
||||
self.assertEqual(b.keys()[0], 30)
|
||||
|
||||
|
||||
class TestOIBTrees(_TestOIBTreesBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.OIBTree import OIBTree
|
||||
return OIBTree()
|
||||
|
||||
|
||||
class TestOIBTreesPy(_TestOIBTreesBase, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.OIBTree import OIBTreePy
|
||||
return OIBTreePy()
|
||||
|
||||
|
||||
class PureOI(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.OIBTree import union
|
||||
return union(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.OIBTree import intersection
|
||||
return intersection(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.OIBTree import difference
|
||||
return difference(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.OIBTree import OIBTree
|
||||
from BTrees.OIBTree import OIBucket
|
||||
from BTrees.OIBTree import OITreeSet
|
||||
from BTrees.OIBTree import OISet
|
||||
return OISet, OITreeSet, makeBuilder(OIBTree), makeBuilder(OIBucket)
|
||||
|
||||
|
||||
class PureOIPy(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.OIBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.OIBTree import intersectionPy
|
||||
return intersectionPy(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.OIBTree import differencePy
|
||||
return differencePy(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.OIBTree import OIBTreePy
|
||||
from BTrees.OIBTree import OIBucketPy
|
||||
from BTrees.OIBTree import OITreeSetPy
|
||||
from BTrees.OIBTree import OISetPy
|
||||
return (OISetPy, OITreeSetPy,
|
||||
makeBuilder(OIBTreePy), makeBuilder(OIBucketPy))
|
||||
|
||||
|
||||
class TestWeightedOI(Weighted, unittest.TestCase):
|
||||
|
||||
def weightedUnion(self):
|
||||
from BTrees.OIBTree import weightedUnion
|
||||
return weightedUnion
|
||||
|
||||
def weightedIntersection(self):
|
||||
from BTrees.OIBTree import weightedIntersection
|
||||
return weightedIntersection
|
||||
|
||||
def union(self):
|
||||
from BTrees.OIBTree import union
|
||||
return union
|
||||
|
||||
def intersection(self):
|
||||
from BTrees.OIBTree import intersection
|
||||
return intersection
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.OIBTree import OIBucket as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.OIBTree import OIBTree
|
||||
from BTrees.OIBTree import OIBucket
|
||||
from BTrees.OIBTree import OITreeSet
|
||||
from BTrees.OIBTree import OISet
|
||||
return OIBucket, OIBTree, itemsToSet(OISet), itemsToSet(OITreeSet)
|
||||
|
||||
|
||||
class TestWeightedOIPy(Weighted, unittest.TestCase):
|
||||
|
||||
def weightedUnion(self):
|
||||
from BTrees.OIBTree import weightedUnionPy
|
||||
return weightedUnionPy
|
||||
|
||||
def weightedIntersection(self):
|
||||
from BTrees.OIBTree import weightedIntersectionPy
|
||||
return weightedIntersectionPy
|
||||
|
||||
def union(self):
|
||||
from BTrees.OIBTree import unionPy
|
||||
return unionPy
|
||||
|
||||
def intersection(self):
|
||||
from BTrees.OIBTree import intersectionPy
|
||||
return intersectionPy
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.OIBTree import OIBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.OIBTree import OIBTreePy
|
||||
from BTrees.OIBTree import OIBucketPy
|
||||
from BTrees.OIBTree import OITreeSetPy
|
||||
from BTrees.OIBTree import OISetPy
|
||||
return (OIBucketPy, OIBTreePy,
|
||||
itemsToSet(OISetPy), itemsToSet(OITreeSetPy))
|
||||
|
||||
|
||||
class OIBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OIBucket
|
||||
return OIBucket
|
||||
|
||||
|
||||
class OIBucketConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OIBucketPy
|
||||
return OIBucketPy
|
||||
|
||||
|
||||
class OISetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OISet
|
||||
return OISet
|
||||
|
||||
|
||||
class OISetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OISetPy
|
||||
return OISetPy
|
||||
|
||||
|
||||
class OIBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OIBTree
|
||||
return OIBTree
|
||||
|
||||
|
||||
class OIBTreeConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OIBTreePy
|
||||
return OIBTreePy
|
||||
|
||||
|
||||
class OITreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OITreeSet
|
||||
return OITreeSet
|
||||
|
||||
|
||||
class OITreeSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OIBTree import OITreeSetPy
|
||||
return OITreeSetPy
|
||||
|
||||
|
||||
class OIModuleTest(ModuleTest, unittest.TestCase):
|
||||
|
||||
prefix = 'OI'
|
||||
|
||||
def _getModule(self):
|
||||
import BTrees
|
||||
return BTrees.OIBTree
|
||||
|
||||
def _getInterface(self):
|
||||
import BTrees.Interfaces
|
||||
return BTrees.Interfaces.IObjectIntegerBTreeModule
|
||||
|
||||
def test_multiunion_not_present(self):
|
||||
try:
|
||||
from BTrees.OIBTree import multiunion
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
self.fail("OIBTree shouldn't have multiunion")
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(OIBTreeInternalKeyTest),
|
||||
unittest.makeSuite(OIBTreePyInternalKeyTest),
|
||||
unittest.makeSuite(OITreeSetInternalKeyTest),
|
||||
unittest.makeSuite(OITreeSetPyInternalKeyTest),
|
||||
unittest.makeSuite(OIBucketTest),
|
||||
unittest.makeSuite(OIBucketPyTest),
|
||||
unittest.makeSuite(OITreeSetTest),
|
||||
unittest.makeSuite(OITreeSetPyTest),
|
||||
unittest.makeSuite(OISetTest),
|
||||
unittest.makeSuite(OISetPyTest),
|
||||
unittest.makeSuite(OIBTreeTest),
|
||||
unittest.makeSuite(OIBTreePyTest),
|
||||
unittest.makeSuite(TestOIBTrees),
|
||||
unittest.makeSuite(TestOIBTreesPy),
|
||||
unittest.makeSuite(PureOI),
|
||||
unittest.makeSuite(PureOIPy),
|
||||
unittest.makeSuite(TestWeightedOI),
|
||||
unittest.makeSuite(TestWeightedOIPy),
|
||||
unittest.makeSuite(OIBucketConflictTests),
|
||||
unittest.makeSuite(OIBucketConflictTestsPy),
|
||||
unittest.makeSuite(OISetConflictTests),
|
||||
unittest.makeSuite(OISetConflictTestsPy),
|
||||
unittest.makeSuite(OIBTreeConflictTests),
|
||||
unittest.makeSuite(OIBTreeConflictTestsPy),
|
||||
unittest.makeSuite(OITreeSetConflictTests),
|
||||
unittest.makeSuite(OITreeSetConflictTestsPy),
|
||||
unittest.makeSuite(OIModuleTest),
|
||||
))
|
||||
@@ -0,0 +1,348 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from .common import BTreeTests
|
||||
from .common import ExtendedSetTests
|
||||
from .common import InternalKeysMappingTest
|
||||
from .common import InternalKeysSetTest
|
||||
from .common import MappingBase
|
||||
from .common import MappingConflictTestBase
|
||||
from .common import ModuleTest
|
||||
from .common import NormalSetTests
|
||||
from .common import SetConflictTestBase
|
||||
from .common import SetResult
|
||||
from .common import TestLongIntValues
|
||||
from .common import Weighted
|
||||
from .common import itemsToSet
|
||||
from .common import makeBuilder
|
||||
from .common import _skip_on_32_bits
|
||||
|
||||
|
||||
class OLBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLBTree
|
||||
return OLBTree
|
||||
|
||||
|
||||
class OLBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLBTreePy
|
||||
return OLBTreePy
|
||||
|
||||
|
||||
class OLTreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLTreeSet
|
||||
return OLTreeSet
|
||||
|
||||
|
||||
class OLTreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLTreeSetPy
|
||||
return OLTreeSetPy
|
||||
|
||||
|
||||
class OLBucketTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLBucket
|
||||
return OLBucket
|
||||
|
||||
|
||||
class OLBucketPyTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLBucketPy
|
||||
return OLBucketPy
|
||||
|
||||
|
||||
class OLTreeSetTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLTreeSet
|
||||
return OLTreeSet
|
||||
|
||||
|
||||
class OLTreeSetPyTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLTreeSetPy
|
||||
return OLTreeSetPy
|
||||
|
||||
|
||||
class OLSetTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLSet
|
||||
return OLSet
|
||||
|
||||
|
||||
class OLSetPyTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLSetPy
|
||||
return OLSetPy
|
||||
|
||||
|
||||
class OLBTreeTest(BTreeTests, TestLongIntValues, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.OLBTree import OLBTree
|
||||
return OLBTree()
|
||||
|
||||
def getTwoKeys(self):
|
||||
return "abc", "def"
|
||||
|
||||
@_skip_on_32_bits
|
||||
def test_extremes(self):
|
||||
from BTrees.tests.common import SMALLEST_64_BITS
|
||||
from BTrees.tests.common import SMALLEST_POSITIVE_65_BITS
|
||||
from BTrees.tests.common import LARGEST_64_BITS
|
||||
from BTrees.tests.common import LARGEST_NEGATIVE_65_BITS
|
||||
btree = self._makeOne()
|
||||
btree['ZERO'] = 0
|
||||
btree['SMALLEST_64_BITS'] = SMALLEST_64_BITS
|
||||
btree['LARGEST_64_BITS'] = LARGEST_64_BITS
|
||||
self.assertRaises((ValueError, OverflowError), btree.__setitem__,
|
||||
'SMALLEST_POSITIVE_65_BITS', SMALLEST_POSITIVE_65_BITS)
|
||||
self.assertRaises((ValueError, OverflowError), btree.__setitem__,
|
||||
'LARGEST_NEGATIVE_65_BITS', LARGEST_NEGATIVE_65_BITS)
|
||||
|
||||
|
||||
class OLBTreePyTest(BTreeTests, TestLongIntValues, unittest.TestCase):
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.OLBTree import OLBTreePy
|
||||
return OLBTreePy()
|
||||
|
||||
def getTwoKeys(self):
|
||||
return "abc", "def"
|
||||
|
||||
|
||||
class PureOL(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.OLBTree import union
|
||||
return union(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.OLBTree import intersection
|
||||
return intersection(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.OLBTree import difference
|
||||
return difference(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.OLBTree import OLBTree
|
||||
from BTrees.OLBTree import OLBucket
|
||||
from BTrees.OLBTree import OLTreeSet
|
||||
from BTrees.OLBTree import OLSet
|
||||
return OLSet, OLTreeSet, makeBuilder(OLBTree), makeBuilder(OLBucket)
|
||||
|
||||
|
||||
class PureOLPy(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.OLBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.OLBTree import intersectionPy
|
||||
return intersectionPy(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.OLBTree import differencePy
|
||||
return differencePy(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.OLBTree import OLBTreePy
|
||||
from BTrees.OLBTree import OLBucketPy
|
||||
from BTrees.OLBTree import OLTreeSetPy
|
||||
from BTrees.OLBTree import OLSetPy
|
||||
return (OLSetPy, OLTreeSetPy,
|
||||
makeBuilder(OLBTreePy), makeBuilder(OLBucketPy))
|
||||
|
||||
|
||||
class TestWeightedOL(Weighted, unittest.TestCase):
|
||||
|
||||
def weightedUnion(self):
|
||||
from BTrees.OLBTree import weightedUnion
|
||||
return weightedUnion
|
||||
|
||||
def weightedIntersection(self):
|
||||
from BTrees.OLBTree import weightedIntersection
|
||||
return weightedIntersection
|
||||
|
||||
def union(self):
|
||||
from BTrees.OLBTree import union
|
||||
return union
|
||||
|
||||
def intersection(self):
|
||||
from BTrees.OLBTree import intersection
|
||||
return intersection
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.OLBTree import OLBucket as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.OLBTree import OLBTree
|
||||
from BTrees.OLBTree import OLBucket
|
||||
from BTrees.OLBTree import OLTreeSet
|
||||
from BTrees.OLBTree import OLSet
|
||||
return OLBucket, OLBTree, itemsToSet(OLSet), itemsToSet(OLTreeSet)
|
||||
|
||||
|
||||
class TestWeightedOLPy(Weighted, unittest.TestCase):
|
||||
|
||||
def weightedUnion(self):
|
||||
from BTrees.OLBTree import weightedUnionPy
|
||||
return weightedUnionPy
|
||||
|
||||
def weightedIntersection(self):
|
||||
from BTrees.OLBTree import weightedIntersectionPy
|
||||
return weightedIntersectionPy
|
||||
|
||||
def union(self):
|
||||
from BTrees.OLBTree import unionPy
|
||||
return unionPy
|
||||
|
||||
def intersection(self):
|
||||
from BTrees.OLBTree import intersectionPy
|
||||
return intersectionPy
|
||||
|
||||
def mkbucket(self, *args):
|
||||
from BTrees.OLBTree import OLBucketPy as mkbucket
|
||||
return mkbucket(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.OLBTree import OLBTreePy
|
||||
from BTrees.OLBTree import OLBucketPy
|
||||
from BTrees.OLBTree import OLTreeSetPy
|
||||
from BTrees.OLBTree import OLSetPy
|
||||
return (OLBucketPy, OLBTreePy,
|
||||
itemsToSet(OLSetPy), itemsToSet(OLTreeSetPy))
|
||||
|
||||
|
||||
class OLBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLBucket
|
||||
return OLBucket
|
||||
|
||||
|
||||
class OLBucketPyConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLBucketPy
|
||||
return OLBucketPy
|
||||
|
||||
|
||||
class OLSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLSet
|
||||
return OLSet
|
||||
|
||||
|
||||
class OLSetPyConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLSetPy
|
||||
return OLSetPy
|
||||
|
||||
|
||||
class OLBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLBTree
|
||||
return OLBTree
|
||||
|
||||
|
||||
class OLBTreePyConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLBTreePy
|
||||
return OLBTreePy
|
||||
|
||||
|
||||
class OLTreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLTreeSet
|
||||
return OLTreeSet
|
||||
|
||||
|
||||
class OLTreeSetPyConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OLBTree import OLTreeSetPy
|
||||
return OLTreeSetPy
|
||||
|
||||
|
||||
class OLModuleTest(ModuleTest, unittest.TestCase):
|
||||
|
||||
prefix = 'OL'
|
||||
|
||||
def _getModule(self):
|
||||
import BTrees
|
||||
return BTrees.OLBTree
|
||||
|
||||
def _getInterface(self):
|
||||
import BTrees.Interfaces
|
||||
return BTrees.Interfaces.IObjectIntegerBTreeModule
|
||||
|
||||
def test_multiunion_not_present(self):
|
||||
try:
|
||||
from BTrees.OLBTree import multiunion
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
self.fail("OLBTree shouldn't have multiunion")
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(OLBTreeInternalKeyTest),
|
||||
unittest.makeSuite(OLBTreePyInternalKeyTest),
|
||||
unittest.makeSuite(OLTreeSetInternalKeyTest),
|
||||
unittest.makeSuite(OLTreeSetPyInternalKeyTest),
|
||||
unittest.makeSuite(OLBucketTest),
|
||||
unittest.makeSuite(OLBucketPyTest),
|
||||
unittest.makeSuite(OLTreeSetTest),
|
||||
unittest.makeSuite(OLTreeSetPyTest),
|
||||
unittest.makeSuite(OLSetTest),
|
||||
unittest.makeSuite(OLSetPyTest),
|
||||
unittest.makeSuite(OLBTreeTest),
|
||||
unittest.makeSuite(OLBTreePyTest),
|
||||
unittest.makeSuite(PureOL),
|
||||
unittest.makeSuite(PureOLPy),
|
||||
unittest.makeSuite(TestWeightedOL),
|
||||
unittest.makeSuite(TestWeightedOLPy),
|
||||
unittest.makeSuite(OLBucketConflictTests),
|
||||
unittest.makeSuite(OLBucketPyConflictTests),
|
||||
unittest.makeSuite(OLSetConflictTests),
|
||||
unittest.makeSuite(OLSetPyConflictTests),
|
||||
unittest.makeSuite(OLBTreeConflictTests),
|
||||
unittest.makeSuite(OLBTreePyConflictTests),
|
||||
unittest.makeSuite(OLTreeSetConflictTests),
|
||||
unittest.makeSuite(OLTreeSetPyConflictTests),
|
||||
unittest.makeSuite(OLModuleTest),
|
||||
))
|
||||
@@ -0,0 +1,419 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
from .common import _skip_under_Py3k
|
||||
from .common import BTreeTests
|
||||
from .common import ExtendedSetTests
|
||||
from .common import InternalKeysMappingTest
|
||||
from .common import InternalKeysSetTest
|
||||
from .common import MappingBase
|
||||
from .common import MappingConflictTestBase
|
||||
from .common import ModuleTest
|
||||
from .common import NormalSetTests
|
||||
from .common import SetResult
|
||||
from .common import SetConflictTestBase
|
||||
from .common import makeBuilder
|
||||
|
||||
|
||||
|
||||
class OOBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOBTreePy
|
||||
return OOBTreePy
|
||||
|
||||
class OOBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOBTree
|
||||
return OOBTree
|
||||
|
||||
|
||||
class OOTreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOTreeSet
|
||||
return OOTreeSet
|
||||
|
||||
|
||||
class OOTreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOTreeSetPy
|
||||
return OOTreeSetPy
|
||||
|
||||
|
||||
class OOBucketTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOBucket
|
||||
return OOBucket
|
||||
|
||||
|
||||
class OOBucketPyTest(MappingBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOBucketPy
|
||||
return OOBucketPy
|
||||
|
||||
|
||||
class OOTreeSetTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOTreeSet
|
||||
return OOTreeSet
|
||||
|
||||
|
||||
class OOTreeSetPyTest(NormalSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOTreeSetPy
|
||||
return OOTreeSetPy
|
||||
|
||||
|
||||
class OOSetTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOSet
|
||||
return OOSet
|
||||
|
||||
|
||||
class OOSetPyTest(ExtendedSetTests, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOSetPy
|
||||
return OOSetPy
|
||||
|
||||
|
||||
|
||||
class OOBTreeTest(BTreeTests, unittest.TestCase):
|
||||
|
||||
def _makeOne(self, *args):
|
||||
from BTrees.OOBTree import OOBTree
|
||||
return OOBTree(*args)
|
||||
|
||||
def test_byValue(self):
|
||||
ITEMS = [(y, x) for x, y in enumerate('abcdefghijklmnopqrstuvwxyz')]
|
||||
tree = self._makeOne(ITEMS)
|
||||
self.assertEqual(list(tree.byValue(22)),
|
||||
[(y, x) for x, y in reversed(ITEMS[22:])])
|
||||
|
||||
def testRejectDefaultComparisonOnSet(self):
|
||||
# Check that passing int keys w default comparison fails.
|
||||
# Only applies to new-style class instances. Old-style
|
||||
# instances are too hard to introspect.
|
||||
|
||||
# This is white box because we know that the check is being
|
||||
# used in a function that's used in lots of places.
|
||||
# Otherwise, there are many permutations that would have to be
|
||||
# checked.
|
||||
from .._compat import PY2
|
||||
t = self._makeOne()
|
||||
|
||||
class C(object):
|
||||
pass
|
||||
|
||||
self.assertRaises(TypeError, lambda : t.__setitem__(C(), 1))
|
||||
|
||||
with self.assertRaises(TypeError) as raising:
|
||||
t[C()] = 1
|
||||
|
||||
self.assertEqual(raising.exception.args[0], "Object has default comparison")
|
||||
|
||||
if PY2: # we only check for __cmp__ on Python2
|
||||
|
||||
class With___cmp__(object):
|
||||
def __cmp__(*args):
|
||||
return 1
|
||||
c = With___cmp__()
|
||||
t[c] = 1
|
||||
|
||||
t.clear()
|
||||
|
||||
class With___lt__(object):
|
||||
def __lt__(*args):
|
||||
return 1
|
||||
|
||||
c = With___lt__()
|
||||
t[c] = 1
|
||||
|
||||
t.clear()
|
||||
|
||||
def testAcceptDefaultComparisonOnGet(self):
|
||||
# Issue #42
|
||||
t = self._makeOne()
|
||||
class C(object):
|
||||
pass
|
||||
|
||||
self.assertEqual(t.get(C(), 42), 42)
|
||||
self.assertRaises(KeyError, t.__getitem__, C())
|
||||
self.assertFalse(C() in t)
|
||||
|
||||
def test_None_is_smallest(self):
|
||||
t = self._makeOne()
|
||||
for i in range(999): # Make sure we multiple buckets
|
||||
t[i] = i*i
|
||||
t[None] = -1
|
||||
for i in range(-99,0): # Make sure we multiple buckets
|
||||
t[i] = i*i
|
||||
self.assertEqual(list(t), [None] + list(range(-99, 999)))
|
||||
self.assertEqual(list(t.values()),
|
||||
[-1] + [i*i for i in range(-99, 999)])
|
||||
self.assertEqual(t[2], 4)
|
||||
self.assertEqual(t[-2], 4)
|
||||
self.assertEqual(t[None], -1)
|
||||
t[None] = -2
|
||||
self.assertEqual(t[None], -2)
|
||||
t2 = t.__class__(t)
|
||||
del t[None]
|
||||
self.assertEqual(list(t), list(range(-99, 999)))
|
||||
|
||||
if 'Py' in self.__class__.__name__:
|
||||
return
|
||||
from BTrees.OOBTree import difference, union, intersection
|
||||
self.assertEqual(list(difference(t2, t).items()), [(None, -2)])
|
||||
self.assertEqual(list(union(t, t2)), list(t2))
|
||||
self.assertEqual(list(intersection(t, t2)), list(t))
|
||||
|
||||
@_skip_under_Py3k
|
||||
def testDeleteNoneKey(self):
|
||||
# Check that a None key can be deleted in Python 2.
|
||||
# This doesn't work on Python 3 because None is unorderable,
|
||||
# so the tree can't be searched. But None also can't be inserted,
|
||||
# and we don't support migrating Python 2 databases to Python 3.
|
||||
t = self._makeOne()
|
||||
bucket_state = ((None, 42),)
|
||||
tree_state = ((bucket_state,),)
|
||||
t.__setstate__(tree_state)
|
||||
|
||||
self.assertEqual(t[None], 42)
|
||||
del t[None]
|
||||
|
||||
def testUnpickleNoneKey(self):
|
||||
# All versions (py2 and py3, C and Python) can unpickle
|
||||
# data that looks like this: {None: 42}, even though None
|
||||
# is unorderable..
|
||||
# This pickle was captured in BTree/ZODB3 3.10.7
|
||||
data = b'ccopy_reg\n__newobj__\np0\n(cBTrees.OOBTree\nOOBTree\np1\ntp2\nRp3\n((((NI42\ntp4\ntp5\ntp6\ntp7\nb.'
|
||||
|
||||
import pickle
|
||||
t = pickle.loads(data)
|
||||
keys = list(t)
|
||||
self.assertEqual([None], keys)
|
||||
|
||||
def testIdentityTrumpsBrokenComparison(self):
|
||||
# Identical keys always match, even if their comparison is
|
||||
# broken. See https://github.com/zopefoundation/BTrees/issues/50
|
||||
from functools import total_ordering
|
||||
|
||||
@total_ordering
|
||||
class Bad(object):
|
||||
def __eq__(self, other):
|
||||
return False
|
||||
|
||||
def __cmp__(self, other):
|
||||
return 1
|
||||
|
||||
def __lt__(self, other):
|
||||
return False
|
||||
|
||||
t = self._makeOne()
|
||||
bad_key = Bad()
|
||||
t[bad_key] = 42
|
||||
|
||||
self.assertIn(bad_key, t)
|
||||
self.assertEqual(list(t), [bad_key])
|
||||
|
||||
del t[bad_key]
|
||||
self.assertNotIn(bad_key, t)
|
||||
self.assertEqual(list(t), [])
|
||||
|
||||
|
||||
class OOBTreePyTest(OOBTreeTest):
|
||||
#
|
||||
# Right now, we can't match the C extension's test / prohibition of the
|
||||
# default 'object' comparison semantics.
|
||||
#class OOBTreePyTest(BTreeTests, unittest.TestCase):
|
||||
|
||||
def _makeOne(self, *args):
|
||||
from BTrees.OOBTree import OOBTreePy
|
||||
return OOBTreePy(*args)
|
||||
|
||||
|
||||
|
||||
class PureOO(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.OOBTree import union
|
||||
return union(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.OOBTree import intersection
|
||||
return intersection(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.OOBTree import difference
|
||||
return difference(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.OOBTree import OOBTree
|
||||
from BTrees.OOBTree import OOBucket
|
||||
from BTrees.OOBTree import OOTreeSet
|
||||
from BTrees.OOBTree import OOSet
|
||||
return OOSet, OOTreeSet, makeBuilder(OOBTree), makeBuilder(OOBucket)
|
||||
|
||||
|
||||
class PureOOPy(SetResult, unittest.TestCase):
|
||||
|
||||
def union(self, *args):
|
||||
from BTrees.OOBTree import unionPy
|
||||
return unionPy(*args)
|
||||
|
||||
def intersection(self, *args):
|
||||
from BTrees.OOBTree import intersectionPy
|
||||
return intersectionPy(*args)
|
||||
|
||||
def difference(self, *args):
|
||||
from BTrees.OOBTree import differencePy
|
||||
return differencePy(*args)
|
||||
|
||||
def builders(self):
|
||||
from BTrees.OOBTree import OOBTreePy
|
||||
from BTrees.OOBTree import OOBucketPy
|
||||
from BTrees.OOBTree import OOTreeSetPy
|
||||
from BTrees.OOBTree import OOSetPy
|
||||
return (OOSetPy, OOTreeSetPy,
|
||||
makeBuilder(OOBTreePy), makeBuilder(OOBucketPy))
|
||||
|
||||
|
||||
class OOBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOBucket
|
||||
return OOBucket
|
||||
|
||||
|
||||
class OOBucketPyConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOBucketPy
|
||||
return OOBucketPy
|
||||
|
||||
|
||||
class OOSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOSet
|
||||
return OOSet
|
||||
|
||||
|
||||
class OOSetPyConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOSetPy
|
||||
return OOSetPy
|
||||
|
||||
|
||||
class OOBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOBTree
|
||||
return OOBTree
|
||||
|
||||
|
||||
class OOBTreePyConflictTests(MappingConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOBTreePy
|
||||
return OOBTreePy
|
||||
|
||||
|
||||
class OOTreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOTreeSet
|
||||
return OOTreeSet
|
||||
|
||||
|
||||
class OOTreeSetPyConflictTests(SetConflictTestBase, unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.OOBTree import OOTreeSetPy
|
||||
return OOTreeSetPy
|
||||
|
||||
|
||||
class OOModuleTest(ModuleTest, unittest.TestCase):
|
||||
|
||||
prefix = 'OO'
|
||||
|
||||
def _getModule(self):
|
||||
import BTrees
|
||||
return BTrees.OOBTree
|
||||
|
||||
def _getInterface(self):
|
||||
import BTrees.Interfaces
|
||||
return BTrees.Interfaces.IObjectObjectBTreeModule
|
||||
|
||||
def test_weightedUnion_not_present(self):
|
||||
try:
|
||||
from BTrees.OOBTree import weightedUnion
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
self.fail("OOBTree shouldn't have weightedUnion")
|
||||
|
||||
def test_weightedIntersection_not_present(self):
|
||||
try:
|
||||
from BTrees.OOBTree import weightedIntersection
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
self.fail("OOBTree shouldn't have weightedIntersection")
|
||||
|
||||
def test_multiunion_not_present(self):
|
||||
try:
|
||||
from BTrees.OOBTree import multiunion
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
self.fail("OOBTree shouldn't have multiunion")
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(OOBTreeInternalKeyTest),
|
||||
unittest.makeSuite(OOBTreePyInternalKeyTest),
|
||||
unittest.makeSuite(OOTreeSetInternalKeyTest),
|
||||
unittest.makeSuite(OOTreeSetPyInternalKeyTest),
|
||||
unittest.makeSuite(OOBucketTest),
|
||||
unittest.makeSuite(OOBucketPyTest),
|
||||
unittest.makeSuite(OOTreeSetTest),
|
||||
unittest.makeSuite(OOTreeSetPyTest),
|
||||
unittest.makeSuite(OOSetTest),
|
||||
unittest.makeSuite(OOSetPyTest),
|
||||
unittest.makeSuite(OOBTreeTest),
|
||||
unittest.makeSuite(OOBTreePyTest),
|
||||
unittest.makeSuite(PureOO),
|
||||
unittest.makeSuite(PureOOPy),
|
||||
unittest.makeSuite(OOBucketConflictTests),
|
||||
unittest.makeSuite(OOBucketPyConflictTests),
|
||||
unittest.makeSuite(OOSetConflictTests),
|
||||
unittest.makeSuite(OOSetPyConflictTests),
|
||||
unittest.makeSuite(OOBTreeConflictTests),
|
||||
unittest.makeSuite(OOBTreePyConflictTests),
|
||||
unittest.makeSuite(OOTreeSetConflictTests),
|
||||
unittest.makeSuite(OOTreeSetPyConflictTests),
|
||||
unittest.makeSuite(OOModuleTest),
|
||||
))
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,54 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
from BTrees.OOBTree import OOBTree, OOBucket
|
||||
|
||||
class B(OOBucket):
|
||||
pass
|
||||
|
||||
class T(OOBTree):
|
||||
_bucket_type = B
|
||||
max_leaf_size = 2
|
||||
max_internal_size = 3
|
||||
|
||||
class S(T):
|
||||
pass
|
||||
|
||||
import unittest
|
||||
|
||||
class SubclassTest(unittest.TestCase):
|
||||
|
||||
def testSubclass(self):
|
||||
# test that a subclass that defines _bucket_type gets buckets
|
||||
# of that type
|
||||
t = T()
|
||||
t[0] = 0
|
||||
self.assertTrue(t._firstbucket.__class__ is B)
|
||||
|
||||
def testCustomNodeSizes(self):
|
||||
# We override btree and bucket split sizes in BTree subclasses.
|
||||
t = S()
|
||||
for i in range(8):
|
||||
t[i] = i
|
||||
state = t.__getstate__()[0]
|
||||
self.assertEqual(len(state), 5)
|
||||
sub = state[0]
|
||||
self.assertEqual(sub.__class__, S)
|
||||
sub = sub.__getstate__()[0]
|
||||
self.assertEqual(len(sub), 5)
|
||||
sub = sub[0]
|
||||
self.assertEqual(sub.__class__, B)
|
||||
self.assertEqual(len(sub), 1)
|
||||
|
||||
def test_suite():
|
||||
return unittest.makeSuite(SubclassTest)
|
||||
@@ -0,0 +1,425 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2003 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
|
||||
def _assertRaises(self, e_type, checked, *args, **kw):
|
||||
try:
|
||||
checked(*args, **kw)
|
||||
except e_type as e:
|
||||
return e
|
||||
self.fail("Didn't raise: %s" % e_type.__name__)
|
||||
|
||||
|
||||
class Test_classify(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, obj):
|
||||
from BTrees.check import classify
|
||||
return classify(obj)
|
||||
|
||||
def test_classify_w_unknown(self):
|
||||
class NotClassified(object):
|
||||
pass
|
||||
self.assertRaises(KeyError, self._callFUT, NotClassified())
|
||||
|
||||
def test_classify_w_bucket(self):
|
||||
from BTrees.OOBTree import OOBucketPy
|
||||
from BTrees.check import TYPE_BUCKET
|
||||
kind, is_mapping = self._callFUT(OOBucketPy())
|
||||
self.assertEqual(kind, TYPE_BUCKET)
|
||||
self.assertTrue(is_mapping)
|
||||
|
||||
def test_classify_w_set(self):
|
||||
from BTrees.OOBTree import OOSetPy
|
||||
from BTrees.check import TYPE_BUCKET
|
||||
kind, is_mapping = self._callFUT(OOSetPy())
|
||||
self.assertEqual(kind, TYPE_BUCKET)
|
||||
self.assertFalse(is_mapping)
|
||||
|
||||
def test_classify_w_tree(self):
|
||||
from BTrees.OOBTree import OOBTreePy
|
||||
from BTrees.check import TYPE_BTREE
|
||||
kind, is_mapping = self._callFUT(OOBTreePy())
|
||||
self.assertEqual(kind, TYPE_BTREE)
|
||||
self.assertTrue(is_mapping)
|
||||
|
||||
def test_classify_w_treeset(self):
|
||||
from BTrees.OOBTree import OOTreeSetPy
|
||||
from BTrees.check import TYPE_BTREE
|
||||
kind, is_mapping = self._callFUT(OOTreeSetPy())
|
||||
self.assertEqual(kind, TYPE_BTREE)
|
||||
self.assertFalse(is_mapping)
|
||||
|
||||
|
||||
class Test_crack_btree(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, obj, is_mapping):
|
||||
from BTrees.check import crack_btree
|
||||
return crack_btree(obj, is_mapping)
|
||||
|
||||
def test_w_empty_tree(self):
|
||||
from BTrees.check import BTREE_EMPTY
|
||||
class Empty(object):
|
||||
def __getstate__(self):
|
||||
return None
|
||||
kind, keys, kids = self._callFUT(Empty(), True)
|
||||
self.assertEqual(kind, BTREE_EMPTY)
|
||||
self.assertEqual(keys, [])
|
||||
self.assertEqual(kids, [])
|
||||
|
||||
def test_w_degenerate_tree(self):
|
||||
from BTrees.check import BTREE_ONE
|
||||
class Degenerate(object):
|
||||
def __getstate__(self):
|
||||
return ((('a', 1, 'b', 2),),)
|
||||
kind, keys, kids = self._callFUT(Degenerate(), True)
|
||||
self.assertEqual(kind, BTREE_ONE)
|
||||
self.assertEqual(keys, ('a', 1, 'b', 2))
|
||||
self.assertEqual(kids, None)
|
||||
|
||||
def test_w_normal_tree(self):
|
||||
from BTrees.check import BTREE_NORMAL
|
||||
first_bucket = [object()] * 8
|
||||
second_bucket = [object()] * 8
|
||||
class Normal(object):
|
||||
def __getstate__(self):
|
||||
return ((first_bucket, 'b', second_bucket), first_bucket)
|
||||
kind, keys, kids = self._callFUT(Normal(), True)
|
||||
self.assertEqual(kind, BTREE_NORMAL)
|
||||
self.assertEqual(keys, ['b'])
|
||||
self.assertEqual(kids, [first_bucket, second_bucket])
|
||||
|
||||
|
||||
class Test_crack_bucket(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, obj, is_mapping):
|
||||
from BTrees.check import crack_bucket
|
||||
return crack_bucket(obj, is_mapping)
|
||||
|
||||
def test_w_empty_set(self):
|
||||
class EmptySet(object):
|
||||
def __getstate__(self):
|
||||
return ([],)
|
||||
keys, values = self._callFUT(EmptySet(), False)
|
||||
self.assertEqual(keys, [])
|
||||
self.assertEqual(values, [])
|
||||
|
||||
def test_w_non_empty_set(self):
|
||||
class NonEmptySet(object):
|
||||
def __getstate__(self):
|
||||
return (['a', 'b', 'c'],)
|
||||
keys, values = self._callFUT(NonEmptySet(), False)
|
||||
self.assertEqual(keys, ['a', 'b', 'c'])
|
||||
self.assertEqual(values, [])
|
||||
|
||||
def test_w_empty_mapping(self):
|
||||
class EmptyMapping(object):
|
||||
def __getstate__(self):
|
||||
return ([], object())
|
||||
keys, values = self._callFUT(EmptyMapping(), True)
|
||||
self.assertEqual(keys, [])
|
||||
self.assertEqual(values, [])
|
||||
|
||||
def test_w_non_empty_mapping(self):
|
||||
class NonEmptyMapping(object):
|
||||
def __getstate__(self):
|
||||
return (['a', 1, 'b', 2, 'c', 3], object())
|
||||
keys, values = self._callFUT(NonEmptyMapping(), True)
|
||||
self.assertEqual(keys, ['a', 'b', 'c'])
|
||||
self.assertEqual(values, [1, 2, 3])
|
||||
|
||||
|
||||
class Test_type_and_adr(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, obj):
|
||||
from BTrees.check import type_and_adr
|
||||
return type_and_adr(obj)
|
||||
|
||||
def test_type_and_adr_w_oid(self):
|
||||
from BTrees.utils import oid_repr
|
||||
class WithOid(object):
|
||||
_p_oid = b'DEADBEEF'
|
||||
t_and_a = self._callFUT(WithOid())
|
||||
self.assertTrue(t_and_a.startswith('WithOid (0x'))
|
||||
self.assertTrue(t_and_a.endswith('oid=%s)' % oid_repr(b'DEADBEEF')))
|
||||
|
||||
def test_type_and_adr_wo_oid(self):
|
||||
class WithoutOid(object):
|
||||
pass
|
||||
t_and_a = self._callFUT(WithoutOid())
|
||||
self.assertTrue(t_and_a.startswith('WithoutOid (0x'))
|
||||
self.assertTrue(t_and_a.endswith('oid=None)'))
|
||||
|
||||
|
||||
class WalkerTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.check import Walker
|
||||
return Walker
|
||||
|
||||
def _makeOne(self, obj):
|
||||
return self._getTargetClass()(obj)
|
||||
|
||||
def test_visit_btree_abstract(self):
|
||||
walker = self._makeOne(object())
|
||||
obj = object()
|
||||
path = '/'
|
||||
parent = object()
|
||||
is_mapping = True
|
||||
keys = []
|
||||
kids = []
|
||||
lo = 0
|
||||
hi = None
|
||||
self.assertRaises(NotImplementedError, walker.visit_btree,
|
||||
obj, path, parent, is_mapping, keys, kids, lo, hi)
|
||||
|
||||
def test_visit_bucket_abstract(self):
|
||||
walker = self._makeOne(object())
|
||||
obj = object()
|
||||
path = '/'
|
||||
parent = object()
|
||||
is_mapping = True
|
||||
keys = []
|
||||
kids = []
|
||||
lo = 0
|
||||
hi = None
|
||||
self.assertRaises(NotImplementedError, walker.visit_bucket,
|
||||
obj, path, parent, is_mapping, keys, kids, lo, hi)
|
||||
|
||||
def test_walk_w_empty_bucket(self):
|
||||
from BTrees.OOBTree import OOBucket
|
||||
obj = OOBucket()
|
||||
walker = self._makeOne(obj)
|
||||
path = '/'
|
||||
parent = object()
|
||||
is_mapping = True
|
||||
keys = []
|
||||
kids = []
|
||||
lo = 0
|
||||
hi = None
|
||||
self.assertRaises(NotImplementedError, walker.walk)
|
||||
|
||||
def test_walk_w_empty_btree(self):
|
||||
from BTrees.OOBTree import OOBTree
|
||||
obj = OOBTree()
|
||||
walker = self._makeOne(obj)
|
||||
path = '/'
|
||||
parent = object()
|
||||
is_mapping = True
|
||||
keys = []
|
||||
kids = []
|
||||
lo = 0
|
||||
hi = None
|
||||
self.assertRaises(NotImplementedError, walker.walk)
|
||||
|
||||
def test_walk_w_degenerate_btree(self):
|
||||
from BTrees.OOBTree import OOBTree
|
||||
obj = OOBTree()
|
||||
obj['a'] = 1
|
||||
walker = self._makeOne(obj)
|
||||
path = '/'
|
||||
parent = object()
|
||||
is_mapping = True
|
||||
keys = []
|
||||
kids = []
|
||||
lo = 0
|
||||
hi = None
|
||||
self.assertRaises(NotImplementedError, walker.walk)
|
||||
|
||||
def test_walk_w_normal_btree(self):
|
||||
from BTrees.IIBTree import IIBTree
|
||||
obj = IIBTree()
|
||||
for i in range(1000):
|
||||
obj[i] = i
|
||||
walker = self._makeOne(obj)
|
||||
path = '/'
|
||||
parent = object()
|
||||
is_mapping = True
|
||||
keys = []
|
||||
kids = []
|
||||
lo = 0
|
||||
hi = None
|
||||
self.assertRaises(NotImplementedError, walker.walk)
|
||||
|
||||
|
||||
class CheckerTests(unittest.TestCase):
|
||||
|
||||
assertRaises = _assertRaises
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.check import Checker
|
||||
return Checker
|
||||
|
||||
def _makeOne(self, obj):
|
||||
return self._getTargetClass()(obj)
|
||||
|
||||
def test_walk_w_empty_bucket(self):
|
||||
from BTrees.OOBTree import OOBucket
|
||||
obj = OOBucket()
|
||||
checker = self._makeOne(obj)
|
||||
path = '/'
|
||||
parent = object()
|
||||
is_mapping = True
|
||||
keys = []
|
||||
kids = []
|
||||
lo = 0
|
||||
hi = None
|
||||
checker.check() #noraise
|
||||
|
||||
def test_walk_w_empty_btree(self):
|
||||
obj = _makeTree(False)
|
||||
checker = self._makeOne(obj)
|
||||
path = '/'
|
||||
parent = object()
|
||||
is_mapping = True
|
||||
keys = []
|
||||
kids = []
|
||||
lo = 0
|
||||
hi = None
|
||||
checker.check() #noraise
|
||||
|
||||
def test_walk_w_degenerate_btree(self):
|
||||
obj = _makeTree(False)
|
||||
obj['a'] = 1
|
||||
checker = self._makeOne(obj)
|
||||
path = '/'
|
||||
parent = object()
|
||||
is_mapping = True
|
||||
keys = []
|
||||
kids = []
|
||||
lo = 0
|
||||
hi = None
|
||||
checker.check() #noraise
|
||||
|
||||
def test_walk_w_normal_btree(self):
|
||||
obj = _makeTree(False)
|
||||
checker = self._makeOne(obj)
|
||||
path = '/'
|
||||
parent = object()
|
||||
is_mapping = True
|
||||
keys = []
|
||||
kids = []
|
||||
lo = 0
|
||||
hi = None
|
||||
checker.check() #noraise
|
||||
|
||||
def test_walk_w_key_too_large(self):
|
||||
obj = _makeTree(True)
|
||||
state = obj.__getstate__()
|
||||
# Damage an invariant by dropping the BTree key to 14.
|
||||
new_state = (state[0][0], 14, state[0][2]), state[1]
|
||||
obj.__setstate__(new_state)
|
||||
checker = self._makeOne(obj)
|
||||
path = '/'
|
||||
parent = object()
|
||||
is_mapping = True
|
||||
keys = []
|
||||
kids = []
|
||||
lo = 0
|
||||
hi = None
|
||||
e = self.assertRaises(AssertionError, checker.check)
|
||||
self.assertTrue(">= upper bound" in str(e))
|
||||
|
||||
def test_walk_w_key_too_small(self):
|
||||
obj = _makeTree(True)
|
||||
state = obj.__getstate__()
|
||||
# Damage an invariant by bumping the BTree key to 16.
|
||||
new_state = (state[0][0], 16, state[0][2]), state[1]
|
||||
obj.__setstate__(new_state)
|
||||
checker = self._makeOne(obj)
|
||||
path = '/'
|
||||
parent = object()
|
||||
is_mapping = True
|
||||
keys = []
|
||||
kids = []
|
||||
lo = 0
|
||||
hi = None
|
||||
e = self.assertRaises(AssertionError, checker.check)
|
||||
self.assertTrue("< lower bound" in str(e))
|
||||
|
||||
def test_walk_w_keys_swapped(self):
|
||||
obj = _makeTree(True)
|
||||
state = obj.__getstate__()
|
||||
# Damage an invariant by bumping the BTree key to 16.
|
||||
(b0, num, b1), firstbucket = state
|
||||
self.assertEqual(b0[4], 8)
|
||||
self.assertEqual(b0[5], 10)
|
||||
b0state = b0.__getstate__()
|
||||
self.assertEqual(len(b0state), 2)
|
||||
# b0state looks like
|
||||
# ((k0, v0, k1, v1, ...), nextbucket)
|
||||
pairs, nextbucket = b0state
|
||||
self.assertEqual(pairs[8], 4)
|
||||
self.assertEqual(pairs[9], 8)
|
||||
self.assertEqual(pairs[10], 5)
|
||||
self.assertEqual(pairs[11], 10)
|
||||
newpairs = pairs[:8] + (5, 10, 4, 8) + pairs[12:]
|
||||
b0.__setstate__((newpairs, nextbucket))
|
||||
checker = self._makeOne(obj)
|
||||
path = '/'
|
||||
parent = object()
|
||||
is_mapping = True
|
||||
keys = []
|
||||
kids = []
|
||||
lo = 0
|
||||
hi = None
|
||||
e = self.assertRaises(AssertionError, checker.check)
|
||||
self.assertTrue("key 5 at index 4 >= key 4 at index 5" in str(e))
|
||||
|
||||
|
||||
class Test_check(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, tree):
|
||||
from BTrees.check import check
|
||||
return check(tree)
|
||||
|
||||
def _makeOne(self):
|
||||
from BTrees.OOBTree import OOBTree
|
||||
tree = OOBTree()
|
||||
for i in range(31):
|
||||
tree[i] = 2*i
|
||||
return tree
|
||||
|
||||
def test_normal(self):
|
||||
from BTrees.OOBTree import OOBTree
|
||||
tree = OOBTree()
|
||||
for i in range(31):
|
||||
tree[i] = 2*i
|
||||
state = tree.__getstate__()
|
||||
self.assertEqual(len(state), 2)
|
||||
self.assertEqual(len(state[0]), 3)
|
||||
self.assertEqual(state[0][1], 15)
|
||||
self._callFUT(tree) #noraise
|
||||
|
||||
|
||||
def _makeTree(fill):
|
||||
from BTrees.OOBTree import OOBTree
|
||||
from BTrees.OOBTree import _BUCKET_SIZE
|
||||
tree = OOBTree()
|
||||
if fill:
|
||||
for i in range(_BUCKET_SIZE + 1):
|
||||
tree[i] = 2*i
|
||||
return tree
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(Test_classify),
|
||||
unittest.makeSuite(Test_crack_btree),
|
||||
unittest.makeSuite(Test_crack_bucket),
|
||||
unittest.makeSuite(Test_type_and_adr),
|
||||
unittest.makeSuite(WalkerTests),
|
||||
unittest.makeSuite(CheckerTests),
|
||||
unittest.makeSuite(Test_check),
|
||||
))
|
||||
@@ -0,0 +1,64 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2010 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
|
||||
class fsBucketBase(object):
|
||||
|
||||
def _makeOne(self, *args, **kw):
|
||||
return self._getTargetClass()(*args, **kw)
|
||||
|
||||
def _makeBytesItems(self):
|
||||
from .._compat import _ascii
|
||||
return[(_ascii(c*2), _ascii(c*6)) for c in 'abcdef']
|
||||
|
||||
def test_toString(self):
|
||||
bucket = self._makeOne(self._makeBytesItems())
|
||||
self.assertEqual(bucket.toString(),
|
||||
b'aabbccddeeffaaaaaabbbbbbccccccddddddeeeeeeffffff')
|
||||
|
||||
def test_fromString(self):
|
||||
before = self._makeOne(self._makeBytesItems())
|
||||
after = before.fromString(before.toString())
|
||||
self.assertEqual(before.__getstate__(), after.__getstate__())
|
||||
|
||||
def test_fromString_empty(self):
|
||||
before = self._makeOne(self._makeBytesItems())
|
||||
after = before.fromString(b'')
|
||||
self.assertEqual(after.__getstate__(), ((),))
|
||||
|
||||
def test_fromString_invalid_length(self):
|
||||
bucket = self._makeOne(self._makeBytesItems())
|
||||
self.assertRaises(ValueError, bucket.fromString, b'xxx')
|
||||
|
||||
|
||||
class fsBucketTests(unittest.TestCase, fsBucketBase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.fsBTree import fsBucket
|
||||
return fsBucket
|
||||
|
||||
|
||||
class fsBucketPyTests(unittest.TestCase, fsBucketBase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from BTrees.fsBTree import fsBucketPy
|
||||
return fsBucketPy
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(fsBucketTests),
|
||||
unittest.makeSuite(fsBucketPyTests),
|
||||
))
|
||||
@@ -0,0 +1,83 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
|
||||
class Test_non_negative(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, int_val):
|
||||
from BTrees.utils import non_negative
|
||||
return non_negative(int_val)
|
||||
|
||||
def test_w_big_negative(self):
|
||||
self.assertEqual(self._callFUT(-(2**63 - 1)), 1)
|
||||
|
||||
def test_w_negative(self):
|
||||
self.assertEqual(self._callFUT(-1), 2**63 - 1)
|
||||
|
||||
def test_w_zero(self):
|
||||
self.assertEqual(self._callFUT(0), 0)
|
||||
|
||||
def test_w_positive(self):
|
||||
self.assertEqual(self._callFUT(1), 1)
|
||||
|
||||
def test_w_big_positive(self):
|
||||
import sys
|
||||
try:
|
||||
self.assertEqual(self._callFUT(sys.maxint), sys.maxint)
|
||||
except AttributeError: #pragma NO COVER Py3k
|
||||
pass
|
||||
|
||||
|
||||
class Test_oid_repr(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, oid):
|
||||
from BTrees.utils import oid_repr
|
||||
return oid_repr(oid)
|
||||
|
||||
def test_w_non_strings(self):
|
||||
self.assertEqual(self._callFUT(None), repr(None))
|
||||
self.assertEqual(self._callFUT(()), repr(()))
|
||||
self.assertEqual(self._callFUT([]), repr([]))
|
||||
self.assertEqual(self._callFUT({}), repr({}))
|
||||
self.assertEqual(self._callFUT(0), repr(0))
|
||||
|
||||
def test_w_short_strings(self):
|
||||
for length in range(8):
|
||||
faux = 'x' * length
|
||||
self.assertEqual(self._callFUT(faux), repr(faux))
|
||||
|
||||
def test_w_long_strings(self):
|
||||
for length in range(9, 1024):
|
||||
faux = 'x' * length
|
||||
self.assertEqual(self._callFUT(faux), repr(faux))
|
||||
|
||||
def test_w_zero(self):
|
||||
self.assertEqual(self._callFUT(b'\0\0\0\0\0\0\0\0'), b'0x00')
|
||||
|
||||
def test_w_one(self):
|
||||
self.assertEqual(self._callFUT(b'\0\0\0\0\0\0\0\1'), b'0x01')
|
||||
|
||||
def test_w_even_length(self):
|
||||
self.assertEqual(self._callFUT(b'\0\0\0\0\0\0\xAB\xC4'), b'0xabc4')
|
||||
|
||||
def test_w_odd_length(self):
|
||||
self.assertEqual(self._callFUT(b'\0\0\0\0\0\0\x0D\xEF'), b'0x0def')
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(Test_non_negative),
|
||||
unittest.makeSuite(Test_oid_repr),
|
||||
))
|
||||
@@ -0,0 +1,45 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE
|
||||
#
|
||||
##############################################################################
|
||||
# Copied from ZODB/utils.py
|
||||
|
||||
from binascii import hexlify
|
||||
|
||||
from ._compat import _bytes
|
||||
|
||||
def non_negative(int_val):
|
||||
if int_val < 0:
|
||||
# Coerce to non-negative.
|
||||
int_val &= 0x7FFFFFFFFFFFFFFF
|
||||
return int_val
|
||||
|
||||
|
||||
def positive_id(obj): #pragma NO COVER
|
||||
"""Return id(obj) as a non-negative integer."""
|
||||
return non_negative(id(obj))
|
||||
|
||||
|
||||
def oid_repr(oid):
|
||||
if isinstance(oid, _bytes) and len(oid) == 8:
|
||||
# Convert to hex and strip leading zeroes.
|
||||
as_hex = hexlify(oid).lstrip(b'0')
|
||||
# Ensure two characters per input byte.
|
||||
chunks = [b'0x']
|
||||
if len(as_hex) & 1:
|
||||
chunks.append(b'0')
|
||||
elif as_hex == b'':
|
||||
as_hex = b'00'
|
||||
chunks.append(as_hex)
|
||||
return b''.join(chunks)
|
||||
else:
|
||||
return repr(oid)
|
||||
Reference in New Issue
Block a user