@@ -17,23 +17,37 @@ license that can be found in the LICENSE file or at
17
17
#include "reftable-error.h"
18
18
#include "system.h"
19
19
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
+
20
35
static int merged_iter_init (struct merged_iter * mi )
21
36
{
22
37
for (size_t i = 0 ; i < mi -> stack_len ; i ++ ) {
23
38
struct pq_entry e = {
24
39
.index = i ,
40
+ .rec = & mi -> subiters [i ].rec ,
25
41
};
26
42
int err ;
27
43
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 );
30
47
if (err < 0 )
31
48
return err ;
32
- if (err > 0 ) {
33
- reftable_iterator_destroy (& mi -> stack [i ]);
34
- reftable_record_release (& e .rec );
49
+ if (err > 0 )
35
50
continue ;
36
- }
37
51
38
52
merged_iter_pqueue_add (& mi -> pq , & e );
39
53
}
@@ -46,54 +60,66 @@ static void merged_iter_close(void *p)
46
60
struct merged_iter * mi = p ;
47
61
48
62
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 );
52
68
}
53
69
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 )
56
71
{
57
72
struct pq_entry e = {
58
73
.index = idx ,
74
+ .rec = & mi -> subiters [idx ].rec ,
59
75
};
60
76
int err ;
61
77
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 )
65
80
return err ;
66
81
67
- if (err > 0 ) {
68
- reftable_iterator_destroy (& mi -> stack [idx ]);
69
- reftable_record_release (& e .rec );
70
- return 0 ;
71
- }
72
-
73
82
merged_iter_pqueue_add (& mi -> pq , & e );
74
83
return 0 ;
75
84
}
76
85
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
-
84
86
static int merged_iter_next_entry (struct merged_iter * mi ,
85
87
struct reftable_record * rec )
86
88
{
87
89
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
+ }
89
118
90
- if (merged_iter_pqueue_is_empty ( mi -> pq ) )
119
+ if (empty )
91
120
return 1 ;
92
121
93
122
entry = merged_iter_pqueue_remove (& mi -> pq );
94
- err = merged_iter_advance_subiter (mi , entry .index );
95
- if (err < 0 )
96
- return err ;
97
123
98
124
/*
99
125
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,
107
133
struct pq_entry top = merged_iter_pqueue_top (mi -> pq );
108
134
int cmp ;
109
135
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 );
119
137
if (cmp > 0 )
120
138
break ;
121
139
122
140
merged_iter_pqueue_remove (& mi -> pq );
123
141
err = merged_iter_advance_subiter (mi , top .index );
124
142
if (err < 0 )
125
- goto done ;
126
- reftable_record_release (& top .rec );
143
+ return err ;
127
144
}
128
145
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 ;
136
149
}
137
150
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 )
139
152
{
153
+ struct merged_iter * mi = p ;
140
154
while (1 ) {
141
155
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 ))
144
159
continue ;
145
- }
146
-
147
- return err ;
160
+ return 0 ;
148
161
}
149
162
}
150
163
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
-
160
164
static struct reftable_iterator_vtable merged_iter_vtable = {
161
165
.next = & merged_iter_next_void ,
162
166
.close = & merged_iter_close ,
@@ -246,14 +250,15 @@ static int merged_table_seek_record(struct reftable_merged_table *mt,
246
250
.typ = reftable_record_type (rec ),
247
251
.hash_id = mt -> hash_id ,
248
252
.suppress_deletions = mt -> suppress_deletions ,
253
+ .advance_index = -1 ,
249
254
};
250
255
struct merged_iter * p ;
251
256
int err ;
252
257
253
- REFTABLE_CALLOC_ARRAY (merged .stack , mt -> stack_len );
258
+ REFTABLE_CALLOC_ARRAY (merged .subiters , mt -> stack_len );
254
259
for (size_t i = 0 ; i < mt -> stack_len ; i ++ ) {
255
260
err = reftable_table_seek_record (& mt -> stack [i ],
256
- & merged .stack [merged .stack_len ], rec );
261
+ & merged .subiters [merged .stack_len ]. iter , rec );
257
262
if (err < 0 )
258
263
goto out ;
259
264
if (!err )
0 commit comments