[Zodb-checkins] CVS: Zope/lib/python/BTrees - sorters.c:1.4.12.1 BTreeItemsTemplate.c:1.7.18.3 BTreeModuleTemplate.c:1.15.18.3 BTreeTemplate.c:1.20.18.3 BucketTemplate.c:1.24.6.3 IIBTree.py:1.3.18.2 IOBTree.py:1.3.18.2 Interfaces.py:1.7.18.5 Length.py:1.4.18.2 MergeTemplate.c:1.8.18.2 OIBTree.py:1.3.18.2 OOBTree.py:1.3.18.2 SetOpTemplate.c:1.8.18.3 SetTemplate.c:1.12.18.2 TreeSetTemplate.c:1.10.18.2 _IIBTree.c:1.3.112.1 _IOBTree.c:1.3.112.1 __init__.py:1.3.84.1 _fsBTree.c:1.2.2.2 convert.py:1.4.18.2 intkeymacros.h:1.7.44.1 intvaluemacros.h:1.7.112.1 objectkeymacros.h:1.2.116.1 objectvaluemacros.h:1.3.116.1

Matthew T. Kromer matt@zope.com
Tue, 10 Sep 2002 17:48:26 -0400


Update of /cvs-repository/Zope/lib/python/BTrees
In directory cvs.zope.org:/tmp/cvs-serv17953

Modified Files:
      Tag: Zope-2_5-branch
	BTreeItemsTemplate.c BTreeModuleTemplate.c BTreeTemplate.c 
	BucketTemplate.c IIBTree.py IOBTree.py Interfaces.py Length.py 
	MergeTemplate.c OIBTree.py OOBTree.py SetOpTemplate.c 
	SetTemplate.c TreeSetTemplate.c _IIBTree.c _IOBTree.c 
	__init__.py _fsBTree.c convert.py intkeymacros.h 
	intvaluemacros.h objectkeymacros.h objectvaluemacros.h 
Added Files:
      Tag: Zope-2_5-branch
	sorters.c 
Log Message:
Backport 2.6 BTrees to 2.5 for a Zope 2.5.2 maintenance release


=== Added File Zope/lib/python/BTrees/sorters.c === (427/527 lines abridged)
/*****************************************************************************

  Copyright (c) 2002 Zope Corporation and Contributors.
  All Rights Reserved.

  This software is subject to the provisions of the Zope Public License,
  Version 2.0 (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: sorters.c,v 1.4.12.1 2002/09/10 21:48:24 matt Exp $ */

/* The only routine here intended to be used outside the file is
   size_t sort_int4_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_int4_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 4-byte signed ints, with endianness natural to the
     platform.

   + 4*n bytes of available heap memory are required for best speed.
*/

#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 int element_type;

/* The radixsort is faster than the quicksort for large arrays, but radixsort

[-=- -=- -=- 427 lines omitted -=- -=- -=-]

		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_int4_nodups(int *p, size_t n)
{
	size_t nunique;
	element_type *work;

	assert(sizeof(int) == 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_int4(p, work, n);
		nunique = uniq(p, out, n);
		free(work);
	}
	else {
		quicksort(p, n);
		nunique = uniq(p, p, n);
	}

	return nunique;
}


=== Zope/lib/python/BTrees/BTreeItemsTemplate.c 1.7.18.2 => 1.7.18.3 === (561/661 lines abridged)
--- Zope/lib/python/BTrees/BTreeItemsTemplate.c:1.7.18.2	Fri Mar  8 14:12:58 2002
+++ Zope/lib/python/BTrees/BTreeItemsTemplate.c	Tue Sep 10 17:48:24 2002
@@ -2,33 +2,61 @@
 
   Copyright (c) 2001, 2002 Zope Corporation and Contributors.
   All Rights Reserved.
-  
+
   This software is subject to the provisions of the Zope Public License,
   Version 2.0 (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 known		*/

[-=- -=- -=- 561 lines omitted -=- -=- -=-]

           INCREF_VALUE(i->value);
 
           i->position ++;
 
-          PER_ALLOW_DEACTIVATION(currentbucket);
+          PER_UNUSE(currentbucket);
         }
       else
         {
@@ -488,7 +498,7 @@
   return 0;
 }
 
-static int 
+static int
 nextTreeSetItems(SetIteration *i)
 {
   if (i->position >= 0)
@@ -497,21 +507,27 @@
         {
           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)) return -1;
