17
17
"""Knit-based pack repository formats."""
19
from __future__ import absolute_import
23
from ..lazy_import import lazy_import
21
from brzlib.lazy_import import lazy_import
24
22
lazy_import(globals(), """
23
from itertools import izip
31
33
revision as _mod_revision,
36
from breezy.bzr import (
42
from breezy.bzr.knit import (
41
from brzlib.knit import (
45
44
KnitVersionedFiles,
52
from ..bzr.index import (
51
from brzlib.index import (
53
52
CombinedGraphIndex,
55
54
GraphIndexPrefixAdapter,
56
55
InMemoryGraphIndex,
58
from .knitrepo import (
57
from brzlib.repofmt.knitrepo import (
61
from .pack_repo import (
60
from brzlib.repofmt.pack_repo import (
64
63
RepositoryFormatPack,
68
PackRootCommitBuilder,
69
69
RepositoryPackCollection,
71
from ..bzr.vf_repository import (
71
from brzlib.vf_repository import (
76
76
class KnitPackRepository(PackRepository, KnitRepository):
78
def __init__(self, _format, a_controldir, control_files, _commit_builder_class,
80
PackRepository.__init__(self, _format, a_controldir, control_files,
81
_commit_builder_class, _serializer)
78
def __init__(self, _format, a_bzrdir, control_files, _commit_builder_class,
80
PackRepository.__init__(self, _format, a_bzrdir, control_files,
81
_commit_builder_class, _serializer)
82
82
if self._format.supports_chks:
83
83
raise AssertionError("chk not supported")
84
84
index_transport = self._transport.clone('indices')
85
85
self._pack_collection = KnitRepositoryPackCollection(self,
88
self._transport.clone(
90
self._transport.clone(
92
_format.index_builder_class,
88
self._transport.clone('upload'),
89
self._transport.clone('packs'),
90
_format.index_builder_class,
96
94
self.inventories = KnitVersionedFiles(
97
95
_KnitGraphIndex(self._pack_collection.inventory_index.combined_index,
98
add_callback=self._pack_collection.inventory_index.add_callback,
99
deltas=True, parents=True, is_locked=self.is_locked),
96
add_callback=self._pack_collection.inventory_index.add_callback,
97
deltas=True, parents=True, is_locked=self.is_locked),
100
98
data_access=self._pack_collection.inventory_index.data_access,
101
99
max_delta_chain=200)
102
100
self.revisions = KnitVersionedFiles(
103
101
_KnitGraphIndex(self._pack_collection.revision_index.combined_index,
104
add_callback=self._pack_collection.revision_index.add_callback,
105
deltas=False, parents=True, is_locked=self.is_locked,
106
track_external_parent_refs=True),
102
add_callback=self._pack_collection.revision_index.add_callback,
103
deltas=False, parents=True, is_locked=self.is_locked,
104
track_external_parent_refs=True),
107
105
data_access=self._pack_collection.revision_index.data_access,
108
106
max_delta_chain=0)
109
107
self.signatures = KnitVersionedFiles(
110
108
_KnitGraphIndex(self._pack_collection.signature_index.combined_index,
111
add_callback=self._pack_collection.signature_index.add_callback,
112
deltas=False, parents=False, is_locked=self.is_locked),
109
add_callback=self._pack_collection.signature_index.add_callback,
110
deltas=False, parents=False, is_locked=self.is_locked),
113
111
data_access=self._pack_collection.signature_index.data_access,
114
112
max_delta_chain=0)
115
113
self.texts = KnitVersionedFiles(
116
114
_KnitGraphIndex(self._pack_collection.text_index.combined_index,
117
add_callback=self._pack_collection.text_index.add_callback,
118
deltas=True, parents=True, is_locked=self.is_locked),
115
add_callback=self._pack_collection.text_index.add_callback,
116
deltas=True, parents=True, is_locked=self.is_locked),
119
117
data_access=self._pack_collection.text_index.data_access,
120
118
max_delta_chain=200)
121
119
self.chk_bytes = None
158
155
index_class = GraphIndex
160
157
def _get_matching_bzrdir(self):
161
return controldir.format_registry.make_controldir('pack-0.92')
158
return controldir.format_registry.make_bzrdir('pack-0.92')
163
160
def _ignore_setting_bzrdir(self, format):
166
_matchingcontroldir = property(
167
_get_matching_bzrdir, _ignore_setting_bzrdir)
163
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
170
166
def get_format_string(cls):
171
167
"""See RepositoryFormat.get_format_string()."""
172
return b"Bazaar pack repository format 1 (needs bzr 0.92)\n"
168
return "Bazaar pack repository format 1 (needs bzr 0.92)\n"
174
170
def get_format_description(self):
175
171
"""See RepositoryFormat.get_format_description()."""
200
195
index_class = GraphIndex
202
197
def _get_matching_bzrdir(self):
203
return controldir.format_registry.make_controldir(
198
return controldir.format_registry.make_bzrdir(
204
199
'pack-0.92-subtree')
206
201
def _ignore_setting_bzrdir(self, format):
209
_matchingcontroldir = property(
210
_get_matching_bzrdir, _ignore_setting_bzrdir)
204
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
213
207
def get_format_string(cls):
214
208
"""See RepositoryFormat.get_format_string()."""
215
return b"Bazaar pack repository format 1 with subtree support (needs bzr 0.92)\n"
209
return "Bazaar pack repository format 1 with subtree support (needs bzr 0.92)\n"
217
211
def get_format_description(self):
218
212
"""See RepositoryFormat.get_format_description()."""
241
234
index_class = GraphIndex
243
236
def _get_matching_bzrdir(self):
244
return controldir.format_registry.make_controldir(
237
return controldir.format_registry.make_bzrdir(
245
238
'rich-root-pack')
247
240
def _ignore_setting_bzrdir(self, format):
250
_matchingcontroldir = property(
251
_get_matching_bzrdir, _ignore_setting_bzrdir)
243
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
254
246
def get_format_string(cls):
255
247
"""See RepositoryFormat.get_format_string()."""
256
return (b"Bazaar pack repository format 1 with rich root"
257
b" (needs bzr 1.0)\n")
248
return ("Bazaar pack repository format 1 with rich root"
249
" (needs bzr 1.0)\n")
259
251
def get_format_description(self):
260
252
"""See RepositoryFormat.get_format_description()."""
282
274
return xml5.serializer_v5
284
276
def _get_matching_bzrdir(self):
285
return controldir.format_registry.make_controldir('1.6')
277
return controldir.format_registry.make_bzrdir('1.6')
287
279
def _ignore_setting_bzrdir(self, format):
290
_matchingcontroldir = property(
291
_get_matching_bzrdir, _ignore_setting_bzrdir)
282
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
294
285
def get_format_string(cls):
295
286
"""See RepositoryFormat.get_format_string()."""
296
return b"Bazaar RepositoryFormatKnitPack5 (bzr 1.6)\n"
287
return "Bazaar RepositoryFormatKnitPack5 (bzr 1.6)\n"
298
289
def get_format_description(self):
299
290
"""See RepositoryFormat.get_format_description()."""
323
314
return xml6.serializer_v6
325
316
def _get_matching_bzrdir(self):
326
return controldir.format_registry.make_controldir(
317
return controldir.format_registry.make_bzrdir(
327
318
'1.6.1-rich-root')
329
320
def _ignore_setting_bzrdir(self, format):
332
_matchingcontroldir = property(
333
_get_matching_bzrdir, _ignore_setting_bzrdir)
323
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
336
326
def get_format_string(cls):
337
327
"""See RepositoryFormat.get_format_string()."""
338
return b"Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6.1)\n"
328
return "Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6.1)\n"
340
330
def get_format_description(self):
341
331
return "Packs 5 rich-root (adds stacking support, requires bzr 1.6.1)"
412
401
return xml5.serializer_v5
414
403
def _get_matching_bzrdir(self):
415
return controldir.format_registry.make_controldir('1.9')
404
return controldir.format_registry.make_bzrdir('1.9')
417
406
def _ignore_setting_bzrdir(self, format):
420
_matchingcontroldir = property(
421
_get_matching_bzrdir, _ignore_setting_bzrdir)
409
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
424
412
def get_format_string(cls):
425
413
"""See RepositoryFormat.get_format_string()."""
426
return b"Bazaar RepositoryFormatKnitPack6 (bzr 1.9)\n"
414
return "Bazaar RepositoryFormatKnitPack6 (bzr 1.9)\n"
428
416
def get_format_description(self):
429
417
"""See RepositoryFormat.get_format_description()."""
450
438
return xml6.serializer_v6
452
440
def _get_matching_bzrdir(self):
453
return controldir.format_registry.make_controldir(
441
return controldir.format_registry.make_bzrdir(
456
444
def _ignore_setting_bzrdir(self, format):
459
_matchingcontroldir = property(
460
_get_matching_bzrdir, _ignore_setting_bzrdir)
447
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
463
450
def get_format_string(cls):
464
451
"""See RepositoryFormat.get_format_string()."""
465
return b"Bazaar RepositoryFormatKnitPack6RichRoot (bzr 1.9)\n"
452
return "Bazaar RepositoryFormatKnitPack6RichRoot (bzr 1.9)\n"
467
454
def get_format_description(self):
468
455
return "Packs 6 rich-root (uses btree indexes, requires bzr 1.9)"
492
479
return xml7.serializer_v7
494
481
def _get_matching_bzrdir(self):
495
return controldir.format_registry.make_controldir(
482
return controldir.format_registry.make_bzrdir(
496
483
'development5-subtree')
498
485
def _ignore_setting_bzrdir(self, format):
501
_matchingcontroldir = property(
502
_get_matching_bzrdir, _ignore_setting_bzrdir)
488
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
505
491
def get_format_string(cls):
506
492
"""See RepositoryFormat.get_format_string()."""
507
return (b"Bazaar development format 2 with subtree support "
508
b"(needs bzr.dev from before 1.8)\n")
493
return ("Bazaar development format 2 with subtree support "
494
"(needs bzr.dev from before 1.8)\n")
510
496
def get_format_description(self):
511
497
"""See RepositoryFormat.get_format_description()."""
512
498
return ("Development repository format, currently the same as "
513
"1.6.1-subtree with B+Tree indices.\n")
499
"1.6.1-subtree with B+Tree indices.\n")
516
502
class KnitPackStreamSource(StreamSource):
540
526
content_text_keys = set()
541
527
knit = KnitVersionedFiles(None, None)
542
528
factory = KnitPlainFactory()
544
529
def find_text_keys_from_content(record):
545
530
if record.storage_kind not in ('knit-delta-gz', 'knit-ft-gz'):
546
531
raise ValueError("Unknown content storage kind for"
547
" inventory text: %s" % (record.storage_kind,))
532
" inventory text: %s" % (record.storage_kind,))
548
533
# It's a knit record, it has a _raw_record field (even if it was
549
534
# reconstituted from a network stream).
550
535
raw_data = record._raw_record
626
610
return all_index.iter_entries(key_filter)
628
612
def _copy_nodes(self, nodes, index_map, writer, write_index,
630
614
"""Copy knit nodes between packs with no graph references.
632
616
:param output_lines: Output full texts of copied items.
634
with ui.ui_factory.nested_progress_bar() as pb:
618
pb = ui.ui_factory.nested_progress_bar()
635
620
return self._do_copy_nodes(nodes, index_map, writer,
636
write_index, pb, output_lines=output_lines)
621
write_index, pb, output_lines=output_lines)
638
625
def _do_copy_nodes(self, nodes, index_map, writer, write_index, pb,
640
627
# for record verification
641
628
knit = KnitVersionedFiles(None, None)
642
629
# plan a readv on each source pack:
653
640
request_groups[index].append((key, value))
655
642
pb.update("Copied record", record_index, len(nodes))
656
for index, items in request_groups.items():
643
for index, items in request_groups.iteritems():
657
644
pack_readv_requests = []
658
645
for key, value in items:
659
646
# ---- KnitGraphIndex.get_position
660
bits = value[1:].split(b' ')
647
bits = value[1:].split(' ')
661
648
offset, length = int(bits[0]), int(bits[1])
662
pack_readv_requests.append((offset, length, (key, value[0:1])))
649
pack_readv_requests.append((offset, length, (key, value[0])))
663
650
# linear scan up the pack
664
651
pack_readv_requests.sort()
667
654
transport, path = pack_obj.access_tuple()
669
656
reader = pack.make_readv_reader(transport, path,
670
[offset[0:2] for offset in pack_readv_requests])
657
[offset[0:2] for offset in pack_readv_requests])
671
658
except errors.NoSuchFile:
672
659
if self._reload_func is not None:
673
660
self._reload_func()
675
for (names, read_func), (_1, _2, (key, eol_flag)) in zip(
676
reader.iter_records(), pack_readv_requests):
662
for (names, read_func), (_1, _2, (key, eol_flag)) in \
663
izip(reader.iter_records(), pack_readv_requests):
677
664
raw_data = read_func(None)
678
665
# check the header only
679
666
if output_lines is not None:
682
669
df, _ = knit._parse_record_header(key, raw_data)
684
pos, size = writer.add_bytes_record([raw_data], len(raw_data), names)
685
write_index.add_node(key, eol_flag + b"%d %d" % (pos, size))
671
pos, size = writer.add_bytes_record(raw_data, names)
672
write_index.add_node(key, eol_flag + "%d %d" % (pos, size))
686
673
pb.update("Copied record", record_index)
687
674
record_index += 1
689
676
def _copy_nodes_graph(self, index_map, writer, write_index,
690
readv_group_iter, total_items, output_lines=False):
677
readv_group_iter, total_items, output_lines=False):
691
678
"""Copy knit nodes between packs.
693
680
:param output_lines: Return lines present in the copied data as
694
681
an iterator of line,version_id.
696
with ui.ui_factory.nested_progress_bar() as pb:
683
pb = ui.ui_factory.nested_progress_bar()
697
685
for result in self._do_copy_nodes_graph(index_map, writer,
698
write_index, output_lines, pb, readv_group_iter, total_items):
686
write_index, output_lines, pb, readv_group_iter, total_items):
689
# Python 2.4 does not permit try:finally: in a generator.
701
695
def _do_copy_nodes_graph(self, index_map, writer, write_index,
702
output_lines, pb, readv_group_iter, total_items):
696
output_lines, pb, readv_group_iter, total_items):
703
697
# for record verification
704
698
knit = KnitVersionedFiles(None, None)
705
699
# for line extraction when requested (inventories only)
745
738
fileid_revisions = repo._find_file_ids_from_xml_inventory_lines(
746
739
inv_lines, self.revision_keys)
748
for fileid, file_revids in fileid_revisions.items():
749
text_filter.extend([(fileid, file_revid)
750
for file_revid in file_revids])
741
for fileid, file_revids in fileid_revisions.iteritems():
742
text_filter.extend([(fileid, file_revid) for file_revid in file_revids])
751
743
self._text_filter = text_filter
753
745
def _copy_inventory_texts(self):
754
746
# select inventory keys
755
inv_keys = self._revision_keys # currently the same keyspace, and note that
747
inv_keys = self._revision_keys # currently the same keyspace, and note that
756
748
# querying for keys here could introduce a bug where an inventory item
757
749
# is missed, so do not change it to query separately without cross
758
750
# checking like the text key check below.
777
769
self._text_filter = None
778
770
if 'pack' in debug.debug_flags:
779
771
trace.mutter('%s: create_pack: inventories copied: %s%s %d items t+%6.3fs',
780
time.ctime(), self._pack_collection._upload_transport.base,
781
self.new_pack.random_name,
782
self.new_pack.inventory_index.key_count(),
783
time.time() - self.new_pack.start_time)
772
time.ctime(), self._pack_collection._upload_transport.base,
773
self.new_pack.random_name,
774
self.new_pack.inventory_index.key_count(),
775
time.time() - self.new_pack.start_time)
785
777
def _update_pack_order(self, entries, index_to_pack_map):
786
778
"""Determine how we want our packs to be ordered.
831
822
self._update_pack_order(revision_nodes, revision_index_map)
832
823
# copy revision keys and adjust values
833
824
self.pb.update("Copying revision texts", 1)
834
total_items, readv_group_iter = self._revision_node_readv(
825
total_items, readv_group_iter = self._revision_node_readv(revision_nodes)
836
826
list(self._copy_nodes_graph(revision_index_map, self.new_pack._writer,
837
self.new_pack.revision_index, readv_group_iter, total_items))
827
self.new_pack.revision_index, readv_group_iter, total_items))
838
828
if 'pack' in debug.debug_flags:
839
829
trace.mutter('%s: create_pack: revisions copied: %s%s %d items t+%6.3fs',
840
time.ctime(), self._pack_collection._upload_transport.base,
841
self.new_pack.random_name,
842
self.new_pack.revision_index.key_count(),
843
time.time() - self.new_pack.start_time)
830
time.ctime(), self._pack_collection._upload_transport.base,
831
self.new_pack.random_name,
832
self.new_pack.revision_index.key_count(),
833
time.time() - self.new_pack.start_time)
844
834
self._revision_keys = revision_keys
846
836
def _get_text_nodes(self):
847
837
text_index_map, text_indices = self._pack_map_and_index_list(
849
839
return text_index_map, self._index_contents(text_indices,
852
842
def _copy_text_texts(self):
853
843
# select text keys
865
855
if missing_text_keys:
866
856
# TODO: raise a specific error that can handle many missing
868
trace.mutter("missing keys during fetch: %r",
858
trace.mutter("missing keys during fetch: %r", missing_text_keys)
870
859
a_missing_key = missing_text_keys.pop()
871
860
raise errors.RevisionNotPresent(a_missing_key[1],
873
862
# copy text keys and adjust values
874
863
self.pb.update("Copying content texts", 3)
875
total_items, readv_group_iter = self._least_readv_node_readv(
864
total_items, readv_group_iter = self._least_readv_node_readv(text_nodes)
877
865
list(self._copy_nodes_graph(text_index_map, self.new_pack._writer,
878
self.new_pack.text_index, readv_group_iter, total_items))
866
self.new_pack.text_index, readv_group_iter, total_items))
879
867
self._log_copied_texts()
881
869
def _create_pack_from_packs(self):
884
872
new_pack = self.new_pack
885
873
# buffer data - we won't be reading-back during the pack creation and
886
874
# this makes a significant difference on sftp pushes.
887
new_pack.set_write_cache_size(1024 * 1024)
875
new_pack.set_write_cache_size(1024*1024)
888
876
if 'pack' in debug.debug_flags:
889
877
plain_pack_list = ['%s%s' % (a_pack.pack_transport.base, a_pack.name)
890
for a_pack in self.packs]
878
for a_pack in self.packs]
891
879
if self.revision_ids is not None:
892
880
rev_count = len(self.revision_ids)
894
882
rev_count = 'all'
895
883
trace.mutter('%s: create_pack: creating pack from source packs: '
896
'%s%s %s revisions wanted %s t=0',
897
time.ctime(), self._pack_collection._upload_transport.base, new_pack.random_name,
898
plain_pack_list, rev_count)
884
'%s%s %s revisions wanted %s t=0',
885
time.ctime(), self._pack_collection._upload_transport.base, new_pack.random_name,
886
plain_pack_list, rev_count)
899
887
self._copy_revision_texts()
900
888
self._copy_inventory_texts()
901
889
self._copy_text_texts()
902
890
# select signature keys
903
signature_filter = self._revision_keys # same keyspace
891
signature_filter = self._revision_keys # same keyspace
904
892
signature_index_map, signature_indices = self._pack_map_and_index_list(
905
893
'signature_index')
906
894
signature_nodes = self._index_contents(signature_indices,
908
896
# copy signature keys and adjust values
909
897
self.pb.update("Copying signature texts", 4)
910
898
self._copy_nodes(signature_nodes, signature_index_map, new_pack._writer,
911
new_pack.signature_index)
899
new_pack.signature_index)
912
900
if 'pack' in debug.debug_flags:
913
901
trace.mutter('%s: create_pack: revision signatures copied: %s%s %d items t+%6.3fs',
914
time.ctime(), self._pack_collection._upload_transport.base, new_pack.random_name,
915
new_pack.signature_index.key_count(),
916
time.time() - new_pack.start_time)
902
time.ctime(), self._pack_collection._upload_transport.base, new_pack.random_name,
903
new_pack.signature_index.key_count(),
904
time.time() - new_pack.start_time)
917
905
new_pack._check_references()
918
906
if not self._use_pack(new_pack):
944
932
request_groups[index] = []
945
933
request_groups[index].append((key, value, references))
947
for index, items in request_groups.items():
935
for index, items in request_groups.iteritems():
948
936
pack_readv_requests = []
949
937
for key, value, references in items:
950
938
# ---- KnitGraphIndex.get_position
951
bits = value[1:].split(b' ')
939
bits = value[1:].split(' ')
952
940
offset, length = int(bits[0]), int(bits[1])
953
941
pack_readv_requests.append(
954
((offset, length), (key, value[0:1], references)))
942
((offset, length), (key, value[0], references)))
955
943
# linear scan up the pack to maximum range combining.
956
944
pack_readv_requests.sort()
957
945
# split out the readv and the node data.
1059
1047
# space (we only topo sort the revisions, which is smaller).
1060
1048
topo_order = tsort.topo_sort(ancestors)
1061
1049
rev_order = dict(zip(topo_order, range(len(topo_order))))
1062
bad_texts.sort(key=lambda key: rev_order.get(key[0][1], 0))
1050
bad_texts.sort(key=lambda key:rev_order.get(key[0][1], 0))
1063
1051
transaction = repo.get_transaction()
1064
1052
file_id_index = GraphIndexPrefixAdapter(
1065
1053
self.new_pack.text_index,
1066
1054
('blank', ), 1,
1067
1055
add_nodes_callback=self.new_pack.text_index.add_nodes)
1068
1056
data_access = _DirectPackAccess(
1069
{self.new_pack.text_index: self.new_pack.access_tuple()})
1057
{self.new_pack.text_index:self.new_pack.access_tuple()})
1070
1058
data_access.set_writer(self.new_pack._writer, self.new_pack.text_index,
1071
self.new_pack.access_tuple())
1059
self.new_pack.access_tuple())
1072
1060
output_texts = KnitVersionedFiles(
1073
1061
_KnitGraphIndex(self.new_pack.text_index,
1074
add_callback=self.new_pack.text_index.add_nodes,
1075
deltas=True, parents=True, is_locked=repo.is_locked),
1062
add_callback=self.new_pack.text_index.add_nodes,
1063
deltas=True, parents=True, is_locked=repo.is_locked),
1076
1064
data_access=data_access, max_delta_chain=200)
1077
1065
for key, parent_keys in bad_texts:
1078
1066
# We refer to the new pack to delta data being output.
1084
1072
if parent_key[0] != key[0]:
1085
1073
# Graph parents must match the fileid
1086
1074
raise errors.BzrError('Mismatched key parent %r:%r' %
1088
1076
parents.append(parent_key[1])
1089
text_lines = next(repo.texts.get_record_stream(
1090
[key], 'unordered', True)).get_bytes_as('lines')
1077
text_lines = osutils.split_lines(repo.texts.get_record_stream(
1078
[key], 'unordered', True).next().get_bytes_as('fulltext'))
1091
1079
output_texts.add_lines(key, parent_keys, text_lines,
1092
random_id=True, check_content=False)
1080
random_id=True, check_content=False)
1093
1081
# 5) check that nothing inserted has a reference outside the keyspace.
1094
1082
missing_text_keys = self.new_pack.text_index._external_references()
1095
1083
if missing_text_keys:
1096
1084
raise errors.BzrCheckError('Reference to missing compression parents %r'
1097
% (missing_text_keys,))
1085
% (missing_text_keys,))
1098
1086
self._log_copied_texts()
1100
1088
def _use_pack(self, new_pack):
1138
1126
for key in reversed(order):
1139
1127
index, value, references = by_key[key]
1140
1128
# ---- KnitGraphIndex.get_position
1141
bits = value[1:].split(b' ')
1129
bits = value[1:].split(' ')
1142
1130
offset, length = int(bits[0]), int(bits[1])
1143
1131
requests.append(
1144
(index, [(offset, length)], [(key, value[0:1], references)]))
1132
(index, [(offset, length)], [(key, value[0], references)]))
1145
1133
# TODO: combine requests in the same index that are in ascending order.
1146
1134
return total, requests