]> git.ipfire.org Git - thirdparty/git.git/commitdiff
reftable/table: introduce iterator for table blocks
authorPatrick Steinhardt <ps@pks.im>
Mon, 7 Apr 2025 13:16:26 +0000 (15:16 +0200)
committerJunio C Hamano <gitster@pobox.com>
Mon, 7 Apr 2025 21:53:12 +0000 (14:53 -0700)
Introduce a new iterator that allows the caller to iterate through all
blocks contained in a table. This gives users more fine-grained control
over how exactly those blocks are being read and exposes information to
callers that was previously inaccessible.

This iterator will be required by a future patch series that adds
consistency checks for the reftable backend. In addition to that though
we will also reimplement `reftable_table_print_blocks()` on top of this
new iterator in a subsequent commit.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
reftable/reftable-table.h
reftable/table.c
t/unit-tests/t-reftable-table.c

index a78db9eea7e51c39149caab078a3949a860f5ac7..4d61ce6cf771bd40411509e3dbcf3eb9de0a578e 100644 (file)
@@ -10,6 +10,7 @@
 #define REFTABLE_TABLE_H
 
 #include "reftable-iterator.h"
+#include "reftable-block.h"
 #include "reftable-blocksource.h"
 
 /*
@@ -99,4 +100,19 @@ uint64_t reftable_table_min_update_index(struct reftable_table *t);
 /* print blocks onto stdout for debugging. */
 int reftable_table_print_blocks(const char *tablename);
 
+/*
+ * An iterator that iterates through the blocks contained in a given table.
+ */
+struct reftable_table_iterator {
+       void *iter_arg;
+};
+
+int reftable_table_iterator_init(struct reftable_table_iterator *it,
+                                struct reftable_table *t);
+
+void reftable_table_iterator_release(struct reftable_table_iterator *it);
+
+int reftable_table_iterator_next(struct reftable_table_iterator *it,
+                                const struct reftable_block **out);
+
 #endif
index 50ffad7edcbabea61c767ea8d554fd52e3db1cd4..a2a0c7a1d2fdb6d9a24010870d5790505f92c18b 100644 (file)
@@ -804,3 +804,50 @@ done:
        table_iter_close(&ti);
        return err;
 }
+
+int reftable_table_iterator_init(struct reftable_table_iterator *it,
+                                struct reftable_table *t)
+{
+       struct table_iter *ti;
+       int err;
+
+       REFTABLE_ALLOC_ARRAY(ti, 1);
+       if (!ti)
+               return REFTABLE_OUT_OF_MEMORY_ERROR;
+
+       err = table_iter_init(ti, t);
+       if (err < 0)
+               goto out;
+
+       it->iter_arg = ti;
+       err = 0;
+
+out:
+       if (err < 0)
+               reftable_free(ti);
+       return err;
+}
+
+void reftable_table_iterator_release(struct reftable_table_iterator *it)
+{
+       if (!it->iter_arg)
+               return;
+       table_iter_close(it->iter_arg);
+       reftable_free(it->iter_arg);
+       it->iter_arg = NULL;
+}
+
+int reftable_table_iterator_next(struct reftable_table_iterator *it,
+                                const struct reftable_block **out)
+{
+       struct table_iter *ti = it->iter_arg;
+       int err;
+
+       err = table_iter_next_block(ti);
+       if (err)
+               return err;
+
+       *out = &ti->block;
+
+       return 0;
+}
index 77c59dbf46d1e6e983b088b2f5dc90e31c9321bc..ba39cdf9a76b1e56b0bff5e4724e7e5a2c79c19e 100644 (file)
@@ -1,7 +1,10 @@
 #include "test-lib.h"
 #include "lib-reftable.h"
 #include "reftable/blocksource.h"
+#include "reftable/constants.h"
+#include "reftable/iter.h"
 #include "reftable/table.h"
