~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/index.py

  • Committer: Matt Nordhoff
  • Date: 2009-04-04 02:50:01 UTC
  • mfrom: (4253 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4256.
  • Revision ID: mnordhoff@mattnordhoff.com-20090404025001-z1403k0tatmc8l91
Merge bzr.dev, fixing conflicts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Indexing facilities."""
18
18
 
56
56
def _has_key_from_parent_map(self, key):
57
57
    """Check if this index has one key.
58
58
 
59
 
    If it's possible to check for multiple keys at once through 
 
59
    If it's possible to check for multiple keys at once through
60
60
    calling get_parent_map that should be faster.
61
61
    """
62
62
    return (key in self.get_parent_map([key]))
68
68
 
69
69
class GraphIndexBuilder(object):
70
70
    """A builder that can build a GraphIndex.
71
 
    
 
71
 
72
72
    The resulting graph has the structure:
73
 
    
 
73
 
74
74
    _SIGNATURE OPTIONS NODES NEWLINE
75
75
    _SIGNATURE     := 'Bazaar Graph Index 1' NEWLINE
76
76
    OPTIONS        := 'node_ref_lists=' DIGITS NEWLINE
99
99
        self._nodes_by_key = None
100
100
        self._key_length = key_elements
101
101
        self._optimize_for_size = False
 
102
        self._combine_backing_indices = True
102
103
 
103
104
    def _check_key(self, key):
104
105
        """Raise BadIndexKey if key is not a valid key for this index."""
246
247
        # one to pad all the data with reference-length and determine entry
247
248
        # addresses.
248
249
        # One to serialise.
249
 
        
 
250
 
250
251
        # forward sorted by key. In future we may consider topological sorting,
251
252
        # at the cost of table scans for direct lookup, or a second index for
252
253
        # direct lookup
315
316
                (len(result.getvalue()), expected_bytes))
316
317
        return result
317
318
 
318
 
    def set_optimize(self, for_size=True):
 
319
    def set_optimize(self, for_size=None, combine_backing_indices=None):
319
320
        """Change how the builder tries to optimize the result.
320
321
 
321
322
        :param for_size: Tell the builder to try and make the index as small as
322
323
            possible.
 
324
        :param combine_backing_indices: If the builder spills to disk to save
 
325
            memory, should the on-disk indices be combined. Set to True if you
 
326
            are going to be probing the index, but to False if you are not. (If
 
327
            you are not querying, then the time spent combining is wasted.)
323
328
        :return: None
324
329
        """
325
330
        # GraphIndexBuilder itself doesn't pay attention to the flag yet, but
326
331
        # other builders do.
327
 
        self._optimize_for_size = for_size
 
332
        if for_size is not None:
 
333
            self._optimize_for_size = for_size
 
334
        if combine_backing_indices is not None:
 
335
            self._combine_backing_indices = combine_backing_indices
328
336
 
329
337
 
330
338
class GraphIndex(object):
331
339
    """An index for data with embedded graphs.
332
 
 
 
340
 
333
341
    The index maps keys to a list of key reference lists, and a value.
334
342
    Each node has the same number of key reference lists. Each key reference
335
343
    list can be empty or an arbitrary length. The value is an opaque NULL
336
 
    terminated string without any newlines. The storage of the index is 
 
344
    terminated string without any newlines. The storage of the index is
337
345
    hidden in the interface: keys and key references are always tuples of
338
346
    bytestrings, never the internal representation (e.g. dictionary offsets).
339
347
 
515
523
 
516
524
    def _resolve_references(self, references):
517
525
        """Return the resolved key references for references.
518
 
        
 
526
 
519
527
        References are resolved by looking up the location of the key in the
520
528
        _keys_by_offset map and substituting the key name, preserving ordering.
521
529
 
522
 
        :param references: An iterable of iterables of key locations. e.g. 
 
530
        :param references: An iterable of iterables of key locations. e.g.
523
531
            [[123, 456], [123]]
524
532
        :return: A tuple of tuples of keys.
525
533
        """
683
691
                    # can't be empty or would not exist
684
692
                    item, value = key_dict.iteritems().next()
685
693
                    if type(value) == dict:
686
 
                        # push keys 
 
694
                        # push keys
687
695
                        dicts.extend(key_dict.itervalues())
688
696
                    else:
689
697
                        # yield keys
697
705
 
698
706
    def key_count(self):
699
707
        """Return an estimate of the number of keys in this index.
700
 
        
 
708
 
701
709
        For GraphIndex the estimate is exact.
