[Zodb-checkins] CVS: Zope3/lib/python/Persistence/BTrees - BTreeItemsTemplate.c:1.1.2.6

Tim Peters tim.one@comcast.net
Sun, 9 Jun 2002 15:45:31 -0400


Update of /cvs-repository/Zope3/lib/python/Persistence/BTrees
In directory cvs.zope.org:/tmp/cvs-serv17621

Modified Files:
      Tag: Zope-3x-branch
	BTreeItemsTemplate.c 
Log Message:
Porting from the trunk:

Fix for http://collector.zope.org/Zope/419,
"BTreeItems slice contains 1 too many elements"
This also fixes many related glitches, such as that giving an
out-of-bound slice index raised IndexError instead of clipping.

BTreeItems_slice():  Emulate Python slicing semantics in all cases.

testBTrees.py:  new testSlicing() tests in MappingBase and NormalSetTests
to ensure that slicing of .keys()/.items()/.values() works exactly the
same way as slicing of Python lists, in all one-sided, two-sided and
whole-structure ([:]) cases, with both negative and positive slice indices,
and mixtures of + and -, and whether in bounds or out of bounds.


=== Zope3/lib/python/Persistence/BTrees/BTreeItemsTemplate.c 1.1.2.5 => 1.1.2.6 ===
   int lowoffset;
   int highoffset;
+  int length = -1;  /* len(self), but computed only if needed */
 
-
-  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;
-
+  /* 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 = self->currentbucket;
+       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);
 }