/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/btree_index.py

  • Committer: Richard Wilbur
  • Date: 2016-02-04 19:07:28 UTC
  • mto: This revision was merged to the branch mainline in revision 6618.
  • Revision ID: richard.wilbur@gmail.com-20160204190728-p0zvfii6zase0fw7
Update COPYING.txt from the original http://www.gnu.org/licenses/gpl-2.0.txt  (Only differences were in whitespace.)  Thanks to Petr Stodulka for pointing out the discrepancy.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
"""B+Tree indices"""
19
19
 
20
 
from io import BytesIO
21
 
 
22
 
from ..lazy_import import lazy_import
 
20
from __future__ import absolute_import
 
21
 
 
22
import cStringIO
 
23
 
 
24
from bzrlib.lazy_import import lazy_import
23
25
lazy_import(globals(), """
24
26
import bisect
25
27
import math
27
29
import zlib
28
30
""")
29
31
 
30
 
from .. import (
 
32
from bzrlib import (
31
33
    chunk_writer,
32
34
    debug,
 
35
    errors,
33
36
    fifo_cache,
 
37
    index,
34
38
    lru_cache,
35
39
    osutils,
36
40
    static_tuple,
37
41
    trace,
38
42
    transport,
39
43
    )
40
 
from . import (
41
 
    index,
42
 
    )
43
 
from .index import _OPTION_NODE_REFS, _OPTION_KEY_ELEMENTS, _OPTION_LEN
44
 
 
45
 
 
46
 
_BTSIGNATURE = b"B+Tree Graph Index 2\n"
47
 
_OPTION_ROW_LENGTHS = b"row_lengths="
48
 
_LEAF_FLAG = b"type=leaf\n"
49
 
_INTERNAL_FLAG = b"type=internal\n"
50
 
_INTERNAL_OFFSET = b"offset="
 
44
from bzrlib.index import _OPTION_NODE_REFS, _OPTION_KEY_ELEMENTS, _OPTION_LEN
 
45
 
 
46
 
 
47
_BTSIGNATURE = "B+Tree Graph Index 2\n"
 
48
_OPTION_ROW_LENGTHS = "row_lengths="
 
49
_LEAF_FLAG = "type=leaf\n"
 
50
_INTERNAL_FLAG = "type=internal\n"
 
51
_INTERNAL_OFFSET = "offset="
51
52
 
52
53
_RESERVED_HEADER_BYTES = 120
53
54
_PAGE_SIZE = 4096
67
68
    def __init__(self):
68
69
        """Create a _BuilderRow."""
69
70
        self.nodes = 0
70
 
        self.spool = None  # tempfile.TemporaryFile(prefix='bzr-index-row-')
 
71
        self.spool = None# tempfile.TemporaryFile(prefix='bzr-index-row-')
71
72
        self.writer = None
72
73
 
73
74
    def finish_node(self, pad=True):
74
75
        byte_lines, _, padding = self.writer.finish()
75
76
        if self.nodes == 0:
76
 
            self.spool = BytesIO()
 
77
            self.spool = cStringIO.StringIO()
77
78
            # padded note:
78
 
            self.spool.write(b"\x00" * _RESERVED_HEADER_BYTES)
 
79
            self.spool.write("\x00" * _RESERVED_HEADER_BYTES)
79
80
        elif self.nodes == 1:
80
81
            # We got bigger than 1 node, switch to a temp file
81
82
            spool = tempfile.TemporaryFile(prefix='bzr-index-row-')
142
143
            of nodes that BTreeBuilder will hold in memory.
143
144
        """
144
145
        index.GraphIndexBuilder.__init__(self, reference_lists=reference_lists,
145
 
                                         key_elements=key_elements)
 
146
            key_elements=key_elements)
146
147
        self._spill_at = spill_at
147
148
        self._backing_indices = []
148
149
        # A map of {key: (node_refs, value)}
170
171
        # we don't care about absent_references
171
172
        node_refs, _ = self._check_key_ref_value(key, references, value)
172
173
        if key in self._nodes:
173
 
            raise index.BadIndexDuplicateKey(key, self)
 
174
            raise errors.BadIndexDuplicateKey(key, self)
174
175
        self._nodes[key] = static_tuple.StaticTuple(node_refs, value)
175
176
        if self._nodes_by_key is not None and self._key_length > 1:
176
177
            self._update_nodes_by_key(key, value, node_refs)
262
263
        current_values = []
263
264
        for iterator in iterators_to_combine:
264
265
            try:
265
 
                current_values.append(next(iterator))
 
266
                current_values.append(iterator.next())
266
267
            except StopIteration:
267
268
                current_values.append(None)
268
269
        last = None
269
270
        while True:
270
271
            # Decorate candidates with the value to allow 2.4's min to be used.
271
272
            candidates = [(item[1][1], item) for item
272
 
                          in enumerate(current_values) if item[1] is not None]
 
273
                in enumerate(current_values) if item[1] is not None]
273
274
            if not len(candidates):
274
275
                return
275
276
            selected = min(candidates)
276
277
            # undecorate back to (pos, node)
277
278
            selected = selected[1]
278
279
            if last == selected[1][1]:
279
 
                raise index.BadIndexDuplicateKey(last, self)
 
280
                raise errors.BadIndexDuplicateKey(last, self)
280
281
            last = selected[1][1]
281
282
            # Yield, with self as the index
282
283
            yield (self,) + selected[1][1:]
283
284
            pos = selected[0]
284
285
            try:
285
 
                current_values[pos] = next(iterators_to_combine[pos])
 
286
                current_values[pos] = iterators_to_combine[pos].next()
286
287
            except StopIteration:
287
288
                current_values[pos] = None
288
289
 
305
306
                if internal_row.writer is None:
306
307
                    length = _PAGE_SIZE
307
308
                    if internal_row.nodes == 0:
308
 
                        length -= _RESERVED_HEADER_BYTES  # padded
 
309
                        length -= _RESERVED_HEADER_BYTES # padded
309
310
                    if allow_optimize:
310
311
                        optimize_for_size = self._optimize_for_size
311
312
                    else:
312
313
                        optimize_for_size = False
313
 
                    internal_row.writer = chunk_writer.ChunkWriter(
314
 
                        length, 0, optimize_for_size=optimize_for_size)
 
314
                    internal_row.writer = chunk_writer.ChunkWriter(length, 0,
 
315
                        optimize_for_size=optimize_for_size)
315
316
                    internal_row.writer.write(_INTERNAL_FLAG)
316
 
                    internal_row.writer.write(_INTERNAL_OFFSET
317
 
                                              + b"%d\n" % rows[pos + 1].nodes)
 
317
                    internal_row.writer.write(_INTERNAL_OFFSET +
 
318
                        str(rows[pos + 1].nodes) + "\n")
318
319
            # add a new leaf
319
320
            length = _PAGE_SIZE
320
321
            if rows[-1].nodes == 0:
321
 
                length -= _RESERVED_HEADER_BYTES  # padded
322
 
            rows[-1].writer = chunk_writer.ChunkWriter(
323
 
                length, optimize_for_size=self._optimize_for_size)
 
322
                length -= _RESERVED_HEADER_BYTES # padded
 
323
            rows[-1].writer = chunk_writer.ChunkWriter(length,
 
324
                optimize_for_size=self._optimize_for_size)
324
325
            rows[-1].writer.write(_LEAF_FLAG)
325
326
        if rows[-1].writer.write(line):
326
327
            # if we failed to write, despite having an empty page to write to,
327
328
            # then line is too big. raising the error avoids infinite recursion
328
329
            # searching for a suitably large page that will not be found.
329
330
            if new_leaf:
330
 
                raise index.BadIndexKey(string_key)
 
331
                raise errors.BadIndexKey(string_key)
331
332
            # this key did not fit in the node:
332
333
            rows[-1].finish_node()
333
 
            key_line = string_key + b"\n"
 
334
            key_line = string_key + "\n"
334
335
            new_row = True
335
336
            for row in reversed(rows[:-1]):
336
337
                # Mark the start of the next node in the node above. If it
357
358
                    reserved_bytes,
358
359
                    optimize_for_size=self._optimize_for_size)
359
360
                new_row.writer.write(_INTERNAL_FLAG)
360
 
                new_row.writer.write(_INTERNAL_OFFSET
361
 
                                     + b"%d\n" % (rows[1].nodes - 1))
 
361
                new_row.writer.write(_INTERNAL_OFFSET +
 
362
                    str(rows[1].nodes - 1) + "\n")
362
363
                new_row.writer.write(key_line)
363
 
            self._add_key(string_key, line, rows,
364
 
                          allow_optimize=allow_optimize)
 
364
            self._add_key(string_key, line, rows, allow_optimize=allow_optimize)
365
365
 
366
366
    def _write_nodes(self, node_iterator, allow_optimize=True):
367
367
        """Write node_iterator out as a B+Tree.
393
393
                # First key triggers the first row
394
394
                rows.append(_LeafBuilderRow())
395
395
            key_count += 1
396
 
            string_key, line = _btree_serializer._flatten_node(
397
 
                node, self.reference_lists)
398
 
            self._add_key(string_key, line, rows,
399
 
                          allow_optimize=allow_optimize)
 
396
            string_key, line = _btree_serializer._flatten_node(node,
 
397
                                    self.reference_lists)
 
398
            self._add_key(string_key, line, rows, allow_optimize=allow_optimize)
400
399
        for row in reversed(rows):
401
 
            pad = (not isinstance(row, _LeafBuilderRow))
 
400
            pad = (type(row) != _LeafBuilderRow)
402
401
            row.finish_node(pad=pad)
403
402
        lines = [_BTSIGNATURE]
404
 
        lines.append(b'%s%d\n' % (_OPTION_NODE_REFS, self.reference_lists))
405
 
        lines.append(b'%s%d\n' % (_OPTION_KEY_ELEMENTS, self._key_length))
406
 
        lines.append(b'%s%d\n' % (_OPTION_LEN, key_count))
 
403
        lines.append(_OPTION_NODE_REFS + str(self.reference_lists) + '\n')
 
404
        lines.append(_OPTION_KEY_ELEMENTS + str(self._key_length) + '\n')
 
405
        lines.append(_OPTION_LEN + str(key_count) + '\n')
407
406
        row_lengths = [row.nodes for row in rows]
408
 
        lines.append(_OPTION_ROW_LENGTHS + ','.join(
409
 
            map(str, row_lengths)).encode('ascii') + b'\n')
 
407
        lines.append(_OPTION_ROW_LENGTHS + ','.join(map(str, row_lengths)) + '\n')
410
408
        if row_lengths and row_lengths[-1] > 1:
411
409
            result = tempfile.NamedTemporaryFile(prefix='bzr-index-')
412
410
        else:
413
 
            result = BytesIO()
 
411
            result = cStringIO.StringIO()
414
412
        result.writelines(lines)
415
413
        position = sum(map(len, lines))
 
414
        root_row = True
416
415
        if position > _RESERVED_HEADER_BYTES:
417
416
            raise AssertionError("Could not fit the header in the"
418
417
                                 " reserved space: %d > %d"
419
418
                                 % (position, _RESERVED_HEADER_BYTES))
420
419
        # write the rows out:
421
420
        for row in rows:
422
 
            reserved = _RESERVED_HEADER_BYTES  # reserved space for first node
 
421
            reserved = _RESERVED_HEADER_BYTES # reserved space for first node
423
422
            row.spool.flush()
424
423
            row.spool.seek(0)
425
424
            # copy nodes to the finalised file.
427
426
            node = row.spool.read(_PAGE_SIZE)
428
427
            result.write(node[reserved:])
429
428
            if len(node) == _PAGE_SIZE:
430
 
                result.write(b"\x00" * (reserved - position))
431
 
            position = 0  # Only the root row actually has an offset
 
429
                result.write("\x00" * (reserved - position))
 
430
            position = 0 # Only the root row actually has an offset
432
431
            copied_len = osutils.pumpfile(row.spool, result)
433
432
            if copied_len != (row.nodes - 1) * _PAGE_SIZE:
434
 
                if not isinstance(row, _LeafBuilderRow):
 
433
                if type(row) != _LeafBuilderRow:
435
434
                    raise AssertionError("Incorrect amount of data copied"
436
 
                                         " expected: %d, got: %d"
437
 
                                         % ((row.nodes - 1) * _PAGE_SIZE,
438
 
                                            copied_len))
 
435
                        " expected: %d, got: %d"
 
436
                        % ((row.nodes - 1) * _PAGE_SIZE,
 
437
                           copied_len))