702
710
        """
703
711
        if self._key_count is None:
719
727
        # Possible improvements:
720
728
        #  - only bisect lookup each key once
721
729
        #  - sort the keys first, and use that to reduce the bisection window
722
 
        # ----- 
 
730
        # -----
723
731
        # this progresses in three parts:
724
732
        # read data
725
733
        # parse it
734
742
                # We have the key parsed.
735
743
                continue
736
744
            index = self._parsed_key_index(key)
737
 
            if (len(self._parsed_key_map) and 
 
745
            if (len(self._parsed_key_map) and
738
746
                self._parsed_key_map[index][0] <= key and
739
747
                (self._parsed_key_map[index][1] >= key or
740
748
                 # end of the file has been parsed
744
752
                continue
745
753
            # - if we have examined this part of the file already - yes
746
754
            index = self._parsed_byte_index(location)
747
 
            if (len(self._parsed_byte_map) and 
 
755
            if (len(self._parsed_byte_map) and
748
756
                self._parsed_byte_map[index][0] <= location and
749
757
                self._parsed_byte_map[index][1] > location):
750
758
                # the byte region has been parsed, so no read is needed.
1005
1013
        # adjust offset and data to the parseable data.
1006
1014
        trimmed_data = data[trim_start:trim_end]
1007
1015
        if not (trimmed_data):
1008
 
            raise AssertionError('read unneeded data [%d:%d] from [%d:%d]' 
 
1016
            raise AssertionError('read unneeded data [%d:%d] from [%d:%d]'
1009
1017
                % (trim_start, trim_end, offset, offset + len(data)))
1010
1018
        if trim_start:
1011
1019
            offset += trim_start
1156
1164
 
1157
1165
class CombinedGraphIndex(object):
1158
1166
    """A GraphIndex made up from smaller GraphIndices.
1159
 
    
 
1167
 
1160
1168
    The backing indices must implement GraphIndex, and are presumed to be
1161
1169
    static data.
1162
1170
 
1183
1191
                self.__class__.__name__,
1184
1192
                ', '.join(map(repr, self._indices)))
1185
1193
 
1186
 
    @symbol_versioning.deprecated_method(symbol_versioning.one_one)
1187
 
    def get_parents(self, revision_ids):
1188
 
        """See graph._StackedParentsProvider.get_parents.
1189
 
        
1190
 
        This implementation thunks the graph.Graph.get_parents api across to
1191
 
        GraphIndex.
1192
 
 
1193
 
        :param revision_ids: An iterable of graph keys for this graph.
1194
 
        :return: A list of parent details for each key in revision_ids.
1195
 
            Each parent details will be one of:
1196
 
             * None when the key was missing
1197
 
             * (NULL_REVISION,) when the key has no parents.
1198
 
             * (parent_key, parent_key...) otherwise.
1199
 
        """
1200
 
        parent_map = self.get_parent_map(revision_ids)
1201
 
        return [parent_map.get(r, None) for r in revision_ids]
1202
 
 
1203
1194
    def get_parent_map(self, keys):
1204
1195
        """See graph._StackedParentsProvider.get_parent_map"""
1205
1196
        search_keys = set(keys)
1441
1432
                    raise errors.BadIndexKey(key)
1442
1433
                node = self._nodes[key]
1443
1434
                if node[0]:
1444
 
                    continue 
 
1435
                    continue
1445
1436
                if self.reference_lists:
1446
1437
                    yield self, key, node[2], node[1]
1447
1438
                else:
1472
1463
                    # can't be empty or would not exist
1473
1464
                    item, value = key_dict.iteritems().next()
1474
1465
                    if type(value) == dict:
1475
 
                        # push keys 
 
1466
                        # push keys
1476
1467
                        dicts.extend(key_dict.itervalues())
1477
1468
                    else:
1478
1469
                        # yield keys
1483
1474
 
1484
1475
    def key_count(self):
1485
1476
        """Return an estimate of the number of keys in this index.
1486
 
        
 
1477
 
1487
1478
        For InMemoryGraphIndex the estimate is exact.
1488
1479
        """
1489
1480
        return len(self._keys)
1497
1488
 
1498
1489
    Queries against this will emit queries against the adapted Graph with the
1499
1490
    prefix added, queries for all items use iter_entries_prefix. The returned
1500
 
    nodes will have their keys and node references adjusted to remove the 
 
1491
    nodes will have their keys and node references adjusted to remove the
1501
1492
    prefix. Finally, an add_nodes_callback can be supplied - when called the
1502
1493
    nodes and references being added will have prefix prepended.
1503
1494
    """
1530
1521
                    adjusted_references))
1531
1522
        except ValueError:
1532
1523
            # XXX: TODO add an explicit interface for getting the reference list
1533
 
            # status, to handle this bit of user-friendliness in the API more 
 
1524
            # status, to handle this bit of user-friendliness in the API more
1534
1525
            # explicitly.
1535
1526
            for (key, value) in nodes:
1536
1527
                translated_nodes.append((self.prefix + key, value))
1608
1599
 
1609
1600
    def key_count(self):
1610
1601
        """Return an estimate of the number of keys in this index.
1611
 
        
 
1602
 
1612
1603
        For GraphIndexPrefixAdapter this is relatively expensive - key
1613
1604
        iteration with the prefix is done.
1614
1605
        """