reftable: make reftable_record a tagged union

This reduces the amount of glue code, because we don't need a void pointer or vtable within the structure. The only snag is that reftable_index_record contain a strbuf, so it cannot be zero-initialized. To address this, use reftable_new_record() to return fresh instance, given a record type. Since reftable_new_record() doesn't cause heap allocation anymore, it should be balanced with reftable_record_release() rather than reftable_record_destroy(). Thanks to Peff for the suggestion. Helped-by: Jeff King <peff@peff.net> Signed-off-by: Han-Wen Nienhuys <hanwen@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Han-Wen Nienhuys committed Jan 20, 2022 at 15:12 UTC 66c0dabab5e15f78d0505be36cac4a383e14cf88
12 files changed +334 -337
reftable/block.c
+1 -1
@@ -421,7 +421,7 @@ int block_reader_seek(struct block_reader *br, struct block_iter *it,
421 done:
422 strbuf_release(&key);
423 strbuf_release(&next.last_key);
424 - reftable_record_destroy(&rec);
424 + reftable_record_release(&rec);
425
426 return err;
427 }
reftable/block_test.c
+11 -11
@@ -26,8 +26,9 @@ static void test_block_read_write(void)
26 struct block_writer bw = {
27 .last_key = STRBUF_INIT,
28 };
29 - struct reftable_ref_record ref = { NULL };
30 - struct reftable_record rec = { NULL };
29 + struct reftable_record rec = {
30 + .type = BLOCK_TYPE_REF,
31 + };
32 int i = 0;
33 int n;
34 struct block_reader br = { 0 };
@@ -40,7 +41,6 @@ static void test_block_read_write(void)
41 block.source = malloc_block_source();
42 block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
43 header_off, hash_size(GIT_SHA1_FORMAT_ID));
43 - reftable_record_from_ref(&rec, &ref);
44
45 for (i = 0; i < N; i++) {
46 char name[100];
@@ -48,14 +48,14 @@ static void test_block_read_write(void)
48 snprintf(name, sizeof(name), "branch%02d", i);
49 memset(hash, i, sizeof(hash));
50
51 - ref.refname = name;
52 - ref.value_type = REFTABLE_REF_VAL1;
53 - ref.value.val1 = hash;
51 + rec.u.ref.refname = name;
52 + rec.u.ref.value_type = REFTABLE_REF_VAL1;
53 + rec.u.ref.value.val1 = hash;
54
55 names[i] = xstrdup(name);
56 n = block_writer_add(&bw, &rec);
57 - ref.refname = NULL;
58 - ref.value_type = REFTABLE_REF_DELETION;
57 + rec.u.ref.refname = NULL;
58 + rec.u.ref.value_type = REFTABLE_REF_DELETION;
59 EXPECT(n == 0);
60 }
61
@@ -74,7 +74,7 @@ static void test_block_read_write(void)
74 if (r > 0) {
75 break;
76 }
77 - EXPECT_STREQ(names[j], ref.refname);
77 + EXPECT_STREQ(names[j], rec.u.ref.refname);
78 j++;
79 }
80
@@ -92,7 +92,7 @@ static void test_block_read_write(void)
92 n = block_iter_next(&it, &rec);
93 EXPECT(n == 0);
94
95 - EXPECT_STREQ(names[i], ref.refname);
95 + EXPECT_STREQ(names[i], rec.u.ref.refname);
96
97 want.len--;
98 n = block_reader_seek(&br, &it, &want);
@@ -100,7 +100,7 @@ static void test_block_read_write(void)
100
101 n = block_iter_next(&it, &rec);
102 EXPECT(n == 0);
103 - EXPECT_STREQ(names[10 * (i / 10)], ref.refname);
103 + EXPECT_STREQ(names[10 * (i / 10)], rec.u.ref.refname);
104
105 block_iter_close(&it);
106 }
reftable/generic.c
+24 -17
@@ -7,6 +7,7 @@ https://developers.google.com/open-source/licenses/bsd
7 */
8
9 #include "basics.h"
10 +#include "constants.h"
11 #include "record.h"
12 #include "generic.h"
13 #include "reftable-iterator.h"
@@ -15,23 +16,21 @@ https://developers.google.com/open-source/licenses/bsd
16 int reftable_table_seek_ref(struct reftable_table *tab,
17 struct reftable_iterator *it, const char *name)
18 {
18 - struct reftable_ref_record ref = {
19 - .refname = (char *)name,
20 - };
21 - struct reftable_record rec = { NULL };
22 - reftable_record_from_ref(&rec, &ref);
19 + struct reftable_record rec = { .type = BLOCK_TYPE_REF,
20 + .u.ref = {
21 + .refname = (char *)name,
22 + } };
23 return tab->ops->seek_record(tab->table_arg, it, &rec);
24 }
25
26 int reftable_table_seek_log(struct reftable_table *tab,
27 struct reftable_iterator *it, const char *name)
28 {
29 - struct reftable_log_record log = {
30 - .refname = (char *)name,
31 - .update_index = ~((uint64_t)0),
32 - };
33 - struct reftable_record rec = { NULL };
34 - reftable_record_from_log(&rec, &log);
29 + struct reftable_record rec = { .type = BLOCK_TYPE_LOG,
30 + .u.log = {
31 + .refname = (char *)name,
32 + .update_index = ~((uint64_t)0),
33 + } };
34 return tab->ops->seek_record(tab->table_arg, it, &rec);
35 }
36
@@ -129,17 +128,25 @@ void reftable_iterator_destroy(struct reftable_iterator *it)
128 int reftable_iterator_next_ref(struct reftable_iterator *it,
129 struct reftable_ref_record *ref)
130 {
132 - struct reftable_record rec = { NULL };
133 - reftable_record_from_ref(&rec, ref);
134 - return iterator_next(it, &rec);
131 + struct reftable_record rec = {
132 + .type = BLOCK_TYPE_REF,
133 + .u.ref = *ref,
134 + };
135 + int err = iterator_next(it, &rec);
136 + *ref = rec.u.ref;
137 + return err;
138 }
139
140 int reftable_iterator_next_log(struct reftable_iterator *it,
141 struct reftable_log_record *log)
142 {
140 - struct reftable_record rec = { NULL };
141 - reftable_record_from_log(&rec, log);
142 - return iterator_next(it, &rec);
143 + struct reftable_record rec = {
144 + .type = BLOCK_TYPE_LOG,
145 + .u.log = *log,
146 + };
147 + int err = iterator_next(it, &rec);
148 + *log = rec.u.log;
149 + return err;
150 }
151
152 int iterator_next(struct reftable_iterator *it, struct reftable_record *rec)
reftable/iter.c
+2 -2
@@ -32,7 +32,7 @@ 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;
35 + struct reftable_ref_record *ref = &rec->u.ref;
36 int err = 0;
37 while (1) {
38 err = reftable_iterator_next_ref(&fri->it, ref);
@@ -127,7 +127,7 @@ static int indexed_table_ref_iter_next_block(struct indexed_table_ref_iter *it)
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;
130 + struct reftable_ref_record *ref = &rec->u.ref;
131
132 while (1) {
133 int err = block_iter_next(&it->cur, rec);
reftable/merged.c
+16 -17
@@ -30,7 +30,7 @@ static int merged_iter_init(struct merged_iter *mi)
30
31 if (err > 0) {
32 reftable_iterator_destroy(&mi->stack[i]);
33 - reftable_record_destroy(&rec);
33 + reftable_record_release(&rec);
34 } else {
35 struct pq_entry e = {
36 .rec = rec,
@@ -57,18 +57,17 @@ static void merged_iter_close(void *p)
57 static int merged_iter_advance_nonnull_subiter(struct merged_iter *mi,
58 size_t idx)
59 {
60 - struct reftable_record rec = reftable_new_record(mi->typ);
60 struct pq_entry e = {
62 - .rec = rec,
61 + .rec = reftable_new_record(mi->typ),
62 .index = idx,
63 };
65 - int err = iterator_next(&mi->stack[idx], &rec);
64 + int err = iterator_next(&mi->stack[idx], &e.rec);
65 if (err < 0)
66 return err;
67
68 if (err > 0) {
69 reftable_iterator_destroy(&mi->stack[idx]);
71 - reftable_record_destroy(&rec);
70 + reftable_record_release(&e.rec);
71 return 0;
72 }
73
@@ -126,11 +125,11 @@ static int merged_iter_next_entry(struct merged_iter *mi,
125 if (err < 0) {
126 return err;
127 }
129 - reftable_record_destroy(&top.rec);
128 + reftable_record_release(&top.rec);
129 }
130
131 reftable_record_copy_from(rec, &entry.rec, hash_size(mi->hash_id));
133 - reftable_record_destroy(&entry.rec);
132 + reftable_record_release(&entry.rec);
133 strbuf_release(&entry_key);
134 return 0;
135 }
@@ -290,11 +289,12 @@ int reftable_merged_table_seek_ref(struct reftable_merged_table *mt,
289 struct reftable_iterator *it,
290 const char *name)
291 {
293 - struct reftable_ref_record ref = {
294 - .refname = (char *)name,
292 + struct reftable_record rec = {
293 + .type = BLOCK_TYPE_REF,
294 + .u.ref = {
295 + .refname = (char *)name,
296 + },
297 };
296 - struct reftable_record rec = { NULL };
297 - reftable_record_from_ref(&rec, &ref);
298 return merged_table_seek_record(mt, it, &rec);
299 }
300
@@ -302,12 +302,11 @@ int reftable_merged_table_seek_log_at(struct reftable_merged_table *mt,
302 struct reftable_iterator *it,
303 const char *name, uint64_t update_index)
304 {
305 - struct reftable_log_record log = {
306 - .refname = (char *)name,
307 - .update_index = update_index,
308 - };
309 - struct reftable_record rec = { NULL };
310 - reftable_record_from_log(&rec, &log);
305 + struct reftable_record rec = { .type = BLOCK_TYPE_LOG,
306 + .u.log = {
307 + .refname = (char *)name,
308 + .update_index = update_index,
309 + } };
310 return merged_table_seek_record(mt, it, &rec);
311 }
312
reftable/pq.c
+2 -1
@@ -74,6 +74,7 @@ struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq)
74 void merged_iter_pqueue_add(struct merged_iter_pqueue *pq, struct pq_entry e)
75 {
76 int i = 0;
77 +
78 if (pq->len == pq->cap) {
79 pq->cap = 2 * pq->cap + 1;
80 pq->heap = reftable_realloc(pq->heap,
@@ -98,7 +99,7 @@ void merged_iter_pqueue_release(struct merged_iter_pqueue *pq)
99 {
100 int i = 0;
101 for (i = 0; i < pq->len; i++) {
101 - reftable_record_destroy(&pq->heap[i].rec);
102 + reftable_record_release(&pq->heap[i].rec);
103 }
104 FREE_AND_NULL(pq->heap);
105 pq->len = pq->cap = 0;
reftable/pq_test.c
+12 -15
@@ -31,7 +31,7 @@ static void test_pq(void)
31 int N = ARRAY_SIZE(names) - 1;
32
33 struct merged_iter_pqueue pq = { NULL };
34 - const char *last = NULL;
34 + char *last = NULL;
35
36 int i = 0;
37 for (i = 0; i < N; i++) {
@@ -42,12 +42,10 @@ static void test_pq(void)
42
43 i = 1;
44 do {
45 - struct reftable_record rec =
46 - reftable_new_record(BLOCK_TYPE_REF);
47 - struct pq_entry e = { 0 };
48 -
49 - reftable_record_as_ref(&rec)->refname = names[i];
50 - e.rec = rec;
45 + struct pq_entry e = { .rec = { .type = BLOCK_TYPE_REF,
46 + .u.ref = {
47 + .refname = names[i],
48 + } } };
49 merged_iter_pqueue_add(&pq, e);
50 merged_iter_pqueue_check(pq);
51 i = (i * 7) % N;
@@ -55,19 +53,18 @@ static void test_pq(void)
53
54 while (!merged_iter_pqueue_is_empty(pq)) {
55 struct pq_entry e = merged_iter_pqueue_remove(&pq);
58 - struct reftable_ref_record *ref =
59 - reftable_record_as_ref(&e.rec);
60 -
56 + struct reftable_record *rec = &e.rec;
57 merged_iter_pqueue_check(pq);
58
59 + EXPECT(reftable_record_type(rec) == BLOCK_TYPE_REF);
60 if (last) {
64 - EXPECT(strcmp(last, ref->refname) < 0);
61 + EXPECT(strcmp(last, rec->u.ref.refname) < 0);
62 }
66 - last = ref->refname;
67 - ref->refname = NULL;
68 - reftable_free(ref);
63 + // this is names[i], so don't dealloc.
64 + last = rec->u.ref.refname;
65 + rec->u.ref.refname = NULL;
66 + reftable_record_release(rec);
67 }
70 -
68 for (i = 0; i < N; i++) {
69 reftable_free(names[i]);
70 }
reftable/reader.c
+45 -42
@@ -239,8 +239,7 @@ static int table_iter_next_in_block(struct table_iter *ti,
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;
242 + rec->u.ref.update_index += ti->r->min_update_index;
243 }
244
245 return res;
@@ -480,7 +479,7 @@ static int reader_seek_linear(struct reftable_reader *r, struct table_iter *ti,
479
480 done:
481 block_iter_close(&next.bi);
483 - reftable_record_destroy(&rec);
482 + reftable_record_release(&rec);
483 strbuf_release(&want_key);
484 strbuf_release(&got_key);
485 return err;
@@ -490,34 +489,35 @@ static int reader_seek_indexed(struct reftable_reader *r,
489 struct reftable_iterator *it,
490 struct reftable_record *rec)
491 {
493 - struct reftable_index_record want_index = { .last_key = STRBUF_INIT };
494 - struct reftable_record want_index_rec = { NULL };
495 - struct reftable_index_record index_result = { .last_key = STRBUF_INIT };
496 - struct reftable_record index_result_rec = { NULL };
492 + struct reftable_record want_index = {
493 + .type = BLOCK_TYPE_INDEX, .u.idx = { .last_key = STRBUF_INIT }
494 + };
495 + struct reftable_record index_result = {
496 + .type = BLOCK_TYPE_INDEX,
497 + .u.idx = { .last_key = STRBUF_INIT },
498 + };
499 struct table_iter index_iter = TABLE_ITER_INIT;
500 struct table_iter next = TABLE_ITER_INIT;
501 int err = 0;
502
501 - reftable_record_key(rec, &want_index.last_key);
502 - reftable_record_from_index(&want_index_rec, &want_index);
503 - reftable_record_from_index(&index_result_rec, &index_result);
504 -
503 + reftable_record_key(rec, &want_index.u.idx.last_key);
504 err = reader_start(r, &index_iter, reftable_record_type(rec), 1);
505 if (err < 0)
506 goto done;
507
509 - err = reader_seek_linear(r, &index_iter, &want_index_rec);
508 + err = reader_seek_linear(r, &index_iter, &want_index);
509 while (1) {
511 - err = table_iter_next(&index_iter, &index_result_rec);
510 + err = table_iter_next(&index_iter, &index_result);
511 table_iter_block_done(&index_iter);
512 if (err != 0)
513 goto done;
514
516 - err = reader_table_iter_at(r, &next, index_result.offset, 0);
515 + err = reader_table_iter_at(r, &next, index_result.u.idx.offset,
516 + 0);
517 if (err != 0)
518 goto done;
519
520 - err = block_iter_seek(&next.bi, &want_index.last_key);
520 + err = block_iter_seek(&next.bi, &want_index.u.idx.last_key);
521 if (err < 0)
522 goto done;
523
@@ -545,8 +545,8 @@ static int reader_seek_indexed(struct reftable_reader *r,
545 done:
546 block_iter_close(&next.bi);
547 table_iter_close(&index_iter);
548 - reftable_record_release(&want_index_rec);
549 - reftable_record_release(&index_result_rec);
548 + reftable_record_release(&want_index);
549 + reftable_record_release(&index_result);
550 return err;
551 }
552
@@ -595,11 +595,12 @@ static int reader_seek(struct reftable_reader *r, struct reftable_iterator *it,
595 int reftable_reader_seek_ref(struct reftable_reader *r,
596 struct reftable_iterator *it, const char *name)
597 {
598 - struct reftable_ref_record ref = {
599 - .refname = (char *)name,
598 + struct reftable_record rec = {
599 + .type = BLOCK_TYPE_REF,
600 + .u.ref = {
601 + .refname = (char *)name,
602 + },
603 };
601 - struct reftable_record rec = { NULL };
602 - reftable_record_from_ref(&rec, &ref);
604 return reader_seek(r, it, &rec);
605 }
606
@@ -607,12 +608,11 @@ int reftable_reader_seek_log_at(struct reftable_reader *r,
608 struct reftable_iterator *it, const char *name,
609 uint64_t update_index)
610 {
610 - struct reftable_log_record log = {
611 - .refname = (char *)name,
612 - .update_index = update_index,
613 - };
614 - struct reftable_record rec = { NULL };
615 - reftable_record_from_log(&rec, &log);
611 + struct reftable_record rec = { .type = BLOCK_TYPE_LOG,
612 + .u.log = {
613 + .refname = (char *)name,
614 + .update_index = update_index,
615 + } };
616 return reader_seek(r, it, &rec);
617 }
618
@@ -656,31 +656,33 @@ static int reftable_reader_refs_for_indexed(struct reftable_reader *r,
656 struct reftable_iterator *it,
657 uint8_t *oid)
658 {
659 - struct reftable_obj_record want = {
660 - .hash_prefix = oid,
661 - .hash_prefix_len = r->object_id_len,
659 + struct reftable_record want = {
660 + .type = BLOCK_TYPE_OBJ,
661 + .u.obj = {
662 + .hash_prefix = oid,
663 + .hash_prefix_len = r->object_id_len,
664 + },
665 };
663 - struct reftable_record want_rec = { NULL };
666 struct reftable_iterator oit = { NULL };
665 - struct reftable_obj_record got = { NULL };
666 - struct reftable_record got_rec = { NULL };
667 + struct reftable_record got = {
668 + .type = BLOCK_TYPE_OBJ,
669 + .u.obj = { 0 },
670 + };
671 int err = 0;
672 struct indexed_table_ref_iter *itr = NULL;
673
674 /* Look through the reverse index. */
671 - reftable_record_from_obj(&want_rec, &want);
672 - err = reader_seek(r, &oit, &want_rec);
675 + err = reader_seek(r, &oit, &want);
676 if (err != 0)
677 goto done;
678
679 /* read out the reftable_obj_record */
677 - reftable_record_from_obj(&got_rec, &got);
678 - err = iterator_next(&oit, &got_rec);
680 + err = iterator_next(&oit, &got);
681 if (err < 0)
682 goto done;
683
682 - if (err > 0 ||
683 - memcmp(want.hash_prefix, got.hash_prefix, r->object_id_len)) {
684 + if (err > 0 || memcmp(want.u.obj.hash_prefix, got.u.obj.hash_prefix,
685 + r->object_id_len)) {
686 /* didn't find it; return empty iterator */
687 iterator_set_empty(it);
688 err = 0;
@@ -688,15 +690,16 @@ static int reftable_reader_refs_for_indexed(struct reftable_reader *r,
690 }
691
692 err = new_indexed_table_ref_iter(&itr, r, oid, hash_size(r->hash_id),
691 - got.offsets, got.offset_len);
693 + got.u.obj.offsets,
694 + got.u.obj.offset_len);
695 if (err < 0)
696 goto done;
694 - got.offsets = NULL;
697 + got.u.obj.offsets = NULL;
698 iterator_from_indexed_table_ref_iter(it, itr);
699
700 done:
701 reftable_iterator_destroy(&oit);
699 - reftable_record_release(&got_rec);
702 + reftable_record_release(&got);
703 return err;
704 }
705
reftable/record.c
+100 -112
@@ -15,6 +15,10 @@ https://developers.google.com/open-source/licenses/bsd
15 #include "reftable-error.h"
16 #include "basics.h"
17
18 +static struct reftable_record_vtable *
19 +reftable_record_vtable(struct reftable_record *rec);
20 +static void *reftable_record_data(struct reftable_record *rec);
21 +
22 int get_var_int(uint64_t *dest, struct string_view *in)
23 {
24 int ptr = 0;
@@ -475,12 +479,14 @@ static void reftable_obj_record_copy_from(void *rec, const void *src_rec,
479 (const struct reftable_obj_record *)src_rec;
480
481 reftable_obj_record_release(obj);
478 - *obj = *src;
479 - obj->hash_prefix = reftable_malloc(obj->hash_prefix_len);
480 - memcpy(obj->hash_prefix, src->hash_prefix, obj->hash_prefix_len);
482 + obj->hash_prefix = reftable_malloc(src->hash_prefix_len);
483 + obj->hash_prefix_len = src->hash_prefix_len;
484 + if (src->hash_prefix_len)
485 + memcpy(obj->hash_prefix, src->hash_prefix, obj->hash_prefix_len);
486
482 - obj->offsets = reftable_malloc(obj->offset_len * sizeof(uint64_t));
483 - COPY_ARRAY(obj->offsets, src->offsets, obj->offset_len);
487 + obj->offsets = reftable_malloc(src->offset_len * sizeof(uint64_t));
488 + obj->offset_len = src->offset_len;
489 + COPY_ARRAY(obj->offsets, src->offsets, src->offset_len);
490 }
491
492 static uint8_t reftable_obj_record_val_type(const void *rec)
@@ -965,58 +971,6 @@ static struct reftable_record_vtable reftable_log_record_vtable = {
971 .equal = &reftable_log_record_equal_void
972 };
973
968 -struct reftable_record reftable_new_record(uint8_t typ)
969 -{
970 - struct reftable_record rec = { NULL };
971 - switch (typ) {
972 - case BLOCK_TYPE_REF: {
973 - struct reftable_ref_record *r =
974 - reftable_calloc(sizeof(struct reftable_ref_record));
975 - reftable_record_from_ref(&rec, r);
976 - return rec;
977 - }
978 -
979 - case BLOCK_TYPE_OBJ: {
980 - struct reftable_obj_record *r =
981 - reftable_calloc(sizeof(struct reftable_obj_record));
982 - reftable_record_from_obj(&rec, r);
983 - return rec;
984 - }
985 - case BLOCK_TYPE_LOG: {
986 - struct reftable_log_record *r =
987 - reftable_calloc(sizeof(struct reftable_log_record));
988 - reftable_record_from_log(&rec, r);
989 - return rec;
990 - }
991 - case BLOCK_TYPE_INDEX: {
992 - struct reftable_index_record empty = { .last_key =
993 - STRBUF_INIT };
994 - struct reftable_index_record *r =
995 - reftable_calloc(sizeof(struct reftable_index_record));
996 - *r = empty;
997 - reftable_record_from_index(&rec, r);
998 - return rec;
999 - }
1000 - }
1001 - abort();
1002 - return rec;
1003 -}
1004 -
1005 -/* clear out the record, yielding the reftable_record data that was
1006 - * encapsulated. */
1007 -static void *reftable_record_yield(struct reftable_record *rec)
1008 -{
1009 - void *p = rec->data;
1010 - rec->data = NULL;
1011 - return p;
1012 -}
1013 -
1014 -void reftable_record_destroy(struct reftable_record *rec)
1015 -{
1016 - reftable_record_release(rec);
1017 - reftable_free(reftable_record_yield(rec));
1018 -}
1019 -
974 static void reftable_index_record_key(const void *r, struct strbuf *dest)
975 {
976 const struct reftable_index_record *rec = r;
@@ -1103,98 +1057,60 @@ static struct reftable_record_vtable reftable_index_record_vtable = {
1057
1058 void reftable_record_key(struct reftable_record *rec, struct strbuf *dest)
1059 {
1106 - rec->ops->key(rec->data, dest);
1060 + reftable_record_vtable(rec)->key(reftable_record_data(rec), dest);
1061 }
1062
1063 uint8_t reftable_record_type(struct reftable_record *rec)
1064 {
1111 - return rec->ops->type;
1065 + return rec->type;
1066 }
1067
1068 int reftable_record_encode(struct reftable_record *rec, struct string_view dest,
1069 int hash_size)
1070 {
1117 - return rec->ops->encode(rec->data, dest, hash_size);
1071 + return reftable_record_vtable(rec)->encode(reftable_record_data(rec),
1072 + dest, hash_size);
1073 }
1074
1075 void reftable_record_copy_from(struct reftable_record *rec,
1076 struct reftable_record *src, int hash_size)
1077 {
1123 - assert(src->ops->type == rec->ops->type);
1078 + assert(src->type == rec->type);
1079
1125 - rec->ops->copy_from(rec->data, src->data, hash_size);
1080 + reftable_record_vtable(rec)->copy_from(reftable_record_data(rec),
1081 + reftable_record_data(src),
1082 + hash_size);
1083 }
1084
1085 uint8_t reftable_record_val_type(struct reftable_record *rec)
1086 {
1130 - return rec->ops->val_type(rec->data);
1087 + return reftable_record_vtable(rec)->val_type(reftable_record_data(rec));
1088 }
1089
1090 int reftable_record_decode(struct reftable_record *rec, struct strbuf key,
1091 uint8_t extra, struct string_view src, int hash_size)
1092 {
1136 - return rec->ops->decode(rec->data, key, extra, src, hash_size);
1093 + return reftable_record_vtable(rec)->decode(reftable_record_data(rec),
1094 + key, extra, src, hash_size);
1095 }
1096
1097 void reftable_record_release(struct reftable_record *rec)
1098 {
1141 - rec->ops->release(rec->data);
1099 + reftable_record_vtable(rec)->release(reftable_record_data(rec));
1100 }
1101
1102 int reftable_record_is_deletion(struct reftable_record *rec)
1103 {
1146 - return rec->ops->is_deletion(rec->data);
1104 + return reftable_record_vtable(rec)->is_deletion(
1105 + reftable_record_data(rec));
1106 }
1107
1108 int reftable_record_equal(struct reftable_record *a, struct reftable_record *b, int hash_size)
1109 {
1151 - if (a->ops != b->ops)
1110 + if (a->type != b->type)
1111 return 0;
1153 - return a->ops->equal(a->data, b->data, hash_size);
1154 -}
1155 -
1156 -void reftable_record_from_ref(struct reftable_record *rec,
1157 - struct reftable_ref_record *ref_rec)
1158 -{
1159 - assert(!rec->ops);
1160 - rec->data = ref_rec;
1161 - rec->ops = &reftable_ref_record_vtable;
1162 -}
1163 -
1164 -void reftable_record_from_obj(struct reftable_record *rec,
1165 - struct reftable_obj_record *obj_rec)
1166 -{
1167 - assert(!rec->ops);
1168 - rec->data = obj_rec;
1169 - rec->ops = &reftable_obj_record_vtable;
1170 -}
1171 -
1172 -void reftable_record_from_index(struct reftable_record *rec,
1173 - struct reftable_index_record *index_rec)
1174 -{
1175 - assert(!rec->ops);
1176 - rec->data = index_rec;
1177 - rec->ops = &reftable_index_record_vtable;
1178 -}
1179 -
1180 -void reftable_record_from_log(struct reftable_record *rec,
1181 - struct reftable_log_record *log_rec)
1182 -{
1183 - assert(!rec->ops);
1184 - rec->data = log_rec;
1185 - rec->ops = &reftable_log_record_vtable;
1186 -}
1187 -
1188 -struct reftable_ref_record *reftable_record_as_ref(struct reftable_record *rec)
1189 -{
1190 - assert(reftable_record_type(rec) == BLOCK_TYPE_REF);
1191 - return rec->data;
1192 -}
1193 -
1194 -struct reftable_log_record *reftable_record_as_log(struct reftable_record *rec)
1195 -{
1196 - assert(reftable_record_type(rec) == BLOCK_TYPE_LOG);
1197 - return rec->data;
1112 + return reftable_record_vtable(a)->equal(
1113 + reftable_record_data(a), reftable_record_data(b), hash_size);
1114 }
1115
1116 static int hash_equal(uint8_t *a, uint8_t *b, int hash_size)
@@ -1267,3 +1183,75 @@ void string_view_consume(struct string_view *s, int n)
1183 s->buf += n;
1184 s->len -= n;
1185 }
1186 +
1187 +static void *reftable_record_data(struct reftable_record *rec)
1188 +{
1189 + switch (rec->type) {
1190 + case BLOCK_TYPE_REF:
1191 + return &rec->u.ref;
1192 + case BLOCK_TYPE_LOG:
1193 + return &rec->u.log;
1194 + case BLOCK_TYPE_INDEX:
1195 + return &rec->u.idx;
1196 + case BLOCK_TYPE_OBJ:
1197 + return &rec->u.obj;
1198 + }
1199 + abort();
1200 +}
1201 +
1202 +static struct reftable_record_vtable *
1203 +reftable_record_vtable(struct reftable_record *rec)
1204 +{
1205 + switch (rec->type) {
1206 + case BLOCK_TYPE_REF:
1207 + return &reftable_ref_record_vtable;
1208 + case BLOCK_TYPE_LOG:
1209 + return &reftable_log_record_vtable;
1210 + case BLOCK_TYPE_INDEX:
1211 + return &reftable_index_record_vtable;
1212 + case BLOCK_TYPE_OBJ:
1213 + return &reftable_obj_record_vtable;
1214 + }
1215 + abort();
1216 +}
1217 +
1218 +struct reftable_record reftable_new_record(uint8_t typ)
1219 +{
1220 + struct reftable_record clean = {
1221 + .type = typ,
1222 + };
1223 +
1224 + /* the following is involved, but the naive solution (just return
1225 + * `clean` as is, except for BLOCK_TYPE_INDEX), returns a garbage
1226 + * clean.u.obj.offsets pointer on Windows VS CI. Go figure.
1227 + */
1228 + switch (typ) {
1229 + case BLOCK_TYPE_OBJ:
1230 + {
1231 + struct reftable_obj_record obj = { 0 };
1232 + clean.u.obj = obj;
1233 + break;
1234 + }
1235 + case BLOCK_TYPE_INDEX:
1236 + {
1237 + struct reftable_index_record idx = {
1238 + .last_key = STRBUF_INIT,
1239 + };
1240 + clean.u.idx = idx;
1241 + break;
1242 + }
1243 + case BLOCK_TYPE_REF:
1244 + {
1245 + struct reftable_ref_record ref = { 0 };
1246 + clean.u.ref = ref;
1247 + break;
1248 + }
1249 + case BLOCK_TYPE_LOG:
1250 + {
1251 + struct reftable_log_record log = { 0 };
1252 + clean.u.log = log;
1253 + break;
1254 + }
1255 + }
1256 + return clean;
1257 +}
reftable/record.h
+18 -24
@@ -63,16 +63,10 @@ struct reftable_record_vtable {
63 int (*equal)(const void *a, const void *b, int hash_size);
64 };
65
66 -/* record is a generic wrapper for different types of records. */
67 -struct reftable_record {
68 - void *data;
69 - struct reftable_record_vtable *ops;
70 -};
71 -
66 /* returns true for recognized block types. Block start with the block type. */
67 int reftable_is_block_type(uint8_t typ);
68
75 -/* creates a malloced record of the given type. Dispose with record_destroy */
69 +/* return an initialized record for the given type */
70 struct reftable_record reftable_new_record(uint8_t typ);
71
72 /* Encode `key` into `dest`. Sets `is_restart` to indicate a restart. Returns
@@ -100,6 +94,22 @@ struct reftable_obj_record {
94 int offset_len;
95 };
96
97 +/* record is a generic wrapper for different types of records. It is normally
98 + * created on the stack, or embedded within another struct. If the type is
99 + * known, a fresh instance can be initialized explicitly. Otherwise, use
100 + * reftable_new_record() to initialize generically (as the index_record is not
101 + * valid as 0-initialized structure)
102 + */
103 +struct reftable_record {
104 + uint8_t type;
105 + union {
106 + struct reftable_ref_record ref;
107 + struct reftable_log_record log;
108 + struct reftable_obj_record obj;
109 + struct reftable_index_record idx;
110 + } u;
111 +};
112 +
113 /* see struct record_vtable */
114 int reftable_record_equal(struct reftable_record *a, struct reftable_record *b, int hash_size);
115 void reftable_record_key(struct reftable_record *rec, struct strbuf *dest);
@@ -114,25 +124,9 @@ int reftable_record_decode(struct reftable_record *rec, struct strbuf key,
124 int hash_size);
125 int reftable_record_is_deletion(struct reftable_record *rec);
126
117 -/* zeroes out the embedded record */
127 +/* frees and zeroes out the embedded record */
128 void reftable_record_release(struct reftable_record *rec);
129
120 -/* clear and deallocate embedded record, and zero `rec`. */
121 -void reftable_record_destroy(struct reftable_record *rec);
122 -
123 -/* initialize generic records from concrete records. The generic record should
124 - * be zeroed out. */
125 -void reftable_record_from_obj(struct reftable_record *rec,
126 - struct reftable_obj_record *objrec);
127 -void reftable_record_from_index(struct reftable_record *rec,
128 - struct reftable_index_record *idxrec);
129 -void reftable_record_from_ref(struct reftable_record *rec,
130 - struct reftable_ref_record *refrec);
131 -void reftable_record_from_log(struct reftable_record *rec,
132 - struct reftable_log_record *logrec);
133 -struct reftable_ref_record *reftable_record_as_ref(struct reftable_record *ref);
134 -struct reftable_log_record *reftable_record_as_log(struct reftable_record *ref);
135 -
130 /* for qsort. */
131 int reftable_ref_record_compare_name(const void *a, const void *b);
132
reftable/record_test.c
+80 -77
@@ -16,13 +16,16 @@
16
17 static void test_copy(struct reftable_record *rec)
18 {
19 - struct reftable_record copy =
20 - reftable_new_record(reftable_record_type(rec));
19 + struct reftable_record copy = { 0 };
20 + uint8_t typ;
21 +
22 + typ = reftable_record_type(rec);
23 + copy = reftable_new_record(typ);
24 reftable_record_copy_from(&copy, rec, GIT_SHA1_RAWSZ);
25 /* do it twice to catch memory leaks */
26 reftable_record_copy_from(&copy, rec, GIT_SHA1_RAWSZ);
27 EXPECT(reftable_record_equal(rec, &copy, GIT_SHA1_RAWSZ));
25 - reftable_record_destroy(&copy);
28 + reftable_record_release(&copy);
29 }
30
31 static void test_varint_roundtrip(void)
@@ -95,61 +98,58 @@ static void test_reftable_ref_record_roundtrip(void)
98 int i = 0;
99
100 for (i = REFTABLE_REF_DELETION; i < REFTABLE_NR_REF_VALUETYPES; i++) {
98 - struct reftable_ref_record in = { NULL };
99 - struct reftable_ref_record out = { NULL };
100 - struct reftable_record rec_out = { NULL };
101 + struct reftable_record in = {
102 + .type = BLOCK_TYPE_REF,
103 + };
104 + struct reftable_record out = { .type = BLOCK_TYPE_REF };
105 struct strbuf key = STRBUF_INIT;
102 - struct reftable_record rec = { NULL };
106 uint8_t buffer[1024] = { 0 };
107 struct string_view dest = {
108 .buf = buffer,
109 .len = sizeof(buffer),
110 };
108 -
111 int n, m;
112
111 - in.value_type = i;
113 + in.u.ref.value_type = i;
114 switch (i) {
115 case REFTABLE_REF_DELETION:
116 break;
117 case REFTABLE_REF_VAL1:
116 - in.value.val1 = reftable_malloc(GIT_SHA1_RAWSZ);
117 - set_hash(in.value.val1, 1);
118 + in.u.ref.value.val1 = reftable_malloc(GIT_SHA1_RAWSZ);
119 + set_hash(in.u.ref.value.val1, 1);
120 break;
121 case REFTABLE_REF_VAL2:
120 - in.value.val2.value = reftable_malloc(GIT_SHA1_RAWSZ);
121 - set_hash(in.value.val2.value, 1);
122 - in.value.val2.target_value =
122 + in.u.ref.value.val2.value =
123 reftable_malloc(GIT_SHA1_RAWSZ);
124 - set_hash(in.value.val2.target_value, 2);
124 + set_hash(in.u.ref.value.val2.value, 1);
125 + in.u.ref.value.val2.target_value =
126 + reftable_malloc(GIT_SHA1_RAWSZ);
127 + set_hash(in.u.ref.value.val2.target_value, 2);
128 break;
129 case REFTABLE_REF_SYMREF:
127 - in.value.symref = xstrdup("target");
130 + in.u.ref.value.symref = xstrdup("target");
131 break;
132 }
130 - in.refname = xstrdup("refs/heads/master");
133 + in.u.ref.refname = xstrdup("refs/heads/master");
134
132 - reftable_record_from_ref(&rec, &in);
133 - test_copy(&rec);
135 + test_copy(&in);
136
135 - EXPECT(reftable_record_val_type(&rec) == i);
137 + EXPECT(reftable_record_val_type(&in) == i);
138
137 - reftable_record_key(&rec, &key);
138 - n = reftable_record_encode(&rec, dest, GIT_SHA1_RAWSZ);
139 + reftable_record_key(&in, &key);
140 + n = reftable_record_encode(&in, dest, GIT_SHA1_RAWSZ);
141 EXPECT(n > 0);
142
143 /* decode into a non-zero reftable_record to test for leaks. */
142 -
143 - reftable_record_from_ref(&rec_out, &out);
144 - m = reftable_record_decode(&rec_out, key, i, dest,
145 - GIT_SHA1_RAWSZ);
144 + m = reftable_record_decode(&out, key, i, dest, GIT_SHA1_RAWSZ);
145 EXPECT(n == m);
146
148 - EXPECT(reftable_ref_record_equal(&in, &out, GIT_SHA1_RAWSZ));
149 - reftable_record_release(&rec_out);
147 + EXPECT(reftable_ref_record_equal(&in.u.ref, &out.u.ref,
148 + GIT_SHA1_RAWSZ));
149 + reftable_record_release(&in);
150
151 strbuf_release(&key);
152 - reftable_ref_record_release(&in);
152 + reftable_record_release(&out);
153 }
154 }
155
@@ -202,7 +202,7 @@ static void test_reftable_log_record_roundtrip(void)
202 set_test_hash(in[0].value.update.new_hash, 1);
203 set_test_hash(in[0].value.update.old_hash, 2);
204 for (i = 0; i < ARRAY_SIZE(in); i++) {
205 - struct reftable_record rec = { NULL };
205 + struct reftable_record rec = { .type = BLOCK_TYPE_LOG };
206 struct strbuf key = STRBUF_INIT;
207 uint8_t buffer[1024] = { 0 };
208 struct string_view dest = {
@@ -210,23 +210,25 @@ static void test_reftable_log_record_roundtrip(void)
210 .len = sizeof(buffer),
211 };
212 /* populate out, to check for leaks. */
213 - struct reftable_log_record out = {
214 - .refname = xstrdup("old name"),
215 - .value_type = REFTABLE_LOG_UPDATE,
216 - .value = {
217 - .update = {
218 - .new_hash = reftable_calloc(GIT_SHA1_RAWSZ),
219 - .old_hash = reftable_calloc(GIT_SHA1_RAWSZ),
220 - .name = xstrdup("old name"),
221 - .email = xstrdup("old@email"),
222 - .message = xstrdup("old message"),
213 + struct reftable_record out = {
214 + .type = BLOCK_TYPE_LOG,
215 + .u.log = {
216 + .refname = xstrdup("old name"),
217 + .value_type = REFTABLE_LOG_UPDATE,
218 + .value = {
219 + .update = {
220 + .new_hash = reftable_calloc(GIT_SHA1_RAWSZ),
221 + .old_hash = reftable_calloc(GIT_SHA1_RAWSZ),
222 + .name = xstrdup("old name"),
223 + .email = xstrdup("old@email"),
224 + .message = xstrdup("old message"),
225 + },
226 },
227 },
228 };
226 - struct reftable_record rec_out = { NULL };
229 int n, m, valtype;
230
229 - reftable_record_from_log(&rec, &in[i]);
231 + rec.u.log = in[i];
232
233 test_copy(&rec);
234
@@ -234,16 +236,16 @@ static void test_reftable_log_record_roundtrip(void)
236
237 n = reftable_record_encode(&rec, dest, GIT_SHA1_RAWSZ);
238 EXPECT(n >= 0);
237 - reftable_record_from_log(&rec_out, &out);
239 valtype = reftable_record_val_type(&rec);
239 - m = reftable_record_decode(&rec_out, key, valtype, dest,
240 + m = reftable_record_decode(&out, key, valtype, dest,
241 GIT_SHA1_RAWSZ);
242 EXPECT(n == m);
243
243 - EXPECT(reftable_log_record_equal(&in[i], &out, GIT_SHA1_RAWSZ));
244 + EXPECT(reftable_log_record_equal(&in[i], &out.u.log,
245 + GIT_SHA1_RAWSZ));
246 reftable_log_record_release(&in[i]);
247 strbuf_release(&key);
246 - reftable_record_release(&rec_out);
248 + reftable_record_release(&out);
249 }
250 }
251
@@ -311,41 +313,43 @@ static void test_reftable_obj_record_roundtrip(void)
313 } };
314 int i = 0;
315 for (i = 0; i < ARRAY_SIZE(recs); i++) {
314 - struct reftable_obj_record in = recs[i];
316 uint8_t buffer[1024] = { 0 };
317 struct string_view dest = {
318 .buf = buffer,
319 .len = sizeof(buffer),
320 };
320 - struct reftable_record rec = { NULL };
321 + struct reftable_record in = {
322 + .type = BLOCK_TYPE_OBJ,
323 + .u.obj = recs[i],
324 + };
325 struct strbuf key = STRBUF_INIT;
322 - struct reftable_obj_record out = { NULL };
323 - struct reftable_record rec_out = { NULL };
326 + struct reftable_record out = { .type = BLOCK_TYPE_OBJ };
327 int n, m;
328 uint8_t extra;
329
327 - reftable_record_from_obj(&rec, &in);
328 - test_copy(&rec);
329 - reftable_record_key(&rec, &key);
330 - n = reftable_record_encode(&rec, dest, GIT_SHA1_RAWSZ);
330 + test_copy(&in);
331 + reftable_record_key(&in, &key);
332 + n = reftable_record_encode(&in, dest, GIT_SHA1_RAWSZ);
333 EXPECT(n > 0);
332 - extra = reftable_record_val_type(&rec);
333 - reftable_record_from_obj(&rec_out, &out);
334 - m = reftable_record_decode(&rec_out, key, extra, dest,
334 + extra = reftable_record_val_type(&in);
335 + m = reftable_record_decode(&out, key, extra, dest,
336 GIT_SHA1_RAWSZ);
337 EXPECT(n == m);
338
338 - EXPECT(reftable_record_equal(&rec, &rec_out, GIT_SHA1_RAWSZ));
339 + EXPECT(reftable_record_equal(&in, &out, GIT_SHA1_RAWSZ));
340 strbuf_release(&key);
340 - reftable_record_release(&rec_out);
341 + reftable_record_release(&out);
342 }
343 }
344
345 static void test_reftable_index_record_roundtrip(void)
346 {
346 - struct reftable_index_record in = {
347 - .offset = 42,
348 - .last_key = STRBUF_INIT,
347 + struct reftable_record in = {
348 + .type = BLOCK_TYPE_INDEX,
349 + .u.idx = {
350 + .offset = 42,
351 + .last_key = STRBUF_INIT,
352 + },
353 };
354 uint8_t buffer[1024] = { 0 };
355 struct string_view dest = {
@@ -353,31 +357,30 @@ static void test_reftable_index_record_roundtrip(void)
357 .len = sizeof(buffer),
358 };
359 struct strbuf key = STRBUF_INIT;
356 - struct reftable_record rec = { NULL };
357 - struct reftable_index_record out = { .last_key = STRBUF_INIT };
358 - struct reftable_record out_rec = { NULL };
360 + struct reftable_record out = {
361 + .type = BLOCK_TYPE_INDEX,
362 + .u.idx = { .last_key = STRBUF_INIT },
363 + };
364 int n, m;
365 uint8_t extra;
366
362 - strbuf_addstr(&in.last_key, "refs/heads/master");
363 - reftable_record_from_index(&rec, &in);
364 - reftable_record_key(&rec, &key);
365 - test_copy(&rec);
367 + strbuf_addstr(&in.u.idx.last_key, "refs/heads/master");
368 + reftable_record_key(&in, &key);
369 + test_copy(&in);
370
367 - EXPECT(0 == strbuf_cmp(&key, &in.last_key));
368 - n = reftable_record_encode(&rec, dest, GIT_SHA1_RAWSZ);
371 + EXPECT(0 == strbuf_cmp(&key, &in.u.idx.last_key));
372 + n = reftable_record_encode(&in, dest, GIT_SHA1_RAWSZ);
373 EXPECT(n > 0);
374
371 - extra = reftable_record_val_type(&rec);
372 - reftable_record_from_index(&out_rec, &out);
373 - m = reftable_record_decode(&out_rec, key, extra, dest, GIT_SHA1_RAWSZ);
375 + extra = reftable_record_val_type(&in);
376 + m = reftable_record_decode(&out, key, extra, dest, GIT_SHA1_RAWSZ);
377 EXPECT(m == n);
378
376 - EXPECT(reftable_record_equal(&rec, &out_rec, GIT_SHA1_RAWSZ));
379 + EXPECT(reftable_record_equal(&in, &out, GIT_SHA1_RAWSZ));
380
378 - reftable_record_release(&out_rec);
381 + reftable_record_release(&out);
382 strbuf_release(&key);
380 - strbuf_release(&in.last_key);
383 + strbuf_release(&in.u.idx.last_key);
384 }
385
386 int record_test_main(int argc, const char *argv[])
reftable/writer.c
+23 -18
@@ -253,8 +253,10 @@ done:
253 int reftable_writer_add_ref(struct reftable_writer *w,
254 struct reftable_ref_record *ref)
255 {
256 - struct reftable_record rec = { NULL };
257 - struct reftable_ref_record copy = *ref;
256 + struct reftable_record rec = {
257 + .type = BLOCK_TYPE_REF,
258 + .u.ref = *ref,
259 + };
260 int err = 0;
261
262 if (ref->refname == NULL)
@@ -263,8 +265,7 @@ int reftable_writer_add_ref(struct reftable_writer *w,
265 ref->update_index > w->max_update_index)
266 return REFTABLE_API_ERROR;
267
266 - reftable_record_from_ref(&rec, &copy);
267 - copy.update_index -= w->min_update_index;
268 + rec.u.ref.update_index -= w->min_update_index;
269
270 err = writer_add_record(w, &rec);
271 if (err < 0)
@@ -303,7 +304,10 @@ int reftable_writer_add_refs(struct reftable_writer *w,
304 static int reftable_writer_add_log_verbatim(struct reftable_writer *w,
305 struct reftable_log_record *log)
306 {
306 - struct reftable_record rec = { NULL };
307 + struct reftable_record rec = {
308 + .type = BLOCK_TYPE_LOG,
309 + .u.log = *log,
310 + };
311 if (w->block_writer &&
312 block_writer_type(w->block_writer) == BLOCK_TYPE_REF) {
313 int err = writer_finish_public_section(w);
@@ -313,8 +317,6 @@ static int reftable_writer_add_log_verbatim(struct reftable_writer *w,
317
318 w->next -= w->pending_padding;
319 w->pending_padding = 0;
316 -
317 - reftable_record_from_log(&rec, log);
320 return writer_add_record(w, &rec);
321 }
322
@@ -395,8 +397,10 @@ static int writer_finish_section(struct reftable_writer *w)
397 w->index_len = 0;
398 w->index_cap = 0;
399 for (i = 0; i < idx_len; i++) {
398 - struct reftable_record rec = { NULL };
399 - reftable_record_from_index(&rec, idx + i);
400 + struct reftable_record rec = {
401 + .type = BLOCK_TYPE_INDEX,
402 + .u.idx = idx[i],
403 + };
404 if (block_writer_add(w->block_writer, &rec) == 0) {
405 continue;
406 }
@@ -464,17 +468,17 @@ static void write_object_record(void *void_arg, void *key)
468 {
469 struct write_record_arg *arg = void_arg;
470 struct obj_index_tree_node *entry = key;
467 - struct reftable_obj_record obj_rec = {
468 - .hash_prefix = (uint8_t *)entry->hash.buf,
469 - .hash_prefix_len = arg->w->stats.object_id_len,
470 - .offsets = entry->offsets,
471 - .offset_len = entry->offset_len,
472 - };
473 - struct reftable_record rec = { NULL };
471 + struct reftable_record
472 + rec = { .type = BLOCK_TYPE_OBJ,
473 + .u.obj = {
474 + .hash_prefix = (uint8_t *)entry->hash.buf,
475 + .hash_prefix_len = arg->w->stats.object_id_len,
476 + .offsets = entry->offsets,
477 + .offset_len = entry->offset_len,
478 + } };
479 if (arg->err < 0)
480 goto done;
481
477 - reftable_record_from_obj(&rec, &obj_rec);
482 arg->err = block_writer_add(arg->w->block_writer, &rec);
483 if (arg->err == 0)
484 goto done;
@@ -487,7 +491,8 @@ static void write_object_record(void *void_arg, void *key)
491 arg->err = block_writer_add(arg->w->block_writer, &rec);
492 if (arg->err == 0)
493 goto done;
490 - obj_rec.offset_len = 0;
494 +
495 + rec.u.obj.offset_len = 0;
496 arg->err = block_writer_add(arg->w->block_writer, &rec);
497
498 /* Should be able to write into a fresh block. */