+          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_ALLOW_DEACTIVATION(currentbucket);
+          PER_UNUSE(currentbucket);
         }
       else
         {


=== Zope/lib/python/BTrees/BTreeModuleTemplate.c 1.15.18.2 => 1.15.18.3 ===
--- Zope/lib/python/BTrees/BTreeModuleTemplate.c:1.15.18.2	Fri Mar  8 14:12:58 2002
+++ Zope/lib/python/BTrees/BTreeModuleTemplate.c	Tue Sep 10 17:48:24 2002
@@ -2,14 +2,14 @@
 
   Copyright (c) 2001, 2002 Zope Corporation and Contributors.
   All Rights Reserved.
-  
+
   This software is subject to the provisions of the Zope Public License,
   Version 2.0 (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
-  
+
  ****************************************************************************/
 
 #ifdef PERSISTENT
@@ -17,7 +17,7 @@
 
 /***************************************************************
    The following are macros that ought to be in cPersistence.h */
-#ifndef PER_USE 
+#ifndef PER_USE
 
 #define PER_USE(O) \
 (((O)->state != cPersistent_GHOST_STATE \
@@ -25,7 +25,7 @@
  ? (((O)->state==cPersistent_UPTODATE_STATE) \
     ? ((O)->state=cPersistent_STICKY_STATE) : 1) : 0)
 
-#define PER_ACCESSED(O) ((O)->atime=((long)(time(NULL)/3))%65536) 
+#define PER_ACCESSED(O) ((O)->atime=((long)(time(NULL)/3))%65536)
 
 
 #endif
@@ -42,6 +42,16 @@
 #define PER_CHANGED(O) 0
 #endif
 
+/* 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)
 
 static PyObject *sort_str, *reverse_str, *items_str, *__setstate___str;
 static PyObject *ConflictError = NULL;
@@ -63,58 +73,185 @@
 #define ASSERT(C, S, R) if (! (C)) { \
   PyErr_SetString(PyExc_AssertionError, (S)); return (R); }
 
-typedef struct BTreeItemStruct {
-  KEY_TYPE key;
-  PyObject *value;
-} BTreeItem;
-
-typedef struct Bucket_s {
+/* 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
-  cPersistent_HEAD
+#define sizedcontainer_HEAD         \
+    cPersistent_HEAD                \
+    int size;                       \
+    int len;
 #else
-  PyObject_HEAD
+#define sizedcontainer_HEAD         \
+    PyObject_HEAD                   \
+    int size;                       \
+    int len;
 #endif
-  int size, len;
-  struct Bucket_s *next;
-  KEY_TYPE *keys;
-  VALUE_TYPE *values;
+
+/* 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))
 
-static void PyVar_AssignB(Bucket **v, Bucket *e) { Py_XDECREF(*v); *v=e;}
-#define ASSIGNB(V,E) PyVar_AssignB(&(V),(E))
-#define ASSIGNBC(V,E) (Py_INCREF((E)), PyVar_AssignB(&(V),(E)))
+/* A BTree is complicated.  See Maintainer.txt.
+ */
 
-typedef struct {
-#ifdef PERSISTENT
-  cPersistent_HEAD
-#else
-  PyObject_HEAD
-#endif
-  int size, len;
+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;
 } BTree;
 
 staticforward PyExtensionClass BTreeType;
 
-
 #define BTREE(O) ((BTree*)(O))
 
-typedef struct SetIteration_s 
+/* 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;
-  int position;
-  int hasValue;
-  KEY_TYPE key;
-  VALUE_TYPE value;
-  int (*next)(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=PyInt_FromLong(i);
@@ -127,81 +264,39 @@
   return NULL;
 }
 
-static Bucket *
-PreviousBucket(Bucket *current, Bucket *first, int i)
-{
-  if (! first) return NULL;
-  if (first==current)
-    {
-      IndexError(i);
-      return NULL;
-    }
-
-  Py_INCREF(first);
-  while (1)
-    {
-      PER_USE_OR_RETURN(first,NULL);
-      if (first->next==current) 
-        {
-          PER_ALLOW_DEACTIVATION(first);
-          PER_ACCESSED(first);
-          return first;
-        }
-      else if (first->next)
-        {
-          Bucket *next = first->next;
-          Py_INCREF(next);
-          PER_ALLOW_DEACTIVATION(first);
-          PER_ACCESSED(first);
-          Py_DECREF(first);
-          first=next;
-        }
-      else
-        {
-          PER_ALLOW_DEACTIVATION(first);
-          PER_ACCESSED(first);
-          Py_DECREF(first);
-          IndexError(i);
-          return NULL;
-        }
-    }
-}
-
-static int 
-firstBucketOffset(Bucket **bucket, int *offset)
+/* 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 *b;
-
-  *offset = (*bucket)->len - 1;
-  while ((*bucket)->len < 1)
-    {
-      b=(*bucket)->next;
-      if (b==NULL) return 0;
-      Py_INCREF(b);
-      PER_ALLOW_DEACTIVATION((*bucket));
-      ASSIGNB((*bucket), b);
-      UNLESS (PER_USE(*bucket)) return -1;
-      *offset = 0;
-    }
-  return 1;
-}
+    Bucket *trailing = NULL;    /* first travels; trailing follows it */
+    int result = 0;
 
-static int 
-lastBucketOffset(Bucket **bucket, int *offset, Bucket *firstbucket, int i)
-{
-  Bucket *b;
+    assert(current && *current && first);
+    if (first == *current)
+        return 0;
+
+    do {
+        trailing = first;
+	PER_USE_OR_RETURN(first, -1);
+        first = first->next;
+        PER_ALLOW_DEACTIVATION(trailing);
+	PER_ACCESSED(trailing);
+
+	if (first == *current) {
+	    *current = trailing;
+	    result = 1;
+	    break;
+	}
+    } while (first);
 
-  *offset = (*bucket)->len - 1;
-  while ((*bucket)->len < 1)
-    {
-      b=PreviousBucket((*bucket), firstbucket, i);
-      if (b==NULL) return 0;
-      PER_ALLOW_DEACTIVATION((*bucket));
-      ASSIGNB((*bucket), b);
-      UNLESS (PER_USE(*bucket)) return -1;
-      *offset = (*bucket)->len - 1;
-    }
-  return 1;
+    return result;
 }
 
 static void *
@@ -211,7 +306,7 @@
 
   ASSERT(sz > 0, "non-positive size malloc", NULL);
 
-  if ((r=PyMem_Malloc(sz))) return r;
+  if ((r = malloc(sz))) return r;
 
   PyErr_NoMemory();
   return NULL;
@@ -224,8 +319,8 @@
 
   ASSERT(sz > 0, "non-positive size realloc", NULL);
 
-  if (p) r=PyMem_Realloc(p,sz);
-  else r=PyMem_Malloc(sz);
+  if (p) r = realloc(p,sz);
+  else r = malloc(sz);
 
   UNLESS (r) PyErr_NoMemory();
 
@@ -263,10 +358,18 @@
    "\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[] = 
+static char BTree_module_documentation[] =
 "\n"
 MASTER_ID
 BTREEITEMSTEMPLATE_C
@@ -282,10 +385,10 @@
 BTREEITEMSTEMPLATE_C
 ;
 
-void 
+void
 INITMODULE (void)
 {
-  PyObject *m, *d, *c, *s;
+  PyObject *m, *d, *c;
 
   UNLESS (sort_str=PyString_FromString("sort")) return;
   UNLESS (reverse_str=PyString_FromString("reverse")) return;
@@ -321,11 +424,11 @@
   m = PyImport_ImportModule("ZODB.POSException");
 
   if (m != NULL) {
-  	c = PyObject_GetAttrString(m,"ConflictError");
-  	if (c != NULL) 
+  	c = PyObject_GetAttrString(m, "BTreesConflictError");
+  	if (c != NULL)
   		ConflictError = c;
-	Py_DECREF(m);	
-  } 
+	Py_DECREF(m);
+  }
 
   if (ConflictError == NULL) {
   	Py_INCREF(PyExc_ValueError);
@@ -344,7 +447,7 @@
 #ifdef INTSET_H
   UNLESS(d = PyImport_ImportModule("intSet")) return;
   UNLESS(intSetType = PyObject_GetAttrString (d, "intSet")) return;
-  Py_DECREF (d); 
+  Py_DECREF (d);
 #endif
 
   /* Create the module and add the functions */
@@ -355,16 +458,8 @@
   /* Add some symbolic constants to the module */
   d = PyModule_GetDict(m);
 
-  s = PyString_FromString("$Revision$");
-  PyDict_SetItemString(d, "__version__", s);
-  Py_XDECREF(s);
-
   PyExtensionClass_Export(d,MOD_NAME_PREFIX "Bucket", BucketType);
   PyExtensionClass_Export(d,MOD_NAME_PREFIX "BTree", BTreeType);
   PyExtensionClass_Export(d,MOD_NAME_PREFIX "Set", SetType);
   PyExtensionClass_Export(d,MOD_NAME_PREFIX "TreeSet", TreeSetType);
- 
-  /* Check for errors */
-  if (PyErr_Occurred())
-    Py_FatalError("can't initialize module " MOD_NAME_PREFIX "BTree");
 }


=== Zope/lib/python/BTrees/BTreeTemplate.c 1.20.18.2 => 1.20.18.3 === (1943/2043 lines abridged)
--- Zope/lib/python/BTrees/BTreeTemplate.c:1.20.18.2	Fri Mar  8 14:12:58 2002
+++ Zope/lib/python/BTrees/BTreeTemplate.c	Tue Sep 10 17:48:24 2002
@@ -2,70 +2,222 @@
 
   Copyright (c) 2001, 2002 Zope Corporation and Contributors.
   All Rights Reserved.
-  
+
   This software is subject to the provisions of the Zope Public License,
   Version 2.0 (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 BTREETEMPLATE_C "$Id$\n"
 
+/* Sanity-check a BTree.  This is a private helper for BTree_check.  Return:
+ *      -1         Error.  If it's an internal inconsistency in the BTree,
+ *                 AssertionError is set.
+ *       0         No problem found.
+ *
+ * nextbucket is the bucket "one beyond the end" of the BTree; the last bucket
+ * directly reachable from following right child pointers *should* be linked
+ * to nextbucket (and this is checked).
+ */
+static int
+BTree_check_inner(BTree *self, Bucket *nextbucket)
+{
+    int i;
+    Bucket *bucketafter;
+    Sized *child;
+    char *errormsg = "internal error";  /* someone should have overriden */
+    Sized *activated_child = NULL;
+    int result = -1;    /* until proved innocent */
+
+#define CHECK(CONDITION, ERRORMSG)          \
+    if (!(CONDITION)) {                     \
+        errormsg = (ERRORMSG);              \
+        goto Error;                         \
+    }
+
+    PER_USE_OR_RETURN(self, -1);
+    CHECK(self->len >= 0, "BTree len < 0");
+    CHECK(self->len <= self->size, "BTree len > size");
+    if (self->len == 0) {
+        /* Empty BTree. */

[-=- -=- -=- 1943 lines omitted -=- -=- -=-]

-          return 1;
-        }
-      n = b->next;
-      Py_XINCREF(n);
-      PER_ALLOW_DEACTIVATION(b);
-      PER_ACCESSED(b);
-      ASSIGNB(b, n);
+    int result;
+    Bucket *b;
+    Bucket *next;
+
+    PER_USE_OR_RETURN(self, -1);
+    b = self->firstbucket;
+    PER_UNUSE(self);
+    if (nonzero)
+        return b != NULL;
+
+    result = 0;
+    while (b) {
+        PER_USE_OR_RETURN(b, -1);
+        result += b->len;
+        next = b->next;
+        PER_UNUSE(b);
+        b = next;
     }
-
-  return c;
+    return result;
 }
 
 static int
@@ -1322,14 +1738,14 @@
   (reprfunc)0,			/*tp_str*/
   (getattrofunc)0,
   0,				/*tp_setattro*/
-  
+
   /* Space for future expansion */
   0L,0L,
-  "Mapping type implemented as sorted list of items", 
+  "Mapping type implemented as sorted list of items",
   METHOD_CHAIN(BTree_methods),
-  EXTENSIONCLASS_BASICNEW_FLAG 
+  EXTENSIONCLASS_BASICNEW_FLAG
 #ifdef PERSISTENT
-  | PERSISTENT_TYPE_FLAG 
+  | PERSISTENT_TYPE_FLAG
 #endif
   | EXTENSIONCLASS_NOINSTDICT_FLAG,
 };


=== Zope/lib/python/BTrees/BucketTemplate.c 1.24.6.2 => 1.24.6.3 === (1138/1238 lines abridged)
--- Zope/lib/python/BTrees/BucketTemplate.c:1.24.6.2	Fri Mar  8 14:12:58 2002
+++ Zope/lib/python/BTrees/BucketTemplate.c	Tue Sep 10 17:48:24 2002
@@ -2,70 +2,104 @@
 
   Copyright (c) 2001, 2002 Zope Corporation and Contributors.
   All Rights Reserved.
-  
+
   This software is subject to the provisions of the Zope Public License,
   Version 2.0 (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 BUCKETTEMPLATE_C "$Id$\n"
 
+/* Use BUCKET_SEARCH to find the index at which a key belongs.
+ * INDEX    An int lvalue to hold the index i such that KEY belongs at
+ *          SELF->keys[i].  Note that this will equal SELF->len if KEY
+ *          is larger than the bucket's largest key.  Else it's the
+ *          smallest i such that SELF->keys[i] >= KEY.
+ * ABSENT   An int lvalue to hold a Boolean result, true (!= 0) if the
+ *          key is absent, false (== 0) if the key is at INDEX.
+ * SELF     A pointer to a Bucket 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 (if an empty bucket is special in
+ * context; INDEX becomes 0 and ABSENT becomes true if this macro is run
+ * with an empty SELF, and that may be all the invoker needs to know).
+ */
+#define BUCKET_SEARCH(INDEX, ABSENT, SELF, KEY, ONERROR) {  \
+    int _lo = 0;                                            \
+    int _hi = (SELF)->len;                                  \
+    int _i;                                                 \
+    int _cmp = 1;                                           \
+    for (_i = _hi >> 1; _lo < _hi; _i = (_lo + _hi) >> 1) { \
+        TEST_KEY_SET_OR(_cmp, (SELF)->keys[_i], (KEY))      \
+            ONERROR;                                        \
+        if      (_cmp < 0)  _lo = _i + 1;                   \
+        else if (_cmp == 0) break;                          \
+        else                _hi = _i;                       \
+    }                                                       \

[-=- -=- -=- 1138 lines omitted -=- -=- -=-]

 /* Code to access Bucket objects as mappings */
@@ -1164,7 +1367,7 @@
   static PyObject *format;
   PyObject *r, *t;
 
-  UNLESS (format) UNLESS (format=PyString_FromString(MOD_NAME_PREFIX "Bucket(%s)")) 
+  UNLESS (format) UNLESS (format=PyString_FromString(MOD_NAME_PREFIX "Bucket(%s)"))
     return NULL;
   UNLESS (t=PyTuple_New(1)) return NULL;
   UNLESS (r=bucket_items(self,NULL)) goto err;
@@ -1198,26 +1401,26 @@
   (reprfunc)0,			/*tp_str*/
   (getattrofunc)0,		/*tp_getattro*/
   0,				/*tp_setattro*/
-  
+
   /* Space for future expansion */
   0L,0L,
-  "Mapping type implemented as sorted list of items", 
+  "Mapping type implemented as sorted list of items",
   METHOD_CHAIN(Bucket_methods),
   EXTENSIONCLASS_BASICNEW_FLAG
 #ifdef PERSISTENT
-  | PERSISTENT_TYPE_FLAG 
+  | PERSISTENT_TYPE_FLAG
 #endif
   | EXTENSIONCLASS_NOINSTDICT_FLAG,
 };
 
 
-static int 
+static int
 nextBucket(SetIteration *i)
 {
   if (i->position >= 0)
     {
       UNLESS(PER_USE(BUCKET(i->set))) return -1;
-          
+
       if (i->position)
         {
           DECREF_KEY(i->key);
@@ -1241,6 +1444,6 @@
       PER_ALLOW_DEACTIVATION(BUCKET(i->set));
     }
 
-          
+
   return 0;
 }


=== Zope/lib/python/BTrees/IIBTree.py 1.3.18.1 => 1.3.18.2 ===
--- Zope/lib/python/BTrees/IIBTree.py:1.3.18.1	Wed Feb 13 11:37:02 2002
+++ Zope/lib/python/BTrees/IIBTree.py	Tue Sep 10 17:48:24 2002
@@ -2,14 +2,14 @@
 #
 # Copyright (c) 2001, 2002 Zope Corporation and Contributors.
 # All Rights Reserved.
-# 
+#
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (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
-# 
+#
 ##############################################################################
 
 # hack to overcome dynamic-linking headache.
@@ -17,6 +17,6 @@
 
 # We don't really want _ names in pickles, so update all of the __module__
 # references.
-for o in globals().values():
-    if hasattr(o, '__module__'):
-        o.__module__=__name__
+##for o in globals().values():
+##    if hasattr(o, '__module__'):
+##        o.__module__=__name__


=== Zope/lib/python/BTrees/IOBTree.py 1.3.18.1 => 1.3.18.2 ===
--- Zope/lib/python/BTrees/IOBTree.py:1.3.18.1	Wed Feb 13 11:37:02 2002
+++ Zope/lib/python/BTrees/IOBTree.py	Tue Sep 10 17:48:24 2002
@@ -2,14 +2,14 @@
 #
 # Copyright (c) 2001, 2002 Zope Corporation and Contributors.
 # All Rights Reserved.
-# 
+#
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (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
-# 
+#
 ##############################################################################
 
 # hack to overcome dynamic-linking headache.
@@ -17,6 +17,6 @@
 
 # We don't really want _ names in pickles, so update all of the __module__
 # references.
-for o in globals().values():
-    if hasattr(o, '__module__'):
-        o.__module__=__name__
+##for o in globals().values():
+##    if hasattr(o, '__module__'):
+##        o.__module__=__name__


=== Zope/lib/python/BTrees/Interfaces.py 1.7.18.4 => 1.7.18.5 ===
--- Zope/lib/python/BTrees/Interfaces.py:1.7.18.4	Fri Mar 15 13:39:55 2002
+++ Zope/lib/python/BTrees/Interfaces.py	Tue Sep 10 17:48:24 2002
@@ -2,19 +2,20 @@
 #
 # Copyright (c) 2001, 2002 Zope Corporation and Contributors.
 # All Rights Reserved.
-# 
+#
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (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 OOBTree, Interface, Interface.Standard
+import OOBTree, Interface
+from Interface import Interface
 
-class ICollection(Interface.Base):
+class ICollection(Interface):
 
     def clear():
         """Remove all of the items from the collection"""
@@ -26,7 +27,14 @@
         false otherwise.
         """
 
-class IReadSequence(Interface.Standard.Sequence):
+
+class IReadSequence(Interface):
+
+    def __getitem__(index):
+        """Return a value at the givem index
+
+        An IndexError is raised if the index cannot be found.
+        """
 
     def __getslice__(index1, index2):
         """Return a subsequence from the original sequence
@@ -70,7 +78,7 @@
         """
 
 class ISetMutable(IKeyed):
-    
+
     def insert(key):
         """Add the key (value) to the set.
 
@@ -79,11 +87,17 @@
 
     def remove(key):
         """Remove the key from the set."""
-        
+
     def update(seq):
         """Add the items from the given sequence to the set"""
 
-class IKeySequence(IKeyed, Interface.Standard.Sized):
+class ISized(Interface):
+    "anything supporting __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
@@ -97,9 +111,52 @@
 
 class ITreeSet(IKeyed, ISetMutable):
     pass
-    
 
-class IDictionaryIsh(IKeyed, Interface.Standard.MinimalDictionary):
+class IMinimalDictionary(ISized):
+
+    def has_key(key):
+        """Check whether the object has an item with the given key"""
+
+
+    def get(key, default):
+        """Get the value for the given key
+
+        Return the default if the key is not in the  collection.
+        """
+
+    def __setitem__(key, value):
+        """Set the value for the given key"""
+
+    def __delitem__(key):
+        """delete the value for the given key
+
+        Raise a key error if the key if not in the collection."""
+
+    def values():
+        """Return a 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.
+        """
+
+    def keys():
+        """Return an Sequence 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.
+        """
+
+    def items():
+        """Return a IReadSequence containing the items in the collection
+
+        An item is a key-value tuple.
+
+        The type of the IReadSequence is not specified. It could be a
+        list or a tuple or some other type.
+        """
+
+
+class IDictionaryIsh(IKeyed, IMinimalDictionary):
 
     def update(collection):
         """Add the items from the given collection object to the collection
@@ -148,7 +205,7 @@
         the minimum value. This normalization may be a noop, but, for
         integer values, the normalization is division.
         """
-    
+
 class IBTree(IDictionaryIsh):
 
     def insert(key, value):
@@ -170,7 +227,7 @@
               key=generate_key()
         """
 
-class IMerge(Interface.Base):
+class IMerge(Interface):
     """Object with methods for merging sets, buckets, and trees.
 
     These methods are supplied in modules that define collection
@@ -192,8 +249,11 @@
         """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
+        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):
@@ -226,61 +286,66 @@
     """
 
     def weightedUnion(c1, c2, weight1=1, weight2=1):
-        """Compute the weighted Union of c1 and c2.
+        """Compute the weighted union of c1 and c2.
 
-        If c1 and c2 are None, the output is 0 and None
+        If c1 and c2 are None, the output is (0, None).
 
-        if c1 is None and c2 is not None, the output is weight2 and
-        c2.
+        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 is not None and c2 not None and both sets, the output is 
-        weight1 and c1.
+        If c1 and c2 are both sets, the output is 1 and the (unweighted)
+        union of the sets.
 
-        If c1 and c2 are not None and not both sets, the output is 1 
-        and a Bucket such that the output values are::
+        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 was not in c1. Otherwise, v1 is 1, if
-            c1 is a set, or the value from c1.
+            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
 
-            v2 is 0 if the key was not in c2. Otherwise, v2 is 2, if
-            c2 is a set, or the value from c2.
-
-        Note that c1 and c2 must be collections. 
+        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 None, None.
+        If c1 and c2 are None, the output is (0, None).
 
-        if c1 is None and c2 is not None, the output is weight2 and
-        c2.
+        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).
 
-        if c1 is not None and c2 not None, the output is weight1 and
-        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.
 
-        If c1 and c2 are not None and not both sets, the output is 1
-        and a Bucket such that the output values are::
+        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 0 if the key was not in c1. Otherwise, v1 is 1, if
-            c1 is a set, or the value from c1.
+            v1 is 1        if c1 is a set
+                  c1[key]  if c1 is a mapping
 
-            v2 is 0 if the key was not in c2. Otherwise, v2 is 2, if
-            c2 is a set, or the value from c2.
+            v2 is 1        if c2 is a set
+                  c2[key]  if c2 is a mapping
 
-        Note that c1 and c2 must be collections. 
+        Note that c1 and c2 must be collections.
         """
 
 ###############################################################


=== Zope/lib/python/BTrees/Length.py 1.4.18.1 => 1.4.18.2 ===
--- Zope/lib/python/BTrees/Length.py:1.4.18.1	Wed Feb 13 11:37:02 2002
+++ Zope/lib/python/BTrees/Length.py	Tue Sep 10 17:48:24 2002
@@ -2,14 +2,14 @@
 #
 # Copyright (c) 2001, 2002 Zope Corporation and Contributors.
 # All Rights Reserved.
-# 
+#
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (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 Persistence


=== Zope/lib/python/BTrees/MergeTemplate.c 1.8.18.1 => 1.8.18.2 ===
--- Zope/lib/python/BTrees/MergeTemplate.c:1.8.18.1	Wed Feb 13 11:37:02 2002
+++ Zope/lib/python/BTrees/MergeTemplate.c	Tue Sep 10 17:48:24 2002
@@ -2,14 +2,14 @@
 
   Copyright (c) 2001, 2002 Zope Corporation and Contributors.
   All Rights Reserved.
-  
+
   This software is subject to the provisions of the Zope Public License,
   Version 2.0 (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"
@@ -21,7 +21,7 @@
 static int
 merge_output(Bucket *r, SetIteration *i, int mapping)
 {
-  if(r->len >= r->size && Bucket_grow(r, ! mapping) < 0) return -1;
+  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)
@@ -44,7 +44,7 @@
 	Py_INCREF(ConflictError);
   }
   PyErr_SetObject(ConflictError, r);
-  if (r != Py_None) 
+  if (r != Py_None)
     {
       Py_DECREF(r);
     }
@@ -58,12 +58,13 @@
   Bucket *r=0;
   PyObject *s;
   SetIteration i1 = {0,0,0}, i2 = {0,0,0}, i3 = {0,0,0};
-  int cmp12, cmp13, cmp23, mapping=0, set;
+  int cmp12, cmp13, cmp23, mapping, set;
 
-  if (initSetIteration(&i1, OBJECT(s1), 0, &mapping) < 0) return NULL;
-  if (initSetIteration(&i2, OBJECT(s2), 0, &mapping) < 0) return NULL;
-  if (initSetIteration(&i3, OBJECT(s3), 0, &mapping) < 0) return NULL;
+  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)
@@ -77,14 +78,14 @@
         goto err;
     }
 
-  if (i1.next(&i1) < 0) return NULL;
-  if (i2.next(&i2) < 0) return NULL;
-  if (i3.next(&i3) < 0) return NULL;
+  if (i1.next(&i1) < 0) goto err;
+  if (i2.next(&i2) < 0) goto err;
+  if (i3.next(&i3) < 0) goto err;
 
   while (i1.position >= 0 && i2.position >= 0 && i3.position >= 0)
     {
-      cmp12=TEST_KEY(i1.key, i2.key);
-      cmp13=TEST_KEY(i1.key, i3.key);
+      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)
@@ -142,7 +143,7 @@
         }
       else
         {                       /* Both keys changed */
-          cmp23=TEST_KEY(i2.key, i3.key);
+          TEST_KEY_SET_OR(cmp23, i2.key, i3.key) goto err;
           if (cmp23==0)
             {                   /* dualing inserts or deletes */
               merge_error(i1.position, i2.position, i3.position, 4);
@@ -176,7 +177,7 @@
 
   while (i2.position >= 0 && i3.position >= 0)
     {                           /* New inserts */
-      cmp23=TEST_KEY(i2.key, i3.key);
+      TEST_KEY_SET_OR(cmp23, i2.key, i3.key) goto err;
       if (cmp23==0)
         {                       /* dualing inserts */
           merge_error(i1.position, i2.position, i3.position, 6);
@@ -196,7 +197,7 @@
 
   while (i1.position >= 0 && i2.position >= 0)
     {                           /* deleting i3 */
-      cmp12=TEST_KEY(i1.key, i2.key);
+      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;
@@ -216,7 +217,7 @@
 
   while (i1.position >= 0 && i3.position >= 0)
     {                           /* deleting i2 */
-      cmp13=TEST_KEY(i1.key, i3.key);
+      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;
@@ -251,10 +252,10 @@
       if (merge_output(r, &i3, mapping) < 0) goto err;
       if (i3.next(&i3) < 0) goto err;
     }
-  
-  Py_DECREF(i1.set);
-  Py_DECREF(i2.set);
-  Py_DECREF(i3.set);
+
+  finiSetIteration(&i1);
+  finiSetIteration(&i2);
+  finiSetIteration(&i3);
 
   if (s1->next)
     {
@@ -267,9 +268,9 @@
   return s;
 
  err:
-  Py_XDECREF(i1.set);
-  Py_XDECREF(i2.set);
-  Py_XDECREF(i3.set);
+  finiSetIteration(&i1);
+  finiSetIteration(&i2);
+  finiSetIteration(&i3);
   Py_XDECREF(r);
   return NULL;
 }


=== Zope/lib/python/BTrees/OIBTree.py 1.3.18.1 => 1.3.18.2 ===
--- Zope/lib/python/BTrees/OIBTree.py:1.3.18.1	Wed Feb 13 11:37:02 2002
+++ Zope/lib/python/BTrees/OIBTree.py	Tue Sep 10 17:48:24 2002
@@ -2,14 +2,14 @@
 #
 # Copyright (c) 2001, 2002 Zope Corporation and Contributors.
 # All Rights Reserved.
-# 
+#
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (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
-# 
+#
 ##############################################################################
 
 # hack to overcome dynamic-linking headache.


=== Zope/lib/python/BTrees/OOBTree.py 1.3.18.1 => 1.3.18.2 ===
--- Zope/lib/python/BTrees/OOBTree.py:1.3.18.1	Wed Feb 13 11:37:02 2002
+++ Zope/lib/python/BTrees/OOBTree.py	Tue Sep 10 17:48:24 2002
@@ -2,14 +2,14 @@
 #
 # Copyright (c) 2001, 2002 Zope Corporation and Contributors.
 # All Rights Reserved.
-# 
+#
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (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
-# 
+#
 ##############################################################################
 
 # hack to overcome dynamic-linking headache.


=== Zope/lib/python/BTrees/SetOpTemplate.c 1.8.18.2 => 1.8.18.3 === (483/583 lines abridged)
--- Zope/lib/python/BTrees/SetOpTemplate.c:1.8.18.2	Wed Feb 13 11:37:02 2002
+++ Zope/lib/python/BTrees/SetOpTemplate.c	Tue Sep 10 17:48:24 2002
@@ -2,14 +2,14 @@
 
   Copyright (c) 2001, 2002 Zope Corporation and Contributors.
   All Rights Reserved.
-  
+
   This software is subject to the provisions of the Zope Public License,
   Version 2.0 (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
-  
+
  ****************************************************************************/
 
 /****************************************************************************
@@ -19,9 +19,9 @@
 #define SETOPTEMPLATE_C "$Id$\n"
 
 #ifdef INTSET_H
-static int 
+static int
 nextIntSet(SetIteration *i)
-{    
+{
   if (i->position >= 0)
     {
       UNLESS(PER_USE(INTSET(i->set))) return -1;
@@ -40,99 +40,126 @@
       PER_ALLOW_DEACTIVATION(INTSET(i->set));
     }
 
-          
+
   return 0;
 }
 #endif
 
 #ifdef KEY_CHECK
-static int 
+static int
 nextKeyAsSet(SetIteration *i)
 {
-  if (i->position >= 0)
-    {
-      if (i->position < 1)
-        {

[-=- -=- -=- 483 lines omitted -=- -=- -=-]

+        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_int4_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


=== Zope/lib/python/BTrees/SetTemplate.c 1.12.18.1 => 1.12.18.2 ===


=== Zope/lib/python/BTrees/TreeSetTemplate.c 1.10.18.1 => 1.10.18.2 ===
--- Zope/lib/python/BTrees/TreeSetTemplate.c:1.10.18.1	Wed Feb 13 11:37:02 2002
+++ Zope/lib/python/BTrees/TreeSetTemplate.c	Tue Sep 10 17:48:24 2002
@@ -2,14 +2,14 @@
 
   Copyright (c) 2001, 2002 Zope Corporation and Contributors.
   All Rights Reserved.
-  
+
   This software is subject to the provisions of the Zope Public License,
   Version 2.0 (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 TREESETTEMPLATE_C "$Id$\n"
@@ -78,8 +78,8 @@
   int r;
 
   if (!PyArg_ParseTuple(args,"O",&args)) return NULL;
- 
-  PER_PREVENT_DEACTIVATION(self); 
+
+  PER_PREVENT_DEACTIVATION(self);
   r=_BTree_setstate(self, args, 1);
   PER_ALLOW_DEACTIVATION(self);
   PER_ACCESSED(self);
@@ -105,7 +105,7 @@
    "minKey([key]) -- Fine the minimum key\n\n"
    "If an argument is given, find the minimum >= the argument"},
   {"clear",	(PyCFunction) BTree_clear,	METH_VARARGS,
-   "clear() -- Remove all of the items from the BTree"},  
+   "clear() -- Remove all of the items from the BTree"},
   {"insert",	(PyCFunction)TreeSet_insert,	METH_VARARGS,
    "insert(id,[ignored]) -- Add an id to the set"},
   {"update",	(PyCFunction)TreeSet_update,	METH_VARARGS,
@@ -114,6 +114,8 @@
    "__init__(seq) -- Initialize with  the items from the given sequence"},
   {"remove",	(PyCFunction)TreeSet_remove,	METH_VARARGS,
    "remove(id) -- Remove a key from the set"},
+  {"_check", (PyCFunction) BTree_check,         METH_VARARGS,
+   "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"},
@@ -148,14 +150,14 @@
   (reprfunc)0,			/*tp_str*/
   (getattrofunc)0,
   0,				/*tp_setattro*/
-  
+
   /* Space for future expansion */
   0L,0L,
-  "Set implemented as sorted tree of items", 
+  "Set implemented as sorted tree of items",
   METHOD_CHAIN(TreeSet_methods),
-  EXTENSIONCLASS_BASICNEW_FLAG 
+  EXTENSIONCLASS_BASICNEW_FLAG
 #ifdef PERSISTENT
-  | PERSISTENT_TYPE_FLAG 
+  | PERSISTENT_TYPE_FLAG
 #endif
   | EXTENSIONCLASS_NOINSTDICT_FLAG,
 };


=== Zope/lib/python/BTrees/_IIBTree.c 1.3 => 1.3.112.1 ===
--- Zope/lib/python/BTrees/_IIBTree.c:1.3	Mon Apr  2 12:31:05 2001
+++ Zope/lib/python/BTrees/_IIBTree.c	Tue Sep 10 17:48:24 2002
@@ -8,7 +8,7 @@
 #define INITMODULE init_IIBTree
 #define DEFAULT_MAX_BUCKET_SIZE 120
 #define DEFAULT_MAX_BTREE_SIZE 500
-                
+
 #include "intkeymacros.h"
 #include "intvaluemacros.h"
 #include "cPersistence.h"


=== Zope/lib/python/BTrees/_IOBTree.c 1.3 => 1.3.112.1 ===


=== Zope/lib/python/BTrees/__init__.py 1.3 => 1.3.84.1 ===
--- Zope/lib/python/BTrees/__init__.py:1.3	Sun May 20 12:42:27 2001
+++ Zope/lib/python/BTrees/__init__.py	Tue Sep 10 17:48:24 2002
@@ -1,13 +1 @@
-import ZODB
-
-try: import intSet
-except: pass
-else: del intSet
-
-# Register interfaces
-try: import Interface
-except ImportError: pass # Don't register interfaces if no scarecrow
-else:
-    import Interfaces
-    del Interfaces
-    del Interface
+# If tests is a package, debugging is a bit easier.


=== Zope/lib/python/BTrees/_fsBTree.c 1.2.2.1 => 1.2.2.2 ===
--- Zope/lib/python/BTrees/_fsBTree.c:1.2.2.1	Wed Feb 13 11:24:05 2002
+++ Zope/lib/python/BTrees/_fsBTree.c	Tue Sep 10 17:48:24 2002
@@ -28,8 +28,9 @@
 
 #define KEYMACROS_H "$Id$\n"
 #define KEY_TYPE char2
+#undef KEY_TYPE_IS_PYOBJECT
 #define KEY_CHECK(K) (PyString_Check(K) && PyString_GET_SIZE(K)==2)
-#define TEST_KEY(K, T) ((*(K) < *(T) || (*(K) == *(T) && (K)[1] < (T)[1])) ? -1 : ((*(K) == *(T) && (K)[1] == (T)[1]) ? 0 : 1))
+#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])
@@ -42,6 +43,7 @@
 /*#include "intvaluemacros.h"*/
 #define VALUEMACROS_H "$Id$\n"
 #define VALUE_TYPE char6
+#undef VALUE_TYPE_IS_PYOBJECT
 #define TEST_VALUE(K, T) strncmp(K,T,6)
 #define DECLARE_VALUE(NAME) VALUE_TYPE NAME
 #define DECREF_VALUE(k)


=== Zope/lib/python/BTrees/convert.py 1.4.18.1 => 1.4.18.2 ===
--- Zope/lib/python/BTrees/convert.py:1.4.18.1	Wed Feb 13 11:37:02 2002
+++ Zope/lib/python/BTrees/convert.py	Tue Sep 10 17:48:24 2002
@@ -2,14 +2,14 @@
 #
 # Copyright (c) 2001, 2002 Zope Corporation and Contributors.
 # All Rights Reserved.
-# 
+#
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (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
-# 
+#
 ##############################################################################
 
 def convert(old, new, threshold=200, f=None, None=None):


=== Zope/lib/python/BTrees/intkeymacros.h 1.7 => 1.7.44.1 ===
--- Zope/lib/python/BTrees/intkeymacros.h:1.7	Wed Oct 10 16:36:58 2001
+++ Zope/lib/python/BTrees/intkeymacros.h	Tue Sep 10 17:48:24 2002
@@ -2,8 +2,9 @@
 #define KEYMACROS_H "$Id$\n"
 
 #define KEY_TYPE int
+#undef KEY_TYPE_IS_PYOBJECT
 #define KEY_CHECK PyInt_Check
-#define TEST_KEY(K, T) (((K) < (T)) ? -1 : (((K) > (T)) ? 1: 0)) 
+#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))
@@ -11,4 +12,5 @@
 #define COPY_KEY_FROM_ARG(TARGET, ARG, STATUS) \
   if (PyInt_Check(ARG)) TARGET=PyInt_AS_LONG(ARG); else { \
       PyErr_SetString(PyExc_TypeError, "expected integer key"); \
-      (STATUS)=0; (TARGET)=0; } 
+      (STATUS)=0; (TARGET)=0; }
+#define MULTI_INT_UNION 1


=== Zope/lib/python/BTrees/intvaluemacros.h 1.7 => 1.7.112.1 ===
--- Zope/lib/python/BTrees/intvaluemacros.h:1.7	Tue Apr  3 11:02:17 2001
+++ Zope/lib/python/BTrees/intvaluemacros.h	Tue Sep 10 17:48:24 2002
@@ -2,6 +2,7 @@
 #define VALUEMACROS_H "$Id$\n"
 
 #define VALUE_TYPE int
+#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


=== Zope/lib/python/BTrees/objectkeymacros.h 1.2 => 1.2.116.1 ===
--- Zope/lib/python/BTrees/objectkeymacros.h:1.2	Tue Mar 20 08:52:00 2001
+++ Zope/lib/python/BTrees/objectkeymacros.h	Tue Sep 10 17:48:24 2002
@@ -1,6 +1,7 @@
 #define KEYMACROS_H "$Id$\n"
 #define KEY_TYPE PyObject *
-#define TEST_KEY(KEY, TARGET) PyObject_Compare((KEY),(TARGET))
+#define KEY_TYPE_IS_PYOBJECT
+#define TEST_KEY_SET_OR(V, KEY, TARGET) if ( ( (V) = PyObject_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)


=== Zope/lib/python/BTrees/objectvaluemacros.h 1.3 => 1.3.116.1 ===
--- Zope/lib/python/BTrees/objectvaluemacros.h:1.3	Tue Mar 20 08:52:00 2001
+++ Zope/lib/python/BTrees/objectvaluemacros.h	Tue Sep 10 17:48:24 2002
@@ -2,6 +2,7 @@
 #define VALUEMACROS_H "$Id$\n"
 
 #define VALUE_TYPE PyObject *
+#define VALUE_TYPE_IS_PYOBJECT
 #define TEST_VALUE(VALUE, TARGET) PyObject_Compare((VALUE),(TARGET))
 #define DECLARE_VALUE(NAME) VALUE_TYPE NAME
 #define INCREF_VALUE(k) Py_INCREF(k)