439
438
        result.flush()
440
439
        size = result.tell()
441
440
        result.seek(0)
457
456
            efficient order for the index (in this case dictionary hash order).
458
457
        """
459
458
        if 'evil' in debug.debug_flags:
460
 
            trace.mutter_callsite(
461
 
                3, "iter_all_entries scales with size of history.")
 
459
            trace.mutter_callsite(3,
 
460
                "iter_all_entries scales with size of history.")
462
461
        # Doing serial rather than ordered would be faster; but this shouldn't
463
462
        # be getting called routinely anyway.
464
463
        iterators = [self._iter_mem_nodes()]
473
472
        """Iterate over keys within the index.
474
473
 
475
474
        :param keys: An iterable providing the keys to be retrieved.
476
 
        :return: An iterable of (index, key, value, reference_lists). There is
477
 
            no defined order for the result iteration - it will be in the most
 
475
        :return: An iterable of (index, key, value, reference_lists). There is no
 
476
            defined order for the result iteration - it will be in the most
478
477
            efficient order for the index (keys iteration order in this case).
479
478
        """
480
479
        keys = set(keys)
498
497
        # Find things that are in backing indices that have not been handled
499
498
        # yet.
500
499
        if not self._backing_indices:
501
 
            return  # We won't find anything there either
 
500
            return # We won't find anything there either
502
501
        # Remove all of the keys that we found locally
503
502
        keys.difference_update(local_keys)
504
503
        for backing in self._backing_indices:
527
526
            will be returned, and every match that is in the index will be
528
527
            returned.
529
528
        """
 
