This adds the reftable library, and hooks it up as a ref backend. Based on hn/refs-cleanup. Includes testing support, to test: make -C t/ GIT_TEST_REFTABLE=1 Summary 21561 tests pass 899 tests fail Some issues: * many tests inspect .git/{logs,heads}/ directly. * worktrees broken. v21 * reftable/ is now fully imported from upstream. * reftable/ uses strbuf * fix of the HEAD file. * check for reload on every read Han-Wen Nienhuys (17): lib-t6000.sh: write tag using git-update-ref checkout: add '\n' to reflog message Write pseudorefs through ref backends. Make refs_ref_exists public Treat BISECT_HEAD as a pseudo ref Treat CHERRY_PICK_HEAD as a pseudo ref Treat REVERT_HEAD as a pseudo ref Move REF_LOG_ONLY to refs-internal.h Iterate over the "refs/" namespace in for_each_[raw]ref Add .gitattributes for the reftable/ directory Add reftable library Add standalone build infrastructure for reftable Reftable support for git-core Hookup unittests for the reftable library. Add GIT_DEBUG_REFS debugging mechanism Add reftable testing infrastructure Add "test-tool dump-reftable" command. Johannes Schindelin (1): vcxproj: adjust for the reftable changes SZEDER Gábor (1): git-prompt: prepare for reftable refs backend .../technical/repository-version.txt | 7 + Makefile | 47 +- builtin/bisect--helper.c | 3 +- builtin/checkout.c | 5 +- builtin/clone.c | 3 +- builtin/commit.c | 34 +- builtin/init-db.c | 56 +- builtin/merge.c | 2 +- cache.h | 6 +- config.mak.uname | 2 +- contrib/buildsystems/Generators/Vcxproj.pm | 11 +- contrib/completion/git-prompt.sh | 7 +- git-bisect.sh | 4 +- path.c | 2 - path.h | 9 +- refs.c | 157 +- refs.h | 16 + refs/debug.c | 411 +++++ refs/files-backend.c | 121 +- refs/packed-backend.c | 21 +- refs/refs-internal.h | 32 + refs/reftable-backend.c | 1335 +++++++++++++++++ reftable/.gitattributes | 1 + reftable/BUILD | 203 +++ reftable/LICENSE | 31 + reftable/README.md | 33 + reftable/VERSION | 1 + reftable/WORKSPACE | 14 + reftable/basics.c | 215 +++ reftable/basics.h | 53 + reftable/block.c | 432 ++++++ reftable/block.h | 129 ++ reftable/block_test.c | 157 ++ reftable/compat.c | 98 ++ reftable/compat.h | 48 + reftable/constants.h | 21 + reftable/dump.c | 212 +++ reftable/file.c | 95 ++ reftable/iter.c | 242 +++ reftable/iter.h | 72 + reftable/merged.c | 321 ++++ reftable/merged.h | 39 + reftable/merged_test.c | 273 ++++ reftable/pq.c | 115 ++ reftable/pq.h | 34 + reftable/reader.c | 744 +++++++++ reftable/reader.h | 65 + reftable/record.c | 1126 ++++++++++++++ reftable/record.h | 143 ++ reftable/record_test.c | 410 +++++ reftable/refname.c | 209 +++ reftable/refname.h | 38 + reftable/refname_test.c | 99 ++ reftable/reftable-tests.h | 22 + reftable/reftable.c | 90 ++ reftable/reftable.h | 571 +++++++ reftable/reftable_test.c | 631 ++++++++ reftable/stack.c | 1209 +++++++++++++++ reftable/stack.h | 48 + reftable/stack_test.c | 787 ++++++++++ reftable/strbuf.c | 206 +++ reftable/strbuf.h | 88 ++ reftable/strbuf_test.c | 39 + reftable/system.h | 53 + reftable/test_framework.c | 69 + reftable/test_framework.h | 64 + reftable/tree.c | 63 + reftable/tree.h | 34 + reftable/tree_test.c | 62 + reftable/update.sh | 19 + reftable/writer.c | 662 ++++++++ reftable/writer.h | 60 + reftable/zlib-compat.c | 92 ++ reftable/zlib.BUILD | 36 + repository.c | 2 + repository.h | 3 + sequencer.c | 56 +- setup.c | 12 +- t/helper/test-reftable.c | 20 + t/helper/test-tool.c | 2 + t/helper/test-tool.h | 2 + t/lib-t6000.sh | 5 +- t/t0031-reftable.sh | 160 ++ t/t0033-debug-refs.sh | 18 + t/t1409-avoid-packing-refs.sh | 6 + t/t1450-fsck.sh | 6 + t/t3210-pack-refs.sh | 6 + t/test-lib.sh | 5 + wt-status.c | 6 +- 89 files changed, 12977 insertions(+), 201 deletions(-) create mode 100644 refs/debug.c create mode 100644 refs/reftable-backend.c create mode 100644 reftable/.gitattributes create mode 100644 reftable/BUILD create mode 100644 reftable/LICENSE create mode 100644 reftable/README.md create mode 100644 reftable/VERSION create mode 100644 reftable/WORKSPACE create mode 100644 reftable/basics.c create mode 100644 reftable/basics.h create mode 100644 reftable/block.c create mode 100644 reftable/block.h create mode 100644 reftable/block_test.c create mode 100644 reftable/compat.c create mode 100644 reftable/compat.h create mode 100644 reftable/constants.h create mode 100644 reftable/dump.c create mode 100644 reftable/file.c create mode 100644 reftable/iter.c create mode 100644 reftable/iter.h create mode 100644 reftable/merged.c create mode 100644 reftable/merged.h create mode 100644 reftable/merged_test.c create mode 100644 reftable/pq.c create mode 100644 reftable/pq.h create mode 100644 reftable/reader.c create mode 100644 reftable/reader.h create mode 100644 reftable/record.c create mode 100644 reftable/record.h create mode 100644 reftable/record_test.c create mode 100644 reftable/refname.c create mode 100644 reftable/refname.h create mode 100644 reftable/refname_test.c create mode 100644 reftable/reftable-tests.h create mode 100644 reftable/reftable.c create mode 100644 reftable/reftable.h create mode 100644 reftable/reftable_test.c create mode 100644 reftable/stack.c create mode 100644 reftable/stack.h create mode 100644 reftable/stack_test.c create mode 100644 reftable/strbuf.c create mode 100644 reftable/strbuf.h create mode 100644 reftable/strbuf_test.c create mode 100644 reftable/system.h create mode 100644 reftable/test_framework.c create mode 100644 reftable/test_framework.h create mode 100644 reftable/tree.c create mode 100644 reftable/tree.h create mode 100644 reftable/tree_test.c create mode 100755 reftable/update.sh create mode 100644 reftable/writer.c create mode 100644 reftable/writer.h create mode 100644 reftable/zlib-compat.c create mode 100644 reftable/zlib.BUILD create mode 100644 t/helper/test-reftable.c create mode 100755 t/t0031-reftable.sh create mode 100755 t/t0033-debug-refs.sh base-commit: 4d328a12d95bbea4e742463974ca03e49933bf7e Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-539%2Fhanwen%2Freftable-v18 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-539/hanwen/reftable-v18 Pull-Request: https://github.com/gitgitgadget/git/pull/539 Range-diff vs v17: 1: 8304c3d6379 = 1: b968b795af9 lib-t6000.sh: write tag using git-update-ref 2: 4012d801e3c = 2: 52102259773 checkout: add '\n' to reflog message 3: 95a6a1d968e = 3: 15c9dd66e17 Write pseudorefs through ref backends. 4: 1f8865f4b3e = 4: a5bce2e3fe6 Make refs_ref_exists public 5: 7f376a76d84 = 5: a29d898907a Treat BISECT_HEAD as a pseudo ref 6: 959c69b5ee4 = 6: 11a690d2b8e Treat CHERRY_PICK_HEAD as a pseudo ref 7: 3f18475d0d3 = 7: 4e52ec0dbc1 Treat REVERT_HEAD as a pseudo ref 8: 4981e5395c6 = 8: 37e350af159 Move REF_LOG_ONLY to refs-internal.h 9: f452c48ae44 = 9: 468f00eaf67 Iterate over the "refs/" namespace in for_each_[raw]ref 10: 1fa68d5d34f = 10: 21febeaa81f Add .gitattributes for the reftable/ directory 11: 86646c834c2 ! 11: 3c84f43cfa0 Add reftable library @@ Metadata ## Commit message ## Add reftable library - Reftable is a format for storing the ref database. Its rationale and - specification is in the preceding commit. + Reftable is a format for storing the ref database. The rationale and format + layout is detailed in commit 35e6c47404 ("reftable: file format documentation", + 2020-05-20). - This imports the upstream library as one big commit. For understanding - the code, it is suggested to read the code in the following order: + This commits imports a fully functioning library for reading and writing + reftables, along with unittests. + + It is suggested to read the code in the following order: * The specification under Documentation/technical/reftable.txt @@ Commit message * block.{c,h} - reading and writing blocks. + * reader.{c,h} - reading a complete reftable file. + * writer.{c,h} - writing a complete reftable file. * merged.{c,h} and pq.{c,h} - reading a stack of reftables @@ reftable/LICENSE (new) +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ## reftable/README.md (new) ## -@@ -+ -+The source code in this directory comes from https://github.com/google/reftable. -+ -+The VERSION file keeps track of the current version of the reftable library. -+ -+To update the library, do: -+ -+ sh reftable/update.sh -+ -+Bugfixes should be accompanied by a test and applied to upstream project at -+https://github.com/google/reftable. - ## reftable/VERSION (new) ## @@ -+2c91c4b305dcbf6500c0806bb1a7fbcfc668510c C: include system.h in compat.h ++d7472cbd1afe6bf3da53e94971b1cc79ce183fa8 Remove outdated bits of the README file ## reftable/basics.c (new) ## @@ @@ reftable/basics.c (new) + +#include "system.h" + -+void put_be24(byte *out, uint32_t i) ++void put_be24(uint8_t *out, uint32_t i) +{ -+ out[0] = (byte)((i >> 16) & 0xff); -+ out[1] = (byte)((i >> 8) & 0xff); -+ out[2] = (byte)(i & 0xff); ++ out[0] = (uint8_t)((i >> 16) & 0xff); ++ out[1] = (uint8_t)((i >> 8) & 0xff); ++ out[2] = (uint8_t)(i & 0xff); +} + -+uint32_t get_be24(byte *in) ++uint32_t get_be24(uint8_t *in) +{ + return (uint32_t)(in[0]) << 16 | (uint32_t)(in[1]) << 8 | + (uint32_t)(in[2]); @@ reftable/basics.h (new) + +/* Bigendian en/decoding of integers */ + -+void put_be24(byte *out, uint32_t i); -+uint32_t get_be24(byte *in); ++void put_be24(uint8_t *out, uint32_t i); ++uint32_t get_be24(uint8_t *in); +void put_be16(uint8_t *out, uint16_t i); + +/* @@ reftable/block.c (new) +} + +int block_writer_register_restart(struct block_writer *w, int n, bool restart, -+ struct slice *key); ++ struct strbuf *key); + -+void block_writer_init(struct block_writer *bw, byte typ, byte *buf, ++void block_writer_init(struct block_writer *bw, uint8_t typ, uint8_t *buf, + uint32_t block_size, uint32_t header_off, int hash_size) +{ + bw->buf = buf; @@ reftable/block.c (new) + bw->entries = 0; + bw->restart_len = 0; + bw->last_key.len = 0; -+ bw->last_key.canary = SLICE_CANARY; +} + -+byte block_writer_type(struct block_writer *bw) ++uint8_t block_writer_type(struct block_writer *bw) +{ + return bw->buf[bw->header_off]; +} @@ reftable/block.c (new) + success */ +int block_writer_add(struct block_writer *w, struct reftable_record *rec) +{ -+ struct slice empty = SLICE_INIT; -+ struct slice last = w->entries % w->restart_interval == 0 ? empty : -+ w->last_key; -+ struct slice out = { ++ struct strbuf empty = STRBUF_INIT; ++ struct strbuf last = ++ w->entries % w->restart_interval == 0 ? empty : w->last_key; ++ struct string_view out = { + .buf = w->buf + w->next, + .len = w->block_size - w->next, -+ .canary = SLICE_CANARY, + }; + -+ struct slice start = out; ++ struct string_view start = out; + + bool restart = false; -+ struct slice key = SLICE_INIT; ++ struct strbuf key = STRBUF_INIT; + int n = 0; + + reftable_record_key(rec, &key); @@ reftable/block.c (new) + reftable_record_val_type(rec)); + if (n < 0) + goto done; -+ slice_consume(&out, n); ++ string_view_consume(&out, n); + + n = reftable_record_encode(rec, out, w->hash_size); + if (n < 0) + goto done; -+ slice_consume(&out, n); ++ string_view_consume(&out, n); + + if (block_writer_register_restart(w, start.len - out.len, restart, + &key) < 0) + goto done; + -+ slice_release(&key); ++ strbuf_release(&key); + return 0; + +done: -+ slice_release(&key); ++ strbuf_release(&key); + return -1; +} + +int block_writer_register_restart(struct block_writer *w, int n, bool restart, -+ struct slice *key) ++ struct strbuf *key) +{ + int rlen = w->restart_len; + if (rlen >= MAX_RESTARTS) { @@ reftable/block.c (new) + + w->next += n; + -+ slice_reset(&w->last_key); -+ slice_addbuf(&w->last_key, key); ++ strbuf_reset(&w->last_key); ++ strbuf_addbuf(&w->last_key, key); + w->entries++; + return 0; +} @@ reftable/block.c (new) + + if (block_writer_type(w) == BLOCK_TYPE_LOG) { + int block_header_skip = 4 + w->header_off; -+ byte *compressed = NULL; ++ uint8_t *compressed = NULL; + int zresult = 0; + uLongf src_len = w->next - block_header_skip; + size_t dest_cap = src_len; @@ reftable/block.c (new) + return w->next; +} + -+byte block_reader_type(struct block_reader *r) ++uint8_t block_reader_type(struct block_reader *r) +{ + return r->block.data[r->header_off]; +} @@ reftable/block.c (new) + int hash_size) +{ + uint32_t full_block_size = table_block_size; -+ byte typ = block->data[header_off]; ++ uint8_t typ = block->data[header_off]; + uint32_t sz = get_be24(block->data + header_off + 1); + + uint16_t restart_count = 0; + uint32_t restart_start = 0; -+ byte *restart_bytes = NULL; ++ uint8_t *restart_bytes = NULL; + + if (!reftable_is_block_type(typ)) + return REFTABLE_FORMAT_ERROR; @@ reftable/block.c (new) + uLongf src_len = block->len - block_header_skip; + /* Log blocks specify the *uncompressed* size in their header. + */ -+ byte *uncompressed = reftable_malloc(sz); ++ uint8_t *uncompressed = reftable_malloc(sz); + + /* Copy over the block header verbatim. It's not compressed. */ + memcpy(uncompressed, block->data, block_header_skip); @@ reftable/block.c (new) +void block_reader_start(struct block_reader *br, struct block_iter *it) +{ + it->br = br; -+ slice_reset(&it->last_key); ++ strbuf_reset(&it->last_key); + it->next_off = br->header_off + 4; +} + +struct restart_find_args { + int error; -+ struct slice key; ++ struct strbuf key; + struct block_reader *r; +}; + @@ reftable/block.c (new) +{ + struct restart_find_args *a = (struct restart_find_args *)args; + uint32_t off = block_reader_restart_offset(a->r, idx); -+ struct slice in = { ++ struct string_view in = { + .buf = a->r->block.data + off, + .len = a->r->block_len - off, -+ .canary = SLICE_CANARY, + }; + + /* the restart key is verbatim in the block, so this could avoid the + alloc for decoding the key */ -+ struct slice rkey = SLICE_INIT; -+ struct slice last_key = SLICE_INIT; -+ byte unused_extra; ++ struct strbuf rkey = STRBUF_INIT; ++ struct strbuf last_key = STRBUF_INIT; ++ uint8_t unused_extra; + int n = reftable_decode_key(&rkey, &unused_extra, last_key, in); + int result; + if (n < 0) { @@ reftable/block.c (new) + return -1; + } + -+ result = slice_cmp(&a->key, &rkey); -+ slice_release(&rkey); ++ result = strbuf_cmp(&a->key, &rkey); ++ strbuf_release(&rkey); + return result; +} + @@ reftable/block.c (new) +{ + dest->br = src->br; + dest->next_off = src->next_off; -+ slice_reset(&dest->last_key); -+ slice_addbuf(&dest->last_key, &src->last_key); ++ strbuf_reset(&dest->last_key); ++ strbuf_addbuf(&dest->last_key, &src->last_key); +} + +int block_iter_next(struct block_iter *it, struct reftable_record *rec) +{ -+ struct slice in = { ++ struct string_view in = { + .buf = it->br->block.data + it->next_off, + .len = it->br->block_len - it->next_off, -+ .canary = SLICE_CANARY, + }; -+ struct slice start = in; -+ struct slice key = SLICE_INIT; -+ byte extra = 0; ++ struct string_view start = in; ++ struct strbuf key = STRBUF_INIT; ++ uint8_t extra = 0; + int n = 0; + + if (it->next_off >= it->br->block_len) @@ reftable/block.c (new) + if (n < 0) + return -1; + -+ slice_consume(&in, n); ++ string_view_consume(&in, n); + n = reftable_record_decode(rec, key, extra, in, it->br->hash_size); + if (n < 0) + return -1; -+ slice_consume(&in, n); ++ string_view_consume(&in, n); + -+ slice_reset(&it->last_key); -+ slice_addbuf(&it->last_key, &key); ++ strbuf_reset(&it->last_key); ++ strbuf_addbuf(&it->last_key, &key); + it->next_off += start.len - in.len; -+ slice_release(&key); ++ strbuf_release(&key); + return 0; +} + -+int block_reader_first_key(struct block_reader *br, struct slice *key) ++int block_reader_first_key(struct block_reader *br, struct strbuf *key) +{ -+ struct slice empty = SLICE_INIT; ++ struct strbuf empty = STRBUF_INIT; + int off = br->header_off + 4; -+ struct slice in = { ++ struct string_view in = { + .buf = br->block.data + off, + .len = br->block_len - off, -+ .canary = SLICE_CANARY, + }; + -+ byte extra = 0; ++ uint8_t extra = 0; + int n = reftable_decode_key(key, &extra, empty, in); + if (n < 0) + return n; @@ reftable/block.c (new) + return 0; +} + -+int block_iter_seek(struct block_iter *it, struct slice *want) ++int block_iter_seek(struct block_iter *it, struct strbuf *want) +{ + return block_reader_seek(it->br, it, want); +} + +void block_iter_close(struct block_iter *it) +{ -+ slice_release(&it->last_key); ++ strbuf_release(&it->last_key); +} + +int block_reader_seek(struct block_reader *br, struct block_iter *it, -+ struct slice *want) ++ struct strbuf *want) +{ + struct restart_find_args args = { + .key = *want, + .r = br, + }; + struct reftable_record rec = reftable_new_record(block_reader_type(br)); -+ struct slice key = SLICE_INIT; ++ struct strbuf key = STRBUF_INIT; + int err = 0; + struct block_iter next = { -+ .last_key = SLICE_INIT, ++ .last_key = STRBUF_INIT, + }; + + int i = binsearch(br->restart_count, &restart_key_less, &args); @@ reftable/block.c (new) + goto done; + + reftable_record_key(&rec, &key); -+ if (err > 0 || slice_cmp(&key, want) >= 0) { ++ if (err > 0 || strbuf_cmp(&key, want) >= 0) { + err = 0; + goto done; + } @@ reftable/block.c (new) + } + +done: -+ slice_release(&key); -+ slice_release(&next.last_key); ++ strbuf_release(&key); ++ strbuf_release(&next.last_key); + reftable_record_destroy(&rec); + + return err; @@ reftable/block.c (new) +void block_writer_clear(struct block_writer *bw) +{ + FREE_AND_NULL(bw->restarts); -+ slice_release(&bw->last_key); ++ strbuf_release(&bw->last_key); + /* the block is not owned. */ +} @@ reftable/block.h (new) + allocation overhead. +*/ +struct block_writer { -+ byte *buf; ++ uint8_t *buf; + uint32_t block_size; + + /* Offset ofof the global header. Nonzero in the first block only. */ @@ reftable/block.h (new) + int restart_interval; + int hash_size; + -+ /* Offset of next byte to write. */ ++ /* Offset of next uint8_t to write. */ + uint32_t next; + uint32_t *restarts; + uint32_t restart_len; + uint32_t restart_cap; + -+ struct slice last_key; ++ struct strbuf last_key; + int entries; +}; + +/* + initializes the blockwriter to write `typ` entries, using `buf` as temporary + storage. `buf` is not owned by the block_writer. */ -+void block_writer_init(struct block_writer *bw, byte typ, byte *buf, ++void block_writer_init(struct block_writer *bw, uint8_t typ, uint8_t *buf, + uint32_t block_size, uint32_t header_off, int hash_size); + +/* + returns the block type (eg. 'r' for ref records. +*/ -+byte block_writer_type(struct block_writer *bw); ++uint8_t block_writer_type(struct block_writer *bw); + +/* appends the record, or -1 if it doesn't fit. */ +int block_writer_add(struct block_writer *w, struct reftable_record *rec); @@ reftable/block.h (new) + + /* size of the data, excluding restart data. */ + uint32_t block_len; -+ byte *restart_bytes; ++ uint8_t *restart_bytes; + uint16_t restart_count; + + /* size of the data in the file. For log blocks, this is the compressed @@ reftable/block.h (new) + struct block_reader *br; + + /* key for last entry we read. */ -+ struct slice last_key; ++ struct strbuf last_key; +}; + +/* initializes a block reader. */ @@ reftable/block.h (new) + +/* Position `it` to the `want` key in the block */ +int block_reader_seek(struct block_reader *br, struct block_iter *it, -+ struct slice *want); ++ struct strbuf *want); + +/* Returns the block type (eg. 'r' for refs) */ -+byte block_reader_type(struct block_reader *r); ++uint8_t block_reader_type(struct block_reader *r); + +/* Decodes the first key in the block */ -+int block_reader_first_key(struct block_reader *br, struct slice *key); ++int block_reader_first_key(struct block_reader *br, struct strbuf *key); + +void block_iter_copy_from(struct block_iter *dest, struct block_iter *src); + @@ reftable/block.h (new) +int block_iter_next(struct block_iter *it, struct reftable_record *rec); + +/* Seek to `want` with in the block pointed to by `it` */ -+int block_iter_seek(struct block_iter *it, struct slice *want); ++int block_iter_seek(struct block_iter *it, struct strbuf *want); + +/* deallocate memory for `it`. The block reader and its block is left intact. */ +void block_iter_close(struct block_iter *it); @@ reftable/block_test.c (new) + const int block_size = 1024; + struct reftable_block block = { 0 }; + struct block_writer bw = { -+ .last_key = SLICE_INIT, ++ .last_key = STRBUF_INIT, + }; + struct reftable_ref_record ref = { 0 }; + struct reftable_record rec = { 0 }; + int i = 0; + int n; + struct block_reader br = { 0 }; -+ struct block_iter it = { .last_key = SLICE_INIT }; ++ struct block_iter it = { .last_key = STRBUF_INIT }; + int j = 0; -+ struct slice want = SLICE_INIT; ++ struct strbuf want = STRBUF_INIT; + + block.data = reftable_calloc(block_size); + block.len = block_size; @@ reftable/block_test.c (new) + + for (i = 0; i < N; i++) { + char name[100]; -+ byte hash[SHA1_SIZE]; ++ uint8_t hash[SHA1_SIZE]; + snprintf(name, sizeof(name), "branch%02d", i); + memset(hash, i, sizeof(hash)); + @@ reftable/block_test.c (new) + block_iter_close(&it); + + for (i = 0; i < N; i++) { -+ struct block_iter it = { .last_key = SLICE_INIT }; -+ slice_reset(&want); -+ slice_addstr(&want, names[i]); ++ struct block_iter it = { .last_key = STRBUF_INIT }; ++ strbuf_reset(&want); ++ strbuf_addstr(&want, names[i]); + + n = block_reader_seek(&br, &it, &want); + assert(n == 0); @@ reftable/block_test.c (new) + + reftable_record_clear(&rec); + reftable_block_done(&br.block); -+ slice_release(&want); ++ strbuf_release(&want); + for (i = 0; i < N; i++) { + reftable_free(names[i]); + } @@ reftable/compat.c (new) +#include "system.h" +#include "basics.h" + -+#ifndef REFTABLE_IN_GITCORE ++#ifdef REFTABLE_STANDALONE + +#include <dirent.h> + +void put_be32(void *p, uint32_t i) +{ -+ byte *out = (byte *)p; ++ uint8_t *out = (uint8_t *)p; + + out[0] = (uint8_t)((i >> 24) & 0xff); + out[1] = (uint8_t)((i >> 16) & 0xff); @@ reftable/compat.c (new) + +void put_be64(void *p, uint64_t v) +{ -+ byte *out = (byte *)p; ++ uint8_t *out = (uint8_t *)p; + int i = sizeof(uint64_t); + while (i--) { + out[i] = (uint8_t)(v & 0xff); @@ reftable/compat.c (new) + } +} + -+uint64_t get_be64(uint8_t *out) ++uint64_t get_be64(void *out) +{ ++ uint8_t *bytes = (uint8_t *)out; + uint64_t v = 0; + int i = 0; + for (i = 0; i < sizeof(uint64_t); i++) { -+ v = (v << 8) | (uint8_t)(out[i] & 0xff); ++ v = (v << 8) | (uint8_t)(bytes[i] & 0xff); + } + return v; +} @@ reftable/compat.h (new) + +#include "system.h" + -+#ifndef REFTABLE_IN_GITCORE ++#ifdef REFTABLE_STANDALONE + +/* functions that git-core provides, for standalone compilation */ +#include <stdint.h> + -+uint64_t get_be64(uint8_t *in); ++uint64_t get_be64(void *in); +void put_be64(void *out, uint64_t i); + +void put_be32(void *out, uint32_t i); @@ reftable/file.c (new) + return 0; +} + -+int reftable_fd_write(void *arg, byte *data, size_t sz) ++int reftable_fd_write(void *arg, const void *data, size_t sz) +{ + int *fdp = (int *)arg; + return write(*fdp, data, sz); @@ reftable/iter.c (new) +{ + struct filtering_ref_iterator *fri = + (struct filtering_ref_iterator *)iter_arg; -+ slice_release(&fri->oid); ++ strbuf_release(&fri->oid); + reftable_iterator_destroy(&fri->it); +} + @@ reftable/iter.c (new) + struct indexed_table_ref_iter *it = (struct indexed_table_ref_iter *)p; + block_iter_close(&it->cur); + reftable_block_done(&it->block_reader.block); -+ slice_release(&it->oid); ++ strbuf_release(&it->oid); +} + +static int indexed_table_ref_iter_next_block(struct indexed_table_ref_iter *it) @@ reftable/iter.c (new) +} + +int new_indexed_table_ref_iter(struct indexed_table_ref_iter **dest, -+ struct reftable_reader *r, byte *oid, ++ struct reftable_reader *r, uint8_t *oid, + int oid_len, uint64_t *offsets, int offset_len) +{ + struct indexed_table_ref_iter empty = INDEXED_TABLE_REF_ITER_INIT; @@ reftable/iter.c (new) + + *itr = empty; + itr->r = r; -+ slice_add(&itr->oid, oid, oid_len); ++ strbuf_add(&itr->oid, oid, oid_len); + + itr->offsets = offsets; + itr->offset_len = offset_len; @@ reftable/iter.h (new) + +#include "block.h" +#include "record.h" -+#include "slice.h" ++#include "strbuf.h" + +struct reftable_iterator_vtable { + int (*next)(void *iter_arg, struct reftable_record *rec); @@ reftable/iter.h (new) +struct filtering_ref_iterator { + bool double_check; + struct reftable_table tab; -+ struct slice oid; ++ struct strbuf oid; + struct reftable_iterator it; +}; +#define FILTERING_REF_ITERATOR_INIT \ + { \ -+ .oid = SLICE_INIT \ ++ .oid = STRBUF_INIT \ + } + +void iterator_from_filtering_ref_iterator(struct reftable_iterator *, @@ reftable/iter.h (new) + */ +struct indexed_table_ref_iter { + struct reftable_reader *r; -+ struct slice oid; ++ struct strbuf oid; + + /* mutable */ + uint64_t *offsets; @@ reftable/iter.h (new) + bool finished; +}; + -+#define INDEXED_TABLE_REF_ITER_INIT \ -+ { \ -+ .cur = { .last_key = SLICE_INIT }, .oid = SLICE_INIT, \ ++#define INDEXED_TABLE_REF_ITER_INIT \ ++ { \ ++ .cur = { .last_key = STRBUF_INIT }, .oid = STRBUF_INIT, \ + } + +void iterator_from_indexed_table_ref_iter(struct reftable_iterator *it, + struct indexed_table_ref_iter *itr); +int new_indexed_table_ref_iter(struct indexed_table_ref_iter **dest, -+ struct reftable_reader *r, byte *oid, ++ struct reftable_reader *r, uint8_t *oid, + int oid_len, uint64_t *offsets, int offset_len); + +#endif @@ reftable/merged.c (new) +#include "iter.h" +#include "pq.h" +#include "reader.h" ++#include "record.h" + +static int merged_iter_init(struct merged_iter *mi) +{ @@ reftable/merged.c (new) +static int merged_iter_next_entry(struct merged_iter *mi, + struct reftable_record *rec) +{ -+ struct slice entry_key = SLICE_INIT; ++ struct strbuf entry_key = STRBUF_INIT; + struct pq_entry entry = { 0 }; + int err = 0; + @@ reftable/merged.c (new) + 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 slice k = SLICE_INIT; ++ struct strbuf k = STRBUF_INIT; + int err = 0, cmp = 0; + + reftable_record_key(&top.rec, &k); + -+ cmp = slice_cmp(&k, &entry_key); -+ slice_release(&k); ++ cmp = strbuf_cmp(&k, &entry_key); ++ strbuf_release(&k); + + if (cmp > 0) { + break; @@ reftable/merged.c (new) + + reftable_record_copy_from(rec, &entry.rec, hash_size(mi->hash_id)); + reftable_record_destroy(&entry.rec); -+ slice_release(&entry_key); ++ strbuf_release(&entry_key); + return 0; +} + @@ reftable/merged.h (new) + struct reftable_iterator *stack; + uint32_t hash_id; + int stack_len; -+ byte typ; ++ uint8_t typ; + bool suppress_deletions; + struct merged_iter_pqueue pq; +}; @@ reftable/merged_test.c (new) + merged_iter_pqueue_clear(&pq); +} + -+static void write_test_table(struct slice *buf, ++static void write_test_table(struct strbuf *buf, + struct reftable_ref_record refs[], int n) +{ + int min = 0xffffffff; @@ reftable/merged_test.c (new) + } + } + -+ w = reftable_new_writer(&slice_add_void, buf, &opts); ++ w = reftable_new_writer(&strbuf_add_void, buf, &opts); + reftable_writer_set_limits(w, min, max); + + for (i = 0; i < n; i++) { @@ reftable/merged_test.c (new) +static struct reftable_merged_table * +merged_table_from_records(struct reftable_ref_record **refs, + struct reftable_block_source **source, int *sizes, -+ struct slice *buf, int n) ++ struct strbuf *buf, int n) +{ + struct reftable_reader **rd = reftable_calloc(n * sizeof(*rd)); + int i = 0; @@ reftable/merged_test.c (new) + *source = reftable_calloc(n * sizeof(**source)); + for (i = 0; i < n; i++) { + write_test_table(&buf[i], refs[i], sizes[i]); -+ block_source_from_slice(&(*source)[i], &buf[i]); ++ block_source_from_strbuf(&(*source)[i], &buf[i]); + + err = reftable_new_reader(&rd[i], &(*source)[i], "name"); + assert_err(err); @@ reftable/merged_test.c (new) + +static void test_merged_between(void) +{ -+ byte hash1[SHA1_SIZE] = { 1, 2, 3, 0 }; ++ uint8_t hash1[SHA1_SIZE] = { 1, 2, 3, 0 }; + + struct reftable_ref_record r1[] = { { + .ref_name = "b", @@ reftable/merged_test.c (new) + + struct reftable_ref_record *refs[] = { r1, r2 }; + int sizes[] = { 1, 1 }; -+ struct slice bufs[2] = { SLICE_INIT, SLICE_INIT }; ++ struct strbuf bufs[2] = { STRBUF_INIT, STRBUF_INIT }; + struct reftable_block_source *bs = NULL; + struct reftable_merged_table *mt = + merged_table_from_records(refs, &bs, sizes, bufs, 2); @@ reftable/merged_test.c (new) + reftable_merged_table_close(mt); + reftable_merged_table_free(mt); + for (i = 0; i < ARRAY_SIZE(bufs); i++) { -+ slice_release(&bufs[i]); ++ strbuf_release(&bufs[i]); + } + reftable_free(bs); +} + +static void test_merged(void) +{ -+ byte hash1[SHA1_SIZE] = { 1 }; -+ byte hash2[SHA1_SIZE] = { 2 }; ++ uint8_t hash1[SHA1_SIZE] = { 1 }; ++ uint8_t hash2[SHA1_SIZE] = { 2 }; + struct reftable_ref_record r1[] = { { + .ref_name = "a", + .update_index = 1, @@ reftable/merged_test.c (new) + + struct reftable_ref_record *refs[] = { r1, r2, r3 }; + int sizes[3] = { 3, 1, 2 }; -+ struct slice bufs[3] = { SLICE_INIT, SLICE_INIT, SLICE_INIT }; ++ struct strbuf bufs[3] = { STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }; + struct reftable_block_source *bs = NULL; + + struct reftable_merged_table *mt = @@ reftable/merged_test.c (new) + reftable_free(out); + + for (i = 0; i < 3; i++) { -+ slice_release(&bufs[i]); ++ strbuf_release(&bufs[i]); + } + reftable_merged_table_close(mt); + reftable_merged_table_free(mt); @@ reftable/pq.c (new) + +#include "pq.h" + ++#include "reftable.h" +#include "system.h" ++#include "basics.h" + +int pq_less(struct pq_entry a, struct pq_entry b) +{ -+ struct slice ak = SLICE_INIT; -+ struct slice bk = SLICE_INIT; ++ struct strbuf ak = STRBUF_INIT; ++ struct strbuf bk = STRBUF_INIT; + int cmp = 0; + reftable_record_key(&a.rec, &ak); + reftable_record_key(&b.rec, &bk); + -+ cmp = slice_cmp(&ak, &bk); ++ cmp = strbuf_cmp(&ak, &bk); + -+ slice_release(&ak); -+ slice_release(&bk); ++ strbuf_release(&ak); ++ strbuf_release(&bk); + + if (cmp == 0) + return a.index > b.index; @@ reftable/reader.c (new) +} + +static struct reftable_reader_offsets * -+reader_offsets_for(struct reftable_reader *r, byte typ) ++reader_offsets_for(struct reftable_reader *r, uint8_t typ) +{ + switch (typ) { + case BLOCK_TYPE_REF: @@ reftable/reader.c (new) + return r->name; +} + -+static int parse_footer(struct reftable_reader *r, byte *footer, byte *header) ++static int parse_footer(struct reftable_reader *r, uint8_t *footer, ++ uint8_t *header) +{ -+ byte *f = footer; -+ byte first_block_typ; ++ uint8_t *f = footer; ++ uint8_t first_block_typ; + int err = 0; + uint32_t computed_crc; + uint32_t file_crc; @@ reftable/reader.c (new) + +struct table_iter { + struct reftable_reader *r; -+ byte typ; ++ uint8_t typ; + uint64_t block_off; + struct block_iter bi; + bool finished; +}; -+#define TABLE_ITER_INIT \ -+ { \ -+ .bi = {.last_key = SLICE_INIT } \ ++#define TABLE_ITER_INIT \ ++ { \ ++ .bi = {.last_key = STRBUF_INIT } \ + } + +static void table_iter_copy_from(struct table_iter *dest, @@ reftable/reader.c (new) + ti->bi.next_off = 0; +} + -+static int32_t extract_block_size(byte *data, byte *typ, uint64_t off, ++static int32_t extract_block_size(uint8_t *data, uint8_t *typ, uint64_t off, + int version) +{ + int32_t result = 0; @@ reftable/reader.c (new) +} + +int reader_init_block_reader(struct reftable_reader *r, struct block_reader *br, -+ uint64_t next_off, byte want_typ) ++ uint64_t next_off, uint8_t want_typ) +{ + int32_t guess_block_size = r->block_size ? r->block_size : + DEFAULT_BLOCK_SIZE; + struct reftable_block block = { 0 }; -+ byte block_typ = 0; ++ uint8_t block_typ = 0; + int err = 0; + uint32_t header_off = next_off ? 0 : header_size(r->version); + int32_t block_size = 0; @@ reftable/reader.c (new) +} + +static int reader_table_iter_at(struct reftable_reader *r, -+ struct table_iter *ti, uint64_t off, byte typ) ++ struct table_iter *ti, uint64_t off, ++ uint8_t typ) +{ + struct block_reader br = { 0 }; + struct block_reader *brp = NULL; @@ reftable/reader.c (new) +} + +static int reader_start(struct reftable_reader *r, struct table_iter *ti, -+ byte typ, bool index) ++ uint8_t typ, bool index) +{ + struct reftable_reader_offsets *offs = reader_offsets_for(r, typ); + uint64_t off = offs->offset; @@ reftable/reader.c (new) +{ + struct reftable_record rec = + reftable_new_record(reftable_record_type(want)); -+ struct slice want_key = SLICE_INIT; -+ struct slice got_key = SLICE_INIT; ++ struct strbuf want_key = STRBUF_INIT; ++ struct strbuf got_key = STRBUF_INIT; + struct table_iter next = TABLE_ITER_INIT; + int err = -1; + @@ reftable/reader.c (new) + if (err < 0) + goto done; + -+ if (slice_cmp(&got_key, &want_key) > 0) { ++ if (strbuf_cmp(&got_key, &want_key) > 0) { + table_iter_block_done(&next); + break; + } @@ reftable/reader.c (new) +done: + block_iter_close(&next.bi); + reftable_record_destroy(&rec); -+ slice_release(&want_key); -+ slice_release(&got_key); ++ strbuf_release(&want_key); ++ strbuf_release(&got_key); + return err; +} + @@ reftable/reader.c (new) + struct reftable_iterator *it, + struct reftable_record *rec) +{ -+ struct reftable_index_record want_index = { .last_key = SLICE_INIT }; ++ struct reftable_index_record want_index = { .last_key = STRBUF_INIT }; + struct reftable_record want_index_rec = { 0 }; -+ struct reftable_index_record index_result = { .last_key = SLICE_INIT }; ++ struct reftable_index_record index_result = { .last_key = STRBUF_INIT }; + struct reftable_record index_result_rec = { 0 }; + struct table_iter index_iter = TABLE_ITER_INIT; + struct table_iter next = TABLE_ITER_INIT; @@ reftable/reader.c (new) +int reader_seek(struct reftable_reader *r, struct reftable_iterator *it, + struct reftable_record *rec) +{ -+ byte typ = reftable_record_type(rec); ++ uint8_t typ = reftable_record_type(rec); + + struct reftable_reader_offsets *offs = reader_offsets_for(r, typ); + if (!offs->present) { @@ reftable/reader.c (new) + +static int reftable_reader_refs_for_indexed(struct reftable_reader *r, + struct reftable_iterator *it, -+ byte *oid) ++ uint8_t *oid) +{ + struct reftable_obj_record want = { + .hash_prefix = oid, @@ reftable/reader.c (new) + +static int reftable_reader_refs_for_unindexed(struct reftable_reader *r, + struct reftable_iterator *it, -+ byte *oid) ++ uint8_t *oid) +{ + struct table_iter ti_empty = TABLE_ITER_INIT; + struct table_iter *ti = reftable_calloc(sizeof(struct table_iter)); @@ reftable/reader.c (new) + filter = reftable_malloc(sizeof(struct filtering_ref_iterator)); + *filter = empty; + -+ slice_add(&filter->oid, oid, oid_len); ++ strbuf_add(&filter->oid, oid, oid_len); + reftable_table_from_reader(&filter->tab, r); + filter->double_check = false; + iterator_from_table_iter(&filter->it, ti); @@ reftable/reader.c (new) +} + +int reftable_reader_refs_for(struct reftable_reader *r, -+ struct reftable_iterator *it, byte *oid) ++ struct reftable_iterator *it, uint8_t *oid) +{ + if (r->obj_offsets.present) + return reftable_reader_refs_for_indexed(r, it, oid); @@ reftable/reader.h (new) + +/* initialize a block reader to read from `r` */ +int reader_init_block_reader(struct reftable_reader *r, struct block_reader *br, -+ uint64_t next_off, byte want_typ); ++ uint64_t next_off, uint8_t want_typ); + +#endif @@ reftable/record.c (new) +#include "record.h" + +#include "system.h" -+ +#include "constants.h" +#include "reftable.h" ++#include "basics.h" + -+int get_var_int(uint64_t *dest, struct slice *in) ++int get_var_int(uint64_t *dest, struct string_view *in) +{ + int ptr = 0; + uint64_t val; @@ reftable/record.c (new) + return ptr + 1; +} + -+int put_var_int(struct slice *dest, uint64_t val) ++int put_var_int(struct string_view *dest, uint64_t val) +{ -+ byte buf[10] = { 0 }; ++ uint8_t buf[10] = { 0 }; + int i = 9; + int n = 0; -+ buf[i] = (byte)(val & 0x7f); ++ buf[i] = (uint8_t)(val & 0x7f); + i--; + while (true) { + val >>= 7; @@ reftable/record.c (new) + break; + } + val--; -+ buf[i] = 0x80 | (byte)(val & 0x7f); ++ buf[i] = 0x80 | (uint8_t)(val & 0x7f); + i--; + } + @@ reftable/record.c (new) + return n; +} + -+int reftable_is_block_type(byte typ) ++int reftable_is_block_type(uint8_t typ) +{ + switch (typ) { + case BLOCK_TYPE_REF: @@ reftable/record.c (new) + return false; +} + -+static int decode_string(struct slice *dest, struct slice in) ++static int decode_string(struct strbuf *dest, struct string_view in) +{ + int start_len = in.len; + uint64_t tsize = 0; + int n = get_var_int(&tsize, &in); + if (n <= 0) + return -1; -+ slice_consume(&in, n); ++ string_view_consume(&in, n); + if (in.len < tsize) + return -1; + -+ slice_reset(dest); -+ slice_add(dest, in.buf, tsize); -+ slice_consume(&in, tsize); ++ strbuf_reset(dest); ++ strbuf_add(dest, in.buf, tsize); ++ string_view_consume(&in, tsize); + + return start_len - in.len; +} + -+static int encode_string(char *str, struct slice s) ++static int encode_string(char *str, struct string_view s) +{ -+ struct slice start = s; ++ struct string_view start = s; + int l = strlen(str); + int n = put_var_int(&s, l); + if (n < 0) + return -1; -+ slice_consume(&s, n); ++ string_view_consume(&s, n); + if (s.len < l) + return -1; + memcpy(s.buf, str, l); -+ slice_consume(&s, l); ++ string_view_consume(&s, l); + + return start.len - s.len; +} + -+int reftable_encode_key(bool *restart, struct slice dest, struct slice prev_key, -+ struct slice key, byte extra) ++int reftable_encode_key(bool *restart, struct string_view dest, ++ struct strbuf prev_key, struct strbuf key, ++ uint8_t extra) +{ -+ struct slice start = dest; ++ struct string_view start = dest; + int prefix_len = common_prefix_size(&prev_key, &key); + uint64_t suffix_len = key.len - prefix_len; + int n = put_var_int(&dest, (uint64_t)prefix_len); + if (n < 0) + return -1; -+ slice_consume(&dest, n); ++ string_view_consume(&dest, n); + + *restart = (prefix_len == 0); + + n = put_var_int(&dest, suffix_len << 3 | (uint64_t)extra); + if (n < 0) + return -1; -+ slice_consume(&dest, n); ++ string_view_consume(&dest, n); + + if (dest.len < suffix_len) + return -1; + memcpy(dest.buf, key.buf + prefix_len, suffix_len); -+ slice_consume(&dest, suffix_len); ++ string_view_consume(&dest, suffix_len); + + return start.len - dest.len; +} + -+int reftable_decode_key(struct slice *key, byte *extra, struct slice last_key, -+ struct slice in) ++int reftable_decode_key(struct strbuf *key, uint8_t *extra, ++ struct strbuf last_key, struct string_view in) +{ + int start_len = in.len; + uint64_t prefix_len = 0; @@ reftable/record.c (new) + int n = get_var_int(&prefix_len, &in); + if (n < 0) + return -1; -+ slice_consume(&in, n); ++ string_view_consume(&in, n); + + if (prefix_len > last_key.len) + return -1; @@ reftable/record.c (new) + n = get_var_int(&suffix_len, &in); + if (n <= 0) + return -1; -+ slice_consume(&in, n); ++ string_view_consume(&in, n); + -+ *extra = (byte)(suffix_len & 0x7); ++ *extra = (uint8_t)(suffix_len & 0x7); + suffix_len >>= 3; + + if (in.len < suffix_len) + return -1; + -+ slice_reset(key); -+ slice_add(key, last_key.buf, prefix_len); -+ slice_add(key, in.buf, suffix_len); -+ slice_consume(&in, suffix_len); ++ strbuf_reset(key); ++ strbuf_add(key, last_key.buf, prefix_len); ++ strbuf_add(key, in.buf, suffix_len); ++ string_view_consume(&in, suffix_len); + + return start_len - in.len; +} + -+static void reftable_ref_record_key(const void *r, struct slice *dest) ++static void reftable_ref_record_key(const void *r, struct strbuf *dest) +{ + const struct reftable_ref_record *rec = + (const struct reftable_ref_record *)r; -+ slice_reset(dest); -+ slice_addstr(dest, rec->ref_name); ++ strbuf_reset(dest); ++ strbuf_addstr(dest, rec->ref_name); +} + +static void reftable_ref_record_copy_from(void *rec, const void *src_rec, @@ reftable/record.c (new) + return 'a' + (c - 10); +} + -+static void hex_format(char *dest, byte *src, int hash_size) ++static void hex_format(char *dest, uint8_t *src, int hash_size) +{ + assert(hash_size > 0); + if (src != NULL) { @@ reftable/record.c (new) + memset(ref, 0, sizeof(struct reftable_ref_record)); +} + -+static byte reftable_ref_record_val_type(const void *rec) ++static uint8_t reftable_ref_record_val_type(const void *rec) +{ + const struct reftable_ref_record *r = + (const struct reftable_ref_record *)rec; @@ reftable/record.c (new) + return 0; +} + -+static int reftable_ref_record_encode(const void *rec, struct slice s, ++static int reftable_ref_record_encode(const void *rec, struct string_view s, + int hash_size) +{ + const struct reftable_ref_record *r = + (const struct reftable_ref_record *)rec; -+ struct slice start = s; ++ struct string_view start = s; + int n = put_var_int(&s, r->update_index); + assert(hash_size > 0); + if (n < 0) + return -1; -+ slice_consume(&s, n); ++ string_view_consume(&s, n); + + if (r->value != NULL) { + if (s.len < hash_size) { + return -1; + } + memcpy(s.buf, r->value, hash_size); -+ slice_consume(&s, hash_size); ++ string_view_consume(&s, hash_size); + } + + if (r->target_value != NULL) { @@ reftable/record.c (new) + return -1; + } + memcpy(s.buf, r->target_value, hash_size); -+ slice_consume(&s, hash_size); ++ string_view_consume(&s, hash_size); + } + + if (r->target != NULL) { @@ reftable/record.c (new) + if (n < 0) { + return -1; + } -+ slice_consume(&s, n); ++ string_view_consume(&s, n); + } + + return start.len - s.len; +} + -+static int reftable_ref_record_decode(void *rec, struct slice key, -+ byte val_type, struct slice in, ++static int reftable_ref_record_decode(void *rec, struct strbuf key, ++ uint8_t val_type, struct string_view in, + int hash_size) +{ + struct reftable_ref_record *r = (struct reftable_ref_record *)rec; -+ struct slice start = in; ++ struct string_view start = in; + bool seen_value = false; + bool seen_target_value = false; + bool seen_target = false; @@ reftable/record.c (new) + return n; + assert(hash_size > 0); + -+ slice_consume(&in, n); ++ string_view_consume(&in, n); + + r->ref_name = reftable_realloc(r->ref_name, key.len + 1); + memcpy(r->ref_name, key.buf, key.len); @@ reftable/record.c (new) + } + seen_value = true; + memcpy(r->value, in.buf, hash_size); -+ slice_consume(&in, hash_size); ++ string_view_consume(&in, hash_size); + if (val_type == 1) { + break; + } @@ reftable/record.c (new) + } + seen_target_value = true; + memcpy(r->target_value, in.buf, hash_size); -+ slice_consume(&in, hash_size); ++ string_view_consume(&in, hash_size); + break; + case 3: { -+ struct slice dest = SLICE_INIT; ++ struct strbuf dest = STRBUF_INIT; + int n = decode_string(&dest, in); + if (n < 0) { + return -1; + } -+ slice_consume(&in, n); ++ string_view_consume(&in, n); + seen_target = true; + if (r->target != NULL) { + reftable_free(r->target); + } -+ r->target = (char *)slice_as_string(&dest); ++ r->target = dest.buf; + } break; + + case 0: @@ reftable/record.c (new) + .is_deletion = &reftable_ref_record_is_deletion_void, +}; + -+static void reftable_obj_record_key(const void *r, struct slice *dest) ++static void reftable_obj_record_key(const void *r, struct strbuf *dest) +{ + const struct reftable_obj_record *rec = + (const struct reftable_obj_record *)r; -+ slice_reset(dest); -+ slice_add(dest, rec->hash_prefix, rec->hash_prefix_len); ++ strbuf_reset(dest); ++ strbuf_add(dest, rec->hash_prefix, rec->hash_prefix_len); +} + +static void reftable_obj_record_clear(void *rec) @@ reftable/record.c (new) + memcpy(obj->offsets, src->offsets, olen); +} + -+static byte reftable_obj_record_val_type(const void *rec) ++static uint8_t reftable_obj_record_val_type(const void *rec) +{ + struct reftable_obj_record *r = (struct reftable_obj_record *)rec; + if (r->offset_len > 0 && r->offset_len < 8) @@ reftable/record.c (new) + return 0; +} + -+static int reftable_obj_record_encode(const void *rec, struct slice s, ++static int reftable_obj_record_encode(const void *rec, struct string_view s, + int hash_size) +{ + struct reftable_obj_record *r = (struct reftable_obj_record *)rec; -+ struct slice start = s; ++ struct string_view start = s; + int i = 0; + int n = 0; + uint64_t last = 0; @@ reftable/record.c (new) + if (n < 0) { + return -1; + } -+ slice_consume(&s, n); ++ string_view_consume(&s, n); + } + if (r->offset_len == 0) + return start.len - s.len; + n = put_var_int(&s, r->offsets[0]); + if (n < 0) + return -1; -+ slice_consume(&s, n); ++ string_view_consume(&s, n); + + last = r->offsets[0]; + for (i = 1; i < r->offset_len; i++) { @@ reftable/record.c (new) + if (n < 0) { + return -1; + } -+ slice_consume(&s, n); ++ string_view_consume(&s, n); + last = r->offsets[i]; + } + return start.len - s.len; +} + -+static int reftable_obj_record_decode(void *rec, struct slice key, -+ byte val_type, struct slice in, ++static int reftable_obj_record_decode(void *rec, struct strbuf key, ++ uint8_t val_type, struct string_view in, + int hash_size) +{ -+ struct slice start = in; ++ struct string_view start = in; + struct reftable_obj_record *r = (struct reftable_obj_record *)rec; + uint64_t count = val_type; + int n = 0; @@ reftable/record.c (new) + return n; + } + -+ slice_consume(&in, n); ++ string_view_consume(&in, n); + } + + r->offsets = NULL; @@ reftable/record.c (new) + n = get_var_int(&r->offsets[0], &in); + if (n < 0) + return n; -+ slice_consume(&in, n); ++ string_view_consume(&in, n); + + last = r->offsets[0]; + j = 1; @@ reftable/record.c (new) + if (n < 0) { + return n; + } -+ slice_consume(&in, n); ++ string_view_consume(&in, n); + + last = r->offsets[j] = (delta + last); + j++; @@ reftable/record.c (new) + printf("%s\n\n%s\n}\n", hex, log->message); +} + -+static void reftable_log_record_key(const void *r, struct slice *dest) ++static void reftable_log_record_key(const void *r, struct strbuf *dest) +{ + const struct reftable_log_record *rec = + (const struct reftable_log_record *)r; + int len = strlen(rec->ref_name); -+ byte i64[8]; ++ uint8_t i64[8]; + uint64_t ts = 0; -+ slice_reset(dest); -+ slice_add(dest, (byte *)rec->ref_name, len + 1); ++ strbuf_reset(dest); ++ strbuf_add(dest, (uint8_t *)rec->ref_name, len + 1); + + ts = (~ts) - rec->update_index; + put_be64(&i64[0], ts); -+ slice_add(dest, i64, sizeof(i64)); ++ strbuf_add(dest, i64, sizeof(i64)); +} + +static void reftable_log_record_copy_from(void *rec, const void *src_rec, @@ reftable/record.c (new) + memset(r, 0, sizeof(struct reftable_log_record)); +} + -+static byte reftable_log_record_val_type(const void *rec) ++static uint8_t reftable_log_record_val_type(const void *rec) +{ + const struct reftable_log_record *log = + (const struct reftable_log_record *)rec; @@ reftable/record.c (new) + return reftable_log_record_is_deletion(log) ? 0 : 1; +} + -+static byte zero[SHA256_SIZE] = { 0 }; ++static uint8_t zero[SHA256_SIZE] = { 0 }; + -+static int reftable_log_record_encode(const void *rec, struct slice s, ++static int reftable_log_record_encode(const void *rec, struct string_view s, + int hash_size) +{ + struct reftable_log_record *r = (struct reftable_log_record *)rec; -+ struct slice start = s; ++ struct string_view start = s; + int n = 0; -+ byte *oldh = r->old_hash; -+ byte *newh = r->new_hash; ++ uint8_t *oldh = r->old_hash; ++ uint8_t *newh = r->new_hash; + if (reftable_log_record_is_deletion(r)) + return 0; + @@ reftable/record.c (new) + + memcpy(s.buf, oldh, hash_size); + memcpy(s.buf + hash_size, newh, hash_size); -+ slice_consume(&s, 2 * hash_size); ++ string_view_consume(&s, 2 * hash_size); + + n = encode_string(r->name ? r->name : "", s); + if (n < 0) + return -1; -+ slice_consume(&s, n); ++ string_view_consume(&s, n); + + n = encode_string(r->email ? r->email : "", s); + if (n < 0) + return -1; -+ slice_consume(&s, n); ++ string_view_consume(&s, n); + + n = put_var_int(&s, r->time); + if (n < 0) + return -1; -+ slice_consume(&s, n); ++ string_view_consume(&s, n); + + if (s.len < 2) + return -1; + + put_be16(s.buf, r->tz_offset); -+ slice_consume(&s, 2); ++ string_view_consume(&s, 2); + + n = encode_string(r->message ? r->message : "", s); + if (n < 0) + return -1; -+ slice_consume(&s, n); ++ string_view_consume(&s, n); + + return start.len - s.len; +} + -+static int reftable_log_record_decode(void *rec, struct slice key, -+ byte val_type, struct slice in, ++static int reftable_log_record_decode(void *rec, struct strbuf key, ++ uint8_t val_type, struct string_view in, + int hash_size) +{ -+ struct slice start = in; ++ struct string_view start = in; + struct reftable_log_record *r = (struct reftable_log_record *)rec; + uint64_t max = 0; + uint64_t ts = 0; -+ struct slice dest = SLICE_INIT; ++ struct strbuf dest = STRBUF_INIT; + int n; + + if (key.len <= 9 || key.buf[key.len - 9] != 0) @@ reftable/record.c (new) + memcpy(r->old_hash, in.buf, hash_size); + memcpy(r->new_hash, in.buf + hash_size, hash_size); + -+ slice_consume(&in, 2 * hash_size); ++ string_view_consume(&in, 2 * hash_size); + + n = decode_string(&dest, in); + if (n < 0) + goto done; -+ slice_consume(&in, n); ++ string_view_consume(&in, n); + + r->name = reftable_realloc(r->name, dest.len + 1); + memcpy(r->name, dest.buf, dest.len); + r->name[dest.len] = 0; + -+ slice_reset(&dest); ++ strbuf_reset(&dest); + n = decode_string(&dest, in); + if (n < 0) + goto done; -+ slice_consume(&in, n); ++ string_view_consume(&in, n); + + r->email = reftable_realloc(r->email, dest.len + 1); + memcpy(r->email, dest.buf, dest.len); @@ reftable/record.c (new) + n = get_var_int(&ts, &in); + if (n < 0) + goto done; -+ slice_consume(&in, n); ++ string_view_consume(&in, n); + r->time = ts; + if (in.len < 2) + goto done; + + r->tz_offset = get_be16(in.buf); -+ slice_consume(&in, 2); ++ string_view_consume(&in, 2); + -+ slice_reset(&dest); ++ strbuf_reset(&dest); + n = decode_string(&dest, in); + if (n < 0) + goto done; -+ slice_consume(&in, n); ++ string_view_consume(&in, n); + + r->message = reftable_realloc(r->message, dest.len + 1); + memcpy(r->message, dest.buf, dest.len); + r->message[dest.len] = 0; + -+ slice_release(&dest); ++ strbuf_release(&dest); + return start.len - in.len; + +done: -+ slice_release(&dest); ++ strbuf_release(&dest); + return REFTABLE_FORMAT_ERROR; +} + @@ reftable/record.c (new) + return 0 == strcmp(a, b); +} + -+static bool zero_hash_eq(byte *a, byte *b, int sz) ++static bool zero_hash_eq(uint8_t *a, uint8_t *b, int sz) +{ + if (a == NULL) + a = zero; @@ reftable/record.c (new) + .is_deletion = &reftable_log_record_is_deletion_void, +}; + -+struct reftable_record reftable_new_record(byte typ) ++struct reftable_record reftable_new_record(uint8_t typ) +{ + struct reftable_record rec = { NULL }; + switch (typ) { @@ reftable/record.c (new) + return rec; + } + case BLOCK_TYPE_INDEX: { -+ struct reftable_index_record empty = { .last_key = SLICE_INIT }; ++ struct reftable_index_record empty = { .last_key = ++ STRBUF_INIT }; + struct reftable_index_record *r = + reftable_calloc(sizeof(struct reftable_index_record)); + *r = empty; @@ reftable/record.c (new) + reftable_free(reftable_record_yield(rec)); +} + -+static void reftable_index_record_key(const void *r, struct slice *dest) ++static void reftable_index_record_key(const void *r, struct strbuf *dest) +{ + struct reftable_index_record *rec = (struct reftable_index_record *)r; -+ slice_reset(dest); -+ slice_addbuf(dest, &rec->last_key); ++ strbuf_reset(dest); ++ strbuf_addbuf(dest, &rec->last_key); +} + +static void reftable_index_record_copy_from(void *rec, const void *src_rec, @@ reftable/record.c (new) + struct reftable_index_record *src = + (struct reftable_index_record *)src_rec; + -+ slice_reset(&dst->last_key); -+ slice_addbuf(&dst->last_key, &src->last_key); ++ strbuf_reset(&dst->last_key); ++ strbuf_addbuf(&dst->last_key, &src->last_key); + dst->offset = src->offset; +} + +static void reftable_index_record_clear(void *rec) +{ + struct reftable_index_record *idx = (struct reftable_index_record *)rec; -+ slice_release(&idx->last_key); ++ strbuf_release(&idx->last_key); +} + -+static byte reftable_index_record_val_type(const void *rec) ++static uint8_t reftable_index_record_val_type(const void *rec) +{ + return 0; +} + -+static int reftable_index_record_encode(const void *rec, struct slice out, ++static int reftable_index_record_encode(const void *rec, struct string_view out, + int hash_size) +{ + const struct reftable_index_record *r = + (const struct reftable_index_record *)rec; -+ struct slice start = out; ++ struct string_view start = out; + + int n = put_var_int(&out, r->offset); + if (n < 0) + return n; + -+ slice_consume(&out, n); ++ string_view_consume(&out, n); + + return start.len - out.len; +} + -+static int reftable_index_record_decode(void *rec, struct slice key, -+ byte val_type, struct slice in, ++static int reftable_index_record_decode(void *rec, struct strbuf key, ++ uint8_t val_type, struct string_view in, + int hash_size) +{ -+ struct slice start = in; ++ struct string_view start = in; + struct reftable_index_record *r = (struct reftable_index_record *)rec; + int n = 0; + -+ slice_reset(&r->last_key); -+ slice_addbuf(&r->last_key, &key); ++ strbuf_reset(&r->last_key); ++ strbuf_addbuf(&r->last_key, &key); + + n = get_var_int(&r->offset, &in); + if (n < 0) + return n; + -+ slice_consume(&in, n); ++ string_view_consume(&in, n); + return start.len - in.len; +} + @@ reftable/record.c (new) + .is_deletion = ¬_a_deletion, +}; + -+void reftable_record_key(struct reftable_record *rec, struct slice *dest) ++void reftable_record_key(struct reftable_record *rec, struct strbuf *dest) +{ + rec->ops->key(rec->data, dest); +} + -+byte reftable_record_type(struct reftable_record *rec) ++uint8_t reftable_record_type(struct reftable_record *rec) +{ + return rec->ops->type; +} + -+int reftable_record_encode(struct reftable_record *rec, struct slice dest, ++int reftable_record_encode(struct reftable_record *rec, struct string_view dest, + int hash_size) +{ + return rec->ops->encode(rec->data, dest, hash_size); @@ reftable/record.c (new) + rec->ops->copy_from(rec->data, src->data, hash_size); +} + -+byte reftable_record_val_type(struct reftable_record *rec) ++uint8_t reftable_record_val_type(struct reftable_record *rec) +{ + return rec->ops->val_type(rec->data); +} + -+int reftable_record_decode(struct reftable_record *rec, struct slice key, -+ byte extra, struct slice src, int hash_size) ++int reftable_record_decode(struct reftable_record *rec, struct strbuf key, ++ uint8_t extra, struct string_view src, int hash_size) +{ + return rec->ops->decode(rec->data, key, extra, src, hash_size); +} @@ reftable/record.c (new) + return (struct reftable_log_record *)rec->data; +} + -+static bool hash_equal(byte *a, byte *b, int hash_size) ++static bool hash_equal(uint8_t *a, uint8_t *b, int hash_size) +{ + if (a != NULL && b != NULL) + return !memcmp(a, b, hash_size); @@ reftable/record.c (new) + return SHA256_SIZE; + } + abort(); ++} ++ ++void string_view_consume(struct string_view *s, int n) ++{ ++ s->buf += n; ++ s->len -= n; +} ## reftable/record.h (new) ## @@ reftable/record.h (new) +#define RECORD_H + +#include "reftable.h" -+#include "slice.h" ++#include "strbuf.h" ++#include "system.h" ++ ++/* ++ A substring of existing string data. This structure takes no responsibility ++ for the lifetime of the data it points to. ++*/ ++struct string_view { ++ uint8_t *buf; ++ int len; ++}; ++ ++/* Advance `s.buf` by `n`, and decrease length. */ ++void string_view_consume(struct string_view *s, int n); + +/* utilities for de/encoding varints */ + -+int get_var_int(uint64_t *dest, struct slice *in); -+int put_var_int(struct slice *dest, uint64_t val); ++int get_var_int(uint64_t *dest, struct string_view *in); ++int put_var_int(struct string_view *dest, uint64_t val); + +/* Methods for records. */ +struct reftable_record_vtable { -+ /* encode the key of to a byte slice. */ -+ void (*key)(const void *rec, struct slice *dest); ++ /* encode the key of to a uint8_t strbuf. */ ++ void (*key)(const void *rec, struct strbuf *dest); + + /* The record type of ('r' for ref). */ -+ byte type; ++ uint8_t type; + + void (*copy_from)(void *dest, const void *src, int hash_size); + + /* a value of [0..7], indicating record subvariants (eg. ref vs. symref + * vs ref deletion) */ -+ byte (*val_type)(const void *rec); ++ uint8_t (*val_type)(const void *rec); + + /* encodes rec into dest, returning how much space was used. */ -+ int (*encode)(const void *rec, struct slice dest, int hash_size); ++ int (*encode)(const void *rec, struct string_view dest, int hash_size); + + /* decode data from `src` into the record. */ -+ int (*decode)(void *rec, struct slice key, byte extra, struct slice src, -+ int hash_size); ++ int (*decode)(void *rec, struct strbuf key, uint8_t extra, ++ struct string_view src, int hash_size); + + /* deallocate and null the record. */ + void (*clear)(void *rec); @@ reftable/record.h (new) +}; + +/* returns true for recognized block types. Block start with the block type. */ -+int reftable_is_block_type(byte typ); ++int reftable_is_block_type(uint8_t typ); + +/* creates a malloced record of the given type. Dispose with record_destroy */ -+struct reftable_record reftable_new_record(byte typ); ++struct reftable_record reftable_new_record(uint8_t typ); + +extern struct reftable_record_vtable reftable_ref_record_vtable; + +/* Encode `key` into `dest`. Sets `restart` to indicate a restart. Returns + number of bytes written. */ -+int reftable_encode_key(bool *restart, struct slice dest, struct slice prev_key, -+ struct slice key, byte extra); ++int reftable_encode_key(bool *restart, struct string_view dest, ++ struct strbuf prev_key, struct strbuf key, ++ uint8_t extra); + +/* Decode into `key` and `extra` from `in` */ -+int reftable_decode_key(struct slice *key, byte *extra, struct slice last_key, -+ struct slice in); ++int reftable_decode_key(struct strbuf *key, uint8_t *extra, ++ struct strbuf last_key, struct string_view in); + +/* reftable_index_record are used internally to speed up lookups. */ +struct reftable_index_record { + uint64_t offset; /* Offset of block */ -+ struct slice last_key; /* Last key of the block. */ ++ struct strbuf last_key; /* Last key of the block. */ +}; + +/* reftable_obj_record stores an object ID => ref mapping. */ +struct reftable_obj_record { -+ byte *hash_prefix; /* leading bytes of the object ID */ ++ uint8_t *hash_prefix; /* leading bytes of the object ID */ + int hash_prefix_len; /* number of leading bytes. Constant + * across a single table. */ + uint64_t *offsets; /* a vector of file offsets. */ @@ reftable/record.h (new) + +/* see struct record_vtable */ + -+void reftable_record_key(struct reftable_record *rec, struct slice *dest); -+byte reftable_record_type(struct reftable_record *rec); ++void reftable_record_key(struct reftable_record *rec, struct strbuf *dest); ++uint8_t reftable_record_type(struct reftable_record *rec); +void reftable_record_copy_from(struct reftable_record *rec, + struct reftable_record *src, int hash_size); -+byte reftable_record_val_type(struct reftable_record *rec); -+int reftable_record_encode(struct reftable_record *rec, struct slice dest, ++uint8_t reftable_record_val_type(struct reftable_record *rec); ++int reftable_record_encode(struct reftable_record *rec, struct string_view dest, ++ int hash_size); ++int reftable_record_decode(struct reftable_record *rec, struct strbuf key, ++ uint8_t extra, struct string_view src, + int hash_size); -+int reftable_record_decode(struct reftable_record *rec, struct slice key, -+ byte extra, struct slice src, int hash_size); +bool reftable_record_is_deletion(struct reftable_record *rec); + +/* zeroes out the embedded record */ @@ reftable/record_test.c (new) + ((uint64_t)1 << 63) + ((uint64_t)1 << 63) - 1 }; + int i = 0; + for (i = 0; i < ARRAY_SIZE(inputs); i++) { -+ byte dest[10]; -+ -+ struct slice out = { .buf = dest, .len = 10, .cap = 10 }; ++ uint8_t dest[10]; + ++ struct string_view out = { ++ .buf = dest, ++ .len = sizeof(dest), ++ }; + uint64_t in = inputs[i]; + int n = put_var_int(&out, in); + uint64_t got = 0; @@ reftable/record_test.c (new) + + int i = 0; + for (i = 0; i < ARRAY_SIZE(cases); i++) { -+ struct slice a = SLICE_INIT; -+ struct slice b = SLICE_INIT; -+ slice_addstr(&a, cases[i].a); -+ slice_addstr(&b, cases[i].b); ++ struct strbuf a = STRBUF_INIT; ++ struct strbuf b = STRBUF_INIT; ++ strbuf_addstr(&a, cases[i].a); ++ strbuf_addstr(&b, cases[i].b); + assert(common_prefix_size(&a, &b) == cases[i].want); + -+ slice_release(&a); -+ slice_release(&b); ++ strbuf_release(&a); ++ strbuf_release(&b); + } +} + -+static void set_hash(byte *h, int j) ++static void set_hash(uint8_t *h, int j) +{ + int i = 0; + for (i = 0; i < hash_size(SHA1_ID); i++) { @@ reftable/record_test.c (new) + .target = xstrdup("old value"), + }; + struct reftable_record rec_out = { 0 }; -+ struct slice key = SLICE_INIT; ++ struct strbuf key = STRBUF_INIT; + struct reftable_record rec = { 0 }; -+ struct slice dest = SLICE_INIT; ++ uint8_t buffer[1024] = { 0 }; ++ struct string_view dest = { ++ .buf = buffer, ++ .len = sizeof(buffer), ++ }; ++ + int n, m; + + switch (i) { @@ reftable/record_test.c (new) + assert(reftable_record_val_type(&rec) == i); + + reftable_record_key(&rec, &key); -+ slice_grow(&dest, 1024); -+ slice_setlen(&dest, 1024); + n = reftable_record_encode(&rec, dest, SHA1_SIZE); + assert(n > 0); + @@ reftable/record_test.c (new) + assert((out.target != NULL) == (in.target != NULL)); + reftable_record_clear(&rec_out); + -+ slice_release(&key); -+ slice_release(&dest); ++ strbuf_release(&key); + reftable_ref_record_clear(&in); + } +} @@ reftable/record_test.c (new) + set_test_hash(in[0].old_hash, 2); + for (int i = 0; i < ARRAY_SIZE(in); i++) { + struct reftable_record rec = { 0 }; -+ struct slice key = SLICE_INIT; -+ struct slice dest = SLICE_INIT; ++ struct strbuf key = STRBUF_INIT; ++ uint8_t buffer[1024] = { 0 }; ++ struct string_view dest = { ++ .buf = buffer, ++ .len = sizeof(buffer), ++ }; + /* populate out, to check for leaks. */ + struct reftable_log_record out = { + .ref_name = xstrdup("old name"), @@ reftable/record_test.c (new) + + reftable_record_key(&rec, &key); + -+ slice_grow(&dest, 1024); -+ slice_setlen(&dest, 1024); -+ + n = reftable_record_encode(&rec, dest, SHA1_SIZE); + assert(n >= 0); + reftable_record_from_log(&rec_out, &out); @@ reftable/record_test.c (new) + + assert(reftable_log_record_equal(&in[i], &out, SHA1_SIZE)); + reftable_log_record_clear(&in[i]); -+ slice_release(&key); -+ slice_release(&dest); ++ strbuf_release(&key); + reftable_record_clear(&rec_out); + } +} @@ reftable/record_test.c (new) +static void test_u24_roundtrip(void) +{ + uint32_t in = 0x112233; -+ byte dest[3]; ++ uint8_t dest[3]; + uint32_t out; + put_be24(dest, in); + out = get_be24(dest); @@ reftable/record_test.c (new) + +static void test_key_roundtrip(void) +{ -+ struct slice dest = SLICE_INIT; -+ struct slice last_key = SLICE_INIT; -+ struct slice key = SLICE_INIT; -+ struct slice roundtrip = SLICE_INIT; ++ uint8_t buffer[1024] = { 0 }; ++ struct string_view dest = { ++ .buf = buffer, ++ .len = sizeof(buffer), ++ }; ++ struct strbuf last_key = STRBUF_INIT; ++ struct strbuf key = STRBUF_INIT; ++ struct strbuf roundtrip = STRBUF_INIT; + bool restart; -+ byte extra; ++ uint8_t extra; + int n, m; -+ byte rt_extra; ++ uint8_t rt_extra; + -+ slice_grow(&dest, 1024); -+ slice_setlen(&dest, 1024); -+ slice_addstr(&last_key, "refs/heads/master"); -+ slice_addstr(&key, "refs/tags/bla"); ++ strbuf_addstr(&last_key, "refs/heads/master"); ++ strbuf_addstr(&key, "refs/tags/bla"); + extra = 6; + n = reftable_encode_key(&restart, dest, last_key, key, extra); + assert(!restart); @@ reftable/record_test.c (new) + + m = reftable_decode_key(&roundtrip, &rt_extra, last_key, dest); + assert(n == m); -+ assert(0 == slice_cmp(&key, &roundtrip)); ++ assert(0 == strbuf_cmp(&key, &roundtrip)); + assert(rt_extra == extra); + -+ slice_release(&last_key); -+ slice_release(&key); -+ slice_release(&dest); -+ slice_release(&roundtrip); ++ strbuf_release(&last_key); ++ strbuf_release(&key); ++ strbuf_release(&roundtrip); +} + +static void test_reftable_obj_record_roundtrip(void) +{ -+ byte testHash1[SHA1_SIZE] = { 1, 2, 3, 4, 0 }; ++ uint8_t testHash1[SHA1_SIZE] = { 1, 2, 3, 4, 0 }; + uint64_t till9[] = { 1, 2, 3, 4, 500, 600, 700, 800, 9000 }; + struct reftable_obj_record recs[3] = { { + .hash_prefix = testHash1, @@ reftable/record_test.c (new) + int i = 0; + for (i = 0; i < ARRAY_SIZE(recs); i++) { + struct reftable_obj_record in = recs[i]; -+ struct slice dest = SLICE_INIT; ++ uint8_t buffer[1024] = { 0 }; ++ struct string_view dest = { ++ .buf = buffer, ++ .len = sizeof(buffer), ++ }; + struct reftable_record rec = { 0 }; -+ struct slice key = SLICE_INIT; ++ struct strbuf key = STRBUF_INIT; + struct reftable_obj_record out = { 0 }; + struct reftable_record rec_out = { 0 }; + int n, m; -+ byte extra; ++ uint8_t extra; + + reftable_record_from_obj(&rec, &in); + test_copy(&rec); + reftable_record_key(&rec, &key); -+ slice_grow(&dest, 1024); -+ slice_setlen(&dest, 1024); + n = reftable_record_encode(&rec, dest, SHA1_SIZE); + assert(n > 0); + extra = reftable_record_val_type(&rec); @@ reftable/record_test.c (new) + in.hash_prefix_len)); + assert(0 == memcmp(in.offsets, out.offsets, + sizeof(uint64_t) * in.offset_len)); -+ slice_release(&key); -+ slice_release(&dest); ++ strbuf_release(&key); + reftable_record_clear(&rec_out); + } +} @@ reftable/record_test.c (new) +{ + struct reftable_index_record in = { + .offset = 42, -+ .last_key = SLICE_INIT, ++ .last_key = STRBUF_INIT, ++ }; ++ uint8_t buffer[1024] = { 0 }; ++ struct string_view dest = { ++ .buf = buffer, ++ .len = sizeof(buffer), + }; -+ struct slice dest = SLICE_INIT; -+ struct slice key = SLICE_INIT; ++ struct strbuf key = STRBUF_INIT; + struct reftable_record rec = { 0 }; -+ struct reftable_index_record out = { .last_key = SLICE_INIT }; ++ struct reftable_index_record out = { .last_key = STRBUF_INIT }; + struct reftable_record out_rec = { NULL }; + int n, m; -+ byte extra; ++ uint8_t extra; + -+ slice_addstr(&in.last_key, "refs/heads/master"); ++ strbuf_addstr(&in.last_key, "refs/heads/master"); + reftable_record_from_index(&rec, &in); + reftable_record_key(&rec, &key); + test_copy(&rec); + -+ assert(0 == slice_cmp(&key, &in.last_key)); -+ slice_grow(&dest, 1024); -+ slice_setlen(&dest, 1024); ++ assert(0 == strbuf_cmp(&key, &in.last_key)); + n = reftable_record_encode(&rec, dest, SHA1_SIZE); + assert(n > 0); + @@ reftable/record_test.c (new) + assert(in.offset == out.offset); + + reftable_record_clear(&out_rec); -+ slice_release(&key); -+ slice_release(&in.last_key); -+ slice_release(&dest); ++ strbuf_release(&key); ++ strbuf_release(&in.last_key); +} + +int record_test_main(int argc, const char *argv[]) @@ reftable/refname.c (new) +#include "reftable.h" +#include "basics.h" +#include "refname.h" -+#include "slice.h" ++#include "strbuf.h" + +struct find_arg { + char **names; @@ reftable/refname.c (new) + return err; +} + -+static void slice_trim_component(struct slice *sl) ++static void strbuf_trim_component(struct strbuf *sl) +{ + while (sl->len > 0) { + bool is_slash = (sl->buf[sl->len - 1] == '/'); -+ slice_setlen(sl, sl->len - 1); ++ strbuf_setlen(sl, sl->len - 1); + if (is_slash) + break; + } @@ reftable/refname.c (new) + +int modification_validate(struct modification *mod) +{ -+ struct slice slashed = SLICE_INIT; ++ struct strbuf slashed = STRBUF_INIT; + int err = 0; + int i = 0; + for (; i < mod->add_len; i++) { + err = validate_ref_name(mod->add[i]); + if (err) + goto done; -+ slice_reset(&slashed); -+ slice_addstr(&slashed, mod->add[i]); -+ slice_addstr(&slashed, "/"); ++ strbuf_reset(&slashed); ++ strbuf_addstr(&slashed, mod->add[i]); ++ strbuf_addstr(&slashed, "/"); + -+ err = modification_has_ref_with_prefix( -+ mod, slice_as_string(&slashed)); ++ err = modification_has_ref_with_prefix(mod, slashed.buf); + if (err == 0) { + err = REFTABLE_NAME_CONFLICT; + goto done; @@ reftable/refname.c (new) + if (err < 0) + goto done; + -+ slice_reset(&slashed); -+ slice_addstr(&slashed, mod->add[i]); ++ strbuf_reset(&slashed); ++ strbuf_addstr(&slashed, mod->add[i]); + while (slashed.len) { -+ slice_trim_component(&slashed); -+ err = modification_has_ref(mod, -+ slice_as_string(&slashed)); ++ strbuf_trim_component(&slashed); ++ err = modification_has_ref(mod, slashed.buf); + if (err == 0) { + err = REFTABLE_NAME_CONFLICT; + goto done; @@ reftable/refname.c (new) + } + err = 0; +done: -+ slice_release(&slashed); ++ strbuf_release(&slashed); + return err; +} @@ reftable/refname_test.c (new) +static void test_conflict(void) +{ + struct reftable_write_options opts = { 0 }; -+ struct slice buf = SLICE_INIT; ++ struct strbuf buf = STRBUF_INIT; + struct reftable_writer *w = -+ reftable_new_writer(&slice_add_void, &buf, &opts); ++ reftable_new_writer(&strbuf_add_void, &buf, &opts); + struct reftable_ref_record rec = { + .ref_name = "a/b", + .target = "destination", /* make sure it's not a symref. */ @@ reftable/refname_test.c (new) + assert_err(err); + reftable_writer_free(w); + -+ block_source_from_slice(&source, &buf); ++ block_source_from_strbuf(&source, &buf); + err = reftable_new_reader(&rd, &source, "filename"); + assert_err(err); + @@ reftable/refname_test.c (new) + } + + reftable_reader_free(rd); -+ slice_release(&buf); ++ strbuf_release(&buf); +} + +int refname_test_main(int argc, const char *argv[]) @@ reftable/reftable-tests.h (new) +int record_test_main(int argc, const char **argv); +int refname_test_main(int argc, const char **argv); +int reftable_test_main(int argc, const char **argv); -+int slice_test_main(int argc, const char **argv); ++int strbuf_test_main(int argc, const char **argv); +int stack_test_main(int argc, const char **argv); +int tree_test_main(int argc, const char **argv); +int reftable_dump_main(int argc, char *const *argv); @@ reftable/reftable.h (new) + +/* reftable_new_writer creates a new writer */ +struct reftable_writer * -+reftable_new_writer(int (*writer_func)(void *, uint8_t *, size_t), ++reftable_new_writer(int (*writer_func)(void *, const void *, size_t), + void *writer_arg, struct reftable_write_options *opts); + +/* write to a file descriptor. fdp should be an int* pointing to the fd. */ -+int reftable_fd_write(void *fdp, uint8_t *data, size_t size); ++int reftable_fd_write(void *fdp, const void *data, size_t size); + +/* Set the range of update indices for the records we will add. When + writing a table into a stack, the min should be at least @@ reftable/reftable_test.c (new) + +static void test_buffer(void) +{ -+ struct slice buf = SLICE_INIT; ++ struct strbuf buf = STRBUF_INIT; + struct reftable_block_source source = { NULL }; + struct reftable_block out = { 0 }; + int n; -+ byte in[] = "hello"; -+ slice_add(&buf, in, sizeof(in)); -+ block_source_from_slice(&source, &buf); ++ uint8_t in[] = "hello"; ++ strbuf_add(&buf, in, sizeof(in)); ++ block_source_from_strbuf(&source, &buf); + assert(block_source_size(&source) == 6); + n = block_source_read_block(&source, &out, 0, sizeof(in)); + assert(n == sizeof(in)); @@ reftable/reftable_test.c (new) + + reftable_block_done(&out); + block_source_close(&source); -+ slice_release(&buf); ++ strbuf_release(&buf); +} + +static void test_default_write_opts(void) +{ + struct reftable_write_options opts = { 0 }; -+ struct slice buf = SLICE_INIT; ++ struct strbuf buf = STRBUF_INIT; + struct reftable_writer *w = -+ reftable_new_writer(&slice_add_void, &buf, &opts); ++ reftable_new_writer(&strbuf_add_void, &buf, &opts); + + struct reftable_ref_record rec = { + .ref_name = "master", @@ reftable/reftable_test.c (new) + assert_err(err); + reftable_writer_free(w); + -+ block_source_from_slice(&source, &buf); ++ block_source_from_strbuf(&source, &buf); + + err = reftable_new_reader(&rd, &source, "filename"); + assert_err(err); @@ reftable/reftable_test.c (new) + + reftable_merged_table_close(merged); + reftable_merged_table_free(merged); -+ slice_release(&buf); ++ strbuf_release(&buf); +} + -+static void write_table(char ***names, struct slice *buf, int N, int block_size, -+ uint32_t hash_id) ++static void write_table(char ***names, struct strbuf *buf, int N, ++ int block_size, uint32_t hash_id) +{ + struct reftable_write_options opts = { + .block_size = block_size, + .hash_id = hash_id, + }; + struct reftable_writer *w = -+ reftable_new_writer(&slice_add_void, buf, &opts); ++ reftable_new_writer(&strbuf_add_void, buf, &opts); + struct reftable_ref_record ref = { 0 }; + int i = 0, n; + struct reftable_log_record log = { 0 }; @@ reftable/reftable_test.c (new) + *names = reftable_calloc(sizeof(char *) * (N + 1)); + reftable_writer_set_limits(w, update_index, update_index); + for (i = 0; i < N; i++) { -+ byte hash[SHA256_SIZE] = { 0 }; ++ uint8_t hash[SHA256_SIZE] = { 0 }; + char name[100]; + int n; + @@ reftable/reftable_test.c (new) + } + + for (i = 0; i < N; i++) { -+ byte hash[SHA256_SIZE] = { 0 }; ++ uint8_t hash[SHA256_SIZE] = { 0 }; + char name[100]; + int n; + @@ reftable/reftable_test.c (new) + +static void test_log_buffer_size(void) +{ -+ struct slice buf = SLICE_INIT; ++ struct strbuf buf = STRBUF_INIT; + struct reftable_write_options opts = { + .block_size = 4096, + }; @@ reftable/reftable_test.c (new) + .message = "commit: 9\n", + }; + struct reftable_writer *w = -+ reftable_new_writer(&slice_add_void, &buf, &opts); ++ reftable_new_writer(&strbuf_add_void, &buf, &opts); + + /* This tests buffer extension for log compression. Must use a random + hash, to ensure that the compressed part is larger than the original. + */ -+ byte hash1[SHA1_SIZE], hash2[SHA1_SIZE]; ++ uint8_t hash1[SHA1_SIZE], hash2[SHA1_SIZE]; + for (int i = 0; i < SHA1_SIZE; i++) { -+ hash1[i] = (byte)(rand() % 256); -+ hash2[i] = (byte)(rand() % 256); ++ hash1[i] = (uint8_t)(rand() % 256); ++ hash2[i] = (uint8_t)(rand() % 256); + } + log.old_hash = hash1; + log.new_hash = hash2; @@ reftable/reftable_test.c (new) + err = reftable_writer_close(w); + assert_err(err); + reftable_writer_free(w); -+ slice_release(&buf); ++ strbuf_release(&buf); +} + +static void test_log_write_read(void) @@ reftable/reftable_test.c (new) + struct reftable_iterator it = { 0 }; + struct reftable_reader rd = { 0 }; + struct reftable_block_source source = { 0 }; -+ struct slice buf = SLICE_INIT; ++ struct strbuf buf = STRBUF_INIT; + struct reftable_writer *w = -+ reftable_new_writer(&slice_add_void, &buf, &opts); ++ reftable_new_writer(&strbuf_add_void, &buf, &opts); + const struct reftable_stats *stats = NULL; + reftable_writer_set_limits(w, 0, N); + for (i = 0; i < N; i++) { @@ reftable/reftable_test.c (new) + assert_err(err); + } + for (i = 0; i < N; i++) { -+ byte hash1[SHA1_SIZE], hash2[SHA1_SIZE]; ++ uint8_t hash1[SHA1_SIZE], hash2[SHA1_SIZE]; + struct reftable_log_record log = { 0 }; + set_test_hash(hash1, i); + set_test_hash(hash2, i + 1); @@ reftable/reftable_test.c (new) + reftable_writer_free(w); + w = NULL; + -+ block_source_from_slice(&source, &buf); ++ block_source_from_strbuf(&source, &buf); + + err = init_reader(&rd, &source, "file.log"); + assert_err(err); @@ reftable/reftable_test.c (new) + reftable_iterator_destroy(&it); + + /* cleanup. */ -+ slice_release(&buf); ++ strbuf_release(&buf); + free_names(names); + reader_close(&rd); +} @@ reftable/reftable_test.c (new) +static void test_table_read_write_sequential(void) +{ + char **names; -+ struct slice buf = SLICE_INIT; ++ struct strbuf buf = STRBUF_INIT; + int N = 50; + struct reftable_iterator it = { 0 }; + struct reftable_block_source source = { 0 }; @@ reftable/reftable_test.c (new) + + write_table(&names, &buf, N, 256, SHA1_ID); + -+ block_source_from_slice(&source, &buf); ++ block_source_from_strbuf(&source, &buf); + + err = init_reader(&rd, &source, "file.ref"); + assert_err(err); @@ reftable/reftable_test.c (new) + } + assert(j == N); + reftable_iterator_destroy(&it); -+ slice_release(&buf); ++ strbuf_release(&buf); + free_names(names); + + reader_close(&rd); @@ reftable/reftable_test.c (new) +static void test_table_write_small_table(void) +{ + char **names; -+ struct slice buf = SLICE_INIT; ++ struct strbuf buf = STRBUF_INIT; + int N = 1; + write_table(&names, &buf, N, 4096, SHA1_ID); + assert(buf.len < 200); -+ slice_release(&buf); ++ strbuf_release(&buf); + free_names(names); +} + +static void test_table_read_api(void) +{ + char **names; -+ struct slice buf = SLICE_INIT; ++ struct strbuf buf = STRBUF_INIT; + int N = 50; + struct reftable_reader rd = { 0 }; + struct reftable_block_source source = { 0 }; @@ reftable/reftable_test.c (new) + + write_table(&names, &buf, N, 256, SHA1_ID); + -+ block_source_from_slice(&source, &buf); ++ block_source_from_strbuf(&source, &buf); + + err = init_reader(&rd, &source, "file.ref"); + assert_err(err); @@ reftable/reftable_test.c (new) + err = reftable_iterator_next_log(&it, &log); + assert(err == REFTABLE_API_ERROR); + -+ slice_release(&buf); ++ strbuf_release(&buf); + for (i = 0; i < N; i++) { + reftable_free(names[i]); + } + reftable_iterator_destroy(&it); + reftable_free(names); + reader_close(&rd); -+ slice_release(&buf); ++ strbuf_release(&buf); +} + +static void test_table_read_write_seek(bool index, int hash_id) +{ + char **names; -+ struct slice buf = SLICE_INIT; ++ struct strbuf buf = STRBUF_INIT; + int N = 50; + struct reftable_reader rd = { 0 }; + struct reftable_block_source source = { 0 }; @@ reftable/reftable_test.c (new) + int i = 0; + + struct reftable_iterator it = { 0 }; -+ struct slice pastLast = SLICE_INIT; ++ struct strbuf pastLast = STRBUF_INIT; + struct reftable_ref_record ref = { 0 }; + + write_table(&names, &buf, N, 256, hash_id); + -+ block_source_from_slice(&source, &buf); ++ block_source_from_strbuf(&source, &buf); + + err = init_reader(&rd, &source, "file.ref"); + assert_err(err); @@ reftable/reftable_test.c (new) + reftable_iterator_destroy(&it); + } + -+ slice_addstr(&pastLast, names[N - 1]); -+ slice_addstr(&pastLast, "/"); ++ strbuf_addstr(&pastLast, names[N - 1]); ++ strbuf_addstr(&pastLast, "/"); + -+ err = reftable_reader_seek_ref(&rd, &it, slice_as_string(&pastLast)); ++ err = reftable_reader_seek_ref(&rd, &it, pastLast.buf); + if (err == 0) { + struct reftable_ref_record ref = { 0 }; + int err = reftable_iterator_next_ref(&it, &ref); @@ reftable/reftable_test.c (new) + assert(err > 0); + } + -+ slice_release(&pastLast); ++ strbuf_release(&pastLast); + reftable_iterator_destroy(&it); + -+ slice_release(&buf); ++ strbuf_release(&buf); + for (i = 0; i < N; i++) { + reftable_free(names[i]); + } @@ reftable/reftable_test.c (new) + int N = 50; + char **want_names = reftable_calloc(sizeof(char *) * (N + 1)); + int want_names_len = 0; -+ byte want_hash[SHA1_SIZE]; ++ uint8_t want_hash[SHA1_SIZE]; + + struct reftable_write_options opts = { + .block_size = 256, @@ reftable/reftable_test.c (new) + struct reftable_reader rd; + struct reftable_block_source source = { 0 }; + -+ struct slice buf = SLICE_INIT; ++ struct strbuf buf = STRBUF_INIT; + struct reftable_writer *w = -+ reftable_new_writer(&slice_add_void, &buf, &opts); ++ reftable_new_writer(&strbuf_add_void, &buf, &opts); + + struct reftable_iterator it = { 0 }; + int j; @@ reftable/reftable_test.c (new) + set_test_hash(want_hash, 4); + + for (i = 0; i < N; i++) { -+ byte hash[SHA1_SIZE]; ++ uint8_t hash[SHA1_SIZE]; + char fill[51] = { 0 }; + char name[100]; -+ byte hash1[SHA1_SIZE]; -+ byte hash2[SHA1_SIZE]; ++ uint8_t hash1[SHA1_SIZE]; ++ uint8_t hash2[SHA1_SIZE]; + struct reftable_ref_record ref = { 0 }; + + memset(hash, i, sizeof(hash)); @@ reftable/reftable_test.c (new) + reftable_writer_free(w); + w = NULL; + -+ block_source_from_slice(&source, &buf); ++ block_source_from_strbuf(&source, &buf); + + err = init_reader(&rd, &source, "file.ref"); + assert_err(err); @@ reftable/reftable_test.c (new) + } + assert(j == want_names_len); + -+ slice_release(&buf); ++ strbuf_release(&buf); + free_names(want_names); + reftable_iterator_destroy(&it); + reader_close(&rd); @@ reftable/reftable_test.c (new) +static void test_table_empty(void) +{ + struct reftable_write_options opts = { 0 }; -+ struct slice buf = SLICE_INIT; ++ struct strbuf buf = STRBUF_INIT; + struct reftable_writer *w = -+ reftable_new_writer(&slice_add_void, &buf, &opts); ++ reftable_new_writer(&strbuf_add_void, &buf, &opts); + struct reftable_block_source source = { 0 }; + struct reftable_reader *rd = NULL; + struct reftable_ref_record rec = { 0 }; @@ reftable/reftable_test.c (new) + + assert(buf.len == header_size(1) + footer_size(1)); + -+ block_source_from_slice(&source, &buf); ++ block_source_from_strbuf(&source, &buf); + + err = reftable_new_reader(&rd, &source, "filename"); + assert_err(err); @@ reftable/reftable_test.c (new) + + reftable_iterator_destroy(&it); + reftable_reader_free(rd); -+ slice_release(&buf); ++ strbuf_release(&buf); +} + +int reftable_test_main(int argc, const char *argv[]) @@ reftable/reftable_test.c (new) + return test_main(argc, argv); +} - ## reftable/slice.c (new) ## + ## reftable/stack.c (new) ## @@ +/* +Copyright 2020 Google LLC @@ reftable/slice.c (new) +https://developers.google.com/open-source/licenses/bsd +*/ + -+#include "slice.h" ++#include "stack.h" + +#include "system.h" -+ ++#include "merged.h" ++#include "reader.h" ++#include "refname.h" +#include "reftable.h" ++#include "writer.h" + -+struct slice reftable_empty_slice = SLICE_INIT; -+ -+void slice_init(struct slice *s) -+{ -+ struct slice empty = SLICE_INIT; -+ *s = empty; -+} -+ -+void slice_grow(struct slice *s, size_t extra) ++int reftable_new_stack(struct reftable_stack **dest, const char *dir, ++ struct reftable_write_options config) +{ -+ size_t newcap = s->len + extra + 1; -+ if (newcap > s->cap) { -+ s->buf = reftable_realloc(s->buf, newcap); -+ s->cap = newcap; -+ } -+} ++ struct reftable_stack *p = ++ reftable_calloc(sizeof(struct reftable_stack)); ++ struct strbuf list_file_name = STRBUF_INIT; ++ int err = 0; + -+static void slice_resize(struct slice *s, int l) -+{ -+ int zl = l + 1; /* one byte for 0 termination. */ -+ assert(s->canary == SLICE_CANARY); -+ if (s->cap < zl) { -+ int c = s->cap * 2; -+ if (c < zl) { -+ c = zl; -+ } -+ s->cap = c; -+ s->buf = reftable_realloc(s->buf, s->cap); ++ if (config.hash_id == 0) { ++ config.hash_id = SHA1_ID; + } -+ s->len = l; -+ s->buf[l] = 0; -+} -+ -+void slice_setlen(struct slice *s, size_t l) -+{ -+ assert(s->cap >= l + 1); -+ s->len = l; -+ s->buf[l] = 0; -+} + -+void slice_reset(struct slice *s) -+{ -+ slice_resize(s, 0); -+} ++ *dest = NULL; + -+void slice_addstr(struct slice *d, const char *s) -+{ -+ int l1 = d->len; -+ int l2 = strlen(s); -+ assert(d->canary == SLICE_CANARY); ++ strbuf_reset(&list_file_name); ++ strbuf_addstr(&list_file_name, dir); ++ strbuf_addstr(&list_file_name, "/tables.list"); + -+ slice_resize(d, l2 + l1); -+ memcpy(d->buf + l1, s, l2); -+} ++ p->list_file = strbuf_detach(&list_file_name, NULL); ++ p->reftable_dir = xstrdup(dir); ++ p->config = config; + -+void slice_addbuf(struct slice *s, struct slice *a) -+{ -+ int end = s->len; -+ assert(s->canary == SLICE_CANARY); -+ slice_resize(s, s->len + a->len); -+ memcpy(s->buf + end, a->buf, a->len); ++ err = reftable_stack_reload_maybe_reuse(p, true); ++ if (err < 0) { ++ reftable_stack_destroy(p); ++ } else { ++ *dest = p; ++ } ++ return err; +} + -+void slice_consume(struct slice *s, int n) ++static int fd_read_lines(int fd, char ***namesp) +{ -+ assert(s->canary == SLICE_CANARY); -+ s->buf += n; -+ s->len -= n; -+} ++ off_t size = lseek(fd, 0, SEEK_END); ++ char *buf = NULL; ++ int err = 0; ++ if (size < 0) { ++ err = REFTABLE_IO_ERROR; ++ goto done; ++ } ++ err = lseek(fd, 0, SEEK_SET); ++ if (err < 0) { ++ err = REFTABLE_IO_ERROR; ++ goto done; ++ } + -+char *slice_detach(struct slice *s) -+{ -+ char *p = NULL; -+ slice_as_string(s); -+ p = (char *)s->buf; -+ s->buf = NULL; -+ s->cap = 0; -+ s->len = 0; -+ return p; -+} ++ buf = reftable_malloc(size + 1); ++ if (read(fd, buf, size) != size) { ++ err = REFTABLE_IO_ERROR; ++ goto done; ++ } ++ buf[size] = 0; + -+void slice_release(struct slice *s) -+{ -+ byte *ptr = s->buf; -+ assert(s->canary == SLICE_CANARY); -+ s->buf = NULL; -+ s->cap = 0; -+ s->len = 0; -+ reftable_free(ptr); -+} ++ parse_names(buf, size, namesp); + -+/* return the underlying data as char*. len is left unchanged, but -+ a \0 is added at the end. */ -+const char *slice_as_string(struct slice *s) -+{ -+ return (const char *)s->buf; ++done: ++ reftable_free(buf); ++ return err; +} + -+int slice_cmp(const struct slice *a, const struct slice *b) ++int read_lines(const char *filename, char ***namesp) +{ -+ int min = a->len < b->len ? a->len : b->len; -+ int res = memcmp(a->buf, b->buf, min); -+ assert(a->canary == SLICE_CANARY); -+ assert(b->canary == SLICE_CANARY); -+ if (res != 0) -+ return res; -+ if (a->len < b->len) -+ return -1; -+ else if (a->len > b->len) -+ return 1; -+ else -+ return 0; -+} ++ int fd = open(filename, O_RDONLY, 0644); ++ int err = 0; ++ if (fd < 0) { ++ if (errno == ENOENT) { ++ *namesp = reftable_calloc(sizeof(char *)); ++ return 0; ++ } + -+int slice_add(struct slice *b, const byte *data, size_t sz) -+{ -+ assert(b->canary == SLICE_CANARY); -+ slice_grow(b, sz); -+ memcpy(b->buf + b->len, data, sz); -+ b->len += sz; -+ b->buf[b->len] = 0; -+ return sz; ++ return REFTABLE_IO_ERROR; ++ } ++ err = fd_read_lines(fd, namesp); ++ close(fd); ++ return err; +} + -+int slice_add_void(void *b, byte *data, size_t sz) ++struct reftable_merged_table * ++reftable_stack_merged_table(struct reftable_stack *st) +{ -+ return slice_add((struct slice *)b, data, sz); ++ return st->merged; +} + -+static uint64_t slice_size(void *b) ++/* Close and free the stack */ ++void reftable_stack_destroy(struct reftable_stack *st) +{ -+ return ((struct slice *)b)->len; ++ if (st->merged != NULL) { ++ reftable_merged_table_close(st->merged); ++ reftable_merged_table_free(st->merged); ++ st->merged = NULL; ++ } ++ FREE_AND_NULL(st->list_file); ++ FREE_AND_NULL(st->reftable_dir); ++ reftable_free(st); +} + -+static void slice_return_block(void *b, struct reftable_block *dest) ++static struct reftable_reader **stack_copy_readers(struct reftable_stack *st, ++ int cur_len) +{ -+ memset(dest->data, 0xff, dest->len); -+ reftable_free(dest->data); ++ struct reftable_reader **cur = ++ reftable_calloc(sizeof(struct reftable_reader *) * cur_len); ++ int i = 0; ++ for (i = 0; i < cur_len; i++) { ++ cur[i] = st->merged->stack[i]; ++ } ++ return cur; +} + -+static void slice_close(void *b) ++static int reftable_stack_reload_once(struct reftable_stack *st, char **names, ++ bool reuse_open) +{ -+} ++ int cur_len = st->merged == NULL ? 0 : st->merged->stack_len; ++ struct reftable_reader **cur = stack_copy_readers(st, cur_len); ++ int err = 0; ++ int names_len = names_length(names); ++ struct reftable_reader **new_tables = ++ reftable_malloc(sizeof(struct reftable_reader *) * names_len); ++ int new_tables_len = 0; ++ struct reftable_merged_table *new_merged = NULL; ++ int i; + -+static int slice_read_block(void *v, struct reftable_block *dest, uint64_t off, -+ uint32_t size) -+{ -+ struct slice *b = (struct slice *)v; -+ assert(off + size <= b->len); -+ dest->data = reftable_calloc(size); -+ memcpy(dest->data, b->buf + off, size); -+ dest->len = size; -+ return size; -+} ++ while (*names) { ++ struct reftable_reader *rd = NULL; ++ char *name = *names++; + -+struct reftable_block_source_vtable slice_vtable = { -+ .size = &slice_size, -+ .read_block = &slice_read_block, -+ .return_block = &slice_return_block, -+ .close = &slice_close, -+}; ++ /* this is linear; we assume compaction keeps the number of ++ tables under control so this is not quadratic. */ ++ int j = 0; ++ for (j = 0; reuse_open && j < cur_len; j++) { ++ if (cur[j] != NULL && 0 == strcmp(cur[j]->name, name)) { ++ rd = cur[j]; ++ cur[j] = NULL; ++ break; ++ } ++ } + -+void block_source_from_slice(struct reftable_block_source *bs, -+ struct slice *buf) -+{ -+ assert(bs->ops == NULL); -+ bs->ops = &slice_vtable; -+ bs->arg = buf; -+} ++ if (rd == NULL) { ++ struct reftable_block_source src = { 0 }; ++ struct strbuf table_path = STRBUF_INIT; ++ strbuf_addstr(&table_path, st->reftable_dir); ++ strbuf_addstr(&table_path, "/"); ++ strbuf_addstr(&table_path, name); + -+static void malloc_return_block(void *b, struct reftable_block *dest) -+{ -+ memset(dest->data, 0xff, dest->len); -+ reftable_free(dest->data); -+} ++ err = reftable_block_source_from_file(&src, ++ table_path.buf); ++ strbuf_release(&table_path); + -+struct reftable_block_source_vtable malloc_vtable = { -+ .return_block = &malloc_return_block, -+}; ++ if (err < 0) ++ goto done; + -+struct reftable_block_source malloc_block_source_instance = { -+ .ops = &malloc_vtable, -+}; ++ err = reftable_new_reader(&rd, &src, name); ++ if (err < 0) ++ goto done; ++ } + -+struct reftable_block_source malloc_block_source(void) -+{ -+ return malloc_block_source_instance; -+} -+ -+int common_prefix_size(struct slice *a, struct slice *b) -+{ -+ int p = 0; -+ assert(a->canary == SLICE_CANARY); -+ assert(b->canary == SLICE_CANARY); -+ while (p < a->len && p < b->len) { -+ if (a->buf[p] != b->buf[p]) { -+ break; -+ } -+ p++; -+ } -+ -+ return p; -+} - - ## reftable/slice.h (new) ## -@@ -+/* -+Copyright 2020 Google LLC -+ -+Use of this source code is governed by a BSD-style -+license that can be found in the LICENSE file or at -+https://developers.google.com/open-source/licenses/bsd -+*/ -+ -+#ifndef SLICE_H -+#define SLICE_H -+ -+#include "basics.h" -+#include "reftable.h" -+ -+/* -+ Provides a bounds-checked, growable byte ranges. To use, initialize as "slice -+ x = SLICE_INIT;" -+ */ -+struct slice { -+ int len; -+ int cap; -+ byte *buf; -+ -+ /* Used to enforce initialization with SLICE_INIT */ -+ byte canary; -+}; -+#define SLICE_CANARY 0x42 -+#define SLICE_INIT \ -+ { \ -+ 0, 0, NULL, SLICE_CANARY \ -+ } -+extern struct slice reftable_empty_slice; -+ -+void slice_addstr(struct slice *dest, const char *src); -+ -+/* Deallocate and clear slice */ -+void slice_release(struct slice *slice); -+ -+/* Set slice to 0 length, but retain buffer. */ -+void slice_reset(struct slice *slice); -+ -+/* Initializes a slice. Accepts a slice with random garbage. */ -+void slice_init(struct slice *slice); -+ -+/* Ensure that `buf` is \0 terminated. */ -+const char *slice_as_string(struct slice *src); -+ -+/* Return `buf`, clearing out `s` */ -+char *slice_detach(struct slice *s); -+ -+/* Set length of the slace to `l`, but don't reallocated. */ -+void slice_setlen(struct slice *s, size_t l); -+ -+/* Ensure `l` bytes beyond current length are available */ -+void slice_grow(struct slice *s, size_t l); -+ -+/* Signed comparison */ -+int slice_cmp(const struct slice *a, const struct slice *b); -+ -+/* Append `data` to the `dest` slice. */ -+int slice_add(struct slice *dest, const byte *data, size_t sz); -+ -+/* Append `add` to `dest. */ -+void slice_addbuf(struct slice *dest, struct slice *add); -+ -+/* Like slice_add, but suitable for passing to reftable_new_writer -+ */ -+int slice_add_void(void *b, byte *data, size_t sz); -+ -+/* Find the longest shared prefix size of `a` and `b` */ -+int common_prefix_size(struct slice *a, struct slice *b); -+ -+struct reftable_block_source; -+ -+/* Create an in-memory block source for reading reftables */ -+void block_source_from_slice(struct reftable_block_source *bs, -+ struct slice *buf); -+ -+struct reftable_block_source malloc_block_source(void); -+ -+/* Advance `buf` by `n`, and decrease length. A copy of the slice -+ should be kept for deallocating the slice. */ -+void slice_consume(struct slice *s, int n); -+ -+#endif - - ## reftable/slice_test.c (new) ## -@@ -+/* -+Copyright 2020 Google LLC -+ -+Use of this source code is governed by a BSD-style -+license that can be found in the LICENSE file or at -+https://developers.google.com/open-source/licenses/bsd -+*/ -+ -+#include "slice.h" -+ -+#include "system.h" -+ -+#include "basics.h" -+#include "record.h" -+#include "reftable.h" -+#include "test_framework.h" -+#include "reftable-tests.h" -+ -+static void test_slice(void) -+{ -+ struct slice s = SLICE_INIT; -+ struct slice t = SLICE_INIT; -+ -+ slice_addstr(&s, "abc"); -+ assert(0 == strcmp("abc", slice_as_string(&s))); -+ -+ slice_addstr(&t, "pqr"); -+ slice_addbuf(&s, &t); -+ assert(0 == strcmp("abcpqr", slice_as_string(&s))); -+ -+ slice_release(&s); -+ slice_release(&t); -+} -+ -+int slice_test_main(int argc, const char *argv[]) -+{ -+ add_test_case("test_slice", &test_slice); -+ return test_main(argc, argv); -+} - - ## reftable/stack.c (new) ## -@@ -+/* -+Copyright 2020 Google LLC -+ -+Use of this source code is governed by a BSD-style -+license that can be found in the LICENSE file or at -+https://developers.google.com/open-source/licenses/bsd -+*/ -+ -+#include "stack.h" -+ -+#include "system.h" -+#include "merged.h" -+#include "reader.h" -+#include "refname.h" -+#include "reftable.h" -+#include "writer.h" -+ -+int reftable_new_stack(struct reftable_stack **dest, const char *dir, -+ struct reftable_write_options config) -+{ -+ struct reftable_stack *p = -+ reftable_calloc(sizeof(struct reftable_stack)); -+ struct slice list_file_name = SLICE_INIT; -+ int err = 0; -+ -+ if (config.hash_id == 0) { -+ config.hash_id = SHA1_ID; -+ } -+ -+ *dest = NULL; -+ -+ slice_reset(&list_file_name); -+ slice_addstr(&list_file_name, dir); -+ slice_addstr(&list_file_name, "/tables.list"); -+ -+ p->list_file = slice_detach(&list_file_name); -+ p->reftable_dir = xstrdup(dir); -+ p->config = config; -+ -+ err = reftable_stack_reload_maybe_reuse(p, true); -+ if (err < 0) { -+ reftable_stack_destroy(p); -+ } else { -+ *dest = p; -+ } -+ return err; -+} -+ -+static int fd_read_lines(int fd, char ***namesp) -+{ -+ off_t size = lseek(fd, 0, SEEK_END); -+ char *buf = NULL; -+ int err = 0; -+ if (size < 0) { -+ err = REFTABLE_IO_ERROR; -+ goto done; -+ } -+ err = lseek(fd, 0, SEEK_SET); -+ if (err < 0) { -+ err = REFTABLE_IO_ERROR; -+ goto done; -+ } -+ -+ buf = reftable_malloc(size + 1); -+ if (read(fd, buf, size) != size) { -+ err = REFTABLE_IO_ERROR; -+ goto done; -+ } -+ buf[size] = 0; -+ -+ parse_names(buf, size, namesp); -+ -+done: -+ reftable_free(buf); -+ return err; -+} -+ -+int read_lines(const char *filename, char ***namesp) -+{ -+ int fd = open(filename, O_RDONLY, 0644); -+ int err = 0; -+ if (fd < 0) { -+ if (errno == ENOENT) { -+ *namesp = reftable_calloc(sizeof(char *)); -+ return 0; -+ } -+ -+ return REFTABLE_IO_ERROR; -+ } -+ err = fd_read_lines(fd, namesp); -+ close(fd); -+ return err; -+} -+ -+struct reftable_merged_table * -+reftable_stack_merged_table(struct reftable_stack *st) -+{ -+ return st->merged; -+} -+ -+/* Close and free the stack */ -+void reftable_stack_destroy(struct reftable_stack *st) -+{ -+ if (st->merged != NULL) { -+ reftable_merged_table_close(st->merged); -+ reftable_merged_table_free(st->merged); -+ st->merged = NULL; -+ } -+ FREE_AND_NULL(st->list_file); -+ FREE_AND_NULL(st->reftable_dir); -+ reftable_free(st); -+} -+ -+static struct reftable_reader **stack_copy_readers(struct reftable_stack *st, -+ int cur_len) -+{ -+ struct reftable_reader **cur = -+ reftable_calloc(sizeof(struct reftable_reader *) * cur_len); -+ int i = 0; -+ for (i = 0; i < cur_len; i++) { -+ cur[i] = st->merged->stack[i]; -+ } -+ return cur; -+} -+ -+static int reftable_stack_reload_once(struct reftable_stack *st, char **names, -+ bool reuse_open) -+{ -+ int cur_len = st->merged == NULL ? 0 : st->merged->stack_len; -+ struct reftable_reader **cur = stack_copy_readers(st, cur_len); -+ int err = 0; -+ int names_len = names_length(names); -+ struct reftable_reader **new_tables = -+ reftable_malloc(sizeof(struct reftable_reader *) * names_len); -+ int new_tables_len = 0; -+ struct reftable_merged_table *new_merged = NULL; -+ int i; -+ -+ while (*names) { -+ struct reftable_reader *rd = NULL; -+ char *name = *names++; -+ -+ /* this is linear; we assume compaction keeps the number of -+ tables under control so this is not quadratic. */ -+ int j = 0; -+ for (j = 0; reuse_open && j < cur_len; j++) { -+ if (cur[j] != NULL && 0 == strcmp(cur[j]->name, name)) { -+ rd = cur[j]; -+ cur[j] = NULL; -+ break; -+ } -+ } -+ -+ if (rd == NULL) { -+ struct reftable_block_source src = { 0 }; -+ struct slice table_path = SLICE_INIT; -+ slice_addstr(&table_path, st->reftable_dir); -+ slice_addstr(&table_path, "/"); -+ slice_addstr(&table_path, name); -+ -+ err = reftable_block_source_from_file( -+ &src, slice_as_string(&table_path)); -+ slice_release(&table_path); -+ -+ if (err < 0) -+ goto done; -+ -+ err = reftable_new_reader(&rd, &src, name); -+ if (err < 0) -+ goto done; -+ } -+ -+ new_tables[new_tables_len++] = rd; -+ } ++ new_tables[new_tables_len++] = rd; ++ } + + /* success! */ + err = reftable_new_merged_table(&new_merged, new_tables, new_tables_len, @@ reftable/stack.c (new) + return 0; +} + -+static void format_name(struct slice *dest, uint64_t min, uint64_t max) ++static void format_name(struct strbuf *dest, uint64_t min, uint64_t max) +{ + char buf[100]; + snprintf(buf, sizeof(buf), "0x%012" PRIx64 "-0x%012" PRIx64, min, max); -+ slice_reset(dest); -+ slice_addstr(dest, buf); ++ strbuf_reset(dest); ++ strbuf_addstr(dest, buf); +} + +struct reftable_addition { + int lock_file_fd; -+ struct slice lock_file_name; ++ struct strbuf lock_file_name; + struct reftable_stack *stack; + char **names; + char **new_tables; @@ reftable/stack.c (new) + uint64_t next_update_index; +}; + -+#define REFTABLE_ADDITION_INIT \ -+ { \ -+ .lock_file_name = SLICE_INIT \ ++#define REFTABLE_ADDITION_INIT \ ++ { \ ++ .lock_file_name = STRBUF_INIT \ + } + +static int reftable_stack_init_addition(struct reftable_addition *add, @@ reftable/stack.c (new) + int err = 0; + add->stack = st; + -+ slice_reset(&add->lock_file_name); -+ slice_addstr(&add->lock_file_name, st->list_file); -+ slice_addstr(&add->lock_file_name, ".lock"); ++ strbuf_reset(&add->lock_file_name); ++ strbuf_addstr(&add->lock_file_name, st->list_file); ++ strbuf_addstr(&add->lock_file_name, ".lock"); + -+ add->lock_file_fd = open(slice_as_string(&add->lock_file_name), ++ add->lock_file_fd = open(add->lock_file_name.buf, + O_EXCL | O_CREAT | O_WRONLY, 0644); + if (add->lock_file_fd < 0) { + if (errno == EEXIST) { @@ reftable/stack.c (new) +void reftable_addition_close(struct reftable_addition *add) +{ + int i = 0; -+ struct slice nm = SLICE_INIT; ++ struct strbuf nm = STRBUF_INIT; + for (i = 0; i < add->new_tables_len; i++) { -+ slice_reset(&nm); -+ slice_addstr(&nm, add->stack->list_file); -+ slice_addstr(&nm, "/"); -+ slice_addstr(&nm, add->new_tables[i]); -+ unlink(slice_as_string(&nm)); ++ strbuf_reset(&nm); ++ strbuf_addstr(&nm, add->stack->list_file); ++ strbuf_addstr(&nm, "/"); ++ strbuf_addstr(&nm, add->new_tables[i]); ++ unlink(nm.buf); + reftable_free(add->new_tables[i]); + add->new_tables[i] = NULL; + } @@ reftable/stack.c (new) + add->lock_file_fd = 0; + } + if (add->lock_file_name.len > 0) { -+ unlink(slice_as_string(&add->lock_file_name)); -+ slice_release(&add->lock_file_name); ++ unlink(add->lock_file_name.buf); ++ strbuf_release(&add->lock_file_name); + } + + free_names(add->names); + add->names = NULL; -+ slice_release(&nm); ++ strbuf_release(&nm); +} + +void reftable_addition_destroy(struct reftable_addition *add) @@ reftable/stack.c (new) + +int reftable_addition_commit(struct reftable_addition *add) +{ -+ struct slice table_list = SLICE_INIT; ++ struct strbuf table_list = STRBUF_INIT; + int i = 0; + int err = 0; + if (add->new_tables_len == 0) + goto done; + + for (i = 0; i < add->stack->merged->stack_len; i++) { -+ slice_addstr(&table_list, add->stack->merged->stack[i]->name); -+ slice_addstr(&table_list, "\n"); ++ strbuf_addstr(&table_list, add->stack->merged->stack[i]->name); ++ strbuf_addstr(&table_list, "\n"); + } + for (i = 0; i < add->new_tables_len; i++) { -+ slice_addstr(&table_list, add->new_tables[i]); -+ slice_addstr(&table_list, "\n"); ++ strbuf_addstr(&table_list, add->new_tables[i]); ++ strbuf_addstr(&table_list, "\n"); + } + + err = write(add->lock_file_fd, table_list.buf, table_list.len); -+ slice_release(&table_list); ++ strbuf_release(&table_list); + if (err < 0) { + err = REFTABLE_IO_ERROR; + goto done; @@ reftable/stack.c (new) + goto done; + } + -+ err = rename(slice_as_string(&add->lock_file_name), -+ add->stack->list_file); ++ err = rename(add->lock_file_name.buf, add->stack->list_file); + if (err < 0) { + err = REFTABLE_IO_ERROR; + goto done; @@ reftable/stack.c (new) + void *arg), + void *arg) +{ -+ struct slice temp_tab_file_name = SLICE_INIT; -+ struct slice tab_file_name = SLICE_INIT; -+ struct slice next_name = SLICE_INIT; ++ struct strbuf temp_tab_file_name = STRBUF_INIT; ++ struct strbuf tab_file_name = STRBUF_INIT; ++ struct strbuf next_name = STRBUF_INIT; + struct reftable_writer *wr = NULL; + int err = 0; + int tab_fd = 0; + -+ slice_reset(&next_name); ++ strbuf_reset(&next_name); + format_name(&next_name, add->next_update_index, add->next_update_index); + -+ slice_addstr(&temp_tab_file_name, add->stack->reftable_dir); -+ slice_addstr(&temp_tab_file_name, "/"); -+ slice_addbuf(&temp_tab_file_name, &next_name); -+ slice_addstr(&temp_tab_file_name, ".temp.XXXXXX"); ++ strbuf_addstr(&temp_tab_file_name, add->stack->reftable_dir); ++ strbuf_addstr(&temp_tab_file_name, "/"); ++ strbuf_addbuf(&temp_tab_file_name, &next_name); ++ strbuf_addstr(&temp_tab_file_name, ".temp.XXXXXX"); + -+ tab_fd = mkstemp((char *)slice_as_string(&temp_tab_file_name)); ++ tab_fd = mkstemp(temp_tab_file_name.buf); + if (tab_fd < 0) { + err = REFTABLE_IO_ERROR; + goto done; @@ reftable/stack.c (new) + goto done; + } + -+ err = stack_check_addition(add->stack, -+ slice_as_string(&temp_tab_file_name)); ++ err = stack_check_addition(add->stack, temp_tab_file_name.buf); + if (err < 0) + goto done; + @@ reftable/stack.c (new) + } + + format_name(&next_name, wr->min_update_index, wr->max_update_index); -+ slice_addstr(&next_name, ".ref"); ++ strbuf_addstr(&next_name, ".ref"); + -+ slice_addstr(&tab_file_name, add->stack->reftable_dir); -+ slice_addstr(&tab_file_name, "/"); -+ slice_addbuf(&tab_file_name, &next_name); ++ strbuf_addstr(&tab_file_name, add->stack->reftable_dir); ++ strbuf_addstr(&tab_file_name, "/"); ++ strbuf_addbuf(&tab_file_name, &next_name); + + /* TODO: should check destination out of paranoia */ -+ err = rename(slice_as_string(&temp_tab_file_name), -+ slice_as_string(&tab_file_name)); ++ err = rename(temp_tab_file_name.buf, tab_file_name.buf); + if (err < 0) { + err = REFTABLE_IO_ERROR; + goto done; @@ reftable/stack.c (new) + add->new_tables = reftable_realloc(add->new_tables, + sizeof(*add->new_tables) * + (add->new_tables_len + 1)); -+ add->new_tables[add->new_tables_len] = slice_detach(&next_name); ++ add->new_tables[add->new_tables_len] = strbuf_detach(&next_name, NULL); + add->new_tables_len++; +done: + if (tab_fd > 0) { @@ reftable/stack.c (new) + tab_fd = 0; + } + if (temp_tab_file_name.len > 0) { -+ unlink(slice_as_string(&temp_tab_file_name)); ++ unlink(temp_tab_file_name.buf); + } + -+ slice_release(&temp_tab_file_name); -+ slice_release(&tab_file_name); -+ slice_release(&next_name); ++ strbuf_release(&temp_tab_file_name); ++ strbuf_release(&tab_file_name); ++ strbuf_release(&next_name); + reftable_writer_free(wr); + return err; +} @@ reftable/stack.c (new) +} + +static int stack_compact_locked(struct reftable_stack *st, int first, int last, -+ struct slice *temp_tab, ++ struct strbuf *temp_tab, + struct reftable_log_expiry_config *config) +{ -+ struct slice next_name = SLICE_INIT; ++ struct strbuf next_name = STRBUF_INIT; + int tab_fd = -1; + struct reftable_writer *wr = NULL; + int err = 0; @@ reftable/stack.c (new) + reftable_reader_min_update_index(st->merged->stack[first]), + reftable_reader_max_update_index(st->merged->stack[first])); + -+ slice_reset(temp_tab); -+ slice_addstr(temp_tab, st->reftable_dir); -+ slice_addstr(temp_tab, "/"); -+ slice_addbuf(temp_tab, &next_name); -+ slice_addstr(temp_tab, ".temp.XXXXXX"); ++ strbuf_reset(temp_tab); ++ strbuf_addstr(temp_tab, st->reftable_dir); ++ strbuf_addstr(temp_tab, "/"); ++ strbuf_addbuf(temp_tab, &next_name); ++ strbuf_addstr(temp_tab, ".temp.XXXXXX"); + -+ tab_fd = mkstemp((char *)slice_as_string(temp_tab)); ++ tab_fd = mkstemp(temp_tab->buf); + wr = reftable_new_writer(reftable_fd_write, &tab_fd, &st->config); + + err = stack_write_compact(st, wr, first, last, config); @@ reftable/stack.c (new) + tab_fd = 0; + } + if (err != 0 && temp_tab->len > 0) { -+ unlink(slice_as_string(temp_tab)); -+ slice_release(temp_tab); ++ unlink(temp_tab->buf); ++ strbuf_release(temp_tab); + } -+ slice_release(&next_name); ++ strbuf_release(&next_name); + return err; +} + @@ reftable/stack.c (new) +static int stack_compact_range(struct reftable_stack *st, int first, int last, + struct reftable_log_expiry_config *expiry) +{ -+ struct slice temp_tab_file_name = SLICE_INIT; -+ struct slice new_table_name = SLICE_INIT; -+ struct slice lock_file_name = SLICE_INIT; -+ struct slice ref_list_contents = SLICE_INIT; -+ struct slice new_table_path = SLICE_INIT; ++ struct strbuf temp_tab_file_name = STRBUF_INIT; ++ struct strbuf new_table_name = STRBUF_INIT; ++ struct strbuf lock_file_name = STRBUF_INIT; ++ struct strbuf ref_list_contents = STRBUF_INIT; ++ struct strbuf new_table_path = STRBUF_INIT; + int err = 0; + bool have_lock = false; + int lock_file_fd = 0; @@ reftable/stack.c (new) + + st->stats.attempts++; + -+ slice_reset(&lock_file_name); -+ slice_addstr(&lock_file_name, st->list_file); -+ slice_addstr(&lock_file_name, ".lock"); ++ strbuf_reset(&lock_file_name); ++ strbuf_addstr(&lock_file_name, st->list_file); ++ strbuf_addstr(&lock_file_name, ".lock"); + -+ lock_file_fd = open(slice_as_string(&lock_file_name), -+ O_EXCL | O_CREAT | O_WRONLY, 0644); ++ lock_file_fd = ++ open(lock_file_name.buf, O_EXCL | O_CREAT | O_WRONLY, 0644); + if (lock_file_fd < 0) { + if (errno == EEXIST) { + err = 1; @@ reftable/stack.c (new) + goto done; + + for (i = first, j = 0; i <= last; i++) { -+ struct slice subtab_file_name = SLICE_INIT; -+ struct slice subtab_lock = SLICE_INIT; ++ struct strbuf subtab_file_name = STRBUF_INIT; ++ struct strbuf subtab_lock = STRBUF_INIT; + int sublock_file_fd = -1; + -+ slice_addstr(&subtab_file_name, st->reftable_dir); -+ slice_addstr(&subtab_file_name, "/"); -+ slice_addstr(&subtab_file_name, -+ reader_name(st->merged->stack[i])); ++ strbuf_addstr(&subtab_file_name, st->reftable_dir); ++ strbuf_addstr(&subtab_file_name, "/"); ++ strbuf_addstr(&subtab_file_name, ++ reader_name(st->merged->stack[i])); + -+ slice_reset(&subtab_lock); -+ slice_addbuf(&subtab_lock, &subtab_file_name); -+ slice_addstr(&subtab_lock, ".lock"); ++ strbuf_reset(&subtab_lock); ++ strbuf_addbuf(&subtab_lock, &subtab_file_name); ++ strbuf_addstr(&subtab_lock, ".lock"); + -+ sublock_file_fd = open(slice_as_string(&subtab_lock), ++ sublock_file_fd = open(subtab_lock.buf, + O_EXCL | O_CREAT | O_WRONLY, 0644); + if (sublock_file_fd > 0) { + close(sublock_file_fd); @@ reftable/stack.c (new) + } + } + -+ subtable_locks[j] = (char *)slice_as_string(&subtab_lock); -+ delete_on_success[j] = -+ (char *)slice_as_string(&subtab_file_name); ++ subtable_locks[j] = subtab_lock.buf; ++ delete_on_success[j] = subtab_file_name.buf; + j++; + + if (err != 0) + goto done; + } + -+ err = unlink(slice_as_string(&lock_file_name)); ++ err = unlink(lock_file_name.buf); + if (err < 0) + goto done; + have_lock = false; @@ reftable/stack.c (new) + if (err < 0) + goto done; + -+ lock_file_fd = open(slice_as_string(&lock_file_name), -+ O_EXCL | O_CREAT | O_WRONLY, 0644); ++ lock_file_fd = ++ open(lock_file_name.buf, O_EXCL | O_CREAT | O_WRONLY, 0644); + if (lock_file_fd < 0) { + if (errno == EEXIST) { + err = 1; @@ reftable/stack.c (new) + + format_name(&new_table_name, st->merged->stack[first]->min_update_index, + st->merged->stack[last]->max_update_index); -+ slice_addstr(&new_table_name, ".ref"); ++ strbuf_addstr(&new_table_name, ".ref"); + -+ slice_reset(&new_table_path); -+ slice_addstr(&new_table_path, st->reftable_dir); -+ slice_addstr(&new_table_path, "/"); -+ slice_addbuf(&new_table_path, &new_table_name); ++ strbuf_reset(&new_table_path); ++ strbuf_addstr(&new_table_path, st->reftable_dir); ++ strbuf_addstr(&new_table_path, "/"); ++ strbuf_addbuf(&new_table_path, &new_table_name); + + if (!is_empty_table) { -+ err = rename(slice_as_string(&temp_tab_file_name), -+ slice_as_string(&new_table_path)); ++ err = rename(temp_tab_file_name.buf, new_table_path.buf); + if (err < 0) { + err = REFTABLE_IO_ERROR; + goto done; @@ reftable/stack.c (new) + } + + for (i = 0; i < first; i++) { -+ slice_addstr(&ref_list_contents, st->merged->stack[i]->name); -+ slice_addstr(&ref_list_contents, "\n"); ++ strbuf_addstr(&ref_list_contents, st->merged->stack[i]->name); ++ strbuf_addstr(&ref_list_contents, "\n"); + } + if (!is_empty_table) { -+ slice_addbuf(&ref_list_contents, &new_table_name); -+ slice_addstr(&ref_list_contents, "\n"); ++ strbuf_addbuf(&ref_list_contents, &new_table_name); ++ strbuf_addstr(&ref_list_contents, "\n"); + } + for (i = last + 1; i < st->merged->stack_len; i++) { -+ slice_addstr(&ref_list_contents, st->merged->stack[i]->name); -+ slice_addstr(&ref_list_contents, "\n"); ++ strbuf_addstr(&ref_list_contents, st->merged->stack[i]->name); ++ strbuf_addstr(&ref_list_contents, "\n"); + } + + err = write(lock_file_fd, ref_list_contents.buf, ref_list_contents.len); + if (err < 0) { + err = REFTABLE_IO_ERROR; -+ unlink(slice_as_string(&new_table_path)); ++ unlink(new_table_path.buf); + goto done; + } + err = close(lock_file_fd); + lock_file_fd = 0; + if (err < 0) { + err = REFTABLE_IO_ERROR; -+ unlink(slice_as_string(&new_table_path)); ++ unlink(new_table_path.buf); + goto done; + } + -+ err = rename(slice_as_string(&lock_file_name), st->list_file); ++ err = rename(lock_file_name.buf, st->list_file); + if (err < 0) { + err = REFTABLE_IO_ERROR; -+ unlink(slice_as_string(&new_table_path)); ++ unlink(new_table_path.buf); + goto done; + } + have_lock = false; @@ reftable/stack.c (new) + + listp = delete_on_success; + while (*listp) { -+ if (strcmp(*listp, slice_as_string(&new_table_path))) { ++ if (strcmp(*listp, new_table_path.buf)) { + unlink(*listp); + } + listp++; @@ reftable/stack.c (new) + lock_file_fd = 0; + } + if (have_lock) { -+ unlink(slice_as_string(&lock_file_name)); ++ unlink(lock_file_name.buf); + } -+ slice_release(&new_table_name); -+ slice_release(&new_table_path); -+ slice_release(&ref_list_contents); -+ slice_release(&temp_tab_file_name); -+ slice_release(&lock_file_name); ++ strbuf_release(&new_table_name); ++ strbuf_release(&new_table_path); ++ strbuf_release(&ref_list_contents); ++ strbuf_release(&temp_tab_file_name); ++ strbuf_release(&lock_file_name); + return err; +} + @@ reftable/stack_test.c (new) + struct reftable_stack *st = NULL; + char dir[256] = "/tmp/stack_test.XXXXXX"; + -+ byte h1[SHA1_SIZE] = { 0x01 }, h2[SHA1_SIZE] = { 0x02 }; ++ uint8_t h1[SHA1_SIZE] = { 0x01 }, h2[SHA1_SIZE] = { 0x02 }; + + struct reftable_log_record input = { + .ref_name = "branch", @@ reftable/stack_test.c (new) + add_test_case("test_names_equal", &test_names_equal); + add_test_case("test_reftable_stack_add", &test_reftable_stack_add); + return test_main(argc, argv); ++} + + ## reftable/strbuf.c (new) ## +@@ ++/* ++Copyright 2020 Google LLC ++ ++Use of this source code is governed by a BSD-style ++license that can be found in the LICENSE file or at ++https://developers.google.com/open-source/licenses/bsd ++*/ ++ ++#include "strbuf.h" ++ ++#include "system.h" ++#include "reftable.h" ++#include "basics.h" ++ ++#ifdef REFTABLE_STANDALONE ++ ++void strbuf_init(struct strbuf *s, size_t alloc) ++{ ++ struct strbuf empty = STRBUF_INIT; ++ *s = empty; ++} ++ ++void strbuf_grow(struct strbuf *s, size_t extra) ++{ ++ size_t newcap = s->len + extra + 1; ++ if (newcap > s->cap) { ++ s->buf = reftable_realloc(s->buf, newcap); ++ s->cap = newcap; ++ } ++} ++ ++static void strbuf_resize(struct strbuf *s, int l) ++{ ++ int zl = l + 1; /* one uint8_t for 0 termination. */ ++ assert(s->canary == STRBUF_CANARY); ++ if (s->cap < zl) { ++ int c = s->cap * 2; ++ if (c < zl) { ++ c = zl; ++ } ++ s->cap = c; ++ s->buf = reftable_realloc(s->buf, s->cap); ++ } ++ s->len = l; ++ s->buf[l] = 0; ++} ++ ++void strbuf_setlen(struct strbuf *s, size_t l) ++{ ++ assert(s->cap >= l + 1); ++ s->len = l; ++ s->buf[l] = 0; ++} ++ ++void strbuf_reset(struct strbuf *s) ++{ ++ strbuf_resize(s, 0); ++} ++ ++void strbuf_addstr(struct strbuf *d, const char *s) ++{ ++ int l1 = d->len; ++ int l2 = strlen(s); ++ assert(d->canary == STRBUF_CANARY); ++ ++ strbuf_resize(d, l2 + l1); ++ memcpy(d->buf + l1, s, l2); ++} ++ ++void strbuf_addbuf(struct strbuf *s, struct strbuf *a) ++{ ++ int end = s->len; ++ assert(s->canary == STRBUF_CANARY); ++ strbuf_resize(s, s->len + a->len); ++ memcpy(s->buf + end, a->buf, a->len); ++} ++ ++char *strbuf_detach(struct strbuf *s, size_t *sz) ++{ ++ char *p = NULL; ++ p = (char *)s->buf; ++ if (sz) ++ *sz = s->len; ++ s->buf = NULL; ++ s->cap = 0; ++ s->len = 0; ++ return p; ++} ++ ++void strbuf_release(struct strbuf *s) ++{ ++ assert(s->canary == STRBUF_CANARY); ++ s->cap = 0; ++ s->len = 0; ++ reftable_free(s->buf); ++ s->buf = NULL; ++} ++ ++int strbuf_cmp(const struct strbuf *a, const struct strbuf *b) ++{ ++ int min = a->len < b->len ? a->len : b->len; ++ int res = memcmp(a->buf, b->buf, min); ++ assert(a->canary == STRBUF_CANARY); ++ assert(b->canary == STRBUF_CANARY); ++ if (res != 0) ++ return res; ++ if (a->len < b->len) ++ return -1; ++ else if (a->len > b->len) ++ return 1; ++ else ++ return 0; ++} ++ ++int strbuf_add(struct strbuf *b, const void *data, size_t sz) ++{ ++ assert(b->canary == STRBUF_CANARY); ++ strbuf_grow(b, sz); ++ memcpy(b->buf + b->len, data, sz); ++ b->len += sz; ++ b->buf[b->len] = 0; ++ return sz; ++} ++ ++#endif ++ ++static uint64_t strbuf_size(void *b) ++{ ++ return ((struct strbuf *)b)->len; ++} ++ ++int strbuf_add_void(void *b, const void *data, size_t sz) ++{ ++ strbuf_add((struct strbuf *)b, data, sz); ++ return sz; ++} ++ ++static void strbuf_return_block(void *b, struct reftable_block *dest) ++{ ++ memset(dest->data, 0xff, dest->len); ++ reftable_free(dest->data); ++} ++ ++static void strbuf_close(void *b) ++{ ++} ++ ++static int strbuf_read_block(void *v, struct reftable_block *dest, uint64_t off, ++ uint32_t size) ++{ ++ struct strbuf *b = (struct strbuf *)v; ++ assert(off + size <= b->len); ++ dest->data = reftable_calloc(size); ++ memcpy(dest->data, b->buf + off, size); ++ dest->len = size; ++ return size; ++} ++ ++struct reftable_block_source_vtable strbuf_vtable = { ++ .size = &strbuf_size, ++ .read_block = &strbuf_read_block, ++ .return_block = &strbuf_return_block, ++ .close = &strbuf_close, ++}; ++ ++void block_source_from_strbuf(struct reftable_block_source *bs, ++ struct strbuf *buf) ++{ ++ assert(bs->ops == NULL); ++ bs->ops = &strbuf_vtable; ++ bs->arg = buf; ++} ++ ++static void malloc_return_block(void *b, struct reftable_block *dest) ++{ ++ memset(dest->data, 0xff, dest->len); ++ reftable_free(dest->data); ++} ++ ++struct reftable_block_source_vtable malloc_vtable = { ++ .return_block = &malloc_return_block, ++}; ++ ++struct reftable_block_source malloc_block_source_instance = { ++ .ops = &malloc_vtable, ++}; ++ ++struct reftable_block_source malloc_block_source(void) ++{ ++ return malloc_block_source_instance; ++} ++ ++int common_prefix_size(struct strbuf *a, struct strbuf *b) ++{ ++ int p = 0; ++ while (p < a->len && p < b->len) { ++ if (a->buf[p] != b->buf[p]) { ++ break; ++ } ++ p++; ++ } ++ ++ return p; ++} ++ ++struct strbuf reftable_empty_strbuf = STRBUF_INIT; + + ## reftable/strbuf.h (new) ## +@@ ++/* ++Copyright 2020 Google LLC ++ ++Use of this source code is governed by a BSD-style ++license that can be found in the LICENSE file or at ++https://developers.google.com/open-source/licenses/bsd ++*/ ++ ++#ifndef SLICE_H ++#define SLICE_H ++ ++#ifdef REFTABLE_STANDALONE ++ ++#include "basics.h" ++#include "reftable.h" ++ ++/* ++ Provides a bounds-checked, growable byte ranges. To use, initialize as "strbuf ++ x = STRBUF_INIT;" ++ */ ++struct strbuf { ++ int len; ++ int cap; ++ char *buf; ++ ++ /* Used to enforce initialization with STRBUF_INIT */ ++ uint8_t canary; ++}; ++#define STRBUF_CANARY 0x42 ++#define STRBUF_INIT \ ++ { \ ++ 0, 0, NULL, STRBUF_CANARY \ ++ } ++ ++void strbuf_addstr(struct strbuf *dest, const char *src); ++ ++/* Deallocate and clear strbuf */ ++void strbuf_release(struct strbuf *strbuf); ++ ++/* Set strbuf to 0 length, but retain buffer. */ ++void strbuf_reset(struct strbuf *strbuf); ++ ++/* Initializes a strbuf. Accepts a strbuf with random garbage. */ ++void strbuf_init(struct strbuf *strbuf, size_t alloc); ++ ++/* Return `buf`, clearing out `s`. Optionally return len (not cap) in `sz`. */ ++char *strbuf_detach(struct strbuf *s, size_t *sz); ++ ++/* Set length of the slace to `l`, but don't reallocated. */ ++void strbuf_setlen(struct strbuf *s, size_t l); ++ ++/* Ensure `l` bytes beyond current length are available */ ++void strbuf_grow(struct strbuf *s, size_t l); ++ ++/* Signed comparison */ ++int strbuf_cmp(const struct strbuf *a, const struct strbuf *b); ++ ++/* Append `data` to the `dest` strbuf. */ ++int strbuf_add(struct strbuf *dest, const void *data, size_t sz); ++ ++/* Append `add` to `dest. */ ++void strbuf_addbuf(struct strbuf *dest, struct strbuf *add); ++ ++#else ++ ++#include "../git-compat-util.h" ++#include "../strbuf.h" ++ ++#endif ++ ++extern struct strbuf reftable_empty_strbuf; ++ ++/* Like strbuf_add, but suitable for passing to reftable_new_writer ++ */ ++int strbuf_add_void(void *b, const void *data, size_t sz); ++ ++/* Find the longest shared prefix size of `a` and `b` */ ++int common_prefix_size(struct strbuf *a, struct strbuf *b); ++ ++struct reftable_block_source; ++ ++/* Create an in-memory block source for reading reftables */ ++void block_source_from_strbuf(struct reftable_block_source *bs, ++ struct strbuf *buf); ++ ++struct reftable_block_source malloc_block_source(void); ++ ++#endif + + ## reftable/strbuf_test.c (new) ## +@@ ++/* ++Copyright 2020 Google LLC ++ ++Use of this source code is governed by a BSD-style ++license that can be found in the LICENSE file or at ++https://developers.google.com/open-source/licenses/bsd ++*/ ++ ++#include "strbuf.h" ++ ++#include "system.h" ++ ++#include "basics.h" ++#include "record.h" ++#include "reftable.h" ++#include "test_framework.h" ++#include "reftable-tests.h" ++ ++static void test_strbuf(void) ++{ ++ struct strbuf s = STRBUF_INIT; ++ struct strbuf t = STRBUF_INIT; ++ ++ strbuf_addstr(&s, "abc"); ++ assert(0 == strcmp("abc", s.buf)); ++ ++ strbuf_addstr(&t, "pqr"); ++ strbuf_addbuf(&s, &t); ++ assert(0 == strcmp("abcpqr", s.buf)); ++ ++ strbuf_release(&s); ++ strbuf_release(&t); ++} ++ ++int strbuf_test_main(int argc, const char *argv[]) ++{ ++ add_test_case("test_strbuf", &test_strbuf); ++ return test_main(argc, argv); +} ## reftable/system.h (new) ## @@ reftable/system.h (new) +#ifndef SYSTEM_H +#define SYSTEM_H + -+#if 1 /* REFTABLE_IN_GITCORE */ -+#define REFTABLE_IN_GITCORE ++#ifndef REFTABLE_STANDALONE + +#include "git-compat-util.h" +#include "cache.h" @@ reftable/system.h (new) + +#include "compat.h" + -+#endif /* REFTABLE_IN_GITCORE */ ++#endif /* REFTABLE_STANDALONE */ + +void reftable_clear_dir(const char *dirname); + @@ reftable/system.h (new) +#define SHA1_SIZE 20 +#define SHA256_SIZE 32 + -+typedef uint8_t byte; +typedef int bool; + +/* This is uncompress2, which is only available in zlib as of 2017. @@ reftable/test_framework.c (new) + return 0; +} + -+void set_test_hash(byte *p, int i) ++void set_test_hash(uint8_t *p, int i) +{ -+ memset(p, (byte)i, hash_size(SHA1_ID)); ++ memset(p, (uint8_t)i, hash_size(SHA1_ID)); +} ## reftable/test_framework.h (new) ## @@ reftable/test_framework.h (new) +struct test_case *add_test_case(const char *name, void (*testfunc)(void)); +int test_main(int argc, const char *argv[]); + -+void set_test_hash(byte *p, int i); ++void set_test_hash(uint8_t *p, int i); + +#endif @@ reftable/writer.c (new) +#include "tree.h" + +static struct reftable_block_stats * -+writer_reftable_block_stats(struct reftable_writer *w, byte typ) ++writer_reftable_block_stats(struct reftable_writer *w, uint8_t typ) +{ + switch (typ) { + case 'r': @@ reftable/writer.c (new) + +/* write data, queuing the padding for the next write. Returns negative for + * error. */ -+static int padded_write(struct reftable_writer *w, byte *data, size_t len, ++static int padded_write(struct reftable_writer *w, uint8_t *data, size_t len, + int padding) +{ + int n = 0; + if (w->pending_padding > 0) { -+ byte *zeroed = reftable_calloc(w->pending_padding); ++ uint8_t *zeroed = reftable_calloc(w->pending_padding); + int n = w->write(w->write_arg, zeroed, w->pending_padding); + if (n < 0) + return n; @@ reftable/writer.c (new) + return (w->opts.hash_id == 0 || w->opts.hash_id == SHA1_ID) ? 1 : 2; +} + -+static int writer_write_header(struct reftable_writer *w, byte *dest) ++static int writer_write_header(struct reftable_writer *w, uint8_t *dest) +{ + memcpy((char *)dest, "REFT", 4); + @@ reftable/writer.c (new) + return header_size(writer_version(w)); +} + -+static void writer_reinit_block_writer(struct reftable_writer *w, byte typ) ++static void writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ) +{ + int block_start = 0; + if (w->next == 0) { + block_start = header_size(writer_version(w)); + } + -+ slice_release(&w->last_key); ++ strbuf_release(&w->last_key); + block_writer_init(&w->block_writer_data, typ, w->block, + w->opts.block_size, block_start, + hash_size(w->opts.hash_id)); @@ reftable/writer.c (new) +} + +struct reftable_writer * -+reftable_new_writer(int (*writer_func)(void *, byte *, size_t), ++reftable_new_writer(int (*writer_func)(void *, const void *, size_t), + void *writer_arg, struct reftable_write_options *opts) +{ + struct reftable_writer *wp = + reftable_calloc(sizeof(struct reftable_writer)); -+ slice_init(&wp->block_writer_data.last_key); ++ strbuf_init(&wp->block_writer_data.last_key, 0); + options_set_defaults(opts); + if (opts->block_size >= (1 << 24)) { + /* TODO - error return? */ + abort(); + } -+ wp->last_key = reftable_empty_slice; ++ wp->last_key = reftable_empty_strbuf; + wp->block = reftable_calloc(opts->block_size); + wp->write = writer_func; + wp->write_arg = writer_arg; @@ reftable/writer.c (new) +} + +struct obj_index_tree_node { -+ struct slice hash; ++ struct strbuf hash; + uint64_t *offsets; + int offset_len; + int offset_cap; +}; -+#define OBJ_INDEX_TREE_NODE_INIT \ -+ { \ -+ .hash = SLICE_INIT \ ++#define OBJ_INDEX_TREE_NODE_INIT \ ++ { \ ++ .hash = STRBUF_INIT \ + } + +static int obj_index_tree_node_compare(const void *a, const void *b) +{ -+ return slice_cmp(&((const struct obj_index_tree_node *)a)->hash, -+ &((const struct obj_index_tree_node *)b)->hash); ++ return strbuf_cmp(&((const struct obj_index_tree_node *)a)->hash, ++ &((const struct obj_index_tree_node *)b)->hash); +} + -+static void writer_index_hash(struct reftable_writer *w, struct slice *hash) ++static void writer_index_hash(struct reftable_writer *w, struct strbuf *hash) +{ + uint64_t off = w->next; + @@ reftable/writer.c (new) + key = reftable_malloc(sizeof(struct obj_index_tree_node)); + *key = empty; + -+ slice_reset(&key->hash); -+ slice_addbuf(&key->hash, hash); ++ strbuf_reset(&key->hash); ++ strbuf_addbuf(&key->hash, hash); + tree_search((void *)key, &w->obj_index_tree, + &obj_index_tree_node_compare, 1); + } else { @@ reftable/writer.c (new) + struct reftable_record *rec) +{ + int result = -1; -+ struct slice key = SLICE_INIT; ++ struct strbuf key = STRBUF_INIT; + int err = 0; + reftable_record_key(rec, &key); -+ if (slice_cmp(&w->last_key, &key) >= 0) ++ if (strbuf_cmp(&w->last_key, &key) >= 0) + goto done; + -+ slice_reset(&w->last_key); -+ slice_addbuf(&w->last_key, &key); ++ strbuf_reset(&w->last_key); ++ strbuf_addbuf(&w->last_key, &key); + if (w->block_writer == NULL) { + writer_reinit_block_writer(w, reftable_record_type(rec)); + } @@ reftable/writer.c (new) + + result = 0; +done: -+ slice_release(&key); ++ strbuf_release(&key); + return result; +} + @@ reftable/writer.c (new) + return err; + + if (!w->opts.skip_index_objects && ref->value != NULL) { -+ struct slice h = SLICE_INIT; -+ slice_add(&h, ref->value, hash_size(w->opts.hash_id)); ++ struct strbuf h = STRBUF_INIT; ++ strbuf_add(&h, (char *)ref->value, hash_size(w->opts.hash_id)); + writer_index_hash(w, &h); -+ slice_release(&h); ++ strbuf_release(&h); + } + + if (!w->opts.skip_index_objects && ref->target_value != NULL) { -+ struct slice h = SLICE_INIT; -+ slice_add(&h, ref->target_value, hash_size(w->opts.hash_id)); ++ struct strbuf h = STRBUF_INIT; ++ strbuf_add(&h, ref->target_value, hash_size(w->opts.hash_id)); + writer_index_hash(w, &h); + } + return 0; @@ reftable/writer.c (new) +{ + struct reftable_record rec = { 0 }; + char *input_log_message = log->message; -+ struct slice cleaned_message = SLICE_INIT; ++ struct strbuf cleaned_message = STRBUF_INIT; + int err; + if (log->ref_name == NULL) + return REFTABLE_API_ERROR; @@ reftable/writer.c (new) + } + + if (!w->opts.exact_log_message && log->message != NULL) { -+ slice_addstr(&cleaned_message, log->message); ++ strbuf_addstr(&cleaned_message, log->message); + while (cleaned_message.len && + cleaned_message.buf[cleaned_message.len - 1] == '\n') -+ slice_setlen(&cleaned_message, cleaned_message.len - 1); -+ if (strchr(slice_as_string(&cleaned_message), '\n')) { ++ strbuf_setlen(&cleaned_message, ++ cleaned_message.len - 1); ++ if (strchr(cleaned_message.buf, '\n')) { + // multiple lines not allowed. + err = REFTABLE_API_ERROR; + goto done; + } -+ slice_addstr(&cleaned_message, "\n"); -+ log->message = (char *)slice_as_string(&cleaned_message); ++ strbuf_addstr(&cleaned_message, "\n"); ++ log->message = cleaned_message.buf; + } + + w->next -= w->pending_padding; @@ reftable/writer.c (new) + +done: + log->message = input_log_message; -+ slice_release(&cleaned_message); ++ strbuf_release(&cleaned_message); + return err; +} + @@ reftable/writer.c (new) + +static int writer_finish_section(struct reftable_writer *w) +{ -+ byte typ = block_writer_type(w->block_writer); ++ uint8_t typ = block_writer_type(w->block_writer); + uint64_t index_start = 0; + int max_level = 0; + int threshold = w->opts.unpadded ? 1 : 3; @@ reftable/writer.c (new) + } + } + for (i = 0; i < idx_len; i++) { -+ slice_release(&idx[i].last_key); ++ strbuf_release(&idx[i].last_key); + } + reftable_free(idx); + } @@ reftable/writer.c (new) +} + +struct common_prefix_arg { -+ struct slice *last; ++ struct strbuf *last; + int max; +}; + @@ reftable/writer.c (new) + struct write_record_arg *arg = (struct write_record_arg *)void_arg; + struct obj_index_tree_node *entry = (struct obj_index_tree_node *)key; + struct reftable_obj_record obj_rec = { -+ .hash_prefix = entry->hash.buf, ++ .hash_prefix = (uint8_t *)entry->hash.buf, + .hash_prefix_len = arg->w->stats.object_id_len, + .offsets = entry->offsets, + .offset_len = entry->offset_len, @@ reftable/writer.c (new) + struct obj_index_tree_node *entry = (struct obj_index_tree_node *)key; + + FREE_AND_NULL(entry->offsets); -+ slice_release(&entry->hash); ++ strbuf_release(&entry->hash); + reftable_free(entry); +} + @@ reftable/writer.c (new) + +int writer_finish_public_section(struct reftable_writer *w) +{ -+ byte typ = 0; ++ uint8_t typ = 0; + int err = 0; + + if (w->block_writer == NULL) @@ reftable/writer.c (new) + +int reftable_writer_close(struct reftable_writer *w) +{ -+ byte footer[72]; -+ byte *p = footer; ++ uint8_t footer[72]; ++ uint8_t *p = footer; + int err = writer_finish_public_section(w); + int empty_table = w->next == 0; + if (err != 0) @@ reftable/writer.c (new) + w->pending_padding = 0; + if (empty_table) { + /* Empty tables need a header anyway. */ -+ byte header[28]; ++ uint8_t header[28]; + int n = writer_write_header(w, header); + err = padded_write(w, header, n, 0); + if (err < 0) @@ reftable/writer.c (new) + /* free up memory. */ + block_writer_clear(&w->block_writer_data); + writer_clear_index(w); -+ slice_release(&w->last_key); ++ strbuf_release(&w->last_key); + return err; +} + @@ reftable/writer.c (new) +{ + int i = 0; + for (i = 0; i < w->index_len; i++) { -+ slice_release(&w->index[i].last_key); ++ strbuf_release(&w->index[i].last_key); + } + + FREE_AND_NULL(w->index); @@ reftable/writer.c (new) + +static int writer_flush_nonempty_block(struct reftable_writer *w) +{ -+ byte typ = block_writer_type(w->block_writer); ++ uint8_t typ = block_writer_type(w->block_writer); + struct reftable_block_stats *bstats = + writer_reftable_block_stats(w, typ); + uint64_t block_typ_off = (bstats->blocks == 0) ? w->next : 0; + int raw_bytes = block_writer_finish(w->block_writer); + int padding = 0; + int err = 0; -+ struct reftable_index_record ir = { .last_key = SLICE_INIT }; ++ struct reftable_index_record ir = { .last_key = STRBUF_INIT }; + if (raw_bytes < 0) + return raw_bytes; + @@ reftable/writer.c (new) + } + + ir.offset = w->next; -+ slice_reset(&ir.last_key); -+ slice_addbuf(&ir.last_key, &w->block_writer->last_key); ++ strbuf_reset(&ir.last_key); ++ strbuf_addbuf(&ir.last_key, &w->block_writer->last_key); + w->index[w->index_len] = ir; + + w->index_len++; @@ reftable/writer.h (new) +#include "basics.h" +#include "block.h" +#include "reftable.h" -+#include "slice.h" ++#include "strbuf.h" +#include "tree.h" + +struct reftable_writer { -+ int (*write)(void *, byte *, size_t); ++ int (*write)(void *, const void *, size_t); + void *write_arg; + int pending_padding; -+ struct slice last_key; ++ struct strbuf last_key; + + /* offset of next block to write. */ + uint64_t next; @@ reftable/writer.h (new) + struct reftable_write_options opts; + + /* memory buffer for writing */ -+ byte *block; ++ uint8_t *block; + + /* writer for the current section. NULL or points to + * block_writer_data */ -: ----------- > 12: c92b8d12ec6 Add standalone build infrastructure for reftable 12: e1b01927454 ! 13: 479fe884e98 Reftable support for git-core @@ Commit message It can be activated by passing --ref-storage=reftable to "git init". - TODO: - - * Fix worktree commands - - * Spots marked XXX - Example use: see t/t0031-reftable.sh Signed-off-by: Han-Wen Nienhuys <hanwen@xxxxxxxxxx> @@ Makefile: VCSSVN_OBJS += vcs-svn/sliding_window.o +REFTABLE_OBJS += reftable/record.o +REFTABLE_OBJS += reftable/refname.o +REFTABLE_OBJS += reftable/reftable.o -+REFTABLE_OBJS += reftable/slice.o ++REFTABLE_OBJS += reftable/strbuf.o +REFTABLE_OBJS += reftable/stack.o +REFTABLE_OBJS += reftable/tree.o +REFTABLE_OBJS += reftable/writer.o @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix) - init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, INIT_DB_QUIET); + init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, -+ DEFAULT_REF_STORAGE, INIT_DB_QUIET); ++ default_ref_storage(), INIT_DB_QUIET); if (real_git_dir) git_dir = real_git_dir; @@ cache.h: int path_inside_repo(const char *prefix, const char *path); void sanitize_stdfds(void); int daemonize(void); @@ cache.h: struct repository_format { - int is_bare; int hash_algo; + int has_extensions; char *work_tree; + char *ref_storage; struct string_list unknown_extensions; @@ refs.c: struct ref_store *get_main_ref_store(struct repository *r) + r->refs_private = ref_store_init(r->gitdir, + r->ref_storage_format ? + r->ref_storage_format : -+ DEFAULT_REF_STORAGE, ++ default_ref_storage(), + REF_STORE_ALL_CAPS); return r->refs_private; } @@ refs.c: struct ref_store *get_submodule_ref_store(const char *submodule) /* assume that add_submodule_odb() has been called */ - refs = ref_store_init(submodule_sb.buf, -+ refs = ref_store_init(submodule_sb.buf, DEFAULT_REF_STORAGE, /* XXX */ ++ refs = ref_store_init(submodule_sb.buf, default_ref_storage(), REF_STORE_READ | REF_STORE_ODB); register_ref_store_map(&submodule_ref_stores, "submodule", refs, submodule); @@ refs.c: struct ref_store *get_submodule_ref_store(const char *submodule) struct ref_store *get_worktree_ref_store(const struct worktree *wt) { -+ const char *format = DEFAULT_REF_STORAGE; /* XXX */ ++ const char *format = default_ref_storage(); struct ref_store *refs; const char *id; @@ refs/reftable-backend.c (new) + safe_create_dir(refs->reftable_dir, 1); + + strbuf_addf(&sb, "%s/HEAD", refs->repo_dir); -+ write_file(sb.buf, "ref: refs/.invalid"); ++ write_file(sb.buf, "ref: refs/heads/.invalid"); + strbuf_reset(&sb); + + strbuf_addf(&sb, "%s/refs", refs->repo_dir); @@ refs/reftable-backend.c (new) + return refs->err; + } + ++ /* This is usually not needed, but Git doesn't signal to ref backend if ++ a subprocess updated the ref DB. So we always check. ++ */ ++ err = reftable_stack_reload(refs->stack); ++ if (err) { ++ goto done; ++ } ++ + err = reftable_stack_read_ref(refs->stack, refname, &ref); + if (err > 0) { + errno = ENOENT; @@ refs/reftable-backend.c (new) + reftable_reflog_expire, +}; + ## reftable/update.sh ## +@@ reftable/update.sh: cp reftable-repo/LICENSE reftable/ + git --git-dir reftable-repo/.git show --no-patch --format=oneline HEAD \ + > reftable/VERSION + +-mv reftable/system.h reftable/system.h~ +-sed 's|if REFTABLE_IN_GITCORE|if 1 /* REFTABLE_IN_GITCORE */|' < reftable/system.h~ > reftable/system.h +- + git add reftable/*.[ch] reftable/LICENSE reftable/VERSION + ## repository.c ## @@ repository.c: int repo_init(struct repository *repo, if (worktree) @@ t/t0031-reftable.sh (new) + for count in $(test_seq 1 10) + do + test_commit "number $count" file.t $count number-$count || -+ return 1 ++ return 1 + done && + git pack-refs && + ls -1 .git/reftable >table-files && @@ t/t0031-reftable.sh (new) + git branch source && + git checkout HEAD^ && + test_commit message3 file3 && -+ git rebase source && ++ git rebase source && + test -f file2 +' + 13: 0359fe416fa ! 14: eafd8eeefcc Hookup unittests for the reftable library. @@ Makefile: REFTABLE_OBJS += reftable/tree.o +REFTABLE_TEST_OBJS += reftable/record_test.o +REFTABLE_TEST_OBJS += reftable/refname_test.o +REFTABLE_TEST_OBJS += reftable/reftable_test.o -+REFTABLE_TEST_OBJS += reftable/slice_test.o ++REFTABLE_TEST_OBJS += reftable/strbuf_test.o +REFTABLE_TEST_OBJS += reftable/stack_test.o +REFTABLE_TEST_OBJS += reftable/tree_test.o +REFTABLE_TEST_OBJS += reftable/test_framework.o @@ t/helper/test-reftable.c (new) + record_test_main(argc, argv); + refname_test_main(argc, argv); + reftable_test_main(argc, argv); -+ slice_test_main(argc, argv); ++ strbuf_test_main(argc, argv); + stack_test_main(argc, argv); + tree_test_main(argc, argv); + return 0; 14: 88640ea13f9 ! 15: 46af142f33b Add GIT_DEBUG_REFS debugging mechanism @@ Makefile: LIB_OBJS += rebase.o ## refs.c ## @@ refs.c: struct ref_store *get_main_ref_store(struct repository *r) r->ref_storage_format : - DEFAULT_REF_STORAGE, + default_ref_storage(), REF_STORE_ALL_CAPS); + if (getenv("GIT_DEBUG_REFS")) { + r->refs_private = debug_wrap(r->refs_private); @@ refs/debug.c (new) +{ + struct debug_ref_store *drefs = (struct debug_ref_store *)refs; + int res = drefs->refs->be->init_db(drefs->refs, err); ++ fprintf(stderr, "init_db: %d\n", res); + return res; +} + @@ refs/debug.c (new) + transaction->ref_store = drefs->refs; + res = drefs->refs->be->transaction_prepare(drefs->refs, transaction, + err); ++ fprintf(stderr, "transaction_prepare: %d\n", res); + return res; +} + @@ refs/debug.c (new) + type &= 0xf; /* see refs.h REF_* */ + flags &= REF_HAVE_NEW | REF_HAVE_OLD | REF_NO_DEREF | + REF_FORCE_CREATE_REFLOG | REF_LOG_ONLY; -+ printf("%d: %s %s -> %s (F=0x%x, T=0x%x) \"%s\"\n", i, refname, o, n, -+ flags, type, msg); ++ fprintf(stderr, "%d: %s %s -> %s (F=0x%x, T=0x%x) \"%s\"\n", i, refname, ++ o, n, flags, type, msg); +} + +static void print_transaction(struct ref_transaction *transaction) +{ -+ printf("transaction {\n"); ++ fprintf(stderr, "transaction {\n"); + for (int i = 0; i < transaction->nr; i++) { + struct ref_update *u = transaction->updates[i]; + print_update(i, u->refname, &u->old_oid, &u->new_oid, u->flags, + u->type, u->msg); + } -+ printf("}\n"); ++ fprintf(stderr, "}\n"); +} + +static int debug_transaction_finish(struct ref_store *refs, @@ refs/debug.c (new) + res = drefs->refs->be->transaction_finish(drefs->refs, transaction, + err); + print_transaction(transaction); -+ printf("finish: %d\n", res); ++ fprintf(stderr, "finish: %d\n", res); + return res; +} + @@ refs/debug.c (new) +{ + struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; + int res = drefs->refs->be->pack_refs(drefs->refs, flags); ++ fprintf(stderr, "pack_refs: %d\n", res); + return res; +} + @@ refs/debug.c (new) + struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; + int res = drefs->refs->be->create_symref(drefs->refs, ref_name, target, + logmsg); -+ printf("create_symref: %s -> %s \"%s\": %d\n", ref_name, target, logmsg, -+ res); ++ fprintf(stderr, "create_symref: %s -> %s \"%s\": %d\n", ref_name, ++ target, logmsg, res); + return res; +} + @@ refs/debug.c (new) + struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; + int res = drefs->refs->be->rename_ref(drefs->refs, oldref, newref, + logmsg); -+ printf("rename_ref: %s -> %s \"%s\": %d\n", oldref, newref, logmsg, -+ res); ++ fprintf(stderr, "rename_ref: %s -> %s \"%s\": %d\n", oldref, newref, ++ logmsg, res); + return res; +} + @@ refs/debug.c (new) + struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; + int res = + drefs->refs->be->copy_ref(drefs->refs, oldref, newref, logmsg); -+ printf("copy_ref: %s -> %s \"%s\": %d\n", oldref, newref, logmsg, res); ++ fprintf(stderr, "copy_ref: %s -> %s \"%s\": %d\n", oldref, newref, ++ logmsg, res); + return res; +} + @@ refs/debug.c (new) + oid_to_hex_r(o, oid); + if (old_oid) + oid_to_hex_r(n, old_oid); -+ printf("write_pseudoref: %s, %s => %s, err %s: %d\n", pseudoref, o, n, -+ err->buf, res); ++ fprintf(stderr, "write_pseudoref: %s, %s => %s, err %s: %d\n", ++ pseudoref, o, n, err->buf, res); + return res; +} + @@ refs/debug.c (new) + char hex[100] = "null"; + if (old_oid) + oid_to_hex_r(hex, old_oid); -+ printf("delete_pseudoref: %s (%s): %d\n", pseudoref, hex, res); ++ fprintf(stderr, "delete_pseudoref: %s (%s): %d\n", pseudoref, hex, res); + return res; +} + ++struct debug_ref_iterator { ++ struct ref_iterator base; ++ struct ref_iterator *iter; ++}; ++ ++static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator) ++{ ++ struct debug_ref_iterator *diter = ++ (struct debug_ref_iterator *)ref_iterator; ++ int res = diter->iter->vtable->advance(diter->iter); ++ fprintf(stderr, "iterator_advance: %s: %d\n", diter->iter->refname, ++ res); ++ diter->base.ordered = diter->iter->ordered; ++ diter->base.refname = diter->iter->refname; ++ diter->base.oid = diter->iter->oid; ++ diter->base.flags = diter->iter->flags; ++ return res; ++} ++static int debug_ref_iterator_peel(struct ref_iterator *ref_iterator, ++ struct object_id *peeled) ++{ ++ struct debug_ref_iterator *diter = ++ (struct debug_ref_iterator *)ref_iterator; ++ int res = diter->iter->vtable->peel(diter->iter, peeled); ++ fprintf(stderr, "iterator_peel: %s: %d\n", diter->iter->refname, res); ++ return res; ++} ++ ++static int debug_ref_iterator_abort(struct ref_iterator *ref_iterator) ++{ ++ struct debug_ref_iterator *diter = ++ (struct debug_ref_iterator *)ref_iterator; ++ int res = diter->iter->vtable->abort(diter->iter); ++ fprintf(stderr, "iterator_abort: %d\n", res); ++ return res; ++} ++ ++static struct ref_iterator_vtable debug_ref_iterator_vtable = { ++ debug_ref_iterator_advance, debug_ref_iterator_peel, ++ debug_ref_iterator_abort ++}; ++ +static struct ref_iterator * +debug_ref_iterator_begin(struct ref_store *ref_store, const char *prefix, + unsigned int flags) @@ refs/debug.c (new) + struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; + struct ref_iterator *res = + drefs->refs->be->iterator_begin(drefs->refs, prefix, flags); -+ return res; ++ struct debug_ref_iterator *diter = xcalloc(1, sizeof(*diter)); ++ base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable, 1); ++ diter->iter = res; ++ fprintf(stderr, "ref_iterator_begin: %s (0x%x)\n", prefix, flags); ++ return &diter->base; +} + +static int debug_read_raw_ref(struct ref_store *ref_store, const char *refname, @@ refs/debug.c (new) + type); + + if (res == 0) { -+ printf("read_raw_ref: %s: %s (=> %s) type %x: %d\n", refname, -+ oid_to_hex(oid), referent->buf, *type, res); ++ fprintf(stderr, "read_raw_ref: %s: %s (=> %s) type %x: %d\n", ++ refname, oid_to_hex(oid), referent->buf, *type, res); + } else { -+ printf("read_raw_ref: %s err %d\n", refname, res); ++ fprintf(stderr, "read_raw_ref: %s err %d\n", refname, res); + } + return res; +} @@ refs/debug.c (new) + struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; + struct ref_iterator *res = + drefs->refs->be->reflog_iterator_begin(drefs->refs); -+ printf("for_each_reflog_iterator_begin\n"); ++ fprintf(stderr, "for_each_reflog_iterator_begin\n"); + return res; +} + @@ refs/debug.c (new) + + ret = dbg->fn(old_oid, new_oid, committer, timestamp, tz, msg, + dbg->cb_data); -+ printf("reflog_ent %s (ret %d): %s -> %s, %s %ld \"%s\"\n", -+ dbg->refname, ret, o, n, committer, (long int)timestamp, msg); ++ fprintf(stderr, "reflog_ent %s (ret %d): %s -> %s, %s %ld \"%s\"\n", ++ dbg->refname, ret, o, n, committer, (long int)timestamp, msg); + return ret; +} + @@ refs/debug.c (new) + + int res = drefs->refs->be->for_each_reflog_ent( + drefs->refs, refname, &debug_print_reflog_ent, &dbg); -+ printf("for_each_reflog: %s: %d\n", refname, res); ++ fprintf(stderr, "for_each_reflog: %s: %d\n", refname, res); + return res; +} + @@ refs/debug.c (new) + }; + int res = drefs->refs->be->for_each_reflog_ent_reverse( + drefs->refs, refname, &debug_print_reflog_ent, &dbg); -+ printf("for_each_reflog_reverse: %s: %d\n", refname, res); ++ fprintf(stderr, "for_each_reflog_reverse: %s: %d\n", refname, res); + return res; +} + @@ refs/debug.c (new) +{ + struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; + int res = drefs->refs->be->reflog_exists(drefs->refs, refname); -+ printf("reflog_exists: %s: %d\n", refname, res); ++ fprintf(stderr, "reflog_exists: %s: %d\n", refname, res); + return res; +} + @@ refs/debug.c (new) + struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; + int res = drefs->refs->be->create_reflog(drefs->refs, refname, + force_create, err); ++ fprintf(stderr, "create_reflog: %s: %d\n", refname, res); + return res; +} + @@ refs/debug.c (new) +{ + struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; + int res = drefs->refs->be->delete_reflog(drefs->refs, refname); ++ fprintf(stderr, "delete_reflog: %s: %d\n", refname, res); + return res; +} + @@ refs/debug.c (new) + flags, prepare_fn, + should_prune_fn, cleanup_fn, + policy_cb_data); ++ fprintf(stderr, "reflog_expire: %s: %d\n", refname, res); + return res; +} + 15: 1c0cc646084 = 16: 5211c643104 vcxproj: adjust for the reftable changes -: ----------- > 17: 9724854088c git-prompt: prepare for reftable refs backend 16: 4f24b5f73de ! 18: ece1fa1f625 Add reftable testing infrastructure @@ Commit message * Skip some tests that are incompatible: * t3210-pack-refs.sh - does not apply - * t9903-bash-prompt - The bash mode reads .git/HEAD directly * t1450-fsck.sh - manipulates .git/ directly to create invalid state Major test failures: @@ Commit message Signed-off-by: Han-Wen Nienhuys <hanwen@xxxxxxxxxx> - ## builtin/clone.c ## -@@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix) - } - - init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, -- DEFAULT_REF_STORAGE, INIT_DB_QUIET); -+ default_ref_storage(), INIT_DB_QUIET); - - if (real_git_dir) - git_dir = real_git_dir; - - ## refs.c ## -@@ refs.c: struct ref_store *get_main_ref_store(struct repository *r) - r->refs_private = ref_store_init(r->gitdir, - r->ref_storage_format ? - r->ref_storage_format : -- DEFAULT_REF_STORAGE, -+ default_ref_storage(), - REF_STORE_ALL_CAPS); - if (getenv("GIT_DEBUG_REFS")) { - r->refs_private = debug_wrap(r->refs_private); -@@ refs.c: struct ref_store *get_submodule_ref_store(const char *submodule) - goto done; - - /* assume that add_submodule_odb() has been called */ -- refs = ref_store_init(submodule_sb.buf, DEFAULT_REF_STORAGE, /* XXX */ -+ refs = ref_store_init(submodule_sb.buf, default_ref_storage(), - REF_STORE_READ | REF_STORE_ODB); - register_ref_store_map(&submodule_ref_stores, "submodule", - refs, submodule); -@@ refs.c: struct ref_store *get_submodule_ref_store(const char *submodule) - - struct ref_store *get_worktree_ref_store(const struct worktree *wt) - { -- const char *format = DEFAULT_REF_STORAGE; /* XXX */ -+ const char *format = default_ref_storage(); - struct ref_store *refs; - const char *id; - - ## t/t1409-avoid-packing-refs.sh ## @@ t/t1409-avoid-packing-refs.sh: test_description='avoid rewriting packed-refs unnecessarily' @@ t/t3210-pack-refs.sh: semantic is still the same. git config core.logallrefupdates true ' - ## t/t9903-bash-prompt.sh ## -@@ t/t9903-bash-prompt.sh: test_description='test git-specific bash prompt functions' - - . ./lib-bash.sh - -+if test_have_prereq REFTABLE -+then -+ skip_all='skipping tests; incompatible with reftable' -+ test_done -+fi -+ - . "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh" - - actual="$TRASH_DIRECTORY/actual" - ## t/test-lib.sh ## @@ t/test-lib.sh: parisc* | hppa*) ;; 17: ad5658ffc51 = 19: 991abf9e1b2 Add "test-tool dump-reftable" command. -- gitgitgadget