This file is indexed.

/usr/share/pyshared/BTrees/check.py is in python-zodb 1:3.9.7-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
##############################################################################
#
# 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 types import TupleType

from BTrees.OOBTree import OOBTree, OOBucket, OOSet, OOTreeSet
from BTrees.OIBTree import OIBTree, OIBucket, OISet, OITreeSet
from BTrees.IOBTree import IOBTree, IOBucket, IOSet, IOTreeSet
from BTrees.IIBTree import IIBTree, IIBucket, IISet, IITreeSet
from BTrees.IFBTree import IFBTree, IFBucket, IFSet, IFTreeSet
from BTrees.OLBTree import OLBTree, OLBucket, OLSet, OLTreeSet
from BTrees.LOBTree import LOBTree, LOBucket, LOSet, LOTreeSet
from BTrees.LLBTree import LLBTree, LLBucket, LLSet, LLTreeSet
from BTrees.LFBTree import LFBTree, LFBucket, LFSet, LFTreeSet

from ZODB.utils import positive_id, oid_repr

TYPE_UNKNOWN, TYPE_BTREE, TYPE_BUCKET = range(3)

_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

# 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']
    _btree2bucket[globals()[kv+'TreeSet']] = globals()[kv+'Set']

def crack_btree(t, is_mapping):
    state = t.__getstate__()
    if state is None:
        return BTREE_EMPTY, [], []

    assert isinstance(state, TupleType)
    if len(state) == 1:
        state = state[0]
        assert isinstance(state, TupleType) 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, TupleType)
    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:
            if lo is not None and not lo <= x:
                s = "key %r < lower bound %r at index %d" % (x, lo, i)
                self.complain(s, obj, path)
            if hi is not None and not x < hi:
                s = "key %r >= upper bound %r at index %d" % (x, hi, i)
                self.complain(s, obj, path)
            if i < n-1 and not x < keys[i+1]:
                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):
    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):
    "Display the internal structure of a BTree, Bucket, TreeSet or Set."
    Printer(btree).display()