529
        # XXX: To much duplication with the GraphIndex class; consider finding
 
530
        # a good place to pull out the actual common logic.
530
531
        keys = set(keys)
531
532
        if not keys:
532
533
            return
537
538
                yield (self,) + node[1:]
538
539
        if self._key_length == 1:
539
540
            for key in keys:
540
 
                index._sanity_check_key(self, key)
 
541
                # sanity check
 
542
                if key[0] is None:
 
543
                    raise errors.BadIndexKey(key)
 
544
                if len(key) != self._key_length:
 
545
                    raise errors.BadIndexKey(key)
541
546
                try:
542
547
                    node = self._nodes[key]
543
548
                except KeyError:
547
552
                else:
548
553
                    yield self, key, node[1]
549
554
            return
550
 
        nodes_by_key = self._get_nodes_by_key()
551
 
        for entry in index._iter_entries_prefix(self, nodes_by_key, keys):
552
 
            yield entry
 
555
        for key in keys:
 
556
            # sanity check
 
557
            if key[0] is None:
 
558
                raise errors.BadIndexKey(key)
 
559
            if len(key) != self._key_length:
 
560
                raise errors.BadIndexKey(key)
 
561
            # find what it refers to:
 
562
            key_dict = self._get_nodes_by_key()
 
563
            elements = list(key)
 