+#include "strbuf.h"
 
 static int t_table_seek_once(void)
 {
@@ -88,9 +91,116 @@ static int t_table_reseek(void)
        return 0;
 }
 
+static int t_table_block_iterator(void)
+{
+       struct reftable_block_source source = { 0 };
+       struct reftable_table_iterator it = { 0 };
+       struct reftable_ref_record *records;
+       const struct reftable_block *block;
+       struct reftable_table *table;
+       struct reftable_buf buf = REFTABLE_BUF_INIT;
+       struct {
+               uint8_t block_type;
+               uint16_t header_off;
+               uint16_t restart_count;
+               uint16_t record_count;
+       } expected_blocks[] = {
+               {
+                       .block_type = BLOCK_TYPE_REF,
+                       .header_off = 24,
+                       .restart_count = 10,
+                       .record_count = 158,
+               },
+               {
+                       .block_type = BLOCK_TYPE_REF,
+                       .restart_count = 10,
+                       .record_count = 159,
+               },
+               {
+                       .block_type = BLOCK_TYPE_REF,
+                       .restart_count = 10,
+                       .record_count = 159,
+               },
+               {
+                       .block_type = BLOCK_TYPE_REF,
+                       .restart_count = 2,
+                       .record_count = 24,
+               },
+               {
+                       .block_type = BLOCK_TYPE_INDEX,
+                       .restart_count = 1,
+                       .record_count = 4,
+               },
+               {
+                       .block_type = BLOCK_TYPE_OBJ,
+                       .restart_count = 1,
+                       .record_count = 1,
+               },
+       };
+       const size_t nrecords = 500;
+       int ret;
+
+       REFTABLE_CALLOC_ARRAY(records, nrecords);
+       for (size_t i = 0; i < nrecords; i++) {
+               records[i].value_type = REFTABLE_REF_VAL1;
+               records[i].refname = xstrfmt("refs/heads/branch-%03"PRIuMAX,
+                                            (uintmax_t) i);
+       }
+
+       t_reftable_write_to_buf(&buf, records, nrecords, NULL, 0, NULL);
+       block_source_from_buf(&source, &buf);
+
+       ret = reftable_table_new(&table, &source, "name");
+       check(!ret);
+
+       ret = reftable_table_iterator_init(&it, table);
+       check(!ret);
+
+       for (size_t i = 0; i < ARRAY_SIZE(expected_blocks); i++) {
+               struct reftable_iterator record_it = { 0 };
+               struct reftable_record record = {
+                       .type = expected_blocks[i].block_type,
+               };
+
+               ret = reftable_table_iterator_next(&it, &block);
+               check(!ret);
+
+               check_int(block->block_type, ==, expected_blocks[i].block_type);
+               check_int(block->header_off, ==, expected_blocks[i].header_off);
+               check_int(block->restart_count, ==, expected_blocks[i].restart_count);
+
+               ret = reftable_block_init_iterator(block, &record_it);
+               check(!ret);
+
+               for (size_t j = 0; ; j++) {
+                       ret = iterator_next(&record_it, &record);
+                       if (ret > 0) {
+                               check_int(j, ==, expected_blocks[i].record_count);
+                               break;
+                       }
+                       check(!ret);
+               }
+
+               reftable_iterator_destroy(&record_it);
+               reftable_record_release(&record);
+       }
+
+       ret = reftable_table_iterator_next(&it, &block);
+       check_int(ret, ==, 1);
+
+       for (size_t i = 0; i < nrecords; i++)
+               reftable_free(records[i].refname);
+       reftable_table_iterator_release(&it);
+       reftable_table_decref(table);
+       reftable_buf_release(&buf);
+       reftable_free(records);
+       return 0;
+}
+
 int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
 {
        TEST(t_table_seek_once(), "table can seek once");
        TEST(t_table_reseek(), "table can reseek multiple times");
+       TEST(t_table_block_iterator(), "table can iterate through blocks");
        return test_done();
 }