git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 12/16] reftable/block: expose a generic iterator over reftable records

From
Patrick Steinhardt <ps@pks.im>
Date
Mar 31, 2025, 08:41 UTC
Message-ID
<20250331-pks-reftable-polishing-v1-12-ebed5247434c@pks.im>
In-Reply-To
<20250331-pks-reftable-polishing-v1-0-ebed5247434c@pks.im>

Expose a generic iterator over reftable records and expose it via the public interface. Together with an upcoming iterator for reftable blocks contained in a table this will allow users to trivially iterate through blocks and their respective records individually.

This functionality will be used to implement consistency checks for the reftable backend, which requires more fine-grained control over how we read data.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 reftable/block.c                | 56 +++++++++++++++++++++++++++++
 reftable/block.h                |  2 +-
 reftable/reftable-block.h       |  5 +++
 t/unit-tests/t-reftable-block.c | 78 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 140 insertions(+), 1 deletion(-)
diff --git a/reftable/block.c b/reftable/block.c
index 6e5c1191f5f..fb91090079b 100644
--- a/reftable/block.c
+++ b/reftable/block.c
@@ -10,6 +10,7 @@
 
 #include "blocksource.h"
 #include "constants.h"
+#include "iter.h"
 #include "record.h"
 #include "reftable-error.h"
 #include "system.h"
@@ -581,6 +582,61 @@ int block_iter_seek_key(struct block_iter *it, struct reftable_buf *want)
 	return err;
 }
 