564
            # find the subdict to return
 
565
            try:
 
566
                while len(elements) and elements[0] is not None:
 
567
                    key_dict = key_dict[elements[0]]
 
568
                    elements.pop(0)
 
569
            except KeyError:
 
570
                # a non-existant lookup.
 
571
                continue
 
572
            if len(elements):
 
573
                dicts = [key_dict]
 
574
                while dicts:
 
575
                    key_dict = dicts.pop(-1)
 
576
                    # can't be empty or would not exist
 
577
                    item, value = key_dict.iteritems().next()
 
578
                    if type(value) == dict:
 
579
                        # push keys
 
580
                        dicts.extend(key_dict.itervalues())
 
581
                    else:
 
582
                        # yield keys
 
583
                        for value in key_dict.itervalues():
 
584
                            yield (self, ) + tuple(value)
 
585
            else:
 
586
                yield (self, ) + key_dict
553
587
 
554
588
    def _get_nodes_by_key(self):
555
589
        if self._nodes_by_key is None:
556
590
            nodes_by_key = {}
557
591
            if self.reference_lists:
558
 
                for key, (references, value) in self._nodes.items():
 
592
                for key, (references, value) in self._nodes.iteritems():
559
593
                    key_dict = nodes_by_key
560
594
                    for subkey in key[:-1]:
561
595
                        key_dict = key_dict.setdefault(subkey, {})
562
596
                    key_dict[key[-1]] = key, value, references
563
597
            else:
564
 
                for key, (references, value) in self._nodes.items():
 
598
                for key, (references, value) in self._nodes.iteritems():
565
599
                    key_dict = nodes_by_key
566
600
                    for subkey in key[:-1]:
567
601
                        key_dict = key_dict.setdefault(subkey, {})
574
608
 
575
609
        For InMemoryGraphIndex the estimate is exact.
576
610
        """
577
 
        return len(self._nodes) + sum(
578
 
            backing.key_count()
579
 
            for backing in self._backing_indices
580
 
            if backing is not None)
 
611
        return len(self._nodes) + sum(backing.key_count() for backing in
 
612
            self._backing_indices if backing is not None)
581
613
 
582
614
    def validate(self):
583
615
        """In memory index's have no known corruption at the moment."""
584
616
 
585
 
    def __lt__(self, other):
586
 
        if isinstance(other, type(self)):
587
 
            return self._nodes < other._nodes
588
 
        # Always sort existing indexes before ones that are still being built.
589
 
        if isinstance(other, BTreeGraphIndex):
590
 
            return False
591
 
        raise TypeError
592
 
 
593
617
 
594
618
class _LeafNode(dict):
595
619
    """A leaf node for a serialised B+Tree index."""
599
623
    def __init__(self, bytes, key_length, ref_list_length):
600
624
        """Parse bytes to create a leaf node object."""
601
625
        # splitlines mangles the \r delimiters.. don't use it.
602
 
        key_list = _btree_serializer._parse_leaf_lines(
603
 
            bytes, key_length, ref_list_length)
 
626
        key_list = _btree_serializer._parse_leaf_lines(bytes,
 
627
            key_length, ref_list_length)
604
628
        if key_list:
605
629
            self.min_key = key_list[0][0]
606
630
            self.max_key = key_list[-1][0]
611
635
 
612
636
    def all_items(self):
613
637
        """Return a sorted list of (key, (value, refs)) items"""
614
 
        items = sorted(self.items())
 
638
        items = self.items()
 
639
        items.sort()
615
640
        return items
616
641
 
617
642
    def all_keys(self):
618
643
        """Return a sorted list of all keys."""
619
 
        keys = sorted(self.keys())
 
644
        keys = self.keys()
 
645
        keys.sort()
620
646
        return keys
621
647
 
622
648
 
628
654
    def __init__(self, bytes):
629
655
        """Parse bytes to create an internal node object."""
630
656
        # splitlines mangles the \r delimiters.. don't use it.
631
 
        self.keys = self._parse_lines(bytes.split(b'\n'))
 
657
        self.keys = self._parse_lines(bytes.split('\n'))
632
658
 
633
659
    def _parse_lines(self, lines):
