diff options
Diffstat (limited to 'reftable/merged.c')
| -rw-r--r-- | reftable/merged.c | 358 |
1 files changed, 148 insertions, 210 deletions
diff --git a/reftable/merged.c b/reftable/merged.c index 5ded470c08..514d6facf4 100644 --- a/reftable/merged.c +++ b/reftable/merged.c @@ -13,89 +13,112 @@ https://developers.google.com/open-source/licenses/bsd #include "pq.h" #include "reader.h" #include "record.h" -#include "generic.h" #include "reftable-merged.h" #include "reftable-error.h" #include "system.h" -static int merged_iter_init(struct merged_iter *mi) -{ - int i = 0; - for (i = 0; i < mi->stack_len; i++) { - struct reftable_record rec = reftable_new_record(mi->typ); - int err = iterator_next(&mi->stack[i], &rec); - if (err < 0) { - return err; - } - - if (err > 0) { - reftable_iterator_destroy(&mi->stack[i]); - reftable_record_release(&rec); - } else { - struct pq_entry e = { - .rec = rec, - .index = i, - }; - merged_iter_pqueue_add(&mi->pq, &e); - } - } +struct merged_subiter { + struct reftable_iterator iter; + struct reftable_record rec; +}; - return 0; -} +struct merged_iter { + struct merged_subiter *subiters; + struct merged_iter_pqueue pq; + size_t subiters_len; + int suppress_deletions; + ssize_t advance_index; +}; static void merged_iter_close(void *p) { struct merged_iter *mi = p; - int i = 0; + merged_iter_pqueue_release(&mi->pq); - for (i = 0; i < mi->stack_len; i++) { - reftable_iterator_destroy(&mi->stack[i]); + for (size_t i = 0; i < mi->subiters_len; i++) { + reftable_iterator_destroy(&mi->subiters[i].iter); + reftable_record_release(&mi->subiters[i].rec); } - reftable_free(mi->stack); + reftable_free(mi->subiters); } -static int merged_iter_advance_nonnull_subiter(struct merged_iter *mi, - size_t idx) +static int merged_iter_advance_subiter(struct merged_iter *mi, size_t idx) { struct pq_entry e = { - .rec = reftable_new_record(mi->typ), .index = idx, + .rec = &mi->subiters[idx].rec, }; - int err = iterator_next(&mi->stack[idx], &e.rec); - if (err < 0) + int err; + + err = iterator_next(&mi->subiters[idx].iter, &mi->subiters[idx].rec); + if (err) return err; - if (err > 0) { - reftable_iterator_destroy(&mi->stack[idx]); - reftable_record_release(&e.rec); - return 0; - } + err = merged_iter_pqueue_add(&mi->pq, &e); + if (err) + return err; - merged_iter_pqueue_add(&mi->pq, &e); return 0; } -static int merged_iter_advance_subiter(struct merged_iter *mi, size_t idx) +static int merged_iter_seek(struct merged_iter *mi, struct reftable_record *want) { - if (iterator_is_null(&mi->stack[idx])) - return 0; - return merged_iter_advance_nonnull_subiter(mi, idx); + int err; + + mi->advance_index = -1; + + for (size_t i = 0; i < mi->subiters_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) { - struct strbuf entry_key = STRBUF_INIT; struct pq_entry entry = { 0 }; - int err = 0; + int err = 0, empty; + + empty = merged_iter_pqueue_is_empty(mi->pq); + + if (mi->advance_index >= 0) { + /* + * When there are no pqueue entries then we only have a single + * subiter left. There is no need to use the pqueue in that + * case anymore as we know that the subiter will return entries + * in the correct order already. + * + * While this may sound like a very specific edge case, it may + * happen more frequently than you think. Most repositories + * will end up having a single large base table that contains + * most of the refs. It's thus likely that we exhaust all + * subiters but the one from that base ref. + */ + if (empty) + return iterator_next(&mi->subiters[mi->advance_index].iter, + rec); + + err = merged_iter_advance_subiter(mi, mi->advance_index); + if (err < 0) + return err; + if (!err) + empty = 0; + mi->advance_index = -1; + } - if (merged_iter_pqueue_is_empty(mi->pq)) + if (empty) return 1; entry = merged_iter_pqueue_remove(&mi->pq); - err = merged_iter_advance_subiter(mi, entry.index); - if (err < 0) - return err; /* One can also use reftable as datacenter-local storage, where the ref @@ -105,58 +128,45 @@ static int merged_iter_next_entry(struct merged_iter *mi, such a deployment, the loop below must be changed to collect all entries for the same key, and return new the newest one. */ - reftable_record_key(&entry.rec, &entry_key); while (!merged_iter_pqueue_is_empty(mi->pq)) { struct pq_entry top = merged_iter_pqueue_top(mi->pq); - struct strbuf k = STRBUF_INIT; - int err = 0, cmp = 0; - - reftable_record_key(&top.rec, &k); + int cmp; - cmp = strbuf_cmp(&k, &entry_key); - strbuf_release(&k); - - if (cmp > 0) { + cmp = reftable_record_cmp(top.rec, entry.rec); + if (cmp > 0) break; - } merged_iter_pqueue_remove(&mi->pq); err = merged_iter_advance_subiter(mi, top.index); - if (err < 0) { + if (err < 0) return err; - } - reftable_record_release(&top.rec); } - reftable_record_copy_from(rec, &entry.rec, hash_size(mi->hash_id)); - reftable_record_release(&entry.rec); - strbuf_release(&entry_key); + mi->advance_index = entry.index; + SWAP(*rec, *entry.rec); return 0; } -static int merged_iter_next(struct merged_iter *mi, struct reftable_record *rec) +static int merged_iter_seek_void(void *it, struct reftable_record *want) { - while (1) { - int err = merged_iter_next_entry(mi, rec); - if (err == 0 && mi->suppress_deletions && - reftable_record_is_deletion(rec)) { - continue; - } - - return err; - } + return merged_iter_seek(it, want); } static int merged_iter_next_void(void *p, struct reftable_record *rec) { struct merged_iter *mi = p; - if (merged_iter_pqueue_is_empty(mi->pq)) - return 1; - - return merged_iter_next(mi, rec); + while (1) { + int err = merged_iter_next_entry(mi, rec); + if (err) + return err; + if (mi->suppress_deletions && reftable_record_is_deletion(rec)) + continue; + return 0; + } } static struct reftable_iterator_vtable merged_iter_vtable = { + .seek = merged_iter_seek_void, .next = &merged_iter_next_void, .close = &merged_iter_close, }; @@ -169,19 +179,19 @@ static void iterator_from_merged_iter(struct reftable_iterator *it, it->ops = &merged_iter_vtable; } -int reftable_new_merged_table(struct reftable_merged_table **dest, - struct reftable_table *stack, int n, +int reftable_merged_table_new(struct reftable_merged_table **dest, + struct reftable_reader **readers, size_t n, uint32_t hash_id) { struct reftable_merged_table *m = NULL; uint64_t last_max = 0; uint64_t first_min = 0; - int i = 0; - for (i = 0; i < n; i++) { - uint64_t min = reftable_table_min_update_index(&stack[i]); - uint64_t max = reftable_table_max_update_index(&stack[i]); - if (reftable_table_hash_id(&stack[i]) != hash_id) { + for (size_t i = 0; i < n; i++) { + uint64_t min = reftable_reader_min_update_index(readers[i]); + uint64_t max = reftable_reader_max_update_index(readers[i]); + + if (reftable_reader_hash_id(readers[i]) != hash_id) { return REFTABLE_FORMAT_ERROR; } if (i == 0 || min < first_min) { @@ -192,9 +202,12 @@ int reftable_new_merged_table(struct reftable_merged_table **dest, } } - m = reftable_calloc(sizeof(struct reftable_merged_table)); - m->stack = stack; - m->stack_len = n; + REFTABLE_CALLOC_ARRAY(m, 1); + if (!m) + return REFTABLE_OUT_OF_MEMORY_ERROR; + + m->readers = readers; + m->readers_len = n; m->min = first_min; m->max = last_max; m->hash_id = hash_id; @@ -202,19 +215,10 @@ int reftable_new_merged_table(struct reftable_merged_table **dest, return 0; } -/* clears the list of subtable, without affecting the readers themselves. */ -void merged_table_release(struct reftable_merged_table *mt) -{ - FREE_AND_NULL(mt->stack); - mt->stack_len = 0; -} - void reftable_merged_table_free(struct reftable_merged_table *mt) { - if (!mt) { + if (!mt) return; - } - merged_table_release(mt); reftable_free(mt); } @@ -230,132 +234,66 @@ 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) +int 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 reftable_iterator *iters = reftable_calloc( - sizeof(struct reftable_iterator) * mt->stack_len); - struct merged_iter merged = { - .stack = iters, - .typ = reftable_record_type(rec), - .hash_id = mt->hash_id, - .suppress_deletions = mt->suppress_deletions, - }; - int n = 0; - int err = 0; - int i = 0; - for (i = 0; i < mt->stack_len && err == 0; i++) { - int e = reftable_table_seek_record(&mt->stack[i], &iters[n], - rec); - if (e < 0) { - err = e; - } - if (e == 0) { - n++; - } + struct merged_subiter *subiters; + struct merged_iter *mi = NULL; + int ret; + + REFTABLE_CALLOC_ARRAY(subiters, mt->readers_len); + if (!subiters) { + ret = REFTABLE_OUT_OF_MEMORY_ERROR; + goto out; } - if (err < 0) { - int i = 0; - for (i = 0; i < n; i++) { - reftable_iterator_destroy(&iters[i]); - } - reftable_free(iters); - return err; + + for (size_t i = 0; i < mt->readers_len; i++) { + reftable_record_init(&subiters[i].rec, typ); + ret = reader_init_iter(mt->readers[i], &subiters[i].iter, typ); + if (ret < 0) + goto out; } - merged.stack_len = n; - err = merged_iter_init(&merged); - if (err < 0) { - merged_iter_close(&merged); - return err; - } else { - struct merged_iter *p = - reftable_malloc(sizeof(struct merged_iter)); - *p = merged; - iterator_from_merged_iter(it, p); + REFTABLE_CALLOC_ARRAY(mi, 1); + if (!mi) { + ret = REFTABLE_OUT_OF_MEMORY_ERROR; + goto out; + } + mi->advance_index = -1; + mi->suppress_deletions = mt->suppress_deletions; + mi->subiters = subiters; + mi->subiters_len = mt->readers_len; + + iterator_from_merged_iter(it, mi); + ret = 0; + +out: + if (ret < 0) { + for (size_t i = 0; subiters && i < mt->readers_len; i++) { + reftable_iterator_destroy(&subiters[i].iter); + reftable_record_release(&subiters[i].rec); + } + reftable_free(subiters); + reftable_free(mi); } - return 0; -} -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); + return ret; } -int reftable_merged_table_seek_log_at(struct reftable_merged_table *mt, - struct reftable_iterator *it, - const char *name, uint64_t update_index) +int reftable_merged_table_init_ref_iterator(struct reftable_merged_table *mt, + struct reftable_iterator *it) { - 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); + return merged_table_init_iter(mt, it, BLOCK_TYPE_REF); } -int reftable_merged_table_seek_log(struct reftable_merged_table *mt, - struct reftable_iterator *it, - const char *name) +int reftable_merged_table_init_log_iterator(struct reftable_merged_table *mt, + struct reftable_iterator *it) { - uint64_t max = ~((uint64_t)0); - return reftable_merged_table_seek_log_at(mt, it, name, max); + return merged_table_init_iter(mt, it, BLOCK_TYPE_LOG); } 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) -{ - return merged_table_seek_record(tab, it, rec); -} - -static uint32_t reftable_merged_table_hash_id_void(void *tab) -{ - return reftable_merged_table_hash_id(tab); -} - -static uint64_t reftable_merged_table_min_update_index_void(void *tab) -{ - return reftable_merged_table_min_update_index(tab); -} - -static uint64_t reftable_merged_table_max_update_index_void(void *tab) -{ - return reftable_merged_table_max_update_index(tab); -} - -static struct reftable_table_vtable merged_table_vtable = { - .seek_record = reftable_merged_table_seek_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, -}; - -void reftable_table_from_merged_table(struct reftable_table *tab, - struct reftable_merged_table *merged) -{ - assert(!tab->ops); - tab->ops = &merged_table_vtable; - tab->table_arg = merged; -} |
