@@ -17,23 +17,37 @@ license that can be found in the LICENSE file or at
1717#include "reftable-error.h"
1818#include "system.h"
1919
20+ struct merged_subiter {
21+ struct reftable_iterator iter ;
22+ struct reftable_record rec ;
23+ };
24+
25+ struct merged_iter {
26+ struct merged_subiter * subiters ;
27+ struct merged_iter_pqueue pq ;
28+ uint32_t hash_id ;
29+ size_t stack_len ;
30+ uint8_t typ ;
31+ int suppress_deletions ;
32+ ssize_t advance_index ;
33+ };
34+
2035static int merged_iter_init (struct merged_iter * mi )
2136{
2237 for (size_t i = 0 ; i < mi -> stack_len ; i ++ ) {
2338 struct pq_entry e = {
2439 .index = i ,
40+ .rec = & mi -> subiters [i ].rec ,
2541 };
2642 int err ;
2743
28- reftable_record_init (& e .rec , mi -> typ );
29- err = iterator_next (& mi -> stack [i ], & e .rec );
44+ reftable_record_init (& mi -> subiters [i ].rec , mi -> typ );
45+ err = iterator_next (& mi -> subiters [i ].iter ,
46+ & mi -> subiters [i ].rec );
3047 if (err < 0 )
3148 return err ;
32- if (err > 0 ) {
33- reftable_iterator_destroy (& mi -> stack [i ]);
34- reftable_record_release (& e .rec );
49+ if (err > 0 )
3550 continue ;
36- }
3751
3852 merged_iter_pqueue_add (& mi -> pq , & e );
3953 }
@@ -46,54 +60,66 @@ static void merged_iter_close(void *p)
4660 struct merged_iter * mi = p ;
4761
4862 merged_iter_pqueue_release (& mi -> pq );
49- for (size_t i = 0 ; i < mi -> stack_len ; i ++ )
50- reftable_iterator_destroy (& mi -> stack [i ]);
51- reftable_free (mi -> stack );
63+ for (size_t i = 0 ; i < mi -> stack_len ; i ++ ) {
64+ reftable_iterator_destroy (& mi -> subiters [i ].iter );
65+ reftable_record_release (& mi -> subiters [i ].rec );
66+ }
67+ reftable_free (mi -> subiters );
5268}
5369
54- static int merged_iter_advance_nonnull_subiter (struct merged_iter * mi ,
55- size_t idx )
70+ static int merged_iter_advance_subiter (struct merged_iter * mi , size_t idx )
5671{
5772 struct pq_entry e = {
5873 .index = idx ,
74+ .rec = & mi -> subiters [idx ].rec ,
5975 };
6076 int err ;
6177
62- reftable_record_init (& e .rec , mi -> typ );
63- err = iterator_next (& mi -> stack [idx ], & e .rec );
64- if (err < 0 )
78+ err = iterator_next (& mi -> subiters [idx ].iter , & mi -> subiters [idx ].rec );
79+ if (err )
6580 return err ;
6681
67- if (err > 0 ) {
68- reftable_iterator_destroy (& mi -> stack [idx ]);
69- reftable_record_release (& e .rec );
70- return 0 ;
71- }
72-
7382 merged_iter_pqueue_add (& mi -> pq , & e );
7483 return 0 ;
7584}
7685
77- static int merged_iter_advance_subiter (struct merged_iter * mi , size_t idx )
78- {
79- if (iterator_is_null (& mi -> stack [idx ]))
80- return 0 ;
81- return merged_iter_advance_nonnull_subiter (mi , idx );
82- }
83-
8486static int merged_iter_next_entry (struct merged_iter * mi ,
8587 struct reftable_record * rec )
8688{
8789 struct pq_entry entry = { 0 };
88- int err = 0 ;
90+ int err = 0 , empty ;
91+
92+ empty = merged_iter_pqueue_is_empty (mi -> pq );
93+
94+ if (mi -> advance_index >= 0 ) {
95+ /*
96+ * When there are no pqueue entries then we only have a single
97+ * subiter left. There is no need to use the pqueue in that
98+ * case anymore as we know that the subiter will return entries
99+ * in the correct order already.
100+ *
101+ * While this may sound like a very specific edge case, it may
102+ * happen more frequently than you think. Most repositories
103+ * will end up having a single large base table that contains
104+ * most of the refs. It's thus likely that we exhaust all
105+ * subiters but the one from that base ref.
106+ */
107+ if (empty )
108+ return iterator_next (& mi -> subiters [mi -> advance_index ].iter ,
109+ rec );
110+
111+ err = merged_iter_advance_subiter (mi , mi -> advance_index );
112+ if (err < 0 )
113+ return err ;
114+ if (!err )
115+ empty = 0 ;
116+ mi -> advance_index = -1 ;
117+ }
89118
90- if (merged_iter_pqueue_is_empty ( mi -> pq ) )
119+ if (empty )
91120 return 1 ;
92121
93122 entry = merged_iter_pqueue_remove (& mi -> pq );
94- err = merged_iter_advance_subiter (mi , entry .index );
95- if (err < 0 )
96- return err ;
97123
98124 /*
99125 One can also use reftable as datacenter-local storage, where the ref
@@ -107,56 +133,34 @@ static int merged_iter_next_entry(struct merged_iter *mi,
107133 struct pq_entry top = merged_iter_pqueue_top (mi -> pq );
108134 int cmp ;
109135
110- /*
111- * When the next entry comes from the same queue as the current
112- * entry then it must by definition be larger. This avoids a
113- * comparison in the most common case.
114- */
115- if (top .index == entry .index )
116- break ;
117-
118- cmp = reftable_record_cmp (& top .rec , & entry .rec );
136+ cmp = reftable_record_cmp (top .rec , entry .rec );
119137 if (cmp > 0 )
120138 break ;
121139
122140 merged_iter_pqueue_remove (& mi -> pq );
123141 err = merged_iter_advance_subiter (mi , top .index );
124142 if (err < 0 )
125- goto done ;
126- reftable_record_release (& top .rec );
143+ return err ;
127144 }
128145
129- reftable_record_release (rec );
130- * rec = entry .rec ;
131-
132- done :
133- if (err )
134- reftable_record_release (& entry .rec );
135- return err ;
146+ mi -> advance_index = entry .index ;
147+ SWAP (* rec , * entry .rec );
148+ return 0 ;
136149}
137150
138- static int merged_iter_next ( struct merged_iter * mi , struct reftable_record * rec )
151+ static int merged_iter_next_void ( void * p , struct reftable_record * rec )
139152{
153+ struct merged_iter * mi = p ;
140154 while (1 ) {
141155 int err = merged_iter_next_entry (mi , rec );
142- if (err == 0 && mi -> suppress_deletions &&
143- reftable_record_is_deletion (rec )) {
156+ if (err )
157+ return err ;
158+ if (mi -> suppress_deletions && reftable_record_is_deletion (rec ))
144159 continue ;
145- }
146-
147- return err ;
160+ return 0 ;
148161 }
149162}
150163
151- static int merged_iter_next_void (void * p , struct reftable_record * rec )
152- {
153- struct merged_iter * mi = p ;
154- if (merged_iter_pqueue_is_empty (mi -> pq ))
155- return 1 ;
156-
157- return merged_iter_next (mi , rec );
158- }
159-
160164static struct reftable_iterator_vtable merged_iter_vtable = {
161165 .next = & merged_iter_next_void ,
162166 .close = & merged_iter_close ,
@@ -246,14 +250,15 @@ static int merged_table_seek_record(struct reftable_merged_table *mt,
246250 .typ = reftable_record_type (rec ),
247251 .hash_id = mt -> hash_id ,
248252 .suppress_deletions = mt -> suppress_deletions ,
253+ .advance_index = -1 ,
249254 };
250255 struct merged_iter * p ;
251256 int err ;
252257
253- REFTABLE_CALLOC_ARRAY (merged .stack , mt -> stack_len );
258+ REFTABLE_CALLOC_ARRAY (merged .subiters , mt -> stack_len );
254259 for (size_t i = 0 ; i < mt -> stack_len ; i ++ ) {
255260 err = reftable_table_seek_record (& mt -> stack [i ],
256- & merged .stack [merged .stack_len ], rec );
261+ & merged .subiters [merged .stack_len ]. iter , rec );
257262 if (err < 0 )
258263 goto out ;
259264 if (!err )
0 commit comments