634
660
        nodes = []
635
661
        self.offset = int(lines[1][7:])
636
662
        as_st = static_tuple.StaticTuple.from_sequence
637
663
        for line in lines[2:]:
638
 
            if line == b'':
 
664
            if line == '':
639
665
                break
640
 
            # GZ 2017-05-24: Used to intern() each chunk of line as well, need
641
 
            # to recheck performance and perhaps adapt StaticTuple to adjust.
642
 
            nodes.append(as_st(line.split(b'\0')).intern())
 
666
            nodes.append(as_st(map(intern, line.split('\0'))).intern())
643
667
        return nodes
644
668
 
645
669
 
676
700
        self._base_offset = offset
677
701
        self._leaf_factory = _LeafNode
678
702
        # Default max size is 100,000 leave values
679
 
        self._leaf_value_cache = None  # lru_cache.LRUCache(100*1000)
 
703
        self._leaf_value_cache = None # lru_cache.LRUCache(100*1000)
680
704
        if unlimited_cache:
681
705
            self._leaf_node_cache = {}
682
706
            self._internal_node_cache = {}
688
712
            self._internal_node_cache = fifo_cache.FIFOCache(100)
689
713
        self._key_count = None
690
714
        self._row_lengths = None
691
 
        self._row_offsets = None  # Start of each row, [-1] is the end
692
 
 
693
 
    def __hash__(self):
694
 
        return id(self)
 
715
        self._row_offsets = None # Start of each row, [-1] is the end
695
716
 
696
717
    def __eq__(self, other):
697
718
        """Equal when self and other were created with the same parameters."""
698
719
        return (
699
 
            isinstance(self, type(other))
700
 
            and self._transport == other._transport
701
 
            and self._name == other._name
702
 
            and self._size == other._size)
703
 
 
704
 
    def __lt__(self, other):
705
 
        if isinstance(other, type(self)):
706
 
            return ((self._name, self._size) < (other._name, other._size))
707
 
        # Always sort existing indexes before ones that are still being built.
708
 
        if isinstance(other, BTreeBuilder):
709
 
            return True
710
 
        raise TypeError
 
720
            type(self) == type(other) and
 
721
            self._transport == other._transport and
 
722
            self._name == other._name and
 
723
            self._size == other._size)
711
724
 
712
725
    def __ne__(self, other):
713
726
        return not self.__eq__(other)
728
741
        found = {}
729
742
        start_of_leaves = None
730
743
        for node_pos, node in self._read_nodes(sorted(nodes)):
731
 
            if node_pos == 0:  # Special case
 
744
            if node_pos == 0: # Special case
732
745
                self._root_node = node
733
746
            else:
734
747
                if start_of_leaves is None:
747
760
        pages fit in that length.
748
761
        """
749
762
        recommended_read = self._transport.recommended_page_size()
750
 
        recommended_pages = int(math.ceil(recommended_read / _PAGE_SIZE))
 
763
        recommended_pages = int(math.ceil(recommended_read /
 
764
                                          float(_PAGE_SIZE)))
751
765
        return recommended_pages
752
766
 
753
767
    def _compute_total_pages_in_index(self):
764
778
            return self._row_offsets[-1]
765
779
        # This is the number of pages as defined by the size of the index. They
766
780
        # should be indentical.
767
 
        total_pages = int(math.ceil(self._size / _PAGE_SIZE))
 
781
        total_pages = int(math.ceil(self._size / float(_PAGE_SIZE)))
768
782
        return total_pages
769
783
 
770
784
    def _expand_offsets(self, offsets):
801
815
        if total_pages - len(cached_offsets) <= self._recommended_pages:
802
816
            # Read whatever is left
803
817
            if cached_offsets:
804
 
                expanded = [x for x in range(total_pages)
805
 
                            if x not in cached_offsets]
 
818
                expanded = [x for x in xrange(total_pages)
 
819
                               if x not in cached_offsets]
806
820
            else:
807
 
                expanded = list(range(total_pages))
 
821
                expanded = range(total_pages)
808
822
            if 'index' in debug.debug_flags:
809
823
                trace.mutter('  reading all unread pages: %s', expanded)
810
824
            return expanded
859
873
                if first is None:
860
874
                    first, end = self._find_layer_first_and_end(pos)
861
875
                previous = pos - 1
862
 
                if (previous > 0 and
863
 
                    previous not in cached_offsets and
864
 
                    previous not in final_offsets and
865
 
                        previous >= first):
 
876
                if (previous > 0
 
877
                    and previous not in cached_offsets
 
878
                    and previous not in final_offsets
 
879
                    and previous >= first):
866
880
                    next_tips.add(previous)
867
881
                after = pos + 1
868
 
                if (after < total_pages and
869
 
                    after not in cached_offsets and
870
 
                    after not in final_offsets and
871
 
                        after < end):
 
882
                if (after < total_pages
 
883
                    and after not in cached_offsets
 
884
                    and after not in final_offsets
 
885
                    and after < end):
872
886
                    next_tips.add(after)
873
887
                # This would keep us from going bigger than
874
888
                # recommended_pages by only expanding the first offsets.
898
912
            self._get_root_node()
899
913
        if ref_list_num + 1 > self.node_ref_lists:
900
914
            raise ValueError('No ref list %d, index has %d ref lists'
901
 
                             % (ref_list_num, self.node_ref_lists))
 
915
                % (ref_list_num, self.node_ref_lists))
902
916
        keys = set()
903
917
        refs = set()
904
918
        for node in self.iter_all_entries():
923
937
 
924
938
    def _get_offsets_to_cached_pages(self):
925
939
        """Determine what nodes we already have cached."""
926
 
        cached_offsets = set(self._internal_node_cache)
927
 
        # cache may be dict or LRUCache, keys() is the common method
 
940
        cached_offsets = set(self._internal_node_cache.keys())
928
941
        cached_offsets.update(self._leaf_node_cache.keys())
929
942
        if self._root_node is not None:
930
943
            cached_offsets.add(0)
963
976
    def _cache_leaf_values(self, nodes):
964
977
        """Cache directly from key => value, skipping the btree."""
965
978
        if self._leaf_value_cache is not None:
966
 
            for node in nodes.values():
 
979
            for node in nodes.itervalues():
967
980
                for key, value in node.all_items():
968
981
                    if key in self._leaf_value_cache:
969
982
                        # Don't add the rest of the keys, we've seen this node
980
993
    def iter_all_entries(self):
981
994
        """Iterate over all keys within the index.
