Git fork

reftable/block: expose a generic iterator over reftable records

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>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Patrick Steinhardt and committed by
Junio C Hamano
50d84594 6da48a5e

+140 -1
+56
reftable/block.c
··· 10 10 11 11 #include "blocksource.h" 12 12 #include "constants.h" 13 + #include "iter.h" 13 14 #include "record.h" 14 15 #include "reftable-error.h" 15 16 #include "system.h" ··· 579 580 done: 580 581 reftable_record_release(&rec); 581 582 return err; 583 + } 584 + 585 + static int block_iter_seek_void(void *it, struct reftable_record *want) 586 + { 587 + struct reftable_buf buf = REFTABLE_BUF_INIT; 588 + struct block_iter *bi = it; 589 + int err; 590 + 591 + if (bi->block->block_type != want->type) 592 + return REFTABLE_API_ERROR; 593 + 594 + err = reftable_record_key(want, &buf); 595 + if (err < 0) 596 + goto out; 597 + 598 + err = block_iter_seek_key(it, &buf); 599 + if (err < 0) 600 + goto out; 601 + 602 + err = 0; 603 + 604 + out: 605 + reftable_buf_release(&buf); 606 + return err; 607 + } 608 + 609 + static int block_iter_next_void(void *it, struct reftable_record *rec) 610 + { 611 + return block_iter_next(it, rec); 612 + } 613 + 614 + static void block_iter_close_void(void *it) 615 + { 616 + block_iter_close(it); 617 + } 618 + 619 + static struct reftable_iterator_vtable block_iter_vtable = { 620 + .seek = &block_iter_seek_void, 621 + .next = &block_iter_next_void, 622 + .close = &block_iter_close_void, 623 + }; 624 + 625 + int reftable_block_init_iterator(const struct reftable_block *b, 626 + struct reftable_iterator *it) 627 + { 628 + struct block_iter *bi; 629 + 630 + REFTABLE_CALLOC_ARRAY(bi, 1); 631 + block_iter_init(bi, b); 632 + 633 + assert(!it->ops); 634 + it->iter_arg = bi; 635 + it->ops = &block_iter_vtable; 636 + 637 + return 0; 582 638 } 583 639 584 640 void block_writer_release(struct block_writer *bw)
+1 -1
reftable/block.h
··· 63 63 /* clears out internally allocated block_writer members. */ 64 64 void block_writer_release(struct block_writer *bw); 65 65 66 - /* Iterate over entries in a block */ 66 + /* Iterator for records contained in a single block. */ 67 67 struct block_iter { 68 68 /* offset within the block of the next entry to read. */ 69 69 uint32_t next_off;
+5
reftable/reftable-block.h
··· 13 13 14 14 #include "reftable-basics.h" 15 15 #include "reftable-blocksource.h" 16 + #include "reftable-iterator.h" 16 17 17 18 struct z_stream_s; 18 19 ··· 59 60 60 61 /* Release resources allocated by the block. */ 61 62 void reftable_block_release(struct reftable_block *b); 63 + 64 + /* Initialize a generic record iterator from the given block. */ 65 + int reftable_block_init_iterator(const struct reftable_block *b, 66 + struct reftable_iterator *it); 62 67 63 68 /* Returns the block type (eg. 'r' for refs). */ 64 69 uint8_t reftable_block_type(const struct reftable_block *b);
+78
t/unit-tests/t-reftable-block.c
··· 372 372 reftable_record_release(&recs[i]); 373 373 } 374 374 375 + static void t_block_iterator(void) 376 + { 377 + struct reftable_block_source source = { 0 }; 378 + struct block_writer writer = { 379 + .last_key = REFTABLE_BUF_INIT, 380 + }; 381 + struct reftable_record expected_refs[20]; 382 + struct reftable_ref_record ref = { 0 }; 383 + struct reftable_iterator it = { 0 }; 384 + struct reftable_block block = { 0 }; 385 + struct reftable_buf data; 386 + int err; 387 + 388 + data.len = 1024; 389 + REFTABLE_CALLOC_ARRAY(data.buf, data.len); 390 + check(data.buf != NULL); 391 + 392 + err = block_writer_init(&writer, BLOCK_TYPE_REF, (uint8_t *) data.buf, data.len, 393 + 0, hash_size(REFTABLE_HASH_SHA1)); 394 + check(!err); 395 + 396 + for (size_t i = 0; i < ARRAY_SIZE(expected_refs); i++) { 397 + expected_refs[i] = (struct reftable_record) { 398 + .type = BLOCK_TYPE_REF, 399 + .u.ref = { 400 + .value_type = REFTABLE_REF_VAL1, 401 + .refname = xstrfmt("refs/heads/branch-%02"PRIuMAX, (uintmax_t)i), 402 + }, 403 + }; 404 + memset(expected_refs[i].u.ref.value.val1, i, REFTABLE_HASH_SIZE_SHA1); 405 + 406 + err = block_writer_add(&writer, &expected_refs[i]); 407 + check_int(err, ==, 0); 408 + } 409 + 410 + err = block_writer_finish(&writer); 411 + check_int(err, >, 0); 412 + 413 + block_source_from_buf(&source, &data); 414 + reftable_block_init(&block, &source, 0, 0, data.len, REFTABLE_HASH_SIZE_SHA1); 415 + 416 + err = reftable_block_init_iterator(&block, &it); 417 + check_int(err, ==, 0); 418 + 419 + for (size_t i = 0; ; i++) { 420 + err = reftable_iterator_next_ref(&it, &ref); 421 + if (err > 0) { 422 + check_int(i, ==, ARRAY_SIZE(expected_refs)); 423 + break; 424 + } 425 + check_int(err, ==, 0); 426 + 427 + check(reftable_ref_record_equal(&ref, &expected_refs[i].u.ref, 428 + REFTABLE_HASH_SIZE_SHA1)); 429 + } 430 + 431 + err = reftable_iterator_seek_ref(&it, "refs/heads/does-not-exist"); 432 + check_int(err, ==, 0); 433 + err = reftable_iterator_next_ref(&it, &ref); 434 + check_int(err, ==, 1); 435 + 436 + err = reftable_iterator_seek_ref(&it, "refs/heads/branch-13"); 437 + check_int(err, ==, 0); 438 + err = reftable_iterator_next_ref(&it, &ref); 439 + check_int(err, ==, 0); 440 + check(reftable_ref_record_equal(&ref, &expected_refs[13].u.ref, 441 + REFTABLE_HASH_SIZE_SHA1)); 442 + 443 + for (size_t i = 0; i < ARRAY_SIZE(expected_refs); i++) 444 + reftable_free(expected_refs[i].u.ref.refname); 445 + reftable_ref_record_release(&ref); 446 + reftable_iterator_destroy(&it); 447 + reftable_block_release(&block); 448 + block_writer_release(&writer); 449 + reftable_buf_release(&data); 450 + } 451 + 375 452 int cmd_main(int argc UNUSED, const char *argv[] UNUSED) 376 453 { 377 454 TEST(t_index_block_read_write(), "read-write operations on index blocks work"); 378 455 TEST(t_log_block_read_write(), "read-write operations on log blocks work"); 379 456 TEST(t_obj_block_read_write(), "read-write operations on obj blocks work"); 380 457 TEST(t_ref_block_read_write(), "read-write operations on ref blocks work"); 458 + TEST(t_block_iterator(), "block iterator works"); 381 459 382 460 return test_done(); 383 461 }