reftable: read reftable files

This supports reading a single reftable file. The commit introduces an abstract iterator type, which captures the usecases both of reading individual refs, and iterating over a segment of the ref namespace. Signed-off-by: Han-Wen Nienhuys <hanwen@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Han-Wen Nienhuys committed Oct 7, 2021 at 20:25 UTC 46bc0e731a70716485bc3c8bdaf7e6e6f88073a9
6 files changed +1231
Makefile
+2
@@ -2460,7 +2460,9 @@ REFTABLE_OBJS += reftable/basics.o
2460 REFTABLE_OBJS += reftable/error.o
2461 REFTABLE_OBJS += reftable/block.o
2462 REFTABLE_OBJS += reftable/blocksource.o
2463 +REFTABLE_OBJS += reftable/iter.o
2464 REFTABLE_OBJS += reftable/publicbasics.o
2465 +REFTABLE_OBJS += reftable/reader.o
2466 REFTABLE_OBJS += reftable/record.o
2467 REFTABLE_OBJS += reftable/refname.o
2468 REFTABLE_OBJS += reftable/generic.o
reftable/iter.c new
+194
@@ -0,0 +1,194 @@
1 +/*
2 +Copyright 2020 Google LLC
3 +
4 +Use of this source code is governed by a BSD-style
5 +license that can be found in the LICENSE file or at
6 +https://developers.google.com/open-source/licenses/bsd
7 +*/
8 +
9 +#include "iter.h"
10 +
11 +#include "system.h"
12 +
13 +#include "block.h"
14 +#include "generic.h"
15 +#include "constants.h"
16 +#include "reader.h"
17 +#include "reftable-error.h"
18 +
19 +int iterator_is_null(struct reftable_iterator *it)
20 +{
21 + return !it->ops;
22 +}
23 +
24 +static void filtering_ref_iterator_close(void *iter_arg)
25 +{
26 + struct filtering_ref_iterator *fri = iter_arg;
27 + strbuf_release(&fri->oid);
28 + reftable_iterator_destroy(&fri->it);
29 +}
30 +
31 +static int filtering_ref_iterator_next(void *iter_arg,
32 + struct reftable_record *rec)
33 +{
34 + struct filtering_ref_iterator *fri = iter_arg;
35 + struct reftable_ref_record *ref = rec->data;
36 + int err = 0;
37 + while (1) {
38 + err = reftable_iterator_next_ref(&fri->it, ref);
39 + if (err != 0) {
40 + break;
41 + }
42 +
43 + if (fri->double_check) {
44 + struct reftable_iterator it = { NULL };
45 +
46 + err = reftable_table_seek_ref(&fri->tab, &it,
47 + ref->refname);
48 + if (err == 0) {
49 + err = reftable_iterator_next_ref(&it, ref);
50 + }
51 +
52 + reftable_iterator_destroy(&it);
53 +
54 + if (err < 0) {
55 + break;
56 + }
57 +
58 + if (err > 0) {
59 + continue;
60 + }
61 + }
62 +
63 + if (ref->value_type == REFTABLE_REF_VAL2 &&
64 + (!memcmp(fri->oid.buf, ref->value.val2.target_value,
65 + fri->oid.len) ||
66 + !memcmp(fri->oid.buf, ref->value.val2.value,
67 + fri->oid.len)))
68 + return 0;
69 +
70 + if (ref->value_type == REFTABLE_REF_VAL1 &&
71 + !memcmp(fri->oid.buf, ref->value.val1, fri->oid.len)) {
72 + return 0;
73 + }
74 + }
75 +
76 + reftable_ref_record_release(ref);
77 + return err;
78 +}
79 +
80 +static struct reftable_iterator_vtable filtering_ref_iterator_vtable = {
81 + .next = &filtering_ref_iterator_next,
82 + .close = &filtering_ref_iterator_close,
83 +};
84 +
85 +void iterator_from_filtering_ref_iterator(struct reftable_iterator *it,
86 + struct filtering_ref_iterator *fri)
87 +{
88 + assert(!it->ops);
89 + it->iter_arg = fri;
90 + it->ops = &filtering_ref_iterator_vtable;
91 +}
92 +
93 +static void indexed_table_ref_iter_close(void *p)
94 +{
95 + struct indexed_table_ref_iter *it = p;
96 + block_iter_close(&it->cur);
97 + reftable_block_done(&it->block_reader.block);
98 + reftable_free(it->offsets);
99 + strbuf_release(&it->oid);
100 +}
101 +
102 +static int indexed_table_ref_iter_next_block(struct indexed_table_ref_iter *it)
103 +{
104 + uint64_t off;
105 + int err = 0;
106 + if (it->offset_idx == it->offset_len) {
107 + it->is_finished = 1;
108 + return 1;
109 + }
110 +
111 + reftable_block_done(&it->block_reader.block);
112 +
113 + off = it->offsets[it->offset_idx++];
114 + err = reader_init_block_reader(it->r, &it->block_reader, off,
115 + BLOCK_TYPE_REF);
116 + if (err < 0) {
117 + return err;
118 + }
119 + if (err > 0) {
120 + /* indexed block does not exist. */
121 + return REFTABLE_FORMAT_ERROR;
122 + }
123 + block_reader_start(&it->block_reader, &it->cur);
124 + return 0;
125 +}
126 +
127 +static int indexed_table_ref_iter_next(void *p, struct reftable_record *rec)
128 +{
129 + struct indexed_table_ref_iter *it = p;
130 + struct reftable_ref_record *ref = rec->data;
131 +
132 + while (1) {
133 + int err = block_iter_next(&it->cur, rec);
134 + if (err < 0) {
135 + return err;
136 + }
137 +
138 + if (err > 0) {
139 + err = indexed_table_ref_iter_next_block(it);
140 + if (err < 0) {
141 + return err;
142 + }
143 +
144 + if (it->is_finished) {
145 + return 1;
146 + }
147 + continue;
148 + }
149 + /* BUG */
150 + if (!memcmp(it->oid.buf, ref->value.val2.target_value,
151 + it->oid.len) ||
152 + !memcmp(it->oid.buf, ref->value.val2.value, it->oid.len)) {
153 + return 0;
154 + }
155 + }
156 +}
157 +
158 +int new_indexed_table_ref_iter(struct indexed_table_ref_iter **dest,
159 + struct reftable_reader *r, uint8_t *oid,
160 + int oid_len, uint64_t *offsets, int offset_len)
161 +{
162 + struct indexed_table_ref_iter empty = INDEXED_TABLE_REF_ITER_INIT;
163 + struct indexed_table_ref_iter *itr =
164 + reftable_calloc(sizeof(struct indexed_table_ref_iter));
165 + int err = 0;
166 +
167 + *itr = empty;
168 + itr->r = r;
169 + strbuf_add(&itr->oid, oid, oid_len);
170 +
171 + itr->offsets = offsets;
172 + itr->offset_len = offset_len;
173 +
174 + err = indexed_table_ref_iter_next_block(itr);
175 + if (err < 0) {
176 + reftable_free(itr);
177 + } else {
178 + *dest = itr;
179 + }
180 + return err;
181 +}
182 +
183 +static struct reftable_iterator_vtable indexed_table_ref_iter_vtable = {
184 + .next = &indexed_table_ref_iter_next,
185 + .close = &indexed_table_ref_iter_close,
186 +};
187 +
188 +void iterator_from_indexed_table_ref_iter(struct reftable_iterator *it,
189 + struct indexed_table_ref_iter *itr)
190 +{
191 + assert(!it->ops);
192 + it->iter_arg = itr;
193 + it->ops = &indexed_table_ref_iter_vtable;
194 +}
reftable/iter.h new
+69
@@ -0,0 +1,69 @@
1 +/*
2 +Copyright 2020 Google LLC
3 +
4 +Use of this source code is governed by a BSD-style
5 +license that can be found in the LICENSE file or at
6 +https://developers.google.com/open-source/licenses/bsd
7 +*/
8 +
9 +#ifndef ITER_H
10 +#define ITER_H
11 +
12 +#include "system.h"
13 +#include "block.h"
14 +#include "record.h"
15 +
16 +#include "reftable-iterator.h"
17 +#include "reftable-generic.h"
18 +
19 +/* Returns true for a zeroed out iterator, such as the one returned from
20 + * iterator_destroy. */
21 +int iterator_is_null(struct reftable_iterator *it);
22 +
23 +/* iterator that produces only ref records that point to `oid` */
24 +struct filtering_ref_iterator {
25 + int double_check;
26 + struct reftable_table tab;
27 + struct strbuf oid;
28 + struct reftable_iterator it;
29 +};
30 +#define FILTERING_REF_ITERATOR_INIT \
31 + { \
32 + .oid = STRBUF_INIT \
33 + }
34 +
35 +void iterator_from_filtering_ref_iterator(struct reftable_iterator *,
36 + struct filtering_ref_iterator *);
37 +
38 +/* iterator that produces only ref records that point to `oid`,
39 + * but using the object index.
40 + */
41 +struct indexed_table_ref_iter {
42 + struct reftable_reader *r;
43 + struct strbuf oid;
44 +
45 + /* mutable */
46 + uint64_t *offsets;
47 +
48 + /* Points to the next offset to read. */
49 + int offset_idx;
50 + int offset_len;
51 + struct block_reader block_reader;
52 + struct block_iter cur;
53 + int is_finished;
54 +};
55 +
56 +#define INDEXED_TABLE_REF_ITER_INIT \
57 + { \
58 + .cur = { .last_key = STRBUF_INIT }, .oid = STRBUF_INIT, \
59 + }
60 +
61 +void iterator_from_indexed_table_ref_iter(struct reftable_iterator *it,
62 + struct indexed_table_ref_iter *itr);
63 +
64 +/* Takes ownership of `offsets` */
65 +int new_indexed_table_ref_iter(struct indexed_table_ref_iter **dest,
66 + struct reftable_reader *r, uint8_t *oid,
67 + int oid_len, uint64_t *offsets, int offset_len);
68 +
69 +#endif
reftable/reader.c new
+801
@@ -0,0 +1,801 @@
1 +/*
2 +Copyright 2020 Google LLC
3 +
4 +Use of this source code is governed by a BSD-style
5 +license that can be found in the LICENSE file or at
6 +https://developers.google.com/open-source/licenses/bsd
7 +*/
8 +
9 +#include "reader.h"
10 +
11 +#include "system.h"
12 +#include "block.h"
13 +#include "constants.h"
14 +#include "generic.h"
15 +#include "iter.h"
16 +#include "record.h"
17 +#include "reftable-error.h"
18 +#include "reftable-generic.h"
19 +#include "tree.h"
20 +
21 +uint64_t block_source_size(struct reftable_block_source *source)
22 +{
23 + return source->ops->size(source->arg);
24 +}
25 +
26 +int block_source_read_block(struct reftable_block_source *source,
27 + struct reftable_block *dest, uint64_t off,
28 + uint32_t size)
29 +{
30 + int result = source->ops->read_block(source->arg, dest, off, size);
31 + dest->source = *source;
32 + return result;
33 +}
34 +
35 +void block_source_close(struct reftable_block_source *source)
36 +{
37 + if (!source->ops) {
38 + return;
39 + }
40 +
41 + source->ops->close(source->arg);
42 + source->ops = NULL;
43 +}
44 +
45 +static struct reftable_reader_offsets *
46 +reader_offsets_for(struct reftable_reader *r, uint8_t typ)
47 +{
48 + switch (typ) {
49 + case BLOCK_TYPE_REF:
50 + return &r->ref_offsets;
51 + case BLOCK_TYPE_LOG:
52 + return &r->log_offsets;
53 + case BLOCK_TYPE_OBJ:
54 + return &r->obj_offsets;
55 + }
56 + abort();
57 +}
58 +
59 +static int reader_get_block(struct reftable_reader *r,
60 + struct reftable_block *dest, uint64_t off,
61 + uint32_t sz)
62 +{
63 + if (off >= r->size)
64 + return 0;
65 +
66 + if (off + sz > r->size) {
67 + sz = r->size - off;
68 + }
69 +
70 + return block_source_read_block(&r->source, dest, off, sz);
71 +}
72 +
73 +uint32_t reftable_reader_hash_id(struct reftable_reader *r)
74 +{
75 + return r->hash_id;
76 +}
77 +
78 +const char *reader_name(struct reftable_reader *r)
79 +{
80 + return r->name;
81 +}
82 +
83 +static int parse_footer(struct reftable_reader *r, uint8_t *footer,
84 + uint8_t *header)
85 +{
86 + uint8_t *f = footer;
87 + uint8_t first_block_typ;
88 + int err = 0;
89 + uint32_t computed_crc;
90 + uint32_t file_crc;
91 +
92 + if (memcmp(f, "REFT", 4)) {
93 + err = REFTABLE_FORMAT_ERROR;
94 + goto done;
95 + }
96 + f += 4;
97 +
98 + if (memcmp(footer, header, header_size(r->version))) {
99 + err = REFTABLE_FORMAT_ERROR;
100 + goto done;
101 + }
102 +
103 + f++;
104 + r->block_size = get_be24(f);
105 +
106 + f += 3;
107 + r->min_update_index = get_be64(f);
108 + f += 8;
109 + r->max_update_index = get_be64(f);
110 + f += 8;
111 +
112 + if (r->version == 1) {
113 + r->hash_id = GIT_SHA1_FORMAT_ID;
114 + } else {
115 + r->hash_id = get_be32(f);
116 + switch (r->hash_id) {
117 + case GIT_SHA1_FORMAT_ID:
118 + break;
119 + case GIT_SHA256_FORMAT_ID:
120 + break;
121 + default:
122 + err = REFTABLE_FORMAT_ERROR;
123 + goto done;
124 + }
125 + f += 4;
126 + }
127 +
128 + r->ref_offsets.index_offset = get_be64(f);
129 + f += 8;
130 +
131 + r->obj_offsets.offset = get_be64(f);
132 + f += 8;
133 +
134 + r->object_id_len = r->obj_offsets.offset & ((1 << 5) - 1);
135 + r->obj_offsets.offset >>= 5;
136 +
137 + r->obj_offsets.index_offset = get_be64(f);
138 + f += 8;
139 + r->log_offsets.offset = get_be64(f);
140 + f += 8;
141 + r->log_offsets.index_offset = get_be64(f);
142 + f += 8;
143 +
144 + computed_crc = crc32(0, footer, f - footer);
145 + file_crc = get_be32(f);
146 + f += 4;
147 + if (computed_crc != file_crc) {
148 + err = REFTABLE_FORMAT_ERROR;
149 + goto done;
150 + }
151 +
152 + first_block_typ = header[header_size(r->version)];
153 + r->ref_offsets.is_present = (first_block_typ == BLOCK_TYPE_REF);
154 + r->ref_offsets.offset = 0;
155 + r->log_offsets.is_present = (first_block_typ == BLOCK_TYPE_LOG ||
156 + r->log_offsets.offset > 0);
157 + r->obj_offsets.is_present = r->obj_offsets.offset > 0;
158 + err = 0;
159 +done:
160 + return err;
161 +}
162 +
163 +int init_reader(struct reftable_reader *r, struct reftable_block_source *source,
164 + const char *name)
165 +{
166 + struct reftable_block footer = { NULL };
167 + struct reftable_block header = { NULL };
168 + int err = 0;
169 + uint64_t file_size = block_source_size(source);
170 +
171 + /* Need +1 to read type of first block. */
172 + uint32_t read_size = header_size(2) + 1; /* read v2 because it's larger. */
173 + memset(r, 0, sizeof(struct reftable_reader));
174 +
175 + if (read_size > file_size) {
176 + err = REFTABLE_FORMAT_ERROR;
177 + goto done;
178 + }
179 +
180 + err = block_source_read_block(source, &header, 0, read_size);
181 + if (err != read_size) {
182 + err = REFTABLE_IO_ERROR;
183 + goto done;
184 + }
185 +
186 + if (memcmp(header.data, "REFT", 4)) {
187 + err = REFTABLE_FORMAT_ERROR;
188 + goto done;
189 + }
190 + r->version = header.data[4];
191 + if (r->version != 1 && r->version != 2) {
192 + err = REFTABLE_FORMAT_ERROR;
193 + goto done;
194 + }
195 +
196 + r->size = file_size - footer_size(r->version);
197 + r->source = *source;
198 + r->name = xstrdup(name);
199 + r->hash_id = 0;
200 +
201 + err = block_source_read_block(source, &footer, r->size,
202 + footer_size(r->version));
203 + if (err != footer_size(r->version)) {
204 + err = REFTABLE_IO_ERROR;
205 + goto done;
206 + }
207 +
208 + err = parse_footer(r, footer.data, header.data);
209 +done:
210 + reftable_block_done(&footer);
211 + reftable_block_done(&header);
212 + return err;
213 +}
214 +
215 +struct table_iter {
216 + struct reftable_reader *r;
217 + uint8_t typ;
218 + uint64_t block_off;
219 + struct block_iter bi;
220 + int is_finished;
221 +};
222 +#define TABLE_ITER_INIT \
223 + { \
224 + .bi = {.last_key = STRBUF_INIT } \
225 + }
226 +
227 +static void table_iter_copy_from(struct table_iter *dest,
228 + struct table_iter *src)
229 +{
230 + dest->r = src->r;
231 + dest->typ = src->typ;
232 + dest->block_off = src->block_off;
233 + dest->is_finished = src->is_finished;
234 + block_iter_copy_from(&dest->bi, &src->bi);
235 +}
236 +
237 +static int table_iter_next_in_block(struct table_iter *ti,
238 + struct reftable_record *rec)
239 +{
240 + int res = block_iter_next(&ti->bi, rec);
241 + if (res == 0 && reftable_record_type(rec) == BLOCK_TYPE_REF) {
242 + ((struct reftable_ref_record *)rec->data)->update_index +=
243 + ti->r->min_update_index;
244 + }
245 +
246 + return res;
247 +}
248 +
249 +static void table_iter_block_done(struct table_iter *ti)
250 +{
251 + if (!ti->bi.br) {
252 + return;
253 + }
254 + reftable_block_done(&ti->bi.br->block);
255 + FREE_AND_NULL(ti->bi.br);
256 +
257 + ti->bi.last_key.len = 0;
258 + ti->bi.next_off = 0;
259 +}
260 +
261 +static int32_t extract_block_size(uint8_t *data, uint8_t *typ, uint64_t off,
262 + int version)
263 +{
264 + int32_t result = 0;
265 +
266 + if (off == 0) {
267 + data += header_size(version);
268 + }
269 +
270 + *typ = data[0];
271 + if (reftable_is_block_type(*typ)) {
272 + result = get_be24(data + 1);
273 + }
274 + return result;
275 +}
276 +
277 +int reader_init_block_reader(struct reftable_reader *r, struct block_reader *br,
278 + uint64_t next_off, uint8_t want_typ)
279 +{
280 + int32_t guess_block_size = r->block_size ? r->block_size :
281 + DEFAULT_BLOCK_SIZE;
282 + struct reftable_block block = { NULL };
283 + uint8_t block_typ = 0;
284 + int err = 0;
285 + uint32_t header_off = next_off ? 0 : header_size(r->version);
286 + int32_t block_size = 0;
287 +
288 + if (next_off >= r->size)
289 + return 1;
290 +
291 + err = reader_get_block(r, &block, next_off, guess_block_size);
292 + if (err < 0)
293 + return err;
294 +
295 + block_size = extract_block_size(block.data, &block_typ, next_off,
296 + r->version);
297 + if (block_size < 0)
298 + return block_size;
299 +
300 + if (want_typ != BLOCK_TYPE_ANY && block_typ != want_typ) {
301 + reftable_block_done(&block);
302 + return 1;
303 + }
304 +
305 + if (block_size > guess_block_size) {
306 + reftable_block_done(&block);
307 + err = reader_get_block(r, &block, next_off, block_size);
308 + if (err < 0) {
309 + return err;
310 + }
311 + }
312 +
313 + return block_reader_init(br, &block, header_off, r->block_size,
314 + hash_size(r->hash_id));
315 +}
316 +
317 +static int table_iter_next_block(struct table_iter *dest,
318 + struct table_iter *src)
319 +{
320 + uint64_t next_block_off = src->block_off + src->bi.br->full_block_size;
321 + struct block_reader br = { 0 };
322 + int err = 0;
323 +
324 + dest->r = src->r;
325 + dest->typ = src->typ;
326 + dest->block_off = next_block_off;
327 +
328 + err = reader_init_block_reader(src->r, &br, next_block_off, src->typ);
329 + if (err > 0) {
330 + dest->is_finished = 1;
331 + return 1;
332 + }
333 + if (err != 0)
334 + return err;
335 + else {
336 + struct block_reader *brp =
337 + reftable_malloc(sizeof(struct block_reader));
338 + *brp = br;
339 +
340 + dest->is_finished = 0;
341 + block_reader_start(brp, &dest->bi);
342 + }
343 + return 0;
344 +}
345 +
346 +static int table_iter_next(struct table_iter *ti, struct reftable_record *rec)
347 +{
348 + if (reftable_record_type(rec) != ti->typ)
349 + return REFTABLE_API_ERROR;
350 +
351 + while (1) {
352 + struct table_iter next = TABLE_ITER_INIT;
353 + int err = 0;
354 + if (ti->is_finished) {
355 + return 1;
356 + }
357 +
358 + err = table_iter_next_in_block(ti, rec);
359 + if (err <= 0) {
360 + return err;
361 + }
362 +
363 + err = table_iter_next_block(&next, ti);
364 + if (err != 0) {
365 + ti->is_finished = 1;
366 + }
367 + table_iter_block_done(ti);
368 + if (err != 0) {
369 + return err;
370 + }
371 + table_iter_copy_from(ti, &next);
372 + block_iter_close(&next.bi);
373 + }
374 +}
375 +
376 +static int table_iter_next_void(void *ti, struct reftable_record *rec)
377 +{
378 + return table_iter_next(ti, rec);
379 +}
380 +
381 +static void table_iter_close(void *p)
382 +{
383 + struct table_iter *ti = p;
384 + table_iter_block_done(ti);
385 + block_iter_close(&ti->bi);
386 +}
387 +
388 +static struct reftable_iterator_vtable table_iter_vtable = {
389 + .next = &table_iter_next_void,
390 + .close = &table_iter_close,
391 +};
392 +
393 +static void iterator_from_table_iter(struct reftable_iterator *it,
394 + struct table_iter *ti)
395 +{
396 + assert(!it->ops);
397 + it->iter_arg = ti;
398 + it->ops = &table_iter_vtable;
399 +}
400 +
401 +static int reader_table_iter_at(struct reftable_reader *r,
402 + struct table_iter *ti, uint64_t off,
403 + uint8_t typ)
404 +{
405 + struct block_reader br = { 0 };
406 + struct block_reader *brp = NULL;
407 +
408 + int err = reader_init_block_reader(r, &br, off, typ);
409 + if (err != 0)
410 + return err;
411 +
412 + brp = reftable_malloc(sizeof(struct block_reader));
413 + *brp = br;
414 + ti->r = r;
415 + ti->typ = block_reader_type(brp);
416 + ti->block_off = off;
417 + block_reader_start(brp, &ti->bi);
418 + return 0;
419 +}
420 +
421 +static int reader_start(struct reftable_reader *r, struct table_iter *ti,
422 + uint8_t typ, int index)
423 +{
424 + struct reftable_reader_offsets *offs = reader_offsets_for(r, typ);
425 + uint64_t off = offs->offset;
426 + if (index) {
427 + off = offs->index_offset;
428 + if (off == 0) {
429 + return 1;
430 + }
431 + typ = BLOCK_TYPE_INDEX;
432 + }
433 +
434 + return reader_table_iter_at(r, ti, off, typ);
435 +}
436 +
437 +static int reader_seek_linear(struct reftable_reader *r, struct table_iter *ti,
438 + struct reftable_record *want)
439 +{
440 + struct reftable_record rec =
441 + reftable_new_record(reftable_record_type(want));
442 + struct strbuf want_key = STRBUF_INIT;
443 + struct strbuf got_key = STRBUF_INIT;
444 + struct table_iter next = TABLE_ITER_INIT;
445 + int err = -1;
446 +
447 + reftable_record_key(want, &want_key);
448 +
449 + while (1) {
450 + err = table_iter_next_block(&next, ti);
451 + if (err < 0)
452 + goto done;
453 +
454 + if (err > 0) {
455 + break;
456 + }
457 +
458 + err = block_reader_first_key(next.bi.br, &got_key);
459 + if (err < 0)
460 + goto done;
461 +
462 + if (strbuf_cmp(&got_key, &want_key) > 0) {
463 + table_iter_block_done(&next);
464 + break;
465 + }
466 +
467 + table_iter_block_done(ti);
468 + table_iter_copy_from(ti, &next);
469 + }
470 +
471 + err = block_iter_seek(&ti->bi, &want_key);
472 + if (err < 0)
473 + goto done;
474 + err = 0;
475 +
476 +done:
477 + block_iter_close(&next.bi);
478 + reftable_record_destroy(&rec);
479 + strbuf_release(&want_key);
480 + strbuf_release(&got_key);
481 + return err;
482 +}
483 +
484 +static int reader_seek_indexed(struct reftable_reader *r,
485 + struct reftable_iterator *it,
486 + struct reftable_record *rec)
487 +{
488 + struct reftable_index_record want_index = { .last_key = STRBUF_INIT };
489 + struct reftable_record want_index_rec = { NULL };
490 + struct reftable_index_record index_result = { .last_key = STRBUF_INIT };
491 + struct reftable_record index_result_rec = { NULL };
492 + struct table_iter index_iter = TABLE_ITER_INIT;
493 + struct table_iter next = TABLE_ITER_INIT;
494 + int err = 0;
495 +
496 + reftable_record_key(rec, &want_index.last_key);
497 + reftable_record_from_index(&want_index_rec, &want_index);
498 + reftable_record_from_index(&index_result_rec, &index_result);
499 +
500 + err = reader_start(r, &index_iter, reftable_record_type(rec), 1);
501 + if (err < 0)
502 + goto done;
503 +
504 + err = reader_seek_linear(r, &index_iter, &want_index_rec);
505 + while (1) {
506 + err = table_iter_next(&index_iter, &index_result_rec);
507 + table_iter_block_done(&index_iter);
508 + if (err != 0)
509 + goto done;
510 +
511 + err = reader_table_iter_at(r, &next, index_result.offset, 0);
512 + if (err != 0)
513 + goto done;
514 +
515 + err = block_iter_seek(&next.bi, &want_index.last_key);
516 + if (err < 0)
517 + goto done;
518 +
519 + if (next.typ == reftable_record_type(rec)) {
520 + err = 0;
521 + break;
522 + }
523 +
524 + if (next.typ != BLOCK_TYPE_INDEX) {
525 + err = REFTABLE_FORMAT_ERROR;
526 + break;
527 + }
528 +
529 + table_iter_copy_from(&index_iter, &next);
530 + }
531 +
532 + if (err == 0) {
533 + struct table_iter empty = TABLE_ITER_INIT;
534 + struct table_iter *malloced =
535 + reftable_calloc(sizeof(struct table_iter));
536 + *malloced = empty;
537 + table_iter_copy_from(malloced, &next);
538 + iterator_from_table_iter(it, malloced);
539 + }
540 +done:
541 + block_iter_close(&next.bi);
542 + table_iter_close(&index_iter);
543 + reftable_record_release(&want_index_rec);
544 + reftable_record_release(&index_result_rec);
545 + return err;
546 +}
547 +
548 +static int reader_seek_internal(struct reftable_reader *r,
549 + struct reftable_iterator *it,
550 + struct reftable_record *rec)
551 +{
552 + struct reftable_reader_offsets *offs =
553 + reader_offsets_for(r, reftable_record_type(rec));
554 + uint64_t idx = offs->index_offset;
555 + struct table_iter ti = TABLE_ITER_INIT;
556 + int err = 0;
557 + if (idx > 0)
558 + return reader_seek_indexed(r, it, rec);
559 +
560 + err = reader_start(r, &ti, reftable_record_type(rec), 0);
561 + if (err < 0)
562 + return err;
563 + err = reader_seek_linear(r, &ti, rec);
564 + if (err < 0)
565 + return err;
566 + else {
567 + struct table_iter *p =
568 + reftable_malloc(sizeof(struct table_iter));
569 + *p = ti;
570 + iterator_from_table_iter(it, p);
571 + }
572 +
573 + return 0;
574 +}
575 +
576 +static int reader_seek(struct reftable_reader *r, struct reftable_iterator *it,
577 + struct reftable_record *rec)
578 +{
579 + uint8_t typ = reftable_record_type(rec);
580 +
581 + struct reftable_reader_offsets *offs = reader_offsets_for(r, typ);
582 + if (!offs->is_present) {
583 + iterator_set_empty(it);
584 + return 0;
585 + }
586 +
587 + return reader_seek_internal(r, it, rec);
588 +}
589 +
590 +int reftable_reader_seek_ref(struct reftable_reader *r,
591 + struct reftable_iterator *it, const char *name)
592 +{
593 + struct reftable_ref_record ref = {
594 + .refname = (char *)name,
595 + };
596 + struct reftable_record rec = { NULL };
597 + reftable_record_from_ref(&rec, &ref);
598 + return reader_seek(r, it, &rec);
599 +}
600 +
601 +int reftable_reader_seek_log_at(struct reftable_reader *r,
602 + struct reftable_iterator *it, const char *name,
603 + uint64_t update_index)
604 +{
605 + struct reftable_log_record log = {
606 + .refname = (char *)name,
607 + .update_index = update_index,
608 + };
609 + struct reftable_record rec = { NULL };
610 + reftable_record_from_log(&rec, &log);
611 + return reader_seek(r, it, &rec);
612 +}
613 +
614 +int reftable_reader_seek_log(struct reftable_reader *r,
615 + struct reftable_iterator *it, const char *name)
616 +{
617 + uint64_t max = ~((uint64_t)0);
618 + return reftable_reader_seek_log_at(r, it, name, max);
619 +}
620 +
621 +void reader_close(struct reftable_reader *r)
622 +{
623 + block_source_close(&r->source);
624 + FREE_AND_NULL(r->name);
625 +}
626 +
627 +int reftable_new_reader(struct reftable_reader **p,
628 + struct reftable_block_source *src, char const *name)
629 +{
630 + struct reftable_reader *rd =
631 + reftable_calloc(sizeof(struct reftable_reader));
632 + int err = init_reader(rd, src, name);
633 + if (err == 0) {
634 + *p = rd;
635 + } else {
636 + block_source_close(src);
637 + reftable_free(rd);
638 + }
639 + return err;
640 +}
641 +
642 +void reftable_reader_free(struct reftable_reader *r)
643 +{
644 + reader_close(r);
645 + reftable_free(r);
646 +}
647 +
648 +static int reftable_reader_refs_for_indexed(struct reftable_reader *r,
649 + struct reftable_iterator *it,
650 + uint8_t *oid)
651 +{
652 + struct reftable_obj_record want = {
653 + .hash_prefix = oid,
654 + .hash_prefix_len = r->object_id_len,
655 + };
656 + struct reftable_record want_rec = { NULL };
657 + struct reftable_iterator oit = { NULL };
658 + struct reftable_obj_record got = { NULL };
659 + struct reftable_record got_rec = { NULL };
660 + int err = 0;
661 + struct indexed_table_ref_iter *itr = NULL;
662 +
663 + /* Look through the reverse index. */
664 + reftable_record_from_obj(&want_rec, &want);
665 + err = reader_seek(r, &oit, &want_rec);
666 + if (err != 0)
667 + goto done;
668 +
669 + /* read out the reftable_obj_record */
670 + reftable_record_from_obj(&got_rec, &got);
671 + err = iterator_next(&oit, &got_rec);
672 + if (err < 0)
673 + goto done;
674 +
675 + if (err > 0 ||
676 + memcmp(want.hash_prefix, got.hash_prefix, r->object_id_len)) {
677 + /* didn't find it; return empty iterator */
678 + iterator_set_empty(it);
679 + err = 0;
680 + goto done;
681 + }
682 +
683 + err = new_indexed_table_ref_iter(&itr, r, oid, hash_size(r->hash_id),
684 + got.offsets, got.offset_len);
685 + if (err < 0)
686 + goto done;
687 + got.offsets = NULL;
688 + iterator_from_indexed_table_ref_iter(it, itr);
689 +
690 +done:
691 + reftable_iterator_destroy(&oit);
692 + reftable_record_release(&got_rec);
693 + return err;
694 +}
695 +
696 +static int reftable_reader_refs_for_unindexed(struct reftable_reader *r,
697 + struct reftable_iterator *it,
698 + uint8_t *oid)
699 +{
700 + struct table_iter ti_empty = TABLE_ITER_INIT;
701 + struct table_iter *ti = reftable_calloc(sizeof(struct table_iter));
702 + struct filtering_ref_iterator *filter = NULL;
703 + struct filtering_ref_iterator empty = FILTERING_REF_ITERATOR_INIT;
704 + int oid_len = hash_size(r->hash_id);
705 + int err;
706 +
707 + *ti = ti_empty;
708 + err = reader_start(r, ti, BLOCK_TYPE_REF, 0);
709 + if (err < 0) {
710 + reftable_free(ti);
711 + return err;
712 + }
713 +
714 + filter = reftable_malloc(sizeof(struct filtering_ref_iterator));
715 + *filter = empty;
716 +
717 + strbuf_add(&filter->oid, oid, oid_len);
718 + reftable_table_from_reader(&filter->tab, r);
719 + filter->double_check = 0;
720 + iterator_from_table_iter(&filter->it, ti);
721 +
722 + iterator_from_filtering_ref_iterator(it, filter);
723 + return 0;
724 +}
725 +
726 +int reftable_reader_refs_for(struct reftable_reader *r,
727 + struct reftable_iterator *it, uint8_t *oid)
728 +{
729 + if (r->obj_offsets.is_present)
730 + return reftable_reader_refs_for_indexed(r, it, oid);
731 + return reftable_reader_refs_for_unindexed(r, it, oid);
732 +}
733 +
734 +uint64_t reftable_reader_max_update_index(struct reftable_reader *r)
735 +{
736 + return r->max_update_index;
737 +}
738 +
739 +uint64_t reftable_reader_min_update_index(struct reftable_reader *r)
740 +{
741 + return r->min_update_index;
742 +}
743 +
744 +/* generic table interface. */
745 +
746 +static int reftable_reader_seek_void(void *tab, struct reftable_iterator *it,
747 + struct reftable_record *rec)
748 +{
749 + return reader_seek(tab, it, rec);
750 +}
751 +
752 +static uint32_t reftable_reader_hash_id_void(void *tab)
753 +{
754 + return reftable_reader_hash_id(tab);
755 +}
756 +
757 +static uint64_t reftable_reader_min_update_index_void(void *tab)
758 +{
759 + return reftable_reader_min_update_index(tab);
760 +}
761 +
762 +static uint64_t reftable_reader_max_update_index_void(void *tab)
763 +{
764 + return reftable_reader_max_update_index(tab);
765 +}
766 +
767 +static struct reftable_table_vtable reader_vtable = {
768 + .seek_record = reftable_reader_seek_void,
769 + .hash_id = reftable_reader_hash_id_void,
770 + .min_update_index = reftable_reader_min_update_index_void,
771 + .max_update_index = reftable_reader_max_update_index_void,
772 +};
773 +
774 +void reftable_table_from_reader(struct reftable_table *tab,
775 + struct reftable_reader *reader)
776 +{
777 + assert(!tab->ops);
778 + tab->ops = &reader_vtable;
779 + tab->table_arg = reader;
780 +}
781 +
782 +
783 +int reftable_reader_print_file(const char *tablename)
784 +{
785 + struct reftable_block_source src = { NULL };
786 + int err = reftable_block_source_from_file(&src, tablename);
787 + struct reftable_reader *r = NULL;
788 + struct reftable_table tab = { NULL };
789 + if (err < 0)
790 + goto done;
791 +
792 + err = reftable_new_reader(&r, &src, tablename);
793 + if (err < 0)
794 + goto done;
795 +
796 + reftable_table_from_reader(&tab, r);
797 + err = reftable_table_print(&tab);
798 +done:
799 + reftable_reader_free(r);
800 + return err;
801 +}
reftable/reader.h new
+64
@@ -0,0 +1,64 @@
1 +/*
2 +Copyright 2020 Google LLC
3 +
4 +Use of this source code is governed by a BSD-style
5 +license that can be found in the LICENSE file or at
6 +https://developers.google.com/open-source/licenses/bsd
7 +*/
8 +
9 +#ifndef READER_H
10 +#define READER_H
11 +
12 +#include "block.h"
13 +#include "record.h"
14 +#include "reftable-iterator.h"
15 +#include "reftable-reader.h"
16 +
17 +uint64_t block_source_size(struct reftable_block_source *source);
18 +
19 +int block_source_read_block(struct reftable_block_source *source,
20 + struct reftable_block *dest, uint64_t off,
21 + uint32_t size);
22 +void block_source_close(struct reftable_block_source *source);
23 +
24 +/* metadata for a block type */
25 +struct reftable_reader_offsets {
26 + int is_present;
27 + uint64_t offset;
28 + uint64_t index_offset;
29 +};
30 +
31 +/* The state for reading a reftable file. */
32 +struct reftable_reader {
33 + /* for convience, associate a name with the instance. */
34 + char *name;
35 + struct reftable_block_source source;
36 +
37 + /* Size of the file, excluding the footer. */
38 + uint64_t size;
39 +
40 + /* 'sha1' for SHA1, 's256' for SHA-256 */
41 + uint32_t hash_id;
42 +
43 + uint32_t block_size;
44 + uint64_t min_update_index;
45 + uint64_t max_update_index;
46 + /* Length of the OID keys in the 'o' section */
47 + int object_id_len;
48 + int version;
49 +
50 + struct reftable_reader_offsets ref_offsets;
51 + struct reftable_reader_offsets obj_offsets;
52 + struct reftable_reader_offsets log_offsets;
53 +};
54 +
55 +int init_reader(struct reftable_reader *r, struct reftable_block_source *source,
56 + const char *name);
57 +void reader_close(struct reftable_reader *r);
58 +const char *reader_name(struct reftable_reader *r);
59 +
60 +/* initialize a block reader to read from `r` */
61 +int reader_init_block_reader(struct reftable_reader *r, struct block_reader *br,
62 + uint64_t next_off, uint8_t want_typ);
63 +
64 +#endif
reftable/reftable-reader.h new
+101
@@ -0,0 +1,101 @@
1 +/*
2 + Copyright 2020 Google LLC
3 +
4 + Use of this source code is governed by a BSD-style
5 + license that can be found in the LICENSE file or at
6 + https://developers.google.com/open-source/licenses/bsd
7 +*/
8 +
9 +#ifndef REFTABLE_READER_H
10 +#define REFTABLE_READER_H
11 +
12 +#include "reftable-iterator.h"
13 +#include "reftable-blocksource.h"
14 +
15 +/*
16 + * Reading single tables
17 + *
18 + * The follow routines are for reading single files. For an
19 + * application-level interface, skip ahead to struct
20 + * reftable_merged_table and struct reftable_stack.
21 + */
22 +
23 +/* The reader struct is a handle to an open reftable file. */
24 +struct reftable_reader;
25 +
26 +/* Generic table. */
27 +struct reftable_table;
28 +
29 +/* reftable_new_reader opens a reftable for reading. If successful,
30 + * returns 0 code and sets pp. The name is used for creating a
31 + * stack. Typically, it is the basename of the file. The block source
32 + * `src` is owned by the reader, and is closed on calling
33 + * reftable_reader_destroy(). On error, the block source `src` is
34 + * closed as well.
35 + */
36 +int reftable_new_reader(struct reftable_reader **pp,
37 + struct reftable_block_source *src, const char *name);
38 +
39 +/* reftable_reader_seek_ref returns an iterator where 'name' would be inserted
40 + in the table. To seek to the start of the table, use name = "".
41 +
42 + example:
43 +
44 + struct reftable_reader *r = NULL;
45 + int err = reftable_new_reader(&r, &src, "filename");
46 + if (err < 0) { ... }
47 + struct reftable_iterator it = {0};
48 + err = reftable_reader_seek_ref(r, &it, "refs/heads/master");
49 + if (err < 0) { ... }
50 + struct reftable_ref_record ref = {0};
51 + while (1) {
52 + err = reftable_iterator_next_ref(&it, &ref);
53 + if (err > 0) {
54 + break;
55 + }
56 + if (err < 0) {
57 + ..error handling..
58 + }
59 + ..found..
60 + }
61 + reftable_iterator_destroy(&it);
62 + reftable_ref_record_release(&ref);
63 +*/
64 +int reftable_reader_seek_ref(struct reftable_reader *r,
65 + struct reftable_iterator *it, const char *name);
66 +
67 +/* returns the hash ID used in this table. */
68 +uint32_t reftable_reader_hash_id(struct reftable_reader *r);
69 +
70 +/* seek to logs for the given name, older than update_index. To seek to the
71 + start of the table, use name = "".
72 +*/
73 +int reftable_reader_seek_log_at(struct reftable_reader *r,
74 + struct reftable_iterator *it, const char *name,
75 + uint64_t update_index);
76 +
77 +/* seek to newest log entry for given name. */
78 +int reftable_reader_seek_log(struct reftable_reader *r,
79 + struct reftable_iterator *it, const char *name);
80 +
81 +/* closes and deallocates a reader. */
82 +void reftable_reader_free(struct reftable_reader *);
83 +
84 +/* return an iterator for the refs pointing to `oid`. */
85 +int reftable_reader_refs_for(struct reftable_reader *r,
86 + struct reftable_iterator *it, uint8_t *oid);
87 +
88 +/* return the max_update_index for a table */
89 +uint64_t reftable_reader_max_update_index(struct reftable_reader *r);
90 +
91 +/* return the min_update_index for a table */
92 +uint64_t reftable_reader_min_update_index(struct reftable_reader *r);
93 +
94 +/* creates a generic table from a file reader. */
95 +void reftable_table_from_reader(struct reftable_table *tab,
96 + struct reftable_reader *reader);
97 +
98 +/* print table onto stdout for debugging. */
99 +int reftable_reader_print_file(const char *tablename);
100 +
101 +#endif