Merge branch 'ps/reftable-reusable-iterator'

Code clean-up to make the reftable iterator closer to be reusable.

* ps/reftable-reusable-iterator:
  reftable/merged: adapt interface to allow reuse of iterators
  reftable/stack: provide convenience functions to create iterators
  reftable/reader: adapt interface to allow reuse of iterators
  reftable/generic: adapt interface to allow reuse of iterators
  reftable/generic: move seeking of records into the iterator
  reftable/merged: simplify indices for subiterators
  reftable/merged: split up initialization and seeking of records
  reftable/reader: set up the reader when initializing table iterator
  reftable/reader: inline `reader_seek_internal()`
  reftable/reader: separate concerns of table iter and reftable reader
  reftable/reader: unify indexed and linear seeking
  reftable/reader: avoid copying index iterator
  reftable/block: use `size_t` to track restart point index
This commit is contained in:
Junio C Hamano 2024-05-30 14:15:12 -07:00
commit 67ce50ba26
16 changed files with 381 additions and 364 deletions

View file

@ -16,7 +16,6 @@
#include "../reftable/reftable-record.h"
#include "../reftable/reftable-error.h"
#include "../reftable/reftable-iterator.h"
#include "../reftable/reftable-merged.h"
#include "../setup.h"
#include "../strmap.h"
#include "parse.h"
@ -502,7 +501,6 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
const char *prefix,
int flags)
{
struct reftable_merged_table *merged_table;
struct reftable_ref_iterator *iter;
int ret;
@ -522,9 +520,8 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
if (ret)
goto done;
merged_table = reftable_stack_merged_table(stack);
ret = reftable_merged_table_seek_ref(merged_table, &iter->iter, prefix);
reftable_stack_init_ref_iterator(stack, &iter->iter);
ret = reftable_iterator_seek_ref(&iter->iter, prefix);
if (ret)
goto done;
@ -1052,8 +1049,6 @@ static int transaction_update_cmp(const void *a, const void *b)
static int write_transaction_table(struct reftable_writer *writer, void *cb_data)
{
struct write_transaction_table_arg *arg = cb_data;
struct reftable_merged_table *mt =
reftable_stack_merged_table(arg->stack);
uint64_t ts = reftable_stack_next_update_index(arg->stack);
struct reftable_log_record *logs = NULL;
struct ident_split committer_ident = {0};
@ -1090,6 +1085,8 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
struct reftable_log_record log = {0};
struct reftable_iterator it = {0};
reftable_stack_init_log_iterator(arg->stack, &it);
/*
* When deleting refs we also delete all reflog entries
* with them. While it is not strictly required to
@ -1099,7 +1096,7 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
* Unfortunately, we have no better way than to delete
* all reflog entries one by one.
*/
ret = reftable_merged_table_seek_log(mt, &it, u->refname);
ret = reftable_iterator_seek_log(&it, u->refname);
while (ret == 0) {
struct reftable_log_record *tombstone;
@ -1317,7 +1314,6 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
{
struct write_copy_arg *arg = cb_data;
uint64_t deletion_ts, creation_ts;
struct reftable_merged_table *mt = reftable_stack_merged_table(arg->stack);
struct reftable_ref_record old_ref = {0}, refs[2] = {0};
struct reftable_log_record old_log = {0}, *logs = NULL;
struct reftable_iterator it = {0};
@ -1451,7 +1447,8 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
* copy over all log entries from the old reflog. Last but not least,
* when renaming we also have to delete all the old reflog entries.
*/
ret = reftable_merged_table_seek_log(mt, &it, arg->oldname);
reftable_stack_init_log_iterator(arg->stack, &it);
ret = reftable_iterator_seek_log(&it, arg->oldname);
if (ret < 0)
goto done;
@ -1657,7 +1654,6 @@ static struct ref_iterator_vtable reftable_reflog_iterator_vtable = {
static struct reftable_reflog_iterator *reflog_iterator_for_stack(struct reftable_ref_store *refs,
struct reftable_stack *stack)
{
struct reftable_merged_table *merged_table;
struct reftable_reflog_iterator *iter;
int ret;
@ -1674,9 +1670,8 @@ static struct reftable_reflog_iterator *reflog_iterator_for_stack(struct reftabl
if (ret < 0)
goto done;
merged_table = reftable_stack_merged_table(stack);
ret = reftable_merged_table_seek_log(merged_table, &iter->iter, "");
reftable_stack_init_log_iterator(stack, &iter->iter);
ret = reftable_iterator_seek_log(&iter->iter, "");
if (ret < 0)
goto done;
@ -1734,7 +1729,6 @@ static int reftable_be_for_each_reflog_ent_reverse(struct ref_store *ref_store,
struct reftable_ref_store *refs =
reftable_be_downcast(ref_store, REF_STORE_READ, "for_each_reflog_ent_reverse");
struct reftable_stack *stack = stack_for(refs, refname, &refname);
struct reftable_merged_table *mt = NULL;
struct reftable_log_record log = {0};
struct reftable_iterator it = {0};
int ret;
@ -1742,8 +1736,8 @@ static int reftable_be_for_each_reflog_ent_reverse(struct ref_store *ref_store,
if (refs->err < 0)
return refs->err;
mt = reftable_stack_merged_table(stack);
ret = reftable_merged_table_seek_log(mt, &it, refname);
reftable_stack_init_log_iterator(stack, &it);
ret = reftable_iterator_seek_log(&it, refname);
while (!ret) {
ret = reftable_iterator_next_log(&it, &log);
if (ret < 0)
@ -1771,7 +1765,6 @@ static int reftable_be_for_each_reflog_ent(struct ref_store *ref_store,
struct reftable_ref_store *refs =
reftable_be_downcast(ref_store, REF_STORE_READ, "for_each_reflog_ent");
struct reftable_stack *stack = stack_for(refs, refname, &refname);
struct reftable_merged_table *mt = NULL;
struct reftable_log_record *logs = NULL;
struct reftable_iterator it = {0};
size_t logs_alloc = 0, logs_nr = 0, i;
@ -1780,8 +1773,8 @@ static int reftable_be_for_each_reflog_ent(struct ref_store *ref_store,
if (refs->err < 0)
return refs->err;
mt = reftable_stack_merged_table(stack);
ret = reftable_merged_table_seek_log(mt, &it, refname);
reftable_stack_init_log_iterator(stack, &it);
ret = reftable_iterator_seek_log(&it, refname);
while (!ret) {
struct reftable_log_record log = {0};
@ -1818,7 +1811,6 @@ static int reftable_be_reflog_exists(struct ref_store *ref_store,
struct reftable_ref_store *refs =
reftable_be_downcast(ref_store, REF_STORE_READ, "reflog_exists");
struct reftable_stack *stack = stack_for(refs, refname, &refname);
struct reftable_merged_table *mt = reftable_stack_merged_table(stack);
struct reftable_log_record log = {0};
struct reftable_iterator it = {0};
int ret;
@ -1831,7 +1823,8 @@ static int reftable_be_reflog_exists(struct ref_store *ref_store,
if (ret < 0)
goto done;
ret = reftable_merged_table_seek_log(mt, &it, refname);
reftable_stack_init_log_iterator(stack, &it);
ret = reftable_iterator_seek_log(&it, refname);
if (ret < 0)
goto done;
@ -1929,8 +1922,6 @@ struct write_reflog_delete_arg {
static int write_reflog_delete_table(struct reftable_writer *writer, void *cb_data)
{
struct write_reflog_delete_arg *arg = cb_data;
struct reftable_merged_table *mt =
reftable_stack_merged_table(arg->stack);
struct reftable_log_record log = {0}, tombstone = {0};
struct reftable_iterator it = {0};
uint64_t ts = reftable_stack_next_update_index(arg->stack);
@ -1938,12 +1929,14 @@ static int write_reflog_delete_table(struct reftable_writer *writer, void *cb_da
reftable_writer_set_limits(writer, ts, ts);
reftable_stack_init_log_iterator(arg->stack, &it);
/*
* In order to delete a table we need to delete all reflog entries one
* by one. This is inefficient, but the reftable format does not have a
* better marker right now.
*/
ret = reftable_merged_table_seek_log(mt, &it, arg->refname);
ret = reftable_iterator_seek_log(&it, arg->refname);
while (ret == 0) {
ret = reftable_iterator_next_log(&it, &log);
if (ret < 0)
@ -2079,7 +2072,6 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store,
struct reftable_ref_store *refs =
reftable_be_downcast(ref_store, REF_STORE_WRITE, "reflog_expire");
struct reftable_stack *stack = stack_for(refs, refname, &refname);
struct reftable_merged_table *mt = reftable_stack_merged_table(stack);
struct reftable_log_record *logs = NULL;
struct reftable_log_record *rewritten = NULL;
struct reftable_ref_record ref_record = {0};
@ -2098,7 +2090,9 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store,
if (ret < 0)
goto done;
ret = reftable_merged_table_seek_log(mt, &it, refname);
reftable_stack_init_log_iterator(stack, &it);
ret = reftable_iterator_seek_log(&it, refname);
if (ret < 0)
goto done;

View file

@ -326,9 +326,9 @@ int block_reader_first_key(const struct block_reader *br, struct strbuf *key)
return 0;
}
static uint32_t block_reader_restart_offset(const struct block_reader *br, int i)
static uint32_t block_reader_restart_offset(const struct block_reader *br, size_t idx)
{
return get_be24(br->restart_bytes + 3 * i);
return get_be24(br->restart_bytes + 3 * idx);
}
void block_iter_seek_start(struct block_iter *it, const struct block_reader *br)

View file

@ -12,32 +12,66 @@ license that can be found in the LICENSE file or at
#include "reftable-iterator.h"
#include "reftable-generic.h"
int reftable_table_seek_ref(struct reftable_table *tab,
struct reftable_iterator *it, const char *name)
void table_init_iter(struct reftable_table *tab,
struct reftable_iterator *it,
uint8_t typ)
{
struct reftable_record rec = { .type = BLOCK_TYPE_REF,
.u.ref = {
.refname = (char *)name,
} };
return tab->ops->seek_record(tab->table_arg, it, &rec);
tab->ops->init_iter(tab->table_arg, it, typ);
}
int reftable_table_seek_log(struct reftable_table *tab,
struct reftable_iterator *it, const char *name)
void reftable_table_init_ref_iter(struct reftable_table *tab,
struct reftable_iterator *it)
{
struct reftable_record rec = { .type = BLOCK_TYPE_LOG,
.u.log = {
.refname = (char *)name,
.update_index = ~((uint64_t)0),
} };
return tab->ops->seek_record(tab->table_arg, it, &rec);
table_init_iter(tab, it, BLOCK_TYPE_REF);
}
void reftable_table_init_log_iter(struct reftable_table *tab,
struct reftable_iterator *it)
{
table_init_iter(tab, it, BLOCK_TYPE_LOG);
}
int reftable_iterator_seek_ref(struct reftable_iterator *it,
const char *name)
{
struct reftable_record want = {
.type = BLOCK_TYPE_REF,
.u.ref = {
.refname = (char *)name,
},
};
return it->ops->seek(it->iter_arg, &want);
}
int reftable_iterator_seek_log_at(struct reftable_iterator *it,
const char *name, uint64_t update_index)
{
struct reftable_record want = {
.type = BLOCK_TYPE_LOG,
.u.log = {
.refname = (char *)name,
.update_index = update_index,
},
};
return it->ops->seek(it->iter_arg, &want);
}
int reftable_iterator_seek_log(struct reftable_iterator *it,
const char *name)
{
return reftable_iterator_seek_log_at(it, name, ~((uint64_t) 0));
}
int reftable_table_read_ref(struct reftable_table *tab, const char *name,
struct reftable_ref_record *ref)
{
struct reftable_iterator it = { NULL };
int err = reftable_table_seek_ref(tab, &it, name);
int err;
reftable_table_init_ref_iter(tab, &it);
err = reftable_iterator_seek_ref(&it, name);
if (err)
goto done;
@ -62,10 +96,13 @@ int reftable_table_print(struct reftable_table *tab) {
struct reftable_ref_record ref = { NULL };
struct reftable_log_record log = { NULL };
uint32_t hash_id = reftable_table_hash_id(tab);
int err = reftable_table_seek_ref(tab, &it, "");
if (err < 0) {
int err;
reftable_table_init_ref_iter(tab, &it);
err = reftable_iterator_seek_ref(&it, "");
if (err < 0)
return err;
}
while (1) {
err = reftable_iterator_next_ref(&it, &ref);
@ -80,10 +117,12 @@ int reftable_table_print(struct reftable_table *tab) {
reftable_iterator_destroy(&it);
reftable_ref_record_release(&ref);
err = reftable_table_seek_log(tab, &it, "");
if (err < 0) {
reftable_table_init_log_iter(tab, &it);
err = reftable_iterator_seek_log(&it, "");
if (err < 0)
return err;
}
while (1) {
err = reftable_iterator_next_log(&it, &log);
if (err > 0) {
@ -152,11 +191,21 @@ int reftable_iterator_next_log(struct reftable_iterator *it,
return err;
}
int iterator_seek(struct reftable_iterator *it, struct reftable_record *want)
{
return it->ops->seek(it->iter_arg, want);
}
int iterator_next(struct reftable_iterator *it, struct reftable_record *rec)
{
return it->ops->next(it->iter_arg, rec);
}
static int empty_iterator_seek(void *arg, struct reftable_record *want)
{
return 0;
}
static int empty_iterator_next(void *arg, struct reftable_record *rec)
{
return 1;
@ -167,6 +216,7 @@ static void empty_iterator_close(void *arg)
}
static struct reftable_iterator_vtable empty_vtable = {
.seek = &empty_iterator_seek,
.next = &empty_iterator_next,
.close = &empty_iterator_close,
};

View file

@ -14,19 +14,24 @@ license that can be found in the LICENSE file or at
/* generic interface to reftables */
struct reftable_table_vtable {
int (*seek_record)(void *tab, struct reftable_iterator *it,
struct reftable_record *);
void (*init_iter)(void *tab, struct reftable_iterator *it, uint8_t typ);
uint32_t (*hash_id)(void *tab);
uint64_t (*min_update_index)(void *tab);
uint64_t (*max_update_index)(void *tab);
};
void table_init_iter(struct reftable_table *tab,
struct reftable_iterator *it,
uint8_t typ);
struct reftable_iterator_vtable {
int (*seek)(void *iter_arg, struct reftable_record *want);
int (*next)(void *iter_arg, struct reftable_record *rec);
void (*close)(void *iter_arg);
};
void iterator_set_empty(struct reftable_iterator *it);
int iterator_seek(struct reftable_iterator *it, struct reftable_record *want);
int iterator_next(struct reftable_iterator *it, struct reftable_record *rec);
#endif

View file

@ -23,6 +23,13 @@ static void filtering_ref_iterator_close(void *iter_arg)
reftable_iterator_destroy(&fri->it);
}
static int filtering_ref_iterator_seek(void *iter_arg,
struct reftable_record *want)
{
struct filtering_ref_iterator *fri = iter_arg;
return iterator_seek(&fri->it, want);
}
static int filtering_ref_iterator_next(void *iter_arg,
struct reftable_record *rec)
{
@ -38,11 +45,11 @@ static int filtering_ref_iterator_next(void *iter_arg,
if (fri->double_check) {
struct reftable_iterator it = { NULL };
err = reftable_table_seek_ref(&fri->tab, &it,
ref->refname);
if (err == 0) {
reftable_table_init_ref_iter(&fri->tab, &it);
err = reftable_iterator_seek_ref(&it, ref->refname);
if (err == 0)
err = reftable_iterator_next_ref(&it, ref);
}
reftable_iterator_destroy(&it);
@ -73,6 +80,7 @@ static int filtering_ref_iterator_next(void *iter_arg,
}
static struct reftable_iterator_vtable filtering_ref_iterator_vtable = {
.seek = &filtering_ref_iterator_seek,
.next = &filtering_ref_iterator_next,
.close = &filtering_ref_iterator_close,
};
@ -119,6 +127,12 @@ static int indexed_table_ref_iter_next_block(struct indexed_table_ref_iter *it)
return 0;
}
static int indexed_table_ref_iter_seek(void *p, struct reftable_record *want)
{
BUG("seeking indexed table is not supported");
return -1;
}
static int indexed_table_ref_iter_next(void *p, struct reftable_record *rec)
{
struct indexed_table_ref_iter *it = p;
@ -175,6 +189,7 @@ int new_indexed_table_ref_iter(struct indexed_table_ref_iter **dest,
}
static struct reftable_iterator_vtable indexed_table_ref_iter_vtable = {
.seek = &indexed_table_ref_iter_seek,
.next = &indexed_table_ref_iter_next,
.close = &indexed_table_ref_iter_close,
};

View file

@ -25,34 +25,25 @@ struct merged_subiter {
struct merged_iter {
struct merged_subiter *subiters;
struct merged_iter_pqueue pq;
uint32_t hash_id;
size_t stack_len;
uint8_t typ;
int suppress_deletions;
ssize_t advance_index;
};
static int merged_iter_init(struct merged_iter *mi)
static void merged_iter_init(struct merged_iter *mi,
struct reftable_merged_table *mt,
uint8_t typ)
{
for (size_t i = 0; i < mi->stack_len; i++) {
struct pq_entry e = {
.index = i,
.rec = &mi->subiters[i].rec,
};
int err;
memset(mi, 0, sizeof(*mi));
mi->advance_index = -1;
mi->suppress_deletions = mt->suppress_deletions;
reftable_record_init(&mi->subiters[i].rec, mi->typ);
err = iterator_next(&mi->subiters[i].iter,
&mi->subiters[i].rec);
if (err < 0)
return err;
if (err > 0)
continue;
merged_iter_pqueue_add(&mi->pq, &e);
REFTABLE_CALLOC_ARRAY(mi->subiters, mt->stack_len);
for (size_t i = 0; i < mt->stack_len; i++) {
reftable_record_init(&mi->subiters[i].rec, typ);
table_init_iter(&mt->stack[i], &mi->subiters[i].iter, typ);
}
return 0;
mi->stack_len = mt->stack_len;
}
static void merged_iter_close(void *p)
@ -83,6 +74,27 @@ static int merged_iter_advance_subiter(struct merged_iter *mi, size_t idx)
return 0;
}
static int merged_iter_seek(struct merged_iter *mi, struct reftable_record *want)
{
int err;
mi->advance_index = -1;
for (size_t i = 0; i < mi->stack_len; i++) {
err = iterator_seek(&mi->subiters[i].iter, want);
if (err < 0)
return err;
if (err > 0)
continue;
err = merged_iter_advance_subiter(mi, i);
if (err < 0)
return err;
}
return 0;
}
static int merged_iter_next_entry(struct merged_iter *mi,
struct reftable_record *rec)
{
@ -148,6 +160,11 @@ static int merged_iter_next_entry(struct merged_iter *mi,
return 0;
}
static int merged_iter_seek_void(void *it, struct reftable_record *want)
{
return merged_iter_seek(it, want);
}
static int merged_iter_next_void(void *p, struct reftable_record *rec)
{
struct merged_iter *mi = p;
@ -162,6 +179,7 @@ static int merged_iter_next_void(void *p, struct reftable_record *rec)
}
static struct reftable_iterator_vtable merged_iter_vtable = {
.seek = merged_iter_seek_void,
.next = &merged_iter_next_void,
.close = &merged_iter_close,
};
@ -235,81 +253,13 @@ reftable_merged_table_min_update_index(struct reftable_merged_table *mt)
return mt->min;
}
static int reftable_table_seek_record(struct reftable_table *tab,
struct reftable_iterator *it,
struct reftable_record *rec)
void merged_table_init_iter(struct reftable_merged_table *mt,
struct reftable_iterator *it,
uint8_t typ)
{
return tab->ops->seek_record(tab->table_arg, it, rec);
}
static int merged_table_seek_record(struct reftable_merged_table *mt,
struct reftable_iterator *it,
struct reftable_record *rec)
{
struct merged_iter merged = {
.typ = reftable_record_type(rec),
.hash_id = mt->hash_id,
.suppress_deletions = mt->suppress_deletions,
.advance_index = -1,
};
struct merged_iter *p;
int err;
REFTABLE_CALLOC_ARRAY(merged.subiters, mt->stack_len);
for (size_t i = 0; i < mt->stack_len; i++) {
err = reftable_table_seek_record(&mt->stack[i],
&merged.subiters[merged.stack_len].iter, rec);
if (err < 0)
goto out;
if (!err)
merged.stack_len++;
}
err = merged_iter_init(&merged);
if (err < 0)
goto out;
p = reftable_malloc(sizeof(struct merged_iter));
*p = merged;
iterator_from_merged_iter(it, p);
out:
if (err < 0)
merged_iter_close(&merged);
return err;
}
int reftable_merged_table_seek_ref(struct reftable_merged_table *mt,
struct reftable_iterator *it,
const char *name)
{
struct reftable_record rec = {
.type = BLOCK_TYPE_REF,
.u.ref = {
.refname = (char *)name,
},
};
return merged_table_seek_record(mt, it, &rec);
}
int reftable_merged_table_seek_log_at(struct reftable_merged_table *mt,
struct reftable_iterator *it,
const char *name, uint64_t update_index)
{
struct reftable_record rec = { .type = BLOCK_TYPE_LOG,
.u.log = {
.refname = (char *)name,
.update_index = update_index,
} };
return merged_table_seek_record(mt, it, &rec);
}
int reftable_merged_table_seek_log(struct reftable_merged_table *mt,
struct reftable_iterator *it,
const char *name)
{
uint64_t max = ~((uint64_t)0);
return reftable_merged_table_seek_log_at(mt, it, name, max);
struct merged_iter *mi = reftable_malloc(sizeof(*mi));
merged_iter_init(mi, mt, typ);
iterator_from_merged_iter(it, mi);
}
uint32_t reftable_merged_table_hash_id(struct reftable_merged_table *mt)
@ -317,11 +267,11 @@ uint32_t reftable_merged_table_hash_id(struct reftable_merged_table *mt)
return mt->hash_id;
}
static int reftable_merged_table_seek_void(void *tab,
struct reftable_iterator *it,
struct reftable_record *rec)
static void reftable_merged_table_init_iter_void(void *tab,
struct reftable_iterator *it,
uint8_t typ)
{
return merged_table_seek_record(tab, it, rec);
merged_table_init_iter(tab, it, typ);
}
static uint32_t reftable_merged_table_hash_id_void(void *tab)
@ -340,7 +290,7 @@ static uint64_t reftable_merged_table_max_update_index_void(void *tab)
}
static struct reftable_table_vtable merged_table_vtable = {
.seek_record = reftable_merged_table_seek_void,
.init_iter = reftable_merged_table_init_iter_void,
.hash_id = reftable_merged_table_hash_id_void,
.min_update_index = reftable_merged_table_min_update_index_void,
.max_update_index = reftable_merged_table_max_update_index_void,

View file

@ -26,4 +26,10 @@ struct reftable_merged_table {
void merged_table_release(struct reftable_merged_table *mt);
struct reftable_iterator;
void merged_table_init_iter(struct reftable_merged_table *mt,
struct reftable_iterator *it,
uint8_t typ);
#endif

View file

@ -12,6 +12,7 @@ license that can be found in the LICENSE file or at
#include "basics.h"
#include "blocksource.h"
#include "constants.h"
#include "reader.h"
#include "record.h"
#include "test_framework.h"
@ -145,7 +146,10 @@ static void test_merged_between(void)
int i;
struct reftable_ref_record ref = { NULL };
struct reftable_iterator it = { NULL };
int err = reftable_merged_table_seek_ref(mt, &it, "a");
int err;
merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
err = reftable_iterator_seek_ref(&it, "a");
EXPECT_ERR(err);
err = reftable_iterator_next_ref(&it, &ref);
@ -217,14 +221,15 @@ static void test_merged(void)
struct reftable_reader **readers = NULL;
struct reftable_merged_table *mt =
merged_table_from_records(refs, &bs, &readers, sizes, bufs, 3);
struct reftable_iterator it = { NULL };
int err = reftable_merged_table_seek_ref(mt, &it, "a");
int err;
struct reftable_ref_record *out = NULL;
size_t len = 0;
size_t cap = 0;
int i = 0;
merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
err = reftable_iterator_seek_ref(&it, "a");
EXPECT_ERR(err);
EXPECT(reftable_merged_table_hash_id(mt) == GIT_SHA1_FORMAT_ID);
EXPECT(reftable_merged_table_min_update_index(mt) == 1);
@ -348,14 +353,15 @@ static void test_merged_logs(void)
struct reftable_reader **readers = NULL;
struct reftable_merged_table *mt = merged_table_from_log_records(
logs, &bs, &readers, sizes, bufs, 3);
struct reftable_iterator it = { NULL };
int err = reftable_merged_table_seek_log(mt, &it, "a");
int err;
struct reftable_log_record *out = NULL;
size_t len = 0;
size_t cap = 0;
int i = 0;
merged_table_init_iter(mt, &it, BLOCK_TYPE_LOG);
err = reftable_iterator_seek_log(&it, "a");
EXPECT_ERR(err);
EXPECT(reftable_merged_table_hash_id(mt) == GIT_SHA1_FORMAT_ID);
EXPECT(reftable_merged_table_min_update_index(mt) == 1);
@ -377,7 +383,8 @@ static void test_merged_logs(void)
GIT_SHA1_RAWSZ));
}
err = reftable_merged_table_seek_log_at(mt, &it, "a", 2);
merged_table_init_iter(mt, &it, BLOCK_TYPE_LOG);
err = reftable_iterator_seek_log_at(&it, "a", 2);
EXPECT_ERR(err);
reftable_log_record_release(&out[0]);
err = reftable_iterator_next_log(&it, &out[0]);

View file

@ -224,8 +224,14 @@ struct table_iter {
struct block_iter bi;
int is_finished;
};
#define TABLE_ITER_INIT { \
.bi = BLOCK_ITER_INIT \
static int table_iter_init(struct table_iter *ti, struct reftable_reader *r)
{
struct block_iter bi = BLOCK_ITER_INIT;
memset(ti, 0, sizeof(*ti));
ti->r = r;
ti->bi = bi;
return 0;
}
static int table_iter_next_in_block(struct table_iter *ti,
@ -363,50 +369,23 @@ static int table_iter_next(struct table_iter *ti, struct reftable_record *rec)
}
}
static int table_iter_next_void(void *ti, struct reftable_record *rec)
{
return table_iter_next(ti, rec);
}
static void table_iter_close_void(void *ti)
{
table_iter_close(ti);
}
static struct reftable_iterator_vtable table_iter_vtable = {
.next = &table_iter_next_void,
.close = &table_iter_close_void,
};
static void iterator_from_table_iter(struct reftable_iterator *it,
struct table_iter *ti)
{
assert(!it->ops);
it->iter_arg = ti;
it->ops = &table_iter_vtable;
}
static int reader_table_iter_at(struct reftable_reader *r,
struct table_iter *ti, uint64_t off,
uint8_t typ)
static int table_iter_seek_to(struct table_iter *ti, uint64_t off, uint8_t typ)
{
int err;
err = reader_init_block_reader(r, &ti->br, off, typ);
err = reader_init_block_reader(ti->r, &ti->br, off, typ);
if (err != 0)
return err;
ti->r = r;
ti->typ = block_reader_type(&ti->br);
ti->block_off = off;
block_iter_seek_start(&ti->bi, &ti->br);
return 0;
}
static int reader_start(struct reftable_reader *r, struct table_iter *ti,
uint8_t typ, int index)
static int table_iter_seek_start(struct table_iter *ti, uint8_t typ, int index)
{
struct reftable_reader_offsets *offs = reader_offsets_for(r, typ);
struct reftable_reader_offsets *offs = reader_offsets_for(ti->r, typ);
uint64_t off = offs->offset;
if (index) {
off = offs->index_offset;
@ -416,16 +395,16 @@ static int reader_start(struct reftable_reader *r, struct table_iter *ti,
typ = BLOCK_TYPE_INDEX;
}
return reader_table_iter_at(r, ti, off, typ);
return table_iter_seek_to(ti, off, typ);
}
static int reader_seek_linear(struct table_iter *ti,
struct reftable_record *want)
static int table_iter_seek_linear(struct table_iter *ti,
struct reftable_record *want)
{
struct strbuf want_key = STRBUF_INIT;
struct strbuf got_key = STRBUF_INIT;
struct reftable_record rec;
int err = -1;
int err;
reftable_record_init(&rec, reftable_record_type(want));
reftable_record_key(want, &want_key);
@ -499,9 +478,8 @@ static int reader_seek_linear(struct table_iter *ti,
return err;
}
static int reader_seek_indexed(struct reftable_reader *r,
struct reftable_iterator *it,
struct reftable_record *rec)
static int table_iter_seek_indexed(struct table_iter *ti,
struct reftable_record *rec)
{
struct reftable_record want_index = {
.type = BLOCK_TYPE_INDEX, .u.idx = { .last_key = STRBUF_INIT }
@ -510,15 +488,9 @@ static int reader_seek_indexed(struct reftable_reader *r,
.type = BLOCK_TYPE_INDEX,
.u.idx = { .last_key = STRBUF_INIT },
};
struct table_iter index_iter = TABLE_ITER_INIT;
struct table_iter empty = TABLE_ITER_INIT;
struct table_iter next = TABLE_ITER_INIT;
int err = 0;
int err;
reftable_record_key(rec, &want_index.u.idx.last_key);
err = reader_start(r, &index_iter, reftable_record_type(rec), 1);
if (err < 0)
goto done;
/*
* The index may consist of multiple levels, where each level may have
@ -526,7 +498,7 @@ static int reader_seek_indexed(struct reftable_reader *r,
* highest layer that identifies the relevant index block as well as
* the record inside that block that corresponds to our wanted key.
*/
err = reader_seek_linear(&index_iter, &want_index);
err = table_iter_seek_linear(ti, &want_index);
if (err < 0)
goto done;
@ -552,123 +524,113 @@ static int reader_seek_indexed(struct reftable_reader *r,
* all levels of the index only to find out that the key does
* not exist.
*/
err = table_iter_next(&index_iter, &index_result);
err = table_iter_next(ti, &index_result);
if (err != 0)
goto done;
err = reader_table_iter_at(r, &next, index_result.u.idx.offset,
0);
err = table_iter_seek_to(ti, index_result.u.idx.offset, 0);
if (err != 0)
goto done;
err = block_iter_seek_key(&next.bi, &next.br, &want_index.u.idx.last_key);
err = block_iter_seek_key(&ti->bi, &ti->br, &want_index.u.idx.last_key);
if (err < 0)
goto done;
if (next.typ == reftable_record_type(rec)) {
if (ti->typ == reftable_record_type(rec)) {
err = 0;
break;
}
if (next.typ != BLOCK_TYPE_INDEX) {
if (ti->typ != BLOCK_TYPE_INDEX) {
err = REFTABLE_FORMAT_ERROR;
break;
goto done;
}
table_iter_close(&index_iter);
index_iter = next;
next = empty;
}
if (err == 0) {
struct table_iter *malloced = reftable_calloc(1, sizeof(*malloced));
*malloced = next;
next = empty;
iterator_from_table_iter(it, malloced);
}
done:
table_iter_close(&next);
table_iter_close(&index_iter);
reftable_record_release(&want_index);
reftable_record_release(&index_result);
return err;
}
static int reader_seek_internal(struct reftable_reader *r,
struct reftable_iterator *it,
struct reftable_record *rec)
static int table_iter_seek(struct table_iter *ti,
struct reftable_record *want)
{
struct reftable_reader_offsets *offs =
reader_offsets_for(r, reftable_record_type(rec));
uint64_t idx = offs->index_offset;
struct table_iter ti = TABLE_ITER_INIT, *p;
uint8_t typ = reftable_record_type(want);
struct reftable_reader_offsets *offs = reader_offsets_for(ti->r, typ);
int err;
if (idx > 0)
return reader_seek_indexed(r, it, rec);
err = reader_start(r, &ti, reftable_record_type(rec), 0);
err = table_iter_seek_start(ti, reftable_record_type(want),
!!offs->index_offset);
if (err < 0)
goto out;
err = reader_seek_linear(&ti, rec);
if (err < 0)
if (offs->index_offset)
err = table_iter_seek_indexed(ti, want);
else
err = table_iter_seek_linear(ti, want);
if (err)
goto out;
REFTABLE_ALLOC_ARRAY(p, 1);
*p = ti;
iterator_from_table_iter(it, p);
out:
if (err)
table_iter_close(&ti);
return err;
}
static int reader_seek(struct reftable_reader *r, struct reftable_iterator *it,
struct reftable_record *rec)
static int table_iter_seek_void(void *ti, struct reftable_record *want)
{
uint8_t typ = reftable_record_type(rec);
return table_iter_seek(ti, want);
}
static int table_iter_next_void(void *ti, struct reftable_record *rec)
{
return table_iter_next(ti, rec);
}
static void table_iter_close_void(void *ti)
{
table_iter_close(ti);
}
static struct reftable_iterator_vtable table_iter_vtable = {
.seek = &table_iter_seek_void,
.next = &table_iter_next_void,
.close = &table_iter_close_void,
};
static void iterator_from_table_iter(struct reftable_iterator *it,
struct table_iter *ti)
{
assert(!it->ops);
it->iter_arg = ti;
it->ops = &table_iter_vtable;
}
static void reader_init_iter(struct reftable_reader *r,
struct reftable_iterator *it,
uint8_t typ)
{
struct reftable_reader_offsets *offs = reader_offsets_for(r, typ);
if (!offs->is_present) {
if (offs->is_present) {
struct table_iter *ti;
REFTABLE_ALLOC_ARRAY(ti, 1);
table_iter_init(ti, r);
iterator_from_table_iter(it, ti);
} else {
iterator_set_empty(it);
return 0;
}
return reader_seek_internal(r, it, rec);
}
int reftable_reader_seek_ref(struct reftable_reader *r,
struct reftable_iterator *it, const char *name)
void reftable_reader_init_ref_iterator(struct reftable_reader *r,
struct reftable_iterator *it)
{
struct reftable_record rec = {
.type = BLOCK_TYPE_REF,
.u.ref = {
.refname = (char *)name,
},
};
return reader_seek(r, it, &rec);
reader_init_iter(r, it, BLOCK_TYPE_REF);
}
int reftable_reader_seek_log_at(struct reftable_reader *r,
struct reftable_iterator *it, const char *name,
uint64_t update_index)
void reftable_reader_init_log_iterator(struct reftable_reader *r,
struct reftable_iterator *it)
{
struct reftable_record rec = { .type = BLOCK_TYPE_LOG,
.u.log = {
.refname = (char *)name,
.update_index = update_index,
} };
return reader_seek(r, it, &rec);
}
int reftable_reader_seek_log(struct reftable_reader *r,
struct reftable_iterator *it, const char *name)
{
uint64_t max = ~((uint64_t)0);
return reftable_reader_seek_log_at(r, it, name, max);
reader_init_iter(r, it, BLOCK_TYPE_LOG);
}
void reader_close(struct reftable_reader *r)
@ -719,7 +681,8 @@ static int reftable_reader_refs_for_indexed(struct reftable_reader *r,
struct indexed_table_ref_iter *itr = NULL;
/* Look through the reverse index. */
err = reader_seek(r, &oit, &want);
reader_init_iter(r, &oit, BLOCK_TYPE_OBJ);
err = iterator_seek(&oit, &want);
if (err != 0)
goto done;
@ -754,15 +717,15 @@ static int reftable_reader_refs_for_unindexed(struct reftable_reader *r,
struct reftable_iterator *it,
uint8_t *oid)
{
struct table_iter ti_empty = TABLE_ITER_INIT;
struct table_iter *ti = reftable_calloc(1, sizeof(*ti));
struct table_iter *ti;
struct filtering_ref_iterator *filter = NULL;
struct filtering_ref_iterator empty = FILTERING_REF_ITERATOR_INIT;
int oid_len = hash_size(r->hash_id);
int err;
*ti = ti_empty;
err = reader_start(r, ti, BLOCK_TYPE_REF, 0);
REFTABLE_ALLOC_ARRAY(ti, 1);
table_iter_init(ti, r);
err = table_iter_seek_start(ti, BLOCK_TYPE_REF, 0);
if (err < 0) {
reftable_free(ti);
return err;
@ -800,10 +763,11 @@ uint64_t reftable_reader_min_update_index(struct reftable_reader *r)
/* generic table interface. */
static int reftable_reader_seek_void(void *tab, struct reftable_iterator *it,
struct reftable_record *rec)
static void reftable_reader_init_iter_void(void *tab,
struct reftable_iterator *it,
uint8_t typ)
{
return reader_seek(tab, it, rec);
reader_init_iter(tab, it, typ);
}
static uint32_t reftable_reader_hash_id_void(void *tab)
@ -822,7 +786,7 @@ static uint64_t reftable_reader_max_update_index_void(void *tab)
}
static struct reftable_table_vtable reader_vtable = {
.seek_record = reftable_reader_seek_void,
.init_iter = reftable_reader_init_iter_void,
.hash_id = reftable_reader_hash_id_void,
.min_update_index = reftable_reader_min_update_index_void,
.max_update_index = reftable_reader_max_update_index_void,
@ -877,8 +841,8 @@ int reftable_reader_print_blocks(const char *tablename)
},
};
struct reftable_block_source src = { 0 };
struct table_iter ti = TABLE_ITER_INIT;
struct reftable_reader *r = NULL;
struct table_iter ti = { 0 };
size_t i;
int err;
@ -890,11 +854,13 @@ int reftable_reader_print_blocks(const char *tablename)
if (err < 0)
goto done;
table_iter_init(&ti, r);
printf("header:\n");
printf(" block_size: %d\n", r->block_size);
for (i = 0; i < ARRAY_SIZE(sections); i++) {
err = reader_start(r, &ti, sections[i].type, 0);
err = table_iter_seek_start(&ti, sections[i].type, 0);
if (err < 0)
goto done;
if (err > 0)

View file

@ -239,7 +239,9 @@ static void test_log_write_read(void)
err = init_reader(&rd, &source, "file.log");
EXPECT_ERR(err);
err = reftable_reader_seek_ref(&rd, &it, names[N - 1]);
reftable_reader_init_ref_iterator(&rd, &it);
err = reftable_iterator_seek_ref(&it, names[N - 1]);
EXPECT_ERR(err);
err = reftable_iterator_next_ref(&it, &ref);
@ -252,7 +254,9 @@ static void test_log_write_read(void)
reftable_iterator_destroy(&it);
reftable_ref_record_release(&ref);
err = reftable_reader_seek_log(&rd, &it, "");
reftable_reader_init_log_iterator(&rd, &it);
err = reftable_iterator_seek_log(&it, "");
EXPECT_ERR(err);
i = 0;
@ -330,7 +334,8 @@ static void test_log_zlib_corruption(void)
err = init_reader(&rd, &source, "file.log");
EXPECT_ERR(err);
err = reftable_reader_seek_log(&rd, &it, "refname");
reftable_reader_init_log_iterator(&rd, &it);
err = reftable_iterator_seek_log(&it, "refname");
EXPECT(err == REFTABLE_ZLIB_ERROR);
reftable_iterator_destroy(&it);
@ -358,7 +363,8 @@ static void test_table_read_write_sequential(void)
err = init_reader(&rd, &source, "file.ref");
EXPECT_ERR(err);
err = reftable_reader_seek_ref(&rd, &it, "");
reftable_reader_init_ref_iterator(&rd, &it);
err = reftable_iterator_seek_ref(&it, "");
EXPECT_ERR(err);
while (1) {
@ -412,7 +418,8 @@ static void test_table_read_api(void)
err = init_reader(&rd, &source, "file.ref");
EXPECT_ERR(err);
err = reftable_reader_seek_ref(&rd, &it, names[0]);
reftable_reader_init_ref_iterator(&rd, &it);
err = reftable_iterator_seek_ref(&it, names[0]);
EXPECT_ERR(err);
err = reftable_iterator_next_log(&it, &log);
@ -457,7 +464,8 @@ static void test_table_read_write_seek(int index, int hash_id)
}
for (i = 1; i < N; i++) {
int err = reftable_reader_seek_ref(&rd, &it, names[i]);
reftable_reader_init_ref_iterator(&rd, &it);
err = reftable_iterator_seek_ref(&it, names[i]);
EXPECT_ERR(err);
err = reftable_iterator_next_ref(&it, &ref);
EXPECT_ERR(err);
@ -472,7 +480,8 @@ static void test_table_read_write_seek(int index, int hash_id)
strbuf_addstr(&pastLast, names[N - 1]);
strbuf_addstr(&pastLast, "/");
err = reftable_reader_seek_ref(&rd, &it, pastLast.buf);
reftable_reader_init_ref_iterator(&rd, &it);
err = reftable_iterator_seek_ref(&it, pastLast.buf);
if (err == 0) {
struct reftable_ref_record ref = { NULL };
int err = reftable_iterator_next_ref(&it, &ref);
@ -576,7 +585,8 @@ static void test_table_refs_for(int indexed)
rd.obj_offsets.is_present = 0;
}
err = reftable_reader_seek_ref(&rd, &it, "");
reftable_reader_init_ref_iterator(&rd, &it);
err = reftable_iterator_seek_ref(&it, "");
EXPECT_ERR(err);
reftable_iterator_destroy(&it);
@ -639,7 +649,8 @@ static void test_write_empty_table(void)
err = reftable_new_reader(&rd, &source, "filename");
EXPECT_ERR(err);
err = reftable_reader_seek_ref(rd, &it, "");
reftable_reader_init_ref_iterator(rd, &it);
err = reftable_iterator_seek_ref(&it, "");
EXPECT_ERR(err);
err = reftable_iterator_next_ref(&it, &rec);
@ -846,7 +857,8 @@ static void test_write_multiple_indices(void)
* Seeking the log uses the log index now. In case there is any
* confusion regarding indices we would notice here.
*/
err = reftable_reader_seek_log(reader, &it, "");
reftable_reader_init_log_iterator(reader, &it);
err = reftable_iterator_seek_log(&it, "");
EXPECT_ERR(err);
reftable_iterator_destroy(&it);
@ -901,7 +913,8 @@ static void test_write_multi_level_index(void)
/*
* Seeking the last ref should work as expected.
*/
err = reftable_reader_seek_ref(reader, &it, "refs/heads/199");
reftable_reader_init_ref_iterator(reader, &it);
err = reftable_iterator_seek_ref(&it, "refs/heads/199");
EXPECT_ERR(err);
reftable_iterator_destroy(&it);

View file

@ -21,11 +21,11 @@ struct reftable_table {
void *table_arg;
};
int reftable_table_seek_log(struct reftable_table *tab,
struct reftable_iterator *it, const char *name);
void reftable_table_init_ref_iter(struct reftable_table *tab,
struct reftable_iterator *it);
int reftable_table_seek_ref(struct reftable_table *tab,
struct reftable_iterator *it, const char *name);
void reftable_table_init_log_iter(struct reftable_table *tab,
struct reftable_iterator *it);
/* returns the hash ID from a generic reftable_table */
uint32_t reftable_table_hash_id(struct reftable_table *tab);

View file

@ -21,12 +21,33 @@ struct reftable_iterator {
void *iter_arg;
};
/*
* Position the iterator at the ref record with given name such that the next
* call to `next_ref()` would yield the record.
*/
int reftable_iterator_seek_ref(struct reftable_iterator *it,
const char *name);
/* reads the next reftable_ref_record. Returns < 0 for error, 0 for OK and > 0:
* end of iteration.
*/
int reftable_iterator_next_ref(struct reftable_iterator *it,
struct reftable_ref_record *ref);
/*
* Position the iterator at the log record with given name and update index
* such that the next call to `next_log()` would yield the record.
*/
int reftable_iterator_seek_log_at(struct reftable_iterator *it,
const char *name, uint64_t update_index);
/*
* Position the iterator at the newest log record with given name such that the
* next call to `next_log()` would yield the record.
*/
int reftable_iterator_seek_log(struct reftable_iterator *it,
const char *name);
/* reads the next reftable_log_record. Returns < 0 for error, 0 for OK and > 0:
* end of iteration.
*/

View file

@ -36,21 +36,6 @@ int reftable_new_merged_table(struct reftable_merged_table **dest,
struct reftable_table *stack, size_t n,
uint32_t hash_id);
/* returns an iterator positioned just before 'name' */
int reftable_merged_table_seek_ref(struct reftable_merged_table *mt,
struct reftable_iterator *it,
const char *name);
/* returns an iterator for log entry, at given update_index */
int reftable_merged_table_seek_log_at(struct reftable_merged_table *mt,
struct reftable_iterator *it,
const char *name, uint64_t update_index);
/* like reftable_merged_table_seek_log_at but look for the newest entry. */
int reftable_merged_table_seek_log(struct reftable_merged_table *mt,
struct reftable_iterator *it,
const char *name);
/* returns the max update_index covered by this merged table. */
uint64_t
reftable_merged_table_max_update_index(struct reftable_merged_table *mt);

View file

@ -36,48 +36,17 @@ struct reftable_table;
int reftable_new_reader(struct reftable_reader **pp,
struct reftable_block_source *src, const char *name);
/* reftable_reader_seek_ref returns an iterator where 'name' would be inserted
in the table. To seek to the start of the table, use name = "".
/* Initialize a reftable iterator for reading refs. */
void reftable_reader_init_ref_iterator(struct reftable_reader *r,
struct reftable_iterator *it);
example:
struct reftable_reader *r = NULL;
int err = reftable_new_reader(&r, &src, "filename");
if (err < 0) { ... }
struct reftable_iterator it = {0};
err = reftable_reader_seek_ref(r, &it, "refs/heads/master");
if (err < 0) { ... }
struct reftable_ref_record ref = {0};
while (1) {
err = reftable_iterator_next_ref(&it, &ref);
if (err > 0) {
break;
}
if (err < 0) {
..error handling..
}
..found..
}
reftable_iterator_destroy(&it);
reftable_ref_record_release(&ref);
*/
int reftable_reader_seek_ref(struct reftable_reader *r,
struct reftable_iterator *it, const char *name);
/* Initialize a reftable iterator for reading logs. */
void reftable_reader_init_log_iterator(struct reftable_reader *r,
struct reftable_iterator *it);
/* returns the hash ID used in this table. */
uint32_t reftable_reader_hash_id(struct reftable_reader *r);
/* seek to logs for the given name, older than update_index. To seek to the
start of the table, use name = "".
*/
int reftable_reader_seek_log_at(struct reftable_reader *r,
struct reftable_iterator *it, const char *name,
uint64_t update_index);
/* seek to newest log entry for given name. */
int reftable_reader_seek_log(struct reftable_reader *r,
struct reftable_iterator *it, const char *name);
/* closes and deallocates a reader. */
void reftable_reader_free(struct reftable_reader *);

View file

@ -66,6 +66,24 @@ int reftable_stack_add(struct reftable_stack *st,
void *write_arg),
void *write_arg);
struct reftable_iterator;
/*
* Initialize an iterator for the merged tables contained in the stack that can
* be used to iterate through refs. The iterator is valid until the next reload
* or write.
*/
void reftable_stack_init_ref_iterator(struct reftable_stack *st,
struct reftable_iterator *it);
/*
* Initialize an iterator for the merged tables contained in the stack that can
* be used to iterate through logs. The iterator is valid until the next reload
* or write.
*/
void reftable_stack_init_log_iterator(struct reftable_stack *st,
struct reftable_iterator *it);
/* returns the merged_table for seeking. This table is valid until the
* next write or reload, and should not be closed or deleted.
*/

View file

@ -133,6 +133,20 @@ int read_lines(const char *filename, char ***namesp)
return err;
}
void reftable_stack_init_ref_iterator(struct reftable_stack *st,
struct reftable_iterator *it)
{
merged_table_init_iter(reftable_stack_merged_table(st),
it, BLOCK_TYPE_REF);
}
void reftable_stack_init_log_iterator(struct reftable_stack *st,
struct reftable_iterator *it)
{
merged_table_init_iter(reftable_stack_merged_table(st),
it, BLOCK_TYPE_LOG);
}
struct reftable_merged_table *
reftable_stack_merged_table(struct reftable_stack *st)
{
@ -913,7 +927,8 @@ static int stack_write_compact(struct reftable_stack *st,
goto done;
}
err = reftable_merged_table_seek_ref(mt, &it, "");
merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
err = reftable_iterator_seek_ref(&it, "");
if (err < 0)
goto done;
@ -937,7 +952,8 @@ static int stack_write_compact(struct reftable_stack *st,
}
reftable_iterator_destroy(&it);
err = reftable_merged_table_seek_log(mt, &it, "");
merged_table_init_iter(mt, &it, BLOCK_TYPE_LOG);
err = reftable_iterator_seek_log(&it, "");
if (err < 0)
goto done;
@ -1333,9 +1349,11 @@ int reftable_stack_read_ref(struct reftable_stack *st, const char *refname,
int reftable_stack_read_log(struct reftable_stack *st, const char *refname,
struct reftable_log_record *log)
{
struct reftable_iterator it = { NULL };
struct reftable_merged_table *mt = reftable_stack_merged_table(st);
int err = reftable_merged_table_seek_log(mt, &it, refname);
struct reftable_iterator it = {0};
int err;
reftable_stack_init_log_iterator(st, &it);
err = reftable_iterator_seek_log(&it, refname);
if (err)
goto done;