982
995
 
983
 
        :return: An iterable of (index, key, value) or
984
 
            (index, key, value, reference_lists).
 
996
        :return: An iterable of (index, key, value) or (index, key, value, reference_lists).
985
997
            The former tuple is used when there are no reference lists in the
986
998
            index, making the API compatible with simple key:value index types.
987
999
            There is no defined order for the result iteration - it will be in
988
1000
            the most efficient order for the index.
989
1001
        """
990
1002
        if 'evil' in debug.debug_flags:
991
 
            trace.mutter_callsite(
992
 
                3, "iter_all_entries scales with size of history.")
 
1003
            trace.mutter_callsite(3,
 
1004
                "iter_all_entries scales with size of history.")
993
1005
        if not self.key_count():
994
1006
            return
995
1007
        if self._row_offsets[-1] == 1:
1003
1015
            return
1004
1016
        start_of_leaves = self._row_offsets[-2]
1005
1017
        end_of_leaves = self._row_offsets[-1]
1006
 
        needed_offsets = list(range(start_of_leaves, end_of_leaves))
 
1018
        needed_offsets = range(start_of_leaves, end_of_leaves)
1007
1019
        if needed_offsets == [0]:
1008
1020
            # Special case when we only have a root node, as we have already
1009
1021
            # read everything
1047
1059
        #       function, so there is even more to be gained.
1048
1060
        # iter_steps = len(in_keys) + len(fixed_keys)
1049
1061
        # bisect_steps = len(in_keys) * math.log(len(fixed_keys), 2)
1050
 
        if len(in_keys) == 1:  # Bisect will always be faster for M = 1
 
1062
        if len(in_keys) == 1: # Bisect will always be faster for M = 1
1051
1063
            return [(bisect.bisect_right(fixed_keys, in_keys[0]), in_keys)]
1052
1064
        # elif bisect_steps < iter_steps:
1053
1065
        #     offsets = {}
1057
1069
        #     return [(o, offsets[o]) for o in sorted(offsets)]
1058
1070
        in_keys_iter = iter(in_keys)
1059
1071
        fixed_keys_iter = enumerate(fixed_keys)
1060
 
        cur_in_key = next(in_keys_iter)
1061
 
        cur_fixed_offset, cur_fixed_key = next(fixed_keys_iter)
1062
 
 
1063
 
        class InputDone(Exception):
1064
 
            pass
1065
 
 
1066
 
        class FixedDone(Exception):
1067
 
            pass
 
1072
        cur_in_key = in_keys_iter.next()
 
1073
        cur_fixed_offset, cur_fixed_key = fixed_keys_iter.next()
 
1074
 
 
1075
        class InputDone(Exception): pass
 
1076
        class FixedDone(Exception): pass
1068
1077
 
1069
1078
        output = []
1070
1079
        cur_out = []
1083
1092
                    while cur_in_key < cur_fixed_key:
1084
1093
                        cur_keys.append(cur_in_key)
1085
1094
                        try:
1086
 
                            cur_in_key = next(in_keys_iter)
 
1095
                            cur_in_key = in_keys_iter.next()
1087
1096
                        except StopIteration:
1088
1097
                            raise InputDone
1089
1098
                    # At this point cur_in_key must be >= cur_fixed_key
1091
1100
                # the end
1092
1101
                while cur_in_key >= cur_fixed_key:
1093
1102
                    try:
1094
 
                        cur_fixed_offset, cur_fixed_key = next(fixed_keys_iter)
 
1103
                        cur_fixed_offset, cur_fixed_key = fixed_keys_iter.next()
1095
1104
                    except StopIteration:
1096
1105
                        raise FixedDone
1097
1106
        except InputDone:
1129
1138
                positions = self._multi_bisect_right(sub_keys, node.keys)
1130
1139
                node_offset = next_row_start + node.offset
1131
1140
                next_nodes_and_keys.extend([(node_offset + pos, s_keys)
1132
 
                                            for pos, s_keys in positions])
 
1141
                                           for pos, s_keys in positions])
1133
1142
            keys_at_index = next_nodes_and_keys
1134
1143
        # We should now be at the _LeafNodes
1135
1144
        node_indexes = [idx for idx, s_keys in keys_at_index]
1178
1187
                else:
1179
1188
                    needed_keys.append(key)
1180
1189
 
 
1190
        last_key = None
1181
1191
        needed_keys = keys
1182
1192
        if not needed_keys:
1183
1193
            return
1226
1236
            return set()
1227
1237
        if ref_list_num >= self.node_ref_lists:
1228
1238
            raise ValueError('No ref list %d, index has %d ref lists'
1229
 
                             % (ref_list_num, self.node_ref_lists))
 
1239
                % (ref_list_num, self.node_ref_lists))
1230
1240
 
1231
1241
        # The main trick we are trying to accomplish is that when we find a
1232
1242
        # key listing its parents, we expect that the parent key is also likely
1307
1317
                            # LeafNode
1308
1318
                            parents_not_on_page.add(key)
1309
1319
                        else:
1310
 
                            # assert (key != node.min_key and
1311
 
                            #         key != node.max_key)
 
1320
                            # assert key != node.min_key and key != node.max_key
1312
1321
                            # If it was going to be present, it would be on
1313
1322
                            # *this* page, so mark it missing.
1314
1323
                            missing_keys.add(key)
1351
1360
            self._get_root_node()
1352
1361
        # TODO: only access nodes that can satisfy the prefixes we are looking
1353
1362
        # for. For now, to meet API usage (as this function is not used by
1354
 
        # current breezy) just suck the entire index and iterate in memory.
 
1363
        # current bzrlib) just suck the entire index and iterate in memory.
1355
1364
        nodes = {}
1356
1365
        if self.node_ref_lists:
1357
1366
            if self._key_length == 1:
1383
1392
                    key_dict[key[-1]] = key_value
1384
1393
        if self._key_length == 1:
1385
1394
            for key in keys:
1386
 
                index._sanity_check_key(self, key)
 
1395
                # sanity check
 
1396
                if key[0] is None:
 
1397
                    raise errors.BadIndexKey(key)
 
1398
                if len(key) != self._key_length:
 
1399
                    raise errors.BadIndexKey(key)
1387
1400
                try:
1388
1401
                    if self.node_ref_lists:
1389
1402
                        value, node_refs = nodes[key]
1393
1406
                except KeyError:
1394
1407
                    pass
1395
1408
            return
1396
 
        for entry in index._iter_entries_prefix(self, nodes_by_key, keys):
1397
 
            yield entry
 
1409
        for key in keys:
 
1410
            # sanity check
 
1411
            if key[0] is None:
 
1412
                raise errors.BadIndexKey(key)
 
1413
            if len(key) != self._key_length:
 
1414
                raise errors.BadIndexKey(key)
 
1415
            # find what it refers to:
 
1416
            key_dict = nodes_by_key
 
1417
            elements = list(key)
 
1418
            # find the subdict whose contents should be returned.
 
1419
            try:
 
1420
                while len(elements) and elements[0] is not None:
 
1421
                    key_dict = key_dict[elements[0]]
 
1422
                    elements.pop(0)
 
1423
            except KeyError:
 
1424
                # a non-existant lookup.
 
1425
                continue
 
1426
            if len(elements):
 
1427
                dicts = [key_dict]
 
1428
                while dicts:
 
1429
                    key_dict = dicts.pop(-1)
 
1430
                    # can't be empty or would not exist
 
1431
                    item, value = key_dict.iteritems().next()
 
1432
                    if type(value) == dict:
 
1433
                        # push keys
 
1434
                        dicts.extend(key_dict.itervalues())
 
1435
                    else:
 
1436
                        # yield keys
 
1437
                        for value in key_dict.itervalues():
 
1438
                            # each value is the key:value:node refs tuple
 
1439
                            # ready to yield.
 
1440
                            yield (self, ) + value
 
1441
            else:
 
1442
                # the last thing looked up was a terminal element
 
1443
                yield (self, ) + key_dict
1398
1444
 
1399
1445
    def key_count(self):
1400
1446
        """Return an estimate of the number of keys in this index.