+static int block_iter_seek_void(void *it, struct reftable_record *want)
+{
+	struct reftable_buf buf = REFTABLE_BUF_INIT;
+	struct block_iter *bi = it;
+	int err;
+
+	if (bi->block->block_type != want->type)
+		return REFTABLE_API_ERROR;
+
+	err = reftable_record_key(want, &buf);
+	if (err < 0)
+		goto out;
+
+	err = block_iter_seek_key(it, &buf);
+	if (err < 0)
+		goto out;
+
+	err = 0;
+
+out:
+	reftable_buf_release(&buf);
+	return err;
+}
+
+static int block_iter_next_void(void *it, struct reftable_record *rec)
+{
+	return block_iter_next(it, rec);
+}
+
+static void block_iter_close_void(void *it)
+{
+	block_iter_close(it);
+}
+
+static struct reftable_iterator_vtable block_iter_vtable = {
+	.seek = &block_iter_seek_void,
+	.next = &block_iter_next_void,
+	.close = &block_iter_close_void,
+};
+
+int reftable_block_init_iterator(const struct reftable_block *b,
+				 struct reftable_iterator *it)
+{
+	struct block_iter *bi;
+
+	REFTABLE_CALLOC_ARRAY(bi, 1);
+	block_iter_init(bi, b);
+
+	assert(!it->ops);
+	it->iter_arg = bi;
+	it->ops = &block_iter_vtable;
+
+	return 0;
+}
+
 void block_writer_release(struct block_writer *bw)
 {
 	deflateEnd(bw->zstream);
diff --git a/reftable/block.h b/reftable/block.h
index 1bfd44f56aa..3d069393922 100644
--- a/reftable/block.h
+++ b/reftable/block.h
@@ -63,7 +63,7 @@ int block_writer_finish(struct block_writer *w);
 /* clears out internally allocated block_writer members. */
 void block_writer_release(struct block_writer *bw);
 
-/* Iterate over entries in a block */
+/* Iterator for records contained in a single block. */
 struct block_iter {
 	/* offset within the block of the next entry to read. */
 	uint32_t next_off;
diff --git a/reftable/reftable-block.h b/reftable/reftable-block.h
index ce1d9d3ce79..ca13e5dd9c3 100644
--- a/reftable/reftable-block.h
+++ b/reftable/reftable-block.h
@@ -13,6 +13,7 @@
 
 #include "reftable-basics.h"
 #include "reftable-blocksource.h"
+#include "reftable-iterator.h"
 
 struct z_stream_s;
 
@@ -60,6 +61,10 @@ int reftable_block_init(struct reftable_block *b,
 /* Release resources allocated by the block. */
 void reftable_block_release(struct reftable_block *b);
 
+/* Initialize a generic record iterator from the given block. */
+int reftable_block_init_iterator(const struct reftable_block *b,
+				 struct reftable_iterator *it);
+
 /* Returns the block type (eg. 'r' for refs). */
 uint8_t reftable_block_type(const struct reftable_block *b);
 
diff --git a/t/unit-tests/t-reftable-block.c b/t/unit-tests/t-reftable-block.c
index c4ced39a73b..e092d0bb8f8 100644
--- a/t/unit-tests/t-reftable-block.c
+++ b/t/unit-tests/t-reftable-block.c
@@ -376,12 +376,90 @@ static void t_index_block_read_write(void)
 		reftable_record_release(&recs[i]);
 }
 
+static void t_block_iterator(void)
+{
+	struct reftable_block_source source = { 0 };
+	struct block_writer writer = {
+		.last_key = REFTABLE_BUF_INIT,
+	};
+	struct reftable_record expected_refs[20];
+	struct reftable_ref_record ref = { 0 };
+	struct reftable_iterator it = { 0 };
+	struct reftable_block block = { 0 };
+	struct reftable_buf data;
+	int err;
+
+	data.len = 1024;
+	REFTABLE_CALLOC_ARRAY(data.buf, data.len);
+	check(data.buf != NULL);
+
+	err = block_writer_init(&writer, BLOCK_TYPE_REF, (uint8_t *) data.buf, data.len,
+				0, hash_size(REFTABLE_HASH_SHA1));
+	check(!err);
+
+	for (size_t i = 0; i < ARRAY_SIZE(expected_refs); i++) {
+		expected_refs[i] = (struct reftable_record) {
+			.type = BLOCK_TYPE_REF,
+			.u.ref = {
+				.value_type = REFTABLE_REF_VAL1,
+				.refname = xstrfmt("refs/heads/branch-%02"PRIuMAX, (uintmax_t)i),
+			},
+		};
+		memset(expected_refs[i].u.ref.value.val1, i, REFTABLE_HASH_SIZE_SHA1);
+
+		err = block_writer_add(&writer, &expected_refs[i]);
+		check_int(err, ==, 0);
+	}
+
+	err = block_writer_finish(&writer);
+	check_int(err, >, 0);
+
+	block_source_from_buf(&source, &data);
+	reftable_block_init(&block, &source, 0, 0, data.len, REFTABLE_HASH_SIZE_SHA1);
+
+	err = reftable_block_init_iterator(&block, &it);
+	check_int(err, ==, 0);
+
+	for (size_t i = 0; ; i++) {
+		err = reftable_iterator_next_ref(&it, &ref);
+		if (err > 0) {
+			check_int(i, ==, ARRAY_SIZE(expected_refs));
+			break;
+		}
+		check_int(err, ==, 0);
+
+		check(reftable_ref_record_equal(&ref, &expected_refs[i].u.ref,
+						REFTABLE_HASH_SIZE_SHA1));
+	}
+
+	err = reftable_iterator_seek_ref(&it, "refs/heads/does-not-exist");
+	check_int(err, ==, 0);
+	err = reftable_iterator_next_ref(&it, &ref);
+	check_int(err, ==, 1);
+
+	err = reftable_iterator_seek_ref(&it, "refs/heads/branch-13");
+	check_int(err, ==, 0);
+	err = reftable_iterator_next_ref(&it, &ref);
+	check_int(err, ==, 0);
+	check(reftable_ref_record_equal(&ref, &expected_refs[13].u.ref,
+					REFTABLE_HASH_SIZE_SHA1));
+
+	for (size_t i = 0; i < ARRAY_SIZE(expected_refs); i++)
+		reftable_free(expected_refs[i].u.ref.refname);
+	reftable_ref_record_release(&ref);
+	reftable_iterator_destroy(&it);
+	reftable_block_release(&block);
+	block_writer_release(&writer);
+	reftable_buf_release(&data);
+}
+
 int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
 {
 	TEST(t_index_block_read_write(), "read-write operations on index blocks work");
 	TEST(t_log_block_read_write(), "read-write operations on log blocks work");
 	TEST(t_obj_block_read_write(), "read-write operations on obj blocks work");
 	TEST(t_ref_block_read_write(), "read-write operations on ref blocks work");
+	TEST(t_block_iterator(), "block iterator works");
 
 	return test_done();
 }
-- 
2.49.0.604.gff1f9ca942.dirty
Previous: Patrick SteinhardtNext: Patrick Steinhardt
Message 28 of 55 in “reftable: overhaul the API to expose access to blocks”
  1. 00/16 reftable: overhaul the API to expose access to blocksPatrick Steinhardt, Mar 31, 2025
  2. 01/16 reftable: fix formatting of the license headerPatrick Steinhardt, Mar 31, 2025
  3. 03/16 reftable/blocksource: consolidate code into a single filePatrick Steinhardt, Mar 31, 2025
  4. Justin ToblerApr 2, 2025
  5. Karthik NayakApr 3, 2025
  6. 02/16 reftable/reader: rename data structure to "table"Patrick Steinhardt, Mar 31, 2025
  7. 04/16 reftable/block: simplify how we track restart pointsPatrick Steinhardt, Mar 31, 2025
  8. Justin ToblerApr 2, 2025
  9. Karthik NayakApr 3, 2025
  10. Patrick SteinhardtApr 7, 2025
  11. 05/16 reftable/table: move reading block into block readerPatrick Steinhardt, Mar 31, 2025
  12. Justin ToblerApr 2, 2025
  13. Patrick SteinhardtApr 7, 2025
  14. 06/16 reftable/block: rename `block` to `block_data`Patrick Steinhardt, Mar 31, 2025
  15. Justin ToblerApr 2, 2025
  16. Patrick SteinhardtApr 7, 2025
  17. 07/16 reftable/block: rename `block_reader` to `reftable_block`Patrick Steinhardt, Mar 31, 2025
  18. Justin ToblerApr 2, 2025
  19. Patrick SteinhardtApr 7, 2025
  20. 08/16 git-zlib: use `struct z_stream_s` instead of typedefPatrick Steinhardt, Mar 31, 2025
  21. 10/16 reftable/block: store block pointer in the block iteratorPatrick Steinhardt, Mar 31, 2025
  22. Justin ToblerApr 2, 2025
  23. Patrick SteinhardtApr 7, 2025
  24. 09/16 reftable/block: create public interface for reading blocksPatrick Steinhardt, Mar 31, 2025
  25. 11/16 reftable/block: make block iterators reseekablePatrick Steinhardt, Mar 31, 2025
  26. Justin ToblerApr 2, 2025
  27. Patrick SteinhardtApr 7, 2025
  28. 12/16 reftable/block: expose a generic iterator over reftable recordsPatrick Steinhardt, Mar 31, 2025
  29. 13/16 reftable/table: add `reftable_table` to the public interfacePatrick Steinhardt, Mar 31, 2025
  30. 14/16 reftable/table: introduce iterator for table blocksPatrick Steinhardt, Mar 31, 2025
  31. Junio C HamanoApr 1, 2025
  32. Patrick SteinhardtApr 2, 2025
  33. Justin ToblerApr 2, 2025
  34. Patrick SteinhardtApr 7, 2025
  35. 15/16 reftable/constants: make block types part of the public interfacePatrick Steinhardt, Mar 31, 2025
  36. 16/16 reftable/table: move printing logic into test helperPatrick Steinhardt, Mar 31, 2025
  37. Justin ToblerApr 2, 2025
  38. 00/16 reftable: overhaul the API to expose access to blocksPatrick Steinhardt, Apr 7, 2025
  39. 01/16 reftable: fix formatting of the license headerPatrick Steinhardt, Apr 7, 2025
  40. 03/16 reftable/blocksource: consolidate code into a single filePatrick Steinhardt, Apr 7, 2025
  41. 02/16 reftable/reader: rename data structure to "table"Patrick Steinhardt, Apr 7, 2025
  42. 04/16 reftable/block: simplify how we track restart pointsPatrick Steinhardt, Apr 7, 2025
  43. 06/16 reftable/block: rename `block` to `block_data`Patrick Steinhardt, Apr 7, 2025
  44. 05/16 reftable/table: move reading block into block readerPatrick Steinhardt, Apr 7, 2025
  45. 07/16 reftable/block: rename `block_reader` to `reftable_block`Patrick Steinhardt, Apr 7, 2025
  46. 08/16 git-zlib: use `struct z_stream_s` instead of typedefPatrick Steinhardt, Apr 7, 2025
  47. 09/16 reftable/block: create public interface for reading blocksPatrick Steinhardt, Apr 7, 2025
  48. 10/16 reftable/block: store block pointer in the block iteratorPatrick Steinhardt, Apr 7, 2025
  49. 11/16 reftable/block: make block iterators reseekablePatrick Steinhardt, Apr 7, 2025
  50. 12/16 reftable/block: expose a generic iterator over reftable recordsPatrick Steinhardt, Apr 7, 2025
  51. 13/16 reftable/table: add `reftable_table` to the public interfacePatrick Steinhardt, Apr 7, 2025
  52. 14/16 reftable/table: introduce iterator for table blocksPatrick Steinhardt, Apr 7, 2025
  53. 15/16 reftable/constants: make block types part of the public interfacePatrick Steinhardt, Apr 7, 2025
  54. 16/16 reftable/table: move printing logic into test helperPatrick Steinhardt, Apr 7, 2025
  55. Justin ToblerApr 14, 2025

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.