/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/index.py

  • Committer: Robert Collins
  • Date: 2007-10-05 04:47:47 UTC
  • mto: (2592.3.168 repository)
  • mto: This revision was merged to the branch mainline in revision 2908.
  • Revision ID: robertc@robertcollins.net-20071005044747-lgtgu13o87egfupg
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
  constructor, for enabling bisection searches. (Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2007 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""Indexing facilities."""
 
18
 
 
19
__all__ = [
 
20
    'CombinedGraphIndex',
 
21
    'GraphIndex',
 
22
    'GraphIndexBuilder',
 
23
    'GraphIndexPrefixAdapter',
 
24
    'InMemoryGraphIndex',
 
25
    ]
 
26
 
 
27
from cStringIO import StringIO
 
28
import re
 
29
 
 
30
from bzrlib.lazy_import import lazy_import
 
31
lazy_import(globals(), """
 
32
from bzrlib import trace
 
33
from bzrlib.trace import mutter
 
34
""")
 
35
from bzrlib import debug, errors
 
36
 
 
37
_OPTION_KEY_ELEMENTS = "key_elements="
 
38
_OPTION_LEN = "len="
 
39
_OPTION_NODE_REFS = "node_ref_lists="
 
40
_SIGNATURE = "Bazaar Graph Index 1\n"
 
41
 
 
42
 
 
43
_whitespace_re = re.compile('[\t\n\x0b\x0c\r\x00 ]')
 
44
_newline_null_re = re.compile('[\n\0]')
 
45
 
 
46
 
 
47
class GraphIndexBuilder(object):
 
48
    """A builder that can build a GraphIndex.
 
49
    
 
50
    The resulting graph has the structure:
 
51
    
 
52
    _SIGNATURE OPTIONS NODES NEWLINE
 
53
    _SIGNATURE     := 'Bazaar Graph Index 1' NEWLINE
 
54
    OPTIONS        := 'node_ref_lists=' DIGITS NEWLINE
 
55
    NODES          := NODE*
 
56
    NODE           := KEY NULL ABSENT? NULL REFERENCES NULL VALUE NEWLINE
 
57
    KEY            := Not-whitespace-utf8
 
58
    ABSENT         := 'a'
 
59
    REFERENCES     := REFERENCE_LIST (TAB REFERENCE_LIST){node_ref_lists - 1}
 
60
    REFERENCE_LIST := (REFERENCE (CR REFERENCE)*)?
 
61
    REFERENCE      := DIGITS  ; digits is the byte offset in the index of the
 
62
                              ; referenced key.
 
63
    VALUE          := no-newline-no-null-bytes
 
64
    """
 
65
 
 
66
    def __init__(self, reference_lists=0, key_elements=1):
 
67
        """Create a GraphIndex builder.
 
68
 
 
69
        :param reference_lists: The number of node references lists for each
 
70
            entry.
 
71
        :param key_elements: The number of bytestrings in each key.
 
72
        """
 
73
        self.reference_lists = reference_lists
 
74
        self._keys = set()
 
75
        self._nodes = {}
 
76
        self._nodes_by_key = {}
 
77
        self._key_length = key_elements
 
78
 
 
79
    def _check_key(self, key):
 
80
        """Raise BadIndexKey if key is not a valid key for this index."""
 
81
        if type(key) != tuple:
 
82
            raise errors.BadIndexKey(key)
 
83
        if self._key_length != len(key):
 
84
            raise errors.BadIndexKey(key)
 
85
        for element in key:
 
86
            if not element or _whitespace_re.search(element) is not None:
 
87
                raise errors.BadIndexKey(element)
 
88
 
 
89
    def add_node(self, key, value, references=()):
 
90
        """Add a node to the index.
 
91
 
 
92
        :param key: The key. keys are non-empty tuples containing
 
93
            as many whitespace-free utf8 bytestrings as the key length
 
94
            defined for this index.
 
95
        :param references: An iterable of iterables of keys. Each is a
 
96
            reference to another key.
 
97
        :param value: The value to associate with the key. It may be any
 
98
            bytes as long as it does not contain \0 or \n.
 
99
        """
 
100
        self._check_key(key)
 
101
        if _newline_null_re.search(value) is not None:
 
102
            raise errors.BadIndexValue(value)
 
103
        if len(references) != self.reference_lists:
 
104
            raise errors.BadIndexValue(references)
 
105
        node_refs = []
 
106
        for reference_list in references:
 
107
            for reference in reference_list:
 
108
                self._check_key(reference)
 
109
                if reference not in self._nodes:
 
110
                    self._nodes[reference] = ('a', (), '')
 
111
            node_refs.append(tuple(reference_list))
 
112
        if key in self._nodes and self._nodes[key][0] == '':
 
113
            raise errors.BadIndexDuplicateKey(key, self)
 
114
        self._nodes[key] = ('', tuple(node_refs), value)
 
115
        self._keys.add(key)
 
116
        if self._key_length > 1:
 
117
            key_dict = self._nodes_by_key
 
118
            if self.reference_lists:
 
119
                key_value = key, value, tuple(node_refs)
 
120
            else:
 
121
                key_value = key, value
 
122
            # possibly should do this on-demand, but it seems likely it is 
 
123
            # always wanted
 
124
            # For a key of (foo, bar, baz) create
 
125
            # _nodes_by_key[foo][bar][baz] = key_value
 
126
            for subkey in key[:-1]:
 
127
                key_dict = key_dict.setdefault(subkey, {})
 
128
            key_dict[key[-1]] = key_value
 
129
 
 
130
    def finish(self):
 
131
        lines = [_SIGNATURE]
 
132
        lines.append(_OPTION_NODE_REFS + str(self.reference_lists) + '\n')
 
133
        lines.append(_OPTION_KEY_ELEMENTS + str(self._key_length) + '\n')
 
134
        lines.append(_OPTION_LEN + str(len(self._keys)) + '\n')
 
135
        prefix_length = sum(len(x) for x in lines)
 
136
        # references are byte offsets. To avoid having to do nasty
 
137
        # polynomial work to resolve offsets (references to later in the 
 
138
        # file cannot be determined until all the inbetween references have
 
139
        # been calculated too) we pad the offsets with 0's to make them be
 
140
        # of consistent length. Using binary offsets would break the trivial
 
141
        # file parsing.
 
142
        # to calculate the width of zero's needed we do three passes:
 
143
        # one to gather all the non-reference data and the number of references.
 
144
        # one to pad all the data with reference-length and determine entry
 
145
        # addresses.
 
146
        # One to serialise.
 
147
        
 
148
        # forward sorted by key. In future we may consider topological sorting,
 
149
        # at the cost of table scans for direct lookup, or a second index for
 
150
        # direct lookup
 
151
        nodes = sorted(self._nodes.items())
 
152
        # if we do not prepass, we don't know how long it will be up front.
 
153
        expected_bytes = None
 
154
        # we only need to pre-pass if we have reference lists at all.
 
155
        if self.reference_lists:
 
156
            key_offset_info = []
 
157
            non_ref_bytes = prefix_length
 
158
            total_references = 0
 
159
            # TODO use simple multiplication for the constants in this loop.
 
160
            for key, (absent, references, value) in nodes:
 
161
                # record the offset known *so far* for this key:
 
162
                # the non reference bytes to date, and the total references to
 
163
                # date - saves reaccumulating on the second pass
 
164
                key_offset_info.append((key, non_ref_bytes, total_references))
 
165
                # key is literal, value is literal, there are 3 null's, 1 NL
 
166
                # key is variable length tuple, \x00 between elements
 
167
                non_ref_bytes += sum(len(element) for element in key)
 
168
                if self._key_length > 1:
 
169
                    non_ref_bytes += self._key_length - 1
 
170
                # value is literal bytes, there are 3 null's, 1 NL.
 
171
                non_ref_bytes += len(value) + 3 + 1
 
172
                # one byte for absent if set.
 
173
                if absent:
 
174
                    non_ref_bytes += 1
 
175
                elif self.reference_lists:
 
176
                    # (ref_lists -1) tabs
 
177
                    non_ref_bytes += self.reference_lists - 1
 
178
                    # (ref-1 cr's per ref_list)
 
179
                    for ref_list in references:
 
180
                        # how many references across the whole file?
 
181
                        total_references += len(ref_list)
 
182
                        # accrue reference separators
 
183
                        if ref_list:
 
184
                            non_ref_bytes += len(ref_list) - 1
 
185
            # how many digits are needed to represent the total byte count?
 
186
            digits = 1
 
187
            possible_total_bytes = non_ref_bytes + total_references*digits
 
188
            while 10 ** digits < possible_total_bytes:
 
189
                digits += 1
 
190
                possible_total_bytes = non_ref_bytes + total_references*digits
 
191
            expected_bytes = possible_total_bytes + 1 # terminating newline
 
192
            # resolve key addresses.
 
193
            key_addresses = {}
 
194
            for key, non_ref_bytes, total_references in key_offset_info:
 
195
                key_addresses[key] = non_ref_bytes + total_references*digits
 
196
            # serialise
 
197
            format_string = '%%0%sd' % digits
 
198
        for key, (absent, references, value) in nodes:
 
199
            flattened_references = []
 
200
            for ref_list in references:
 
201
                ref_addresses = []
 
202
                for reference in ref_list:
 
203
                    ref_addresses.append(format_string % key_addresses[reference])
 
204
                flattened_references.append('\r'.join(ref_addresses))
 
205
            string_key = '\x00'.join(key)
 
206
            lines.append("%s\x00%s\x00%s\x00%s\n" % (string_key, absent,
 
207
                '\t'.join(flattened_references), value))
 
208
        lines.append('\n')
 
209
        result = StringIO(''.join(lines))
 
210
        if expected_bytes and len(result.getvalue()) != expected_bytes:
 
211
            raise errors.BzrError('Failed index creation. Internal error:'
 
212
                ' mismatched output length and expected length: %d %d' %
 
213
                (len(result.getvalue()), expected_bytes))
 
214
        return StringIO(''.join(lines))
 
215
 
 
216
 
 
217
class GraphIndex(object):
 
218
    """An index for data with embedded graphs.
 
219
 
 
220
    The index maps keys to a list of key reference lists, and a value.
 
221
    Each node has the same number of key reference lists. Each key reference
 
222
    list can be empty or an arbitrary length. The value is an opaque NULL
 
223
    terminated string without any newlines. The storage of the index is 
 
224
    hidden in the interface: keys and key references are always tuples of
 
225
    bytestrings, never the internal representation (e.g. dictionary offsets).
 
226
 
 
227
    It is presumed that the index will not be mutated - it is static data.
 
228
 
 
229
    Successive iter_all_entries calls will read the entire index each time.
 
230
    Additionally, iter_entries calls will read the index linearly until the
 
231
    desired keys are found. XXX: This must be fixed before the index is
 
232
    suitable for production use. :XXX
 
233
    """
 
234
 
 
235
    def __init__(self, transport, name, size):
 
236
        """Open an index called name on transport.
 
237
 
 
238
        :param transport: A bzrlib.transport.Transport.
 
239
        :param name: A path to provide to transport API calls.
 
240
        :param size: The size of the index in bytes. This is used for bisection
 
241
            logic to perform partial index reads. While the size could be
 
242
            obtained by statting the file this introduced an additional round
 
243
            trip that is avoided by having it supplied.
 
244
        """
 
245
        self._transport = transport
 
246
        self._name = name
 
247
        self._nodes = None
 
248
        self._key_count = None
 
249
        self._keys_by_offset = None
 
250
        self._nodes_by_key = None
 
251
        self._size = size
 
252
 
 
253
    def _buffer_all(self):
 
254
        """Buffer all the index data.
 
255
 
 
256
        Mutates self._nodes and self.keys_by_offset.
 
257
        """
 
258
        if 'index' in debug.debug_flags:
 
259
            mutter('Reading entire index %s', self._transport.abspath(self._name))
 
260
        stream = self._transport.get(self._name)
 
261
        self._read_prefix(stream)
 
262
        expected_elements = 3 + self._key_length
 
263
        line_count = 0
 
264
        # raw data keyed by offset
 
265
        self._keys_by_offset = {}
 
266
        # ready-to-return key:value or key:value, node_ref_lists
 
267
        self._nodes = {}
 
268
        self._nodes_by_key = {}
 
269
        trailers = 0
 
270
        pos = stream.tell()
 
271
        for line in stream.readlines():
 
272
            if line == '\n':
 
273
                trailers += 1
 
274
                continue
 
275
            elements = line.split('\0')
 
276
            if len(elements) != expected_elements:
 
277
                raise errors.BadIndexData(self)
 
278
            # keys are tuples
 
279
            key = tuple(elements[:self._key_length])
 
280
            absent, references, value = elements[-3:]
 
281
            value = value[:-1] # remove the newline
 
282
            ref_lists = []
 
283
            for ref_string in references.split('\t'):
 
284
                ref_lists.append(tuple([
 
285
                    int(ref) for ref in ref_string.split('\r') if ref
 
286
                    ]))
 
287
            ref_lists = tuple(ref_lists)
 
288
            self._keys_by_offset[pos] = (key, absent, ref_lists, value)
 
289
            pos += len(line)
 
290
        for key, absent, references, value in self._keys_by_offset.itervalues():
 
291
            if absent:
 
292
                continue
 
293
            # resolve references:
 
294
            if self.node_ref_lists:
 
295
                node_refs = []
 
296
                for ref_list in references:
 
297
                    node_refs.append(tuple([self._keys_by_offset[ref][0] for ref in ref_list]))
 
298
                node_value = (value, tuple(node_refs))
 
299
            else:
 
300
                node_value = value
 
301
            self._nodes[key] = node_value
 
302
            if self._key_length > 1:
 
303
                subkey = list(reversed(key[:-1]))
 
304
                key_dict = self._nodes_by_key
 
305
                if self.node_ref_lists:
 
306
                    key_value = key, node_value[0], node_value[1]
 
307
                else:
 
308
                    key_value = key, node_value
 
309
                # possibly should do this on-demand, but it seems likely it is 
 
310
                # always wanted
 
311
                # For a key of (foo, bar, baz) create
 
312
                # _nodes_by_key[foo][bar][baz] = key_value
 
313
                for subkey in key[:-1]:
 
314
                    key_dict = key_dict.setdefault(subkey, {})
 
315
                key_dict[key[-1]] = key_value
 
316
        # cache the keys for quick set intersections
 
317
        self._keys = set(self._nodes)
 
318
        if trailers != 1:
 
319
            # there must be one line - the empty trailer line.
 
320
            raise errors.BadIndexData(self)
 
321
 
 
322
    def iter_all_entries(self):
 
323
        """Iterate over all keys within the index.
 
324
 
 
325
        :return: An iterable of (index, key, value) or (index, key, value, reference_lists).
 
326
            The former tuple is used when there are no reference lists in the
 
327
            index, making the API compatible with simple key:value index types.
 
328
            There is no defined order for the result iteration - it will be in
 
329
            the most efficient order for the index.
 
330
        """
 
331
        if 'evil' in debug.debug_flags:
 
332
            trace.mutter_callsite(3,
 
333
                "iter_all_entries scales with size of history.")
 
334
        if self._nodes is None:
 
335
            self._buffer_all()
 
336
        if self.node_ref_lists:
 
337
            for key, (value, node_ref_lists) in self._nodes.iteritems():
 
338
                yield self, key, value, node_ref_lists
 
339
        else:
 
340
            for key, value in self._nodes.iteritems():
 
341
                yield self, key, value
 
342
 
 
343
    def _read_prefix(self, stream):
 
344
        signature = stream.read(len(self._signature()))
 
345
        if not signature == self._signature():
 
346
            raise errors.BadIndexFormatSignature(self._name, GraphIndex)
 
347
        options_line = stream.readline()
 
348
        if not options_line.startswith(_OPTION_NODE_REFS):
 
349
            raise errors.BadIndexOptions(self)
 
350
        try:
 
351
            self.node_ref_lists = int(options_line[len(_OPTION_NODE_REFS):-1])
 
352
        except ValueError:
 
353
            raise errors.BadIndexOptions(self)
 
354
        options_line = stream.readline()
 
355
        if not options_line.startswith(_OPTION_KEY_ELEMENTS):
 
356
            raise errors.BadIndexOptions(self)
 
357
        try:
 
358
            self._key_length = int(options_line[len(_OPTION_KEY_ELEMENTS):-1])
 
359
        except ValueError:
 
360
            raise errors.BadIndexOptions(self)
 
361
        options_line = stream.readline()
 
362
        if not options_line.startswith(_OPTION_LEN):
 
363
            raise errors.BadIndexOptions(self)
 
364
        try:
 
365
            self._key_count = int(options_line[len(_OPTION_LEN):-1])
 
366
        except ValueError:
 
367
            raise errors.BadIndexOptions(self)
 
368
 
 
369
    def iter_entries(self, keys):
 
370
        """Iterate over keys within the index.
 
371
 
 
372
        :param keys: An iterable providing the keys to be retrieved.
 
373
        :return: An iterable as per iter_all_entries, but restricted to the
 
374
            keys supplied. No additional keys will be returned, and every
 
375
            key supplied that is in the index will be returned.
 
376
        """
 
377
        keys = set(keys)
 
378
        if not keys:
 
379
            return
 
380
        if self._nodes is None:
 
381
            self._buffer_all()
 
382
        keys = keys.intersection(self._keys)
 
383
        if self.node_ref_lists:
 
384
            for key in keys:
 
385
                value, node_refs = self._nodes[key]
 
386
                yield self, key, value, node_refs
 
387
        else:
 
388
            for key in keys:
 
389
                yield self, key, self._nodes[key]
 
390
 
 
391
    def iter_entries_prefix(self, keys):
 
392
        """Iterate over keys within the index using prefix matching.
 
393
 
 
394
        Prefix matching is applied within the tuple of a key, not to within
 
395
        the bytestring of each key element. e.g. if you have the keys ('foo',
 
396
        'bar'), ('foobar', 'gam') and do a prefix search for ('foo', None) then
 
397
        only the former key is returned.
 
398
 
 
399
        :param keys: An iterable providing the key prefixes to be retrieved.
 
400
            Each key prefix takes the form of a tuple the length of a key, but
 
401
            with the last N elements 'None' rather than a regular bytestring.
 
402
            The first element cannot be 'None'.
 
403
        :return: An iterable as per iter_all_entries, but restricted to the
 
404
            keys with a matching prefix to those supplied. No additional keys
 
405
            will be returned, and every match that is in the index will be
 
406
            returned.
 
407
        """
 
408
        keys = set(keys)
 
409
        if not keys:
 
410
            return
 
411
        # load data - also finds key lengths
 
412
        if self._nodes is None:
 
413
            self._buffer_all()
 
414
        if self._key_length == 1:
 
415
            for key in keys:
 
416
                # sanity check
 
417
                if key[0] is None:
 
418
                    raise errors.BadIndexKey(key)
 
419
                if len(key) != self._key_length:
 
420
                    raise errors.BadIndexKey(key)
 
421
                if self.node_ref_lists:
 
422
                    value, node_refs = self._nodes[key]
 
423
                    yield self, key, value, node_refs
 
424
                else:
 
425
                    yield self, key, self._nodes[key]
 
426
            return
 
427
        for key in keys:
 
428
            # sanity check
 
429
            if key[0] is None:
 
430
                raise errors.BadIndexKey(key)
 
431
            if len(key) != self._key_length:
 
432
                raise errors.BadIndexKey(key)
 
433
            # find what it refers to:
 
434
            key_dict = self._nodes_by_key
 
435
            elements = list(key)
 
436
            # find the subdict whose contents should be returned.
 
437
            try:
 
438
                while len(elements) and elements[0] is not None:
 
439
                    key_dict = key_dict[elements[0]]
 
440
                    elements.pop(0)
 
441
            except KeyError:
 
442
                # a non-existant lookup.
 
443
                continue
 
444
            if len(elements):
 
445
                dicts = [key_dict]
 
446
                while dicts:
 
447
                    key_dict = dicts.pop(-1)
 
448
                    # can't be empty or would not exist
 
449
                    item, value = key_dict.iteritems().next()
 
450
                    if type(value) == dict:
 
451
                        # push keys 
 
452
                        dicts.extend(key_dict.itervalues())
 
453
                    else:
 
454
                        # yield keys
 
455
                        for value in key_dict.itervalues():
 
456
                            # each value is the key:value:node refs tuple
 
457
                            # ready to yield.
 
458
                            yield (self, ) + value
 
459
            else:
 
460
                # the last thing looked up was a terminal element
 
461
                yield (self, ) + key_dict
 
462
 
 
463
    def key_count(self):
 
464
        """Return an estimate of the number of keys in this index.
 
465
        
 
466
        For GraphIndex the estimate is exact.
 
467
        """
 
468
        if self._key_count is None:
 
469
            # really this should just read the prefix
 
470
            self._buffer_all()
 
471
        return self._key_count
 
472
 
 
473
    def _signature(self):
 
474
        """The file signature for this index type."""
 
475
        return _SIGNATURE
 
476
 
 
477
    def validate(self):
 
478
        """Validate that everything in the index can be accessed."""
 
479
        # iter_all validates completely at the moment, so just do that.
 
480
        for node in self.iter_all_entries():
 
481
            pass
 
482
 
 
483
 
 
484
class CombinedGraphIndex(object):
 
485
    """A GraphIndex made up from smaller GraphIndices.
 
486
    
 
487
    The backing indices must implement GraphIndex, and are presumed to be
 
488
    static data.
 
489
 
 
490
    Queries against the combined index will be made against the first index,
 
491
    and then the second and so on. The order of index's can thus influence
 
492
    performance significantly. For example, if one index is on local disk and a
 
493
    second on a remote server, the local disk index should be before the other
 
494
    in the index list.
 
495
    """
 
496
 
 
497
    def __init__(self, indices):
 
498
        """Create a CombinedGraphIndex backed by indices.
 
499
 
 
500
        :param indices: An ordered list of indices to query for data.
 
501
        """
 
502
        self._indices = indices
 
503
 
 
504
    def __repr__(self):
 
505
        return "%s(%s)" % (
 
506
                self.__class__.__name__,
 
507
                ', '.join(map(repr, self._indices)))
 
508
 
 
509
    def insert_index(self, pos, index):
 
510
        """Insert a new index in the list of indices to query.
 
511
 
 
512
        :param pos: The position to insert the index.
 
513
        :param index: The index to insert.
 
514
        """
 
515
        self._indices.insert(pos, index)
 
516
 
 
517
    def iter_all_entries(self):
 
518
        """Iterate over all keys within the index
 
519
 
 
520
        Duplicate keys across child indices are presumed to have the same
 
521
        value and are only reported once.
 
522
 
 
523
        :return: An iterable of (index, key, reference_lists, value).
 
524
            There is no defined order for the result iteration - it will be in
 
525
            the most efficient order for the index.
 
526
        """
 
527
        seen_keys = set()
 
528
        for index in self._indices:
 
529
            for node in index.iter_all_entries():
 
530
                if node[1] not in seen_keys:
 
531
                    yield node
 
532
                    seen_keys.add(node[1])
 
533
 
 
534
    def iter_entries(self, keys):
 
535
        """Iterate over keys within the index.
 
536
 
 
537
        Duplicate keys across child indices are presumed to have the same
 
538
        value and are only reported once.
 
539
 
 
540
        :param keys: An iterable providing the keys to be retrieved.
 
541
        :return: An iterable of (index, key, reference_lists, value). There is no
 
542
            defined order for the result iteration - it will be in the most
 
543
            efficient order for the index.
 
544
        """
 
545
        keys = set(keys)
 
546
        for index in self._indices:
 
547
            if not keys:
 
548
                return
 
549
            for node in index.iter_entries(keys):
 
550
                keys.remove(node[1])
 
551
                yield node
 
552
 
 
553
    def iter_entries_prefix(self, keys):
 
554
        """Iterate over keys within the index using prefix matching.
 
555
 
 
556
        Duplicate keys across child indices are presumed to have the same
 
557
        value and are only reported once.
 
558
 
 
559
        Prefix matching is applied within the tuple of a key, not to within
 
560
        the bytestring of each key element. e.g. if you have the keys ('foo',
 
561
        'bar'), ('foobar', 'gam') and do a prefix search for ('foo', None) then
 
562
        only the former key is returned.
 
563
 
 
564
        :param keys: An iterable providing the key prefixes to be retrieved.
 
565
            Each key prefix takes the form of a tuple the length of a key, but
 
566
            with the last N elements 'None' rather than a regular bytestring.
 
567
            The first element cannot be 'None'.
 
568
        :return: An iterable as per iter_all_entries, but restricted to the
 
569
            keys with a matching prefix to those supplied. No additional keys
 
570
            will be returned, and every match that is in the index will be
 
571
            returned.
 
572
        """
 
573
        keys = set(keys)
 
574
        if not keys:
 
575
            return
 
576
        seen_keys = set()
 
577
        for index in self._indices:
 
578
            for node in index.iter_entries_prefix(keys):
 
579
                if node[1] in seen_keys:
 
580
                    continue
 
581
                seen_keys.add(node[1])
 
582
                yield node
 
583
 
 
584
    def key_count(self):
 
585
        """Return an estimate of the number of keys in this index.
 
586
        
 
587
        For CombinedGraphIndex this is approximated by the sum of the keys of
 
588
        the child indices. As child indices may have duplicate keys this can
 
589
        have a maximum error of the number of child indices * largest number of
 
590
        keys in any index.
 
591
        """
 
592
        return sum((index.key_count() for index in self._indices), 0)
 
593
 
 
594
    def validate(self):
 
595
        """Validate that everything in the index can be accessed."""
 
596
        for index in self._indices:
 
597
            index.validate()
 
598
 
 
599
 
 
600
class InMemoryGraphIndex(GraphIndexBuilder):
 
601
    """A GraphIndex which operates entirely out of memory and is mutable.
 
602
 
 
603
    This is designed to allow the accumulation of GraphIndex entries during a
 
604
    single write operation, where the accumulated entries need to be immediately
 
605
    available - for example via a CombinedGraphIndex.
 
606
    """
 
607
 
 
608
    def add_nodes(self, nodes):
 
609
        """Add nodes to the index.
 
610
 
 
611
        :param nodes: An iterable of (key, node_refs, value) entries to add.
 
612
        """
 
613
        if self.reference_lists:
 
614
            for (key, value, node_refs) in nodes:
 
615
                self.add_node(key, value, node_refs)
 
616
        else:
 
617
            for (key, value) in nodes:
 
618
                self.add_node(key, value)
 
619
 
 
620
    def iter_all_entries(self):
 
621
        """Iterate over all keys within the index
 
622
 
 
623
        :return: An iterable of (index, key, reference_lists, value). There is no
 
624
            defined order for the result iteration - it will be in the most
 
625
            efficient order for the index (in this case dictionary hash order).
 
626
        """
 
627
        if 'evil' in debug.debug_flags:
 
628
            trace.mutter_callsite(3,
 
629
                "iter_all_entries scales with size of history.")
 
630
        if self.reference_lists:
 
631
            for key, (absent, references, value) in self._nodes.iteritems():
 
632
                if not absent:
 
633
                    yield self, key, value, references
 
634
        else:
 
635
            for key, (absent, references, value) in self._nodes.iteritems():
 
636
                if not absent:
 
637
                    yield self, key, value
 
638
 
 
639
    def iter_entries(self, keys):
 
640
        """Iterate over keys within the index.
 
641
 
 
642
        :param keys: An iterable providing the keys to be retrieved.
 
643
        :return: An iterable of (index, key, reference_lists, value). There is no
 
644
            defined order for the result iteration - it will be in the most
 
645
            efficient order for the index (keys iteration order in this case).
 
646
        """
 
647
        keys = set(keys)
 
648
        if self.reference_lists:
 
649
            for key in keys.intersection(self._keys):
 
650
                node = self._nodes[key]
 
651
                if not node[0]:
 
652
                    yield self, key, node[2], node[1]
 
653
        else:
 
654
            for key in keys.intersection(self._keys):
 
655
                node = self._nodes[key]
 
656
                if not node[0]:
 
657
                    yield self, key, node[2]
 
658
 
 
659
    def iter_entries_prefix(self, keys):
 
660
        """Iterate over keys within the index using prefix matching.
 
661
 
 
662
        Prefix matching is applied within the tuple of a key, not to within
 
663
        the bytestring of each key element. e.g. if you have the keys ('foo',
 
664
        'bar'), ('foobar', 'gam') and do a prefix search for ('foo', None) then
 
665
        only the former key is returned.
 
666
 
 
667
        :param keys: An iterable providing the key prefixes to be retrieved.
 
668
            Each key prefix takes the form of a tuple the length of a key, but
 
669
            with the last N elements 'None' rather than a regular bytestring.
 
670
            The first element cannot be 'None'.
 
671
        :return: An iterable as per iter_all_entries, but restricted to the
 
672
            keys with a matching prefix to those supplied. No additional keys
 
673
            will be returned, and every match that is in the index will be
 
674
            returned.
 
675
        """
 
676
        # XXX: To much duplication with the GraphIndex class; consider finding
 
677
        # a good place to pull out the actual common logic.
 
678
        keys = set(keys)
 
679
        if not keys:
 
680
            return
 
681
        if self._key_length == 1:
 
682
            for key in keys:
 
683
                # sanity check
 
684
                if key[0] is None:
 
685
                    raise errors.BadIndexKey(key)
 
686
                if len(key) != self._key_length:
 
687
                    raise errors.BadIndexKey(key)
 
688
                node = self._nodes[key]
 
689
                if node[0]:
 
690
                    continue 
 
691
                if self.reference_lists:
 
692
                    yield self, key, node[2], node[1]
 
693
                else:
 
694
                    yield self, key, node[2]
 
695
            return
 
696
        for key in keys:
 
697
            # sanity check
 
698
            if key[0] is None:
 
699
                raise errors.BadIndexKey(key)
 
700
            if len(key) != self._key_length:
 
701
                raise errors.BadIndexKey(key)
 
702
            # find what it refers to:
 
703
            key_dict = self._nodes_by_key
 
704
            elements = list(key)
 
705
            # find the subdict to return
 
706
            try:
 
707
                while len(elements) and elements[0] is not None:
 
708
                    key_dict = key_dict[elements[0]]
 
709
                    elements.pop(0)
 
710
            except KeyError:
 
711
                # a non-existant lookup.
 
712
                continue
 
713
            if len(elements):
 
714
                dicts = [key_dict]
 
715
                while dicts:
 
716
                    key_dict = dicts.pop(-1)
 
717
                    # can't be empty or would not exist
 
718
                    item, value = key_dict.iteritems().next()
 
719
                    if type(value) == dict:
 
720
                        # push keys 
 
721
                        dicts.extend(key_dict.itervalues())
 
722
                    else:
 
723
                        # yield keys
 
724
                        for value in key_dict.itervalues():
 
725
                            yield (self, ) + value
 
726
            else:
 
727
                yield (self, ) + key_dict
 
728
 
 
729
    def key_count(self):
 
730
        """Return an estimate of the number of keys in this index.
 
731
        
 
732
        For InMemoryGraphIndex the estimate is exact.
 
733
        """
 
734
        return len(self._keys)
 
735
 
 
736
    def validate(self):
 
737
        """In memory index's have no known corruption at the moment."""
 
738
 
 
739
 
 
740
class GraphIndexPrefixAdapter(object):
 
741
    """An adapter between GraphIndex with different key lengths.
 
742
 
 
743
    Queries against this will emit queries against the adapted Graph with the
 
744
    prefix added, queries for all items use iter_entries_prefix. The returned
 
745
    nodes will have their keys and node references adjusted to remove the 
 
746
    prefix. Finally, an add_nodes_callback can be supplied - when called the
 
747
    nodes and references being added will have prefix prepended.
 
748
    """
 
749
 
 
750
    def __init__(self, adapted, prefix, missing_key_length,
 
751
        add_nodes_callback=None):
 
752
        """Construct an adapter against adapted with prefix."""
 
753
        self.adapted = adapted
 
754
        self.prefix_key = prefix + (None,)*missing_key_length
 
755
        self.prefix = prefix
 
756
        self.prefix_len = len(prefix)
 
757
        self.add_nodes_callback = add_nodes_callback
 
758
 
 
759
    def add_nodes(self, nodes):
 
760
        """Add nodes to the index.
 
761
 
 
762
        :param nodes: An iterable of (key, node_refs, value) entries to add.
 
763
        """
 
764
        # save nodes in case its an iterator
 
765
        nodes = tuple(nodes)
 
766
        translated_nodes = []
 
767
        try:
 
768
            # Add prefix_key to each reference node_refs is a tuple of tuples,
 
769
            # so split it apart, and add prefix_key to the internal reference
 
770
            for (key, value, node_refs) in nodes:
 
771
                adjusted_references = (
 
772
                    tuple(tuple(self.prefix + ref_node for ref_node in ref_list)
 
773
                        for ref_list in node_refs))
 
774
                translated_nodes.append((self.prefix + key, value,
 
775
                    adjusted_references))
 
776
        except ValueError:
 
777
            # XXX: TODO add an explicit interface for getting the reference list
 
778
            # status, to handle this bit of user-friendliness in the API more 
 
779
            # explicitly.
 
780
            for (key, value) in nodes:
 
781
                translated_nodes.append((self.prefix + key, value))
 
782
        self.add_nodes_callback(translated_nodes)
 
783
 
 
784
    def add_node(self, key, value, references=()):
 
785
        """Add a node to the index.
 
786
 
 
787
        :param key: The key. keys are non-empty tuples containing
 
788
            as many whitespace-free utf8 bytestrings as the key length
 
789
            defined for this index.
 
790
        :param references: An iterable of iterables of keys. Each is a
 
791
            reference to another key.
 
792
        :param value: The value to associate with the key. It may be any
 
793
            bytes as long as it does not contain \0 or \n.
 
794
        """
 
795
        self.add_nodes(((key, value, references), ))
 
796
 
 
797
    def _strip_prefix(self, an_iter):
 
798
        """Strip prefix data from nodes and return it."""
 
799
        for node in an_iter:
 
800
            # cross checks
 
801
            if node[1][:self.prefix_len] != self.prefix:
 
802
                raise errors.BadIndexData(self)
 
803
            for ref_list in node[3]:
 
804
                for ref_node in ref_list:
 
805
                    if ref_node[:self.prefix_len] != self.prefix:
 
806
                        raise errors.BadIndexData(self)
 
807
            yield node[0], node[1][self.prefix_len:], node[2], (
 
808
                tuple(tuple(ref_node[self.prefix_len:] for ref_node in ref_list)
 
809
                for ref_list in node[3]))
 
810
 
 
811
    def iter_all_entries(self):
 
812
        """Iterate over all keys within the index
 
813
 
 
814
        iter_all_entries is implemented against the adapted index using
 
815
        iter_entries_prefix.
 
816
 
 
817
        :return: An iterable of (index, key, reference_lists, value). There is no
 
818
            defined order for the result iteration - it will be in the most
 
819
            efficient order for the index (in this case dictionary hash order).
 
820
        """
 
821
        return self._strip_prefix(self.adapted.iter_entries_prefix([self.prefix_key]))
 
822
 
 
823
    def iter_entries(self, keys):
 
824
        """Iterate over keys within the index.
 
825
 
 
826
        :param keys: An iterable providing the keys to be retrieved.
 
827
        :return: An iterable of (key, reference_lists, value). There is no
 
828
            defined order for the result iteration - it will be in the most
 
829
            efficient order for the index (keys iteration order in this case).
 
830
        """
 
831
        return self._strip_prefix(self.adapted.iter_entries(
 
832
            self.prefix + key for key in keys))
 
833
 
 
834
    def iter_entries_prefix(self, keys):
 
835
        """Iterate over keys within the index using prefix matching.
 
836
 
 
837
        Prefix matching is applied within the tuple of a key, not to within
 
838
        the bytestring of each key element. e.g. if you have the keys ('foo',
 
839
        'bar'), ('foobar', 'gam') and do a prefix search for ('foo', None) then
 
840
        only the former key is returned.
 
841
 
 
842
        :param keys: An iterable providing the key prefixes to be retrieved.
 
843
            Each key prefix takes the form of a tuple the length of a key, but
 
844
            with the last N elements 'None' rather than a regular bytestring.
 
845
            The first element cannot be 'None'.
 
846
        :return: An iterable as per iter_all_entries, but restricted to the
 
847
            keys with a matching prefix to those supplied. No additional keys
 
848
            will be returned, and every match that is in the index will be
 
849
            returned.
 
850
        """
 
851
        return self._strip_prefix(self.adapted.iter_entries_prefix(
 
852
            self.prefix + key for key in keys))
 
853
 
 
854
    def key_count(self):
 
855
        """Return an estimate of the number of keys in this index.
 
856
        
 
857
        For GraphIndexPrefixAdapter this is relatively expensive - key
 
858
        iteration with the prefix is done.
 
859
        """
 
860
        return len(list(self.iter_all_entries()))
 
861
 
 
862
    def validate(self):
 
863
        """Call the adapted's validate."""
 
864
        self.adapted.validate()