1425
1471
        """
1426
1472
        signature = bytes[0:len(self._signature())]
1427
1473
        if not signature == self._signature():
1428
 
            raise index.BadIndexFormatSignature(self._name, BTreeGraphIndex)
 
1474
            raise errors.BadIndexFormatSignature(self._name, BTreeGraphIndex)
1429
1475
        lines = bytes[len(self._signature()):].splitlines()
1430
1476
        options_line = lines[0]
1431
1477
        if not options_line.startswith(_OPTION_NODE_REFS):
1432
 
            raise index.BadIndexOptions(self)
 
1478
            raise errors.BadIndexOptions(self)
1433
1479
        try:
1434
1480
            self.node_ref_lists = int(options_line[len(_OPTION_NODE_REFS):])
1435
1481
        except ValueError:
1436
 
            raise index.BadIndexOptions(self)
 
1482
            raise errors.BadIndexOptions(self)
1437
1483
        options_line = lines[1]
1438
1484
        if not options_line.startswith(_OPTION_KEY_ELEMENTS):
1439
 
            raise index.BadIndexOptions(self)
 
1485
            raise errors.BadIndexOptions(self)
1440
1486
        try:
1441
1487
            self._key_length = int(options_line[len(_OPTION_KEY_ELEMENTS):])
1442
1488
        except ValueError:
1443
 
            raise index.BadIndexOptions(self)
 
1489
            raise errors.BadIndexOptions(self)
1444
1490
        options_line = lines[2]
1445
1491
        if not options_line.startswith(_OPTION_LEN):
1446
 
            raise index.BadIndexOptions(self)
 
1492
            raise errors.BadIndexOptions(self)
1447
1493
        try:
1448
1494
            self._key_count = int(options_line[len(_OPTION_LEN):])
1449
1495
        except ValueError:
1450
 
            raise index.BadIndexOptions(self)
 
1496
            raise errors.BadIndexOptions(self)
1451
1497
        options_line = lines[3]
1452
1498
        if not options_line.startswith(_OPTION_ROW_LENGTHS):
1453
 
            raise index.BadIndexOptions(self)
 
1499
            raise errors.BadIndexOptions(self)
1454
1500
        try:
1455
 
            self._row_lengths = [int(length) for length in
1456
 
                                 options_line[len(_OPTION_ROW_LENGTHS):].split(
1457
 
                                     b',')
1458
 
                                 if length]
 
1501
            self._row_lengths = map(int, [length for length in
 
1502
                options_line[len(_OPTION_ROW_LENGTHS):].split(',')
 
1503
                if len(length)])
1459
1504
        except ValueError:
1460
 
            raise index.BadIndexOptions(self)
 
1505
            raise errors.BadIndexOptions(self)
1461
1506
        self._compute_row_offsets()
1462
1507
 
1463
1508
        # calculate the bytes we have processed
1496
1541
                    self._size = num_bytes - base_offset
1497
1542
                    # the whole thing should be parsed out of 'bytes'
1498
1543
                    ranges = [(start, min(_PAGE_SIZE, num_bytes - start))
1499
 
                              for start in range(
1500
 
                                  base_offset, num_bytes, _PAGE_SIZE)]
 
1544
                        for start in xrange(base_offset, num_bytes, _PAGE_SIZE)]
1501
1545
                    break
1502
1546
            else:
1503
1547
                if offset > self._size:
1510
1554
            return
1511
1555
        elif bytes is not None:
1512
1556
            # already have the whole file
1513
 
            data_ranges = [(start, bytes[start:start + size])
 
1557
            data_ranges = [(start, bytes[start:start+size])
1514
1558
                           for start, size in ranges]
1515
1559
        elif self._file is None:
1516
1560
            data_ranges = self._transport.readv(self._name, ranges)
1534
1578
                node = _InternalNode(bytes)
1535
1579
            else:
1536
1580
                raise AssertionError("Unknown node type for %r" % bytes)
1537
 
            yield offset // _PAGE_SIZE, node
 
1581
            yield offset / _PAGE_SIZE, node
1538
1582
 
1539
1583
    def _signature(self):
1540
1584
        """The file signature for this index type."""
1550
1594
            # We shouldn't be reading anything anyway
1551
1595
            start_node = 1
1552
1596
        node_end = self._row_offsets[-1]
1553
 
        for node in self._read_nodes(list(range(start_node, node_end))):
 
1597
        for node in self._read_nodes(range(start_node, node_end)):
1554
1598
            pass
1555
1599
 
1556
1600
 
1557
1601
_gcchk_factory = _LeafNode
1558
1602
 
1559
1603
try:
1560
 
    from . import _btree_serializer_pyx as _btree_serializer
 
1604
    from bzrlib import _btree_serializer_pyx as _btree_serializer
1561
1605
    _gcchk_factory = _btree_serializer._parse_into_chk
1562
 
except ImportError as e:
 
1606
except ImportError, e:
1563
1607
    osutils.failed_to_load_extension(e)
1564
 
    from . import _btree_serializer_py as _btree_serializer
 
1608
    from bzrlib import _btree_serializer_py as _btree_serializer