reftable library
Subsystems (2)
cover reftable library
This splits the giant commit from
https://github.com/gitgitgadget/git/pull/539 into a series of smaller commits, which build and have unittests.
The final commit should also be split up, but I want to wait until we have consensus that the bottom commits look good.
Han-Wen Nienhuys (12):
reftable: add LICENSE
reftable: define the public API
reftable: add a barebones unittest framework
reftable: utility functions
reftable: (de)serialization for the polymorphic record type.
reftable: reading/writing blocks
reftable: a generic binary tree implementation
reftable: write reftable files
reftable: read reftable files
reftable: file level tests
reftable: rest of library
reftable: "test-tool dump-reftable" command.
Johannes Schindelin (1):
vcxproj: adjust for the reftable changes
Makefile | 46 +- config.mak.uname | 2 +- contrib/buildsystems/Generators/Vcxproj.pm | 11 +- reftable/.gitattributes | 1 + reftable/LICENSE | 31 + reftable/VERSION | 1 + reftable/basics.c | 131 +++ reftable/basics.h | 48 + reftable/block.c | 443 +++++++ reftable/block.h | 129 +++ reftable/block_test.c | 158 +++ reftable/blocksource.c | 148 +++ reftable/blocksource.h | 22 + reftable/compat.c | 110 ++ reftable/compat.h | 48 + reftable/constants.h | 21 + reftable/dump.c | 212 ++++ reftable/iter.c | 242 ++++ reftable/iter.h | 72 ++ reftable/merged.c | 358 ++++++ reftable/merged.h | 39 + reftable/merged_test.c | 331 ++++++ reftable/pq.c | 115 ++ reftable/pq.h | 34 + reftable/publicbasics.c | 100 ++ reftable/reader.c | 733 ++++++++++++ reftable/reader.h | 78 ++ reftable/record.c | 1114 ++++++++++++++++++ reftable/record.h | 143 +++ reftable/record_test.c | 410 +++++++ reftable/refname.c | 209 ++++ reftable/refname.h | 38 + reftable/refname_test.c | 100 ++ reftable/reftable-tests.h | 22 + reftable/reftable.c | 104 ++ reftable/reftable.h | 585 ++++++++++ reftable/reftable_test.c | 585 ++++++++++ reftable/stack.c | 1224 ++++++++++++++++++++ reftable/stack.h | 50 + reftable/stack_test.c | 787 +++++++++++++ reftable/strbuf.c | 142 +++ reftable/strbuf.h | 80 ++ reftable/strbuf_test.c | 37 + reftable/system.h | 51 + reftable/test_framework.c | 68 ++ reftable/test_framework.h | 60 + reftable/tree.c | 63 + reftable/tree.h | 34 + reftable/tree_test.c | 62 + reftable/update.sh | 22 + reftable/writer.c | 664 +++++++++++ reftable/writer.h | 60 + reftable/zlib-compat.c | 92 ++ t/helper/test-reftable.c | 20 + t/helper/test-tool.c | 2 + t/helper/test-tool.h | 2 + 56 files changed, 10489 insertions(+), 5 deletions(-) create mode 100644 reftable/.gitattributes create mode 100644 reftable/LICENSE create mode 100644 reftable/VERSION 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/blocksource.c create mode 100644 reftable/blocksource.h 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/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/publicbasics.c 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 t/helper/test-reftable.c
base-commit: 54e85e7af1ac9e9a92888060d6811ae767fea1bc
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-847%2Fhanwen%2Flibreftable-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-847/hanwen/libreftable-v1
Pull-Request: https://github.com/git/git/pull/847
Signature · 1 line
gitgitgadget
PATCH 1/13 reftable: add LICENSE
From: Han-Wen Nienhuys <[email protected]>
TODO: relicense?
--- reftable/LICENSE | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 reftable/LICENSE
reftable/LICENSE new file +31 -0
@@ -0,0 +1,31 @@ +BSD License + +Copyright (c) 2020, Google LLC +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +* Neither the name of Google LLC nor the names of its contributors may +be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Signature · 1 line
gitgitgadget
PATCH 2/13 reftable: define the public API
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- reftable/reftable.h | 585 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 585 insertions(+) create mode 100644 reftable/reftable.h
reftable/reftable.h new file +585 -0
@@ -0,0 +1,585 @@ +/* +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 REFTABLE_H +#define REFTABLE_H + +#include <stdint.h> +#include <stddef.h> + +void reftable_set_alloc (void *(*malloc)(size_t ), +void *(*realloc)(void *,size_t ),void (*free)(void *)); + +/**************************************************************** +Basic data types + +Reftables store the state of each ref in struct reftable_ref_record, and they +store a sequence of reflog updates in struct reftable_log_record. +****************************************************************/ + +/* reftable_ref_record holds a ref database entry target_value */ +struct reftable_ref_record { +char *refname;/* Name of the ref, malloced. */ +uint64_t update_index;/* Logical timestamp at which this value is +written */ +uint8_t *value;/* SHA1, or NULL. malloced. */ +uint8_t *target_value;/* peeled annotated tag, or NULL. malloced. */ +char *target;/* symref, or NULL. malloced. */ +}; + +/* returns whether 'ref' represents a deletion */ +int reftable_ref_record_is_deletion (const struct reftable_ref_record *ref); + +/* prints a reftable_ref_record onto stdout */ +void reftable_ref_record_print (struct reftable_ref_record *ref, +uint32_t hash_id); + +/* frees and nulls all pointer values. */ +void reftable_ref_record_clear (struct reftable_ref_record *ref); + +/* returns whether two reftable_ref_records are the same */ +int reftable_ref_record_equal (struct reftable_ref_record *a, +struct reftable_ref_record *b,int hash_size); + +/* reftable_log_record holds a reflog entry */ +struct reftable_log_record { +char *refname; +uint64_t update_index;/* logical timestamp of a transactional update. +*/ +uint8_t *new_hash; +uint8_t *old_hash; +char *name; +char *email; +uint64_t time; +int16_t tz_offset; +char *message; +}; + +/* returns whether 'ref' represents the deletion of a log record. */ +int reftable_log_record_is_deletion (const struct reftable_log_record *log); + +/* frees and nulls all pointer values. */ +void reftable_log_record_clear (struct reftable_log_record *log); + +/* returns whether two records are equal. */ +int reftable_log_record_equal (struct reftable_log_record *a, +struct reftable_log_record *b,int hash_size); + +/* dumps a reftable_log_record on stdout, for debugging/testing. */ +void reftable_log_record_print (struct reftable_log_record *log, +uint32_t hash_id); + +/**************************************************************** +Error handling + +Error are signaled with negative integer return values. 0 means success. +****************************************************************/ + +/* different types of errors */ +enum reftable_error { +/* Unexpected file system behavior */ +REFTABLE_IO_ERROR =-2 , + +/* Format inconsistency on reading data +*/ +REFTABLE_FORMAT_ERROR =-3 , + +/* File does not exist. Returned from block_source_from_file(), because +it needs special handling in stack. +*/ +REFTABLE_NOT_EXIST_ERROR =-4 , + +/* Trying to write out-of-date data. */ +REFTABLE_LOCK_ERROR =-5 , + +/* Misuse of the API: +- on writing a record with NULL refname. +- on writing a reftable_ref_record outside the table limits +- on writing a ref or log record before the stack's next_update_index +- on writing a log record with multiline message with +exact_log_message unset +- on reading a reftable_ref_record from log iterator, or vice versa. +*/ +REFTABLE_API_ERROR =-6 , + +/* Decompression error */ +REFTABLE_ZLIB_ERROR =-7 , + +/* Wrote a table without blocks. */ +REFTABLE_EMPTY_TABLE_ERROR =-8 , + +/* Dir/file conflict. */ +REFTABLE_NAME_CONFLICT =-9 , + +/* Illegal ref name. */ +REFTABLE_REFNAME_ERROR =-10 , +}; + +/* convert the numeric error code to a string. The string should not be +* deallocated. */ +const char *reftable_error_str (int err); + +/* +* Convert the numeric error code to an equivalent errno code. +*/ +int reftable_error_to_errno (int err); + +/**************************************************************** +Writing + +Writing single reftables +****************************************************************/ + +/* reftable_write_options sets options for writing a single reftable. */ +struct reftable_write_options { +/* boolean: do not pad out blocks to block size. */ +int unpadded; + +/* the blocksize. Should be less than 2^24. */ +uint32_t block_size; + +/* boolean: do not generate a SHA1 => ref index. */ +int skip_index_objects; + +/* how often to write complete keys in each block. */ +int restart_interval; + +/* 4-byte identifier ("sha1", "s256") of the hash. +* Defaults to SHA1 if unset +*/ +uint32_t hash_id; + +/* boolean: do not check ref names for validity or dir/file conflicts. +*/ +int skip_name_check; + +/* boolean: copy log messages exactly. If unset, check that the message +* is a single line, and add '\n' if missing. +*/ +int exact_log_message; +}; + +/* reftable_block_stats holds statistics for a single block type */ +struct reftable_block_stats { +/* total number of entries written */ +int entries; +/* total number of key restarts */ +int restarts; +/* total number of blocks */ +int blocks; +/* total number of index blocks */ +int index_blocks; +/* depth of the index */ +int max_index_level; + +/* offset of the first block for this type */ +uint64_t offset; +/* offset of the top level index block for this type, or 0 if not +* present */ +uint64_t index_offset; +}; + +/* stats holds overall statistics for a single reftable */ +struct reftable_stats { +/* total number of blocks written. */ +int blocks; +/* stats for ref data */ +struct reftable_block_stats ref_stats; +/* stats for the SHA1 to ref map. */ +struct reftable_block_stats obj_stats; +/* stats for index blocks */ +struct reftable_block_stats idx_stats; +/* stats for log blocks */ +struct reftable_block_stats log_stats; + +/* disambiguation length of shortened object IDs. */ +int object_id_len; +}; + +/* reftable_new_writer creates a new writer */ +struct reftable_writer * +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,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_stack_next_update_index(), or REFTABLE_API_ERROR is returned. + +For transactional updates, typically min==max. When converting an existing +ref database into a single reftable, this would be a range of update-index +timestamps. +*/ +void reftable_writer_set_limits (struct reftable_writer *w,uint64_t min, +uint64_t max); + +/* adds a reftable_ref_record. Must be called in ascending +order. The update_index must be within the limits set by +reftable_writer_set_limits(), or REFTABLE_API_ERROR is returned. + +It is an error to write a ref record after a log record. +*/ +int reftable_writer_add_ref (struct reftable_writer *w, +struct reftable_ref_record *ref); + +/* Convenience function to add multiple refs. Will sort the refs by +name before adding. */ +int reftable_writer_add_refs (struct reftable_writer *w, +struct reftable_ref_record *refs,int n); + +/* adds a reftable_log_record. Must be called in ascending order (with more +recent log entries first.) +*/ +int reftable_writer_add_log (struct reftable_writer *w, +struct reftable_log_record *log); + +/* Convenience function to add multiple logs. Will sort the records by +key before adding. */ +int reftable_writer_add_logs (struct reftable_writer *w, +struct reftable_log_record *logs,int n); + +/* reftable_writer_close finalizes the reftable. The writer is retained so +* statistics can be inspected. */ +int reftable_writer_close (struct reftable_writer *w); + +/* writer_stats returns the statistics on the reftable being written. + +This struct becomes invalid when the writer is freed. +*/ +const struct reftable_stats *writer_stats (struct reftable_writer *w); + +/* reftable_writer_free deallocates memory for the writer */ +void reftable_writer_free (struct reftable_writer *w); + +/**************************************************************** +* ITERATING +****************************************************************/ + +/* iterator is the generic interface for walking over data stored in a +reftable. It is generally passed around by value. +*/ +struct reftable_iterator { +struct reftable_iterator_vtable *ops; +void *iter_arg; +}; + +/* reads the next reftable_ref_record. Returns < 0 for error, 0 for OK and > 0: +end of iteration. +*/ +int reftable_iterator_next_ref (struct reftable_iterator *it, +struct reftable_ref_record *ref); + +/* reads the next reftable_log_record. Returns < 0 for error, 0 for OK and > 0: +end of iteration. +*/ +int reftable_iterator_next_log (struct reftable_iterator *it, +struct reftable_log_record *log); + +/* releases resources associated with an iterator. */ +void reftable_iterator_destroy (struct reftable_iterator *it); + +/**************************************************************** +Reading single tables + +The follow routines are for reading single files. For an application-level +interface, skip ahead to struct reftable_merged_table and struct +reftable_stack. +****************************************************************/ + +/* block_source is a generic wrapper for a seekable readable file. +It is generally passed around by value. +*/ +struct reftable_block_source { +struct reftable_block_source_vtable *ops; +void *arg; +}; + +/* a contiguous segment of bytes. It keeps track of its generating block_source +so it can return itself into the pool. +*/ +struct reftable_block { +uint8_t *data; +int len; +struct reftable_block_source source; +}; + +/* block_source_vtable are the operations that make up block_source */ +struct reftable_block_source_vtable { +/* returns the size of a block source */ +uint64_t (*size)(void *source); + +/* reads a segment from the block source. It is an error to read +beyond the end of the block */ +int (*read_block)(void *source,struct reftable_block *dest, +uint64_t off,uint32_t size); +/* mark the block as read; may return the data back to malloc */ +void (*return_block)(void *source,struct reftable_block *blockp); + +/* release all resources associated with the block source */ +void (*close)(void *source); +}; + +/* opens a file on the file system as a block_source */ +int reftable_block_source_from_file (struct reftable_block_source *block_src, +const char *name); + +/* The reader struct is a handle to an open reftable file. */ +struct reftable_reader ; + +/* reftable_new_reader opens a reftable for reading. If successful, returns 0 +* code and sets pp. The name is used for creating a stack. Typically, it is the +* basename of the file. The block source `src` is owned by the reader, and is +* closed on calling reftable_reader_destroy(). +*/ +int reftable_new_reader (struct reftable_reader **pp, +struct reftable_block_source *src,const char *name); + +/* reftable_reader_seek_ref returns an iterator where 'name' would be inserted +in the table. To seek to the start of the table, use name = "". + +example: + +struct reftable_reader *r = NULL; +int err = reftable_new_reader(&r, &src, "filename"); +if (err < 0) { ... } +struct reftable_iterator it = {0}; +err = reftable_reader_seek_ref(r, &it, "refs/heads/master"); +if (err < 0) { ... } +struct reftable_ref_record ref = {0}; +while (1) { +err = reftable_iterator_next_ref(&it, &ref); +if (err > 0) { +break; +} +if (err < 0) { +..error handling.. +} +..found.. +} +reftable_iterator_destroy(&it); +reftable_ref_record_clear(&ref); +*/ +int reftable_reader_seek_ref (struct reftable_reader *r, +struct reftable_iterator *it,const char *name); + +/* returns the hash ID used in this table. */ +uint32_t reftable_reader_hash_id (struct reftable_reader *r); + +/* seek to logs for the given name, older than update_index. To seek to the +start of the table, use name = "". +*/ +int reftable_reader_seek_log_at (struct reftable_reader *r, +struct reftable_iterator *it,const char *name, +uint64_t update_index); + +/* seek to newest log entry for given name. */ +int reftable_reader_seek_log (struct reftable_reader *r, +struct reftable_iterator *it,const char *name); + +/* closes and deallocates a reader. */ +void reftable_reader_free (struct reftable_reader *); + +/* return an iterator for the refs pointing to `oid`. */ +int reftable_reader_refs_for (struct reftable_reader *r, +struct reftable_iterator *it,uint8_t *oid); + +/* return the max_update_index for a table */ +uint64_t reftable_reader_max_update_index (struct reftable_reader *r); + +/* return the min_update_index for a table */ +uint64_t reftable_reader_min_update_index (struct reftable_reader *r); + +/**************************************************************** +Merged tables + +A ref database kept in a sequence of table files. The merged_table presents a +unified view to reading (seeking, iterating) a sequence of immutable tables. +****************************************************************/ + +/* A merged table is implements seeking/iterating over a stack of tables. */ +struct reftable_merged_table ; + +/* A generic reftable; see below. */ +struct reftable_table ; + +/* reftable_new_merged_table creates a new merged table. It takes ownership of +the stack array. +*/ +int reftable_new_merged_table (struct reftable_merged_table **dest, +struct reftable_table *stack,int n, +uint32_t hash_id); + +/* returns an iterator positioned just before 'name' */ +int reftable_merged_table_seek_ref (struct reftable_merged_table *mt, +struct reftable_iterator *it, +const char *name); + +/* returns an iterator for log entry, at given update_index */ +int reftable_merged_table_seek_log_at (struct reftable_merged_table *mt, +struct reftable_iterator *it, +const char *name,uint64_t update_index); + +/* like reftable_merged_table_seek_log_at but look for the newest entry. */ +int reftable_merged_table_seek_log (struct reftable_merged_table *mt, +struct reftable_iterator *it, +const char *name); + +/* returns the max update_index covered by this merged table. */ +uint64_t +reftable_merged_table_max_update_index (struct reftable_merged_table *mt); + +/* returns the min update_index covered by this merged table. */ +uint64_t +reftable_merged_table_min_update_index (struct reftable_merged_table *mt); + +/* releases memory for the merged_table */ +void reftable_merged_table_free (struct reftable_merged_table *m); + +/* return the hash ID of the merged table. */ +uint32_t reftable_merged_table_hash_id (struct reftable_merged_table *m); + +/**************************************************************** +Generic tables + +A unified API for reading tables, either merged tables, or single readers. +****************************************************************/ + +struct reftable_table { +struct reftable_table_vtable *ops; +void *table_arg; +}; + +int reftable_table_seek_ref (struct reftable_table *tab, +struct reftable_iterator *it,const char *name); + +void reftable_table_from_reader (struct reftable_table *tab, +struct reftable_reader *reader); + +/* returns the hash ID from a generic reftable_table */ +uint32_t reftable_table_hash_id (struct reftable_table *tab); + +/* create a generic table from reftable_merged_table */ +void reftable_table_from_merged_table (struct reftable_table *tab, +struct reftable_merged_table *table); + +/* returns the max update_index covered by this table. */ +uint64_t reftable_table_max_update_index (struct reftable_table *tab); + +/* returns the min update_index covered by this table. */ +uint64_t reftable_table_min_update_index (struct reftable_table *tab); + +/* convenience function to read a single ref. Returns < 0 for error, 0 +for success, and 1 if ref not found. */ +int reftable_table_read_ref (struct reftable_table *tab,const char *name, +struct reftable_ref_record *ref); + +/**************************************************************** +Mutable ref database + +The stack presents an interface to a mutable sequence of reftables. +****************************************************************/ + +/* a stack is a stack of reftables, which can be mutated by pushing a table to +* the top of the stack */ +struct reftable_stack ; + +/* open a new reftable stack. The tables along with the table list will be +stored in 'dir'. Typically, this should be .git/reftables. +*/ +int reftable_new_stack (struct reftable_stack **dest,const char *dir, +struct reftable_write_options config); + +/* returns the update_index at which a next table should be written. */ +uint64_t reftable_stack_next_update_index (struct reftable_stack *st); + +/* holds a transaction to add tables at the top of a stack. */ +struct reftable_addition ; + +/* +returns a new transaction to add reftables to the given stack. As a side +effect, the ref database is locked. +*/ +int reftable_stack_new_addition (struct reftable_addition **dest, +struct reftable_stack *st); + +/* Adds a reftable to transaction. */ +int reftable_addition_add (struct reftable_addition *add, +int (*write_table)(struct reftable_writer *wr, +void *arg), +void *arg); + +/* Commits the transaction, releasing the lock. */ +int reftable_addition_commit (struct reftable_addition *add); + +/* Release all non-committed data from the transaction, and deallocate the +transaction. Releases the lock if held. */ +void reftable_addition_destroy (struct reftable_addition *add); + +/* add a new table to the stack. The write_table function must call +reftable_writer_set_limits, add refs and return an error value. */ +int reftable_stack_add (struct reftable_stack *st, +int (*write_table)(struct reftable_writer *wr, +void *write_arg), +void *write_arg); + +/* returns the merged_table for seeking. This table is valid until the +next write or reload, and should not be closed or deleted. +*/ +struct reftable_merged_table * +reftable_stack_merged_table (struct reftable_stack *st); + +/* frees all resources associated with the stack. */ +void reftable_stack_destroy (struct reftable_stack *st); + +/* Reloads the stack if necessary. This is very cheap to run if the stack was up +* to date */ +int reftable_stack_reload (struct reftable_stack *st); + +/* Policy for expiring reflog entries. */ +struct reftable_log_expiry_config { +/* Drop entries older than this timestamp */ +uint64_t time; + +/* Drop older entries */ +uint64_t min_update_index; +}; + +/* compacts all reftables into a giant table. Expire reflog entries if config is +* non-NULL */ +int reftable_stack_compact_all (struct reftable_stack *st, +struct reftable_log_expiry_config *config); + +/* heuristically compact unbalanced table stack. */ +int reftable_stack_auto_compact (struct reftable_stack *st); + +/* convenience function to read a single ref. Returns < 0 for error, 0 +for success, and 1 if ref not found. */ +int reftable_stack_read_ref (struct reftable_stack *st,const char *refname, +struct reftable_ref_record *ref); + +/* convenience function to read a single log. Returns < 0 for error, 0 +for success, and 1 if ref not found. */ +int reftable_stack_read_log (struct reftable_stack *st,const char *refname, +struct reftable_log_record *log); + +/* statistics on past compactions. */ +struct reftable_compaction_stats { +uint64_t bytes;/* total number of bytes written */ +uint64_t entries_written;/* total number of entries written, including +failures. */ +int attempts;/* how often we tried to compact */ +int failures;/* failures happen on concurrent updates */ +}; + +/* return statistics for compaction up till now. */ +struct reftable_compaction_stats * +reftable_stack_compaction_stats (struct reftable_stack *st); + +#endif
Signature · 1 line
gitgitgadget
PATCH 3/13 vcxproj: adjust for the reftable changes
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
This allows Git to be compiled via Visual Studio again after integrating the hn/reftable branch.
--- config.mak.uname | 2 +- contrib/buildsystems/Generators/Vcxproj.pm | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-)
config.mak.uname +1 -1
@@ -709,7 +709,7 @@ vcxproj: # Make .vcxproj files and add them unset QUIET_GEN QUIET_BUILT_IN; \ perl contrib/buildsystems/generate -g Vcxproj - git add -f git.sln {*,*/lib,t/helper/*}/*.vcxproj + git add -f git.sln {*,*/lib,*/libreftable,t/helper/*}/*.vcxproj # Generate the LinkOrCopyBuiltins.targets and LinkOrCopyRemoteHttp.targets file (echo '<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">' && \
contrib/buildsystems/Generators/Vcxproj.pm +10 -1
@@ -77,7 +77,7 @@ sub createProject { my $libs_release = "\n "; my $libs_debug = "\n "; if (!$static_library) { - $libs_release = join(";", sort(grep /^(?!libgit\.lib|xdiff\/lib\.lib|vcs-svn\/lib\.lib)/, @{$$build_structure{"$prefix${name}_LIBS"}})); + $libs_release = join(";", sort(grep /^(?!libgit\.lib|xdiff\/lib\.lib|vcs-svn\/lib\.lib|reftable\/libreftable\.lib)/, @{$$build_structure{"$prefix${name}_LIBS"}})); $libs_debug = $libs_release; $libs_debug =~ s/zlib\.lib/zlibd\.lib/g; $libs_debug =~ s/libexpat\.lib/libexpatd\.lib/g;
@@ -232,6 +232,7 @@ sub createProject { EOM if (!$static_library || $target =~ 'vcs-svn' || $target =~ 'xdiff') { my $uuid_libgit = $$build_structure{"LIBS_libgit_GUID"}; + my $uuid_libreftable = $$build_structure{"LIBS_reftable/libreftable_GUID"}; my $uuid_xdiff_lib = $$build_structure{"LIBS_xdiff/lib_GUID"}; print F << "EOM";
@@ -241,6 +242,14 @@ sub createProject { <ReferenceOutputAssembly>false</ReferenceOutputAssembly> </ProjectReference> EOM + if (!($name =~ /xdiff|libreftable/)) { + print F << "EOM"; + <ProjectReference Include="$cdup\\reftable\\libreftable\\libreftable.vcxproj"> + <Project>$uuid_libreftable</Project> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + </ProjectReference> +EOM + } if (!($name =~ 'xdiff')) { print F << "EOM"; <ProjectReference Include="$cdup\\xdiff\\lib\\xdiff_lib.vcxproj">
Signature · 1 line
gitgitgadget
PATCH 4/13 reftable: add a barebones unittest framework
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- reftable/test_framework.c | 68 +++++++++++++++++++++++++++++++++++++++ reftable/test_framework.h | 60 ++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 reftable/test_framework.c create mode 100644 reftable/test_framework.h
reftable/test_framework.c new file +68 -0
@@ -0,0 +1,68 @@ +/* +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 "test_framework.h" + +#include "system.h" +#include "basics.h" + +struct test_case **test_cases; +int test_case_len; +int test_case_cap; + +struct test_case *new_test_case (const char *name,void (*testfunc)(void )) +{ +struct test_case *tc =reftable_malloc (sizeof (struct test_case )); + tc->name = name; + tc->testfunc = testfunc; +return tc; +} + +struct test_case *add_test_case (const char *name,void (*testfunc)(void )) +{ +struct test_case *tc =new_test_case (name, testfunc); +if (test_case_len == test_case_cap) { + test_case_cap =2 * test_case_cap +1 ; + test_cases =reftable_realloc ( + test_cases,sizeof (struct test_case ) * test_case_cap); + } + + test_cases[test_case_len++] = tc; +return tc; +} + +int test_main (int argc,const char *argv[]) +{ +const char *filter =NULL ; +int i =0 ; +if (argc >1 ) { + filter = argv[1 ]; + } + +for (i =0 ; i < test_case_len; i++) { +const char *name = test_cases[i]->name; +if (filter ==NULL ||strstr (name, filter) !=NULL ) { +printf ("case %s\n" , name); + test_cases[i]->testfunc (); + }else { +printf ("skip %s\n" , name); + } + +reftable_free (test_cases[i]); + } +reftable_free (test_cases); + test_cases =0 ; + test_case_len =0 ; + test_case_cap =0 ; +return 0 ; +} + +void set_test_hash (uint8_t *p,int i) +{ +memset (p, (uint8_t )i,hash_size (SHA1_ID )); +}
reftable/test_framework.h new file +60 -0
60 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH 5/13 reftable: utility functions
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
This commit provides basic utility classes for the reftable library.
Since the reftable library must compile standalone, there may be some overlap with git-core utility functions.
--- Makefile | 26 ++++++- reftable/basics.c | 131 +++++++++++++++++++++++++++++++++ reftable/basics.h | 48 +++++++++++++ reftable/blocksource.c | 148 ++++++++++++++++++++++++++++++++++++++ reftable/blocksource.h | 22 ++++++ reftable/compat.c | 110 ++++++++++++++++++++++++++++ reftable/compat.h | 48 +++++++++++++ reftable/publicbasics.c | 100 ++++++++++++++++++++++++++ reftable/reftable-tests.h | 22 ++++++ reftable/strbuf.c | 142 ++++++++++++++++++++++++++++++++++++ reftable/strbuf.h | 80 +++++++++++++++++++++ reftable/strbuf_test.c | 37 ++++++++++ reftable/system.h | 51 +++++++++++++ t/helper/test-reftable.c | 8 +++ t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 + 16 files changed, 972 insertions(+), 3 deletions(-) create mode 100644 reftable/basics.c create mode 100644 reftable/basics.h create mode 100644 reftable/blocksource.c create mode 100644 reftable/blocksource.h create mode 100644 reftable/compat.c create mode 100644 reftable/compat.h create mode 100644 reftable/publicbasics.c create mode 100644 reftable/reftable-tests.h 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 t/helper/test-reftable.c
Makefile +23 -3
@@ -720,6 +720,7 @@ TEST_BUILTINS_OBJS += test-read-cache.o TEST_BUILTINS_OBJS += test-read-graph.o TEST_BUILTINS_OBJS += test-read-midx.o TEST_BUILTINS_OBJS += test-ref-store.o +TEST_BUILTINS_OBJS += test-reftable.o TEST_BUILTINS_OBJS += test-regex.o TEST_BUILTINS_OBJS += test-repository.o TEST_BUILTINS_OBJS += test-revision-walking.o
@@ -799,6 +800,8 @@ TEST_SHELL_PATH = $(SHELL_PATH) LIB_FILE = libgit.a XDIFF_LIB = xdiff/lib.a +REFTABLE_LIB = reftable/libreftable.a +REFTABLE_TEST_LIB = reftable/libreftable_test.a GENERATED_H += config-list.h GENERATED_H += command-list.h
@@ -1160,7 +1163,7 @@ THIRD_PARTY_SOURCES += compat/regex/% THIRD_PARTY_SOURCES += sha1collisiondetection/% THIRD_PARTY_SOURCES += sha1dc/% -GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) +GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) EXTLIBS = GIT_USER_AGENT = git/$(GIT_VERSION)
@@ -2341,10 +2344,21 @@ XDIFF_OBJS += xdiff/xpatience.o XDIFF_OBJS += xdiff/xprepare.o XDIFF_OBJS += xdiff/xutils.o +REFTABLE_OBJS += reftable/basics.o +REFTABLE_OBJS += reftable/blocksource.o +REFTABLE_OBJS += reftable/publicbasics.o +REFTABLE_OBJS += reftable/compat.o +REFTABLE_OBJS += reftable/strbuf.o + +REFTABLE_TEST_OBJS += reftable/strbuf_test.o +REFTABLE_TEST_OBJS += reftable/test_framework.o + TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \ $(XDIFF_OBJS) \ $(FUZZ_OBJS) \ + $(REFTABLE_OBJS) \ + $(REFTABLE_TEST_OBJS) \ common-main.o \ git.o ifndef NO_CURL
@@ -2474,6 +2488,12 @@ $(LIB_FILE): $(LIB_OBJS) $(XDIFF_LIB): $(XDIFF_OBJS) $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^ +$(REFTABLE_LIB): $(REFTABLE_OBJS) + $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^ + +$(REFTABLE_TEST_LIB): $(REFTABLE_TEST_OBJS) + $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^ + export DEFAULT_EDITOR DEFAULT_PAGER Documentation/GIT-EXCLUDED-PROGRAMS: FORCE
@@ -2752,7 +2772,7 @@ perf: all t/helper/test-tool$X: $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) -t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS) +t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS) $(REFTABLE_TEST_LIB) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS) check-sha1:: t/helper/test-tool$X
@@ -3080,7 +3100,7 @@ cocciclean: clean: profile-clean coverage-clean cocciclean $(RM) *.res $(RM) $(OBJECTS) - $(RM) $(LIB_FILE) $(XDIFF_LIB) + $(RM) $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(REFTABLE_TEST_LIB) $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X $(RM) $(TEST_PROGRAMS) $(RM) $(FUZZ_PROGRAMS)
reftable/basics.c new file +131 -0
131 lines not on this page · raw message
reftable/basics.h new file +48 -0
48 lines not on this page · raw message
reftable/blocksource.c new file +148 -0
148 lines not on this page · raw message
reftable/blocksource.h new file +22 -0
@@ -0,0 +1,22 @@ +/* +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 BLOCKSOURCE_H +#define BLOCKSOURCE_H + +#include "strbuf.h" + +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/compat.c new file +110 -0
110 lines not on this page · raw message
reftable/compat.h new file +48 -0
48 lines not on this page · raw message
reftable/publicbasics.c new file +100 -0
100 lines not on this page · raw message
reftable/reftable-tests.h new file +22 -0
@@ -0,0 +1,22 @@ +/* +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 REFTABLE_TESTS_H +#define REFTABLE_TESTS_H + +int block_test_main (int argc,const char **argv); +int merged_test_main (int argc,const char **argv); +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 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); + +#endif
reftable/strbuf.c new file +142 -0
142 lines not on this page · raw message
reftable/strbuf.h new file +80 -0
80 lines not on this page · raw message
reftable/strbuf_test.c new file +37 -0
@@ -0,0 +1,37 @@ +/* +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 "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 file +51 -0
51 lines not on this page · raw message
t/helper/test-reftable.c new file +8 -0
@@ -0,0 +1,8 @@ +#include "reftable/reftable-tests.h" +#include "test-tool.h" + +int cmd__reftable (int argc,const char **argv) +{ +strbuf_test_main (argc, argv); +return 0 ; +}
t/helper/test-tool.c +1 -0
@@ -52,6 +52,7 @@ static struct test_cmd cmds[] = { { "read-graph", cmd__read_graph }, { "read-midx", cmd__read_midx }, { "ref-store", cmd__ref_store }, + { "reftable", cmd__reftable }, { "regex", cmd__regex }, { "repository", cmd__repository }, { "revision-walking", cmd__revision_walking },
t/helper/test-tool.h +1 -0
@@ -41,6 +41,7 @@ int cmd__read_cache(int argc, const char **argv);int cmd__read_graph (int argc,const char **argv);int cmd__read_midx (int argc,const char **argv);int cmd__ref_store (int argc,const char **argv); +int cmd__reftable (int argc,const char **argv);int cmd__regex (int argc,const char **argv);int cmd__repository (int argc,const char **argv);int cmd__revision_walking (int argc,const char **argv);
Signature · 1 line
gitgitgadget
PATCH 6/13 reftable: (de)serialization for the polymorphic record type.
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- Makefile | 2 + reftable/constants.h | 21 + reftable/record.c | 1114 ++++++++++++++++++++++++++++++++++++++ reftable/record.h | 143 +++++ reftable/record_test.c | 410 ++++++++++++++ t/helper/test-reftable.c | 1 + 6 files changed, 1691 insertions(+) create mode 100644 reftable/constants.h create mode 100644 reftable/record.c create mode 100644 reftable/record.h create mode 100644 reftable/record_test.c
Makefile +2 -0
@@ -2348,8 +2348,10 @@ REFTABLE_OBJS += reftable/basics.o REFTABLE_OBJS += reftable/blocksource.o REFTABLE_OBJS += reftable/publicbasics.o REFTABLE_OBJS += reftable/compat.o +REFTABLE_OBJS += reftable/record.o REFTABLE_OBJS += reftable/strbuf.o +REFTABLE_TEST_OBJS += reftable/record_test.o REFTABLE_TEST_OBJS += reftable/strbuf_test.o REFTABLE_TEST_OBJS += reftable/test_framework.o
reftable/constants.h new file +21 -0
@@ -0,0 +1,21 @@ +/* +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 CONSTANTS_H +#define CONSTANTS_H + +#define BLOCK_TYPE_LOG 'g' +#define BLOCK_TYPE_INDEX 'i' +#define BLOCK_TYPE_REF 'r' +#define BLOCK_TYPE_OBJ 'o' +#define BLOCK_TYPE_ANY 0 + +#define MAX_RESTARTS ((1 << 16) - 1) +#define DEFAULT_BLOCK_SIZE 4096 + +#endif
reftable/record.c new file +1114 -0
1,114 lines not on this page · raw message
reftable/record.h new file +143 -0
143 lines not on this page · raw message
reftable/record_test.c new file +410 -0
410 lines not on this page · raw message
t/helper/test-reftable.c +1 -0
@@ -3,6 +3,7 @@int cmd__reftable (int argc,const char **argv) { +record_test_main (argc, argv);strbuf_test_main (argc, argv);return 0 ; }
Signature · 1 line
gitgitgadget
Re: [PATCH 06/13] reftable: (de)serialization for the polymorphic record type.
Quoted 4 lines · Han-Wen Nienhuys via GitGitGadget
reftable/record.c new file +4
-0+static void reftable_log_record_key (const void *r,struct strbuf *dest) +{ +const struct reftable_log_record *rec = + (const struct reftable_log_record *)r;
reftable/record.c line 580
Quoted 10 lines
reftable/record.c new file +10
-0+int len =strlen (rec->refname); +uint8_t i64[8 ]; +uint64_t ts =0 ; +strbuf_reset (dest); +strbuf_add (dest, (uint8_t *)rec->refname, len +1 ); + + ts = (~ts) - rec->update_index; +put_be64 (&i64[0 ], ts); +strbuf_add (dest, i64,sizeof (i64)); +}
We seem to be getting
reftable/record.c: In function 'reftable_log_record_key':reftable/record.c:578:2: error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]
put_be64(&i64[0], ts);
^
CC reftable/refname.owhen this series is merged to 'seen'.
cf. e.g. https://travis-ci.org/github/git/git/jobs/728655368
Thanks.
Re: [PATCH 06/13] reftable: (de)serialization for the polymorphic record type.
Quoted 17 lines · Junio C Hamano
Quoted 4 lines
reftable/record.c new file +4
-0+static void reftable_log_record_key (const void *r,struct strbuf *dest) +{ +const struct reftable_log_record *rec = + (const struct reftable_log_record *)r;reftable/record.c line 580
Quoted 10 lines
reftable/record.c new file +10
-0+int len =strlen (rec->refname); +uint8_t i64[8 ]; +uint64_t ts =0 ; +strbuf_reset (dest); +strbuf_add (dest, (uint8_t *)rec->refname, len +1 ); + + ts = (~ts) - rec->update_index; +put_be64 (&i64[0 ], ts); +strbuf_add (dest, i64,sizeof (i64)); +}We seem to be getting
Quoted 7 lines
reftable/record.c: In function 'reftable_log_record_key':reftable/record.c:578:2: error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]put_be64(&i64[0], ts); ^ CC reftable/refname.owhen this series is merged to 'seen'.
Thanks for bringing this up. I did see this before, but I've been unable to reproduce this locally, and I forgot about it.
The problem is actually triggered by the Git-provided put_be64() (which appears unused in the Git source code). The definition
#define put_be64(p, v) do { *(uint64_t *)(p) = htonll(v); } while (0)is trying to reinterpret a char* as a uint64_t* , which is illegal in strict aliasing rules? I originally had
+void put_be64(uint8_t *out, uint64_t v)
+{
+ int i = sizeof(uint64_t);
+ while (i--) {
+ out[i] = (uint8_t)(v & 0xff);
+ v >>= 8;
+ }
+}
in my reftable library, which is portable. Is there a reason for the magic with htonll and friends?
Signature · 7 lines
Han-Wen Nienhuys - Google Munich I work 80%. Don't expect answers from me on Fridays. -- Google Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Re: [PATCH 06/13] reftable: (de)serialization for the polymorphic record type.
Quoted 7 lines · Han-Wen Nienhuys
The problem is actually triggered by the Git-provided
put_be64()(which appears unused in the Git source code). The definition#define put_be64(p, v) do { *(uint64_t *)(p) = htonll(v); } while (0)is trying to reinterpret a char* as a uint64_t* , which is illegal in strict aliasing rules?
Ah, thanks for finding that. I think this resolves the mystery I had around aliasing warnings with put_be32() in f39ad38410 (fast-export: use local array to store anonymized oid, 2020-06-25). I got confused because when I looked at the implementation of put_be32(), I stupidly looked at the NO_ALIGNED_LOADS fallback code, which indeed does not alias. But the one we use on x86 does.
I thought this would be OK because C allows aliasing through "char *" pointers. But I think it may only go the other way. I.e., you can type-pun a uint64_t as a char, but not the other way around. But then why doesn't basically every use of put_be32() and put_be64() complain?
Quoted 11 lines
I originally had
(quoted diff) +8
-0+void put_be64(uint8_t *out, uint64_t v) +{ + int i = sizeof(uint64_t); + while (i--) { + out[i] = (uint8_t)(v & 0xff); + v >>= 8; + } +}
Quoted 2 lines
in my reftable library, which is portable. Is there a reason for the magic with htonll and friends?
Presumably it was thought to be faster. This comes originally from the block-sha1 code in 660231aa97 (block-sha1: support for architectures with memory alignment restrictions, 2009-08-12). I don't know how it compares in practice, and especially these days.
Our fallback routines are similar to an unrolled version of what you wrote above.
-Peff
Re: [PATCH 06/13] reftable: (de)serialization for the polymorphic record type.
Quoted 19 lines · Jeff King
Quoted 11 lines
I originally had
(quoted diff) +8
-0+void put_be64(uint8_t *out, uint64_t v) +{ + int i = sizeof(uint64_t); + while (i--) { + out[i] = (uint8_t)(v & 0xff); + v >>= 8; + } +}Quoted 2 lines
in my reftable library, which is portable. Is there a reason for the magic with htonll and friends?
Presumably it was thought to be faster. This comes originally from the block-sha1 code in
660231aa97(block-sha1: support for architectures with memory alignment restrictions, 2009-08-12). I don't know how it compares in practice, and especially these days.
Quoted 2 lines
Our fallback routines are similar to an unrolled version of what you wrote above.
We should be able to measure it pretty easily, since block-sha1 uses a lot of get_be32/put_be32. I generated a 4GB random file, built with BLK_SHA1=Yes and -O2, and timed:
t/helper/test-tool sha1 <foo.rand
Then I did the same, but building with -DNO_UNALIGNED_LOADS. The latter actually ran faster, by a small margin. Here are the hyperfine results:
[stock]
Time (mean ± σ): 6.638 s ± 0.081 s [User: 6.269 s, System: 0.368 s]
Range (min … max): 6.550 s … 6.841 s 10 runs
[-DNO_UNALIGNED_LOADS]
Time (mean ± σ): 6.418 s ± 0.015 s [User: 6.058 s, System: 0.360 s]
Range (min … max): 6.394 s … 6.447 s 10 runs
For casual use as in reftables I doubt the difference is even measurable. But this result implies that perhaps we ought to just be using the fallback version all the time.
-Peff
Re: [PATCH 06/13] reftable: (de)serialization for the polymorphic record type.
Quoted 7 lines · Jeff King
Then I did the same, but building with -DNO_UNALIGNED_LOADS. The latter actually ran faster, by a small margin. Here are the hyperfine results:
[stock]
Time (mean ± σ): 6.638 s ± 0.081 s [User: 6.269 s, System: 0.368 s]
Range (min … max): 6.550 s … 6.841 s 10 runs
Quoted 7 lines
[-DNO_UNALIGNED_LOADS]
Time (mean ± σ): 6.418 s ± 0.015 s [User: 6.058 s, System: 0.360 s]
Range (min … max): 6.394 s … 6.447 s 10 runsFor casual use as in reftables I doubt the difference is even measurable. But this result implies that perhaps we ought to just be using the fallback version all the time.
I like that one. One less configurable knob that makes us execute different codepaths is one less thing to be worried about.
PATCH 7/13 reftable: reading/writing blocks
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
Includes a code snippet copied from zlib
--- Makefile | 3 + reftable/.gitattributes | 1 + reftable/block.c | 443 +++++++++++++++++++++++++++++++++++++++ reftable/block.h | 129 ++++++++++++ reftable/block_test.c | 158 ++++++++++++++ reftable/zlib-compat.c | 92 ++++++++ t/helper/test-reftable.c | 1 + 7 files changed, 827 insertions(+) create mode 100644 reftable/.gitattributes create mode 100644 reftable/block.c create mode 100644 reftable/block.h create mode 100644 reftable/block_test.c create mode 100644 reftable/zlib-compat.c
Makefile +3 -0
@@ -2345,12 +2345,15 @@ XDIFF_OBJS += xdiff/xprepare.o XDIFF_OBJS += xdiff/xutils.o REFTABLE_OBJS += reftable/basics.o +REFTABLE_OBJS += reftable/block.o REFTABLE_OBJS += reftable/blocksource.o REFTABLE_OBJS += reftable/publicbasics.o REFTABLE_OBJS += reftable/compat.o REFTABLE_OBJS += reftable/record.o REFTABLE_OBJS += reftable/strbuf.o +REFTABLE_OBJS += reftable/zlib-compat.o +REFTABLE_TEST_OBJS += reftable/block_test.o REFTABLE_TEST_OBJS += reftable/record_test.o REFTABLE_TEST_OBJS += reftable/strbuf_test.o REFTABLE_TEST_OBJS += reftable/test_framework.o
reftable/.gitattributes new file +1 -0
@@ -0,0 +1 @@ +/zlib-compat.c whitespace=-indent-with-non-tab,-trailing-space
reftable/block.c new file +443 -0
443 lines not on this page · raw message
reftable/block.h new file +129 -0
129 lines not on this page · raw message
reftable/block_test.c new file +158 -0
158 lines not on this page · raw message
reftable/zlib-compat.c new file +92 -0
92 lines not on this page · raw message
t/helper/test-reftable.c +1 -0
@@ -3,6 +3,7 @@int cmd__reftable (int argc,const char **argv) { +block_test_main (argc, argv);record_test_main (argc, argv);strbuf_test_main (argc, argv);return 0 ;
Signature · 1 line
gitgitgadget
PATCH 8/13 reftable: a generic binary tree implementation
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
This is necessary for building a OID => ref map on write
--- Makefile | 2 ++ reftable/tree.c | 63 ++++++++++++++++++++++++++++++++++++++++ reftable/tree.h | 34 ++++++++++++++++++++++ reftable/tree_test.c | 62 +++++++++++++++++++++++++++++++++++++++ t/helper/test-reftable.c | 1 + 5 files changed, 162 insertions(+) create mode 100644 reftable/tree.c create mode 100644 reftable/tree.h create mode 100644 reftable/tree_test.c
Makefile +2 -0
@@ -2351,12 +2351,14 @@ REFTABLE_OBJS += reftable/publicbasics.o REFTABLE_OBJS += reftable/compat.o REFTABLE_OBJS += reftable/record.o REFTABLE_OBJS += reftable/strbuf.o +REFTABLE_OBJS += reftable/tree.o REFTABLE_OBJS += reftable/zlib-compat.o REFTABLE_TEST_OBJS += reftable/block_test.o REFTABLE_TEST_OBJS += reftable/record_test.o REFTABLE_TEST_OBJS += reftable/strbuf_test.o REFTABLE_TEST_OBJS += reftable/test_framework.o +REFTABLE_TEST_OBJS += reftable/tree_test.o TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
reftable/tree.c new file +63 -0
63 lines not on this page · raw message
reftable/tree.h new file +34 -0
@@ -0,0 +1,34 @@ +/* +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 TREE_H +#define TREE_H + +/* tree_node is a generic binary search tree. */ +struct tree_node { +void *key; +struct tree_node *left, *right; +}; + +/* looks for `key` in `rootp` using `compare` as comparison function. If insert +is set, insert the key if it's not found. Else, return NULL. +*/ +struct tree_node *tree_search (void *key,struct tree_node **rootp, +int (*compare)(const void *,const void *), +int insert); + +/* performs an infix walk of the tree. */ +void infix_walk (struct tree_node *t,void (*action)(void *arg,void *key), +void *arg); + +/* +deallocates the tree nodes recursively. Keys should be deallocated separately +by walking over the tree. */ +void tree_free (struct tree_node *t); + +#endif
reftable/tree_test.c new file +62 -0
62 lines not on this page · raw message
t/helper/test-reftable.c +1 -0
@@ -6,5 +6,6 @@ int cmd__reftable(int argc, const char **argv)block_test_main (argc, argv);record_test_main (argc, argv);strbuf_test_main (argc, argv); +tree_test_main (argc, argv);return 0 ; }
Signature · 1 line
gitgitgadget
PATCH 9/13 reftable: write reftable files
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- Makefile | 1 + reftable/writer.c | 664 ++++++++++++++++++++++++++++++++++++++++++++++ reftable/writer.h | 60 +++++ 3 files changed, 725 insertions(+) create mode 100644 reftable/writer.c create mode 100644 reftable/writer.h
Makefile +1 -0
@@ -2352,6 +2352,7 @@ REFTABLE_OBJS += reftable/compat.o REFTABLE_OBJS += reftable/record.o REFTABLE_OBJS += reftable/strbuf.o REFTABLE_OBJS += reftable/tree.o +REFTABLE_OBJS += reftable/writer.o REFTABLE_OBJS += reftable/zlib-compat.o REFTABLE_TEST_OBJS += reftable/block_test.o
reftable/writer.c new file +664 -0
664 lines not on this page · raw message
reftable/writer.h new file +60 -0
60 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH 10/13 reftable: read reftable files
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- Makefile | 3 + reftable/iter.c | 242 +++++++++++++++ reftable/iter.h | 72 +++++ reftable/reader.c | 733 ++++++++++++++++++++++++++++++++++++++++++++ reftable/reader.h | 78 +++++ reftable/reftable.c | 104 +++++++ 6 files changed, 1232 insertions(+) create mode 100644 reftable/iter.c create mode 100644 reftable/iter.h create mode 100644 reftable/reader.c create mode 100644 reftable/reader.h create mode 100644 reftable/reftable.c
Makefile +3 -0
@@ -2349,7 +2349,10 @@ REFTABLE_OBJS += reftable/block.o REFTABLE_OBJS += reftable/blocksource.o REFTABLE_OBJS += reftable/publicbasics.o REFTABLE_OBJS += reftable/compat.o +REFTABLE_OBJS += reftable/iter.o +REFTABLE_OBJS += reftable/reader.o REFTABLE_OBJS += reftable/record.o +REFTABLE_OBJS += reftable/reftable.o REFTABLE_OBJS += reftable/strbuf.o REFTABLE_OBJS += reftable/tree.o REFTABLE_OBJS += reftable/writer.o
reftable/iter.c new file +242 -0
242 lines not on this page · raw message
reftable/iter.h new file +72 -0
72 lines not on this page · raw message
reftable/reader.c new file +733 -0
733 lines not on this page · raw message
reftable/reader.h new file +78 -0
78 lines not on this page · raw message
reftable/reftable.c new file +104 -0
104 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH 11/13 reftable: file level tests
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- Makefile | 1 + reftable/reftable_test.c | 585 +++++++++++++++++++++++++++++++++++++++ t/helper/test-reftable.c | 1 + 3 files changed, 587 insertions(+) create mode 100644 reftable/reftable_test.c
Makefile +1 -0
@@ -2360,6 +2360,7 @@ REFTABLE_OBJS += reftable/zlib-compat.o REFTABLE_TEST_OBJS += reftable/block_test.o REFTABLE_TEST_OBJS += reftable/record_test.o +REFTABLE_TEST_OBJS += reftable/reftable_test.o REFTABLE_TEST_OBJS += reftable/strbuf_test.o REFTABLE_TEST_OBJS += reftable/test_framework.o REFTABLE_TEST_OBJS += reftable/tree_test.o
reftable/reftable_test.c new file +585 -0
585 lines not on this page · raw message
t/helper/test-reftable.c +1 -0
@@ -5,6 +5,7 @@ int cmd__reftable(int argc, const char **argv) {block_test_main (argc, argv);record_test_main (argc, argv); +reftable_test_main (argc, argv);strbuf_test_main (argc, argv);tree_test_main (argc, argv);return 0 ;
Signature · 1 line
gitgitgadget
PATCH 12/13 reftable: rest of library
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
This will be further split up once preceding commits have passed review.
--- Makefile | 8 +- reftable/VERSION | 1 + reftable/dump.c | 212 +++++++ reftable/merged.c | 358 +++++++++++ reftable/merged.h | 39 ++ reftable/merged_test.c | 331 +++++++++++ reftable/pq.c | 115 ++++ reftable/pq.h | 34 ++ reftable/refname.c | 209 +++++++ reftable/refname.h | 38 ++ reftable/refname_test.c | 100 ++++ reftable/stack.c | 1224 ++++++++++++++++++++++++++++++++++++++ reftable/stack.h | 50 ++ reftable/stack_test.c | 787 ++++++++++++++++++++++++ reftable/update.sh | 22 + t/helper/test-reftable.c | 3 + 16 files changed, 3530 insertions(+), 1 deletion(-) create mode 100644 reftable/VERSION create mode 100644 reftable/dump.c 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/refname.c create mode 100644 reftable/refname.h create mode 100644 reftable/refname_test.c create mode 100644 reftable/stack.c create mode 100644 reftable/stack.h create mode 100644 reftable/stack_test.c create mode 100755 reftable/update.sh
Makefile +7 -1
@@ -2347,12 +2347,16 @@ XDIFF_OBJS += xdiff/xutils.o REFTABLE_OBJS += reftable/basics.o REFTABLE_OBJS += reftable/block.o REFTABLE_OBJS += reftable/blocksource.o -REFTABLE_OBJS += reftable/publicbasics.o REFTABLE_OBJS += reftable/compat.o REFTABLE_OBJS += reftable/iter.o +REFTABLE_OBJS += reftable/merged.o +REFTABLE_OBJS += reftable/pq.o +REFTABLE_OBJS += reftable/publicbasics.o REFTABLE_OBJS += reftable/reader.o REFTABLE_OBJS += reftable/record.o +REFTABLE_OBJS += reftable/refname.o REFTABLE_OBJS += reftable/reftable.o +REFTABLE_OBJS += reftable/stack.o REFTABLE_OBJS += reftable/strbuf.o REFTABLE_OBJS += reftable/tree.o REFTABLE_OBJS += reftable/writer.o
@@ -2360,7 +2364,9 @@ REFTABLE_OBJS += reftable/zlib-compat.o REFTABLE_TEST_OBJS += reftable/block_test.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/stack_test.o REFTABLE_TEST_OBJS += reftable/strbuf_test.o REFTABLE_TEST_OBJS += reftable/test_framework.o REFTABLE_TEST_OBJS += reftable/tree_test.o
reftable/VERSION new file +1 -0
@@ -0,0 +1 @@ +b8ec0f74c74cb6752eb2033ad8e755a9c19aad15 C: add missing header
reftable/dump.c new file +212 -0
212 lines not on this page · raw message
reftable/merged.c new file +358 -0
358 lines not on this page · raw message
reftable/merged.h new file +39 -0
@@ -0,0 +1,39 @@ +/* +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 MERGED_H +#define MERGED_H + +#include "pq.h" +#include "reftable.h" + +struct reftable_merged_table { +struct reftable_table *stack; +size_t stack_len; +uint32_t hash_id; +int suppress_deletions; + +uint64_t min; +uint64_t max; +}; + +struct merged_iter { +struct reftable_iterator *stack; +uint32_t hash_id; +size_t stack_len; +uint8_t typ; +int suppress_deletions; +struct merged_iter_pqueue pq; +}; + +void merged_table_clear (struct reftable_merged_table *mt); +int merged_table_seek_record (struct reftable_merged_table *mt, +struct reftable_iterator *it, +struct reftable_record *rec); + +#endif
reftable/merged_test.c new file +331 -0
331 lines not on this page · raw message
reftable/pq.c new file +115 -0
115 lines not on this page · raw message
reftable/pq.h new file +34 -0
@@ -0,0 +1,34 @@ +/* +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 PQ_H +#define PQ_H + +#include "record.h" + +struct pq_entry { +int index; +struct reftable_record rec; +}; + +int pq_less (struct pq_entry a,struct pq_entry b); + +struct merged_iter_pqueue { +struct pq_entry *heap; +size_t len; +size_t cap; +}; + +struct pq_entry merged_iter_pqueue_top (struct merged_iter_pqueue pq); +int merged_iter_pqueue_is_empty (struct merged_iter_pqueue pq); +void merged_iter_pqueue_check (struct merged_iter_pqueue pq); +struct pq_entry merged_iter_pqueue_remove (struct merged_iter_pqueue *pq); +void merged_iter_pqueue_add (struct merged_iter_pqueue *pq,struct pq_entry e); +void merged_iter_pqueue_clear (struct merged_iter_pqueue *pq); + +#endif
reftable/refname.c new file +209 -0
209 lines not on this page · raw message
reftable/refname.h new file +38 -0
@@ -0,0 +1,38 @@ +/* +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 REFNAME_H +#define REFNAME_H + +#include "reftable.h" + +struct modification { +struct reftable_table tab; + +char **add; +size_t add_len; + +char **del; +size_t del_len; +}; + +// -1 = error, 0 = found, 1 = not found +int modification_has_ref (struct modification *mod,const char *name); + +// -1 = error, 0 = found, 1 = not found. +int modification_has_ref_with_prefix (struct modification *mod, +const char *prefix); + +// 0 = OK. +int validate_refname (const char *name); + +int validate_ref_record_addition (struct reftable_table tab, +struct reftable_ref_record *recs,size_t sz); + +int modification_validate (struct modification *mod); + +#endif
reftable/refname_test.c new file +100 -0
100 lines not on this page · raw message
reftable/stack.c new file +1224 -0
1,224 lines not on this page · raw message
reftable/stack.h new file +50 -0
50 lines not on this page · raw message
reftable/stack_test.c new file +787 -0
263 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Signature · 1 line
gitgitgadget
Part of the diff is not shown here. View the raw message.
PATCH 13/13 reftable: "test-tool dump-reftable" command.
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
This command dumps individual tables or a stack of of tables.
--- Makefile | 2 ++ t/helper/test-reftable.c | 5 +++++ t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 + 4 files changed, 9 insertions(+)
Makefile +2 -0
@@ -2363,6 +2363,8 @@ REFTABLE_OBJS += reftable/writer.o REFTABLE_OBJS += reftable/zlib-compat.o REFTABLE_TEST_OBJS += reftable/block_test.o +REFTABLE_TEST_OBJS += reftable/dump.o +REFTABLE_TEST_OBJS += reftable/merged_test.o REFTABLE_TEST_OBJS += reftable/record_test.o REFTABLE_TEST_OBJS += reftable/refname_test.o REFTABLE_TEST_OBJS += reftable/reftable_test.o
t/helper/test-reftable.c +5 -0
@@ -13,3 +13,8 @@ int cmd__reftable(int argc, const char **argv)tree_test_main (argc, argv);return 0 ; } + +int cmd__dump_reftable (int argc,const char **argv) +{ +return reftable_dump_main (argc, (char *const *)argv); +}
t/helper/test-tool.c +1 -0
@@ -53,6 +53,7 @@ static struct test_cmd cmds[] = { { "read-midx", cmd__read_midx }, { "ref-store", cmd__ref_store }, { "reftable", cmd__reftable }, + { "dump-reftable", cmd__dump_reftable }, { "regex", cmd__regex }, { "repository", cmd__repository }, { "revision-walking", cmd__revision_walking },
t/helper/test-tool.h +1 -0
@@ -17,6 +17,7 @@ int cmd__dump_cache_tree(int argc, const char **argv);int cmd__dump_fsmonitor (int argc,const char **argv);int cmd__dump_split_index (int argc,const char **argv);int cmd__dump_untracked_cache (int argc,const char **argv); +int cmd__dump_reftable (int argc,const char **argv);int cmd__example_decorate (int argc,const char **argv);int cmd__genrandom (int argc,const char **argv);int cmd__genzeros (int argc,const char **argv);
Signature · 1 line
gitgitgadget
v2 cover reftable library
In reply to Han-Wen Nienhuys via GitGitGadgetThis splits the giant commit from
https://github.com/gitgitgadget/git/pull/539 into a series of smaller commits, which build and have unittests.
The final commit should also be split up, but I want to wait until we have consensus that the bottom commits look good.
Han-Wen Nienhuys (12):
reftable: add LICENSE
reftable: define the public API
reftable: add a barebones unittest framework
reftable: utility functions
reftable: (de)serialization for the polymorphic record type.
reftable: reading/writing blocks
reftable: a generic binary tree implementation
reftable: write reftable files
reftable: read reftable files
reftable: file level tests
reftable: rest of library
reftable: "test-tool dump-reftable" command.
Johannes Schindelin (1):
vcxproj: adjust for the reftable changes
Makefile | 46 +- config.mak.uname | 2 +- contrib/buildsystems/Generators/Vcxproj.pm | 11 +- reftable/.gitattributes | 1 + reftable/LICENSE | 31 + reftable/VERSION | 1 + reftable/basics.c | 131 +++ reftable/basics.h | 48 + reftable/block.c | 441 +++++++ reftable/block.h | 129 ++ reftable/block_test.c | 158 +++ reftable/blocksource.c | 148 +++ reftable/blocksource.h | 22 + reftable/compat.c | 110 ++ reftable/compat.h | 48 + reftable/constants.h | 21 + reftable/dump.c | 212 ++++ reftable/iter.c | 242 ++++ reftable/iter.h | 72 ++ reftable/merged.c | 358 ++++++ reftable/merged.h | 36 + reftable/merged_test.c | 331 ++++++ reftable/pq.c | 115 ++ reftable/pq.h | 32 + reftable/publicbasics.c | 100 ++ reftable/reader.c | 733 ++++++++++++ reftable/reader.h | 78 ++ reftable/record.c | 1116 ++++++++++++++++++ reftable/record.h | 137 +++ reftable/record_test.c | 410 +++++++ reftable/refname.c | 209 ++++ reftable/refname.h | 28 + reftable/refname_test.c | 100 ++ reftable/reftable-tests.h | 22 + reftable/reftable.c | 104 ++ reftable/reftable.h | 585 +++++++++ reftable/reftable_test.c | 585 +++++++++ reftable/stack.c | 1240 ++++++++++++++++++++ reftable/stack.h | 40 + reftable/stack_test.c | 788 +++++++++++++ reftable/strbuf.c | 142 +++ reftable/strbuf.h | 80 ++ reftable/strbuf_test.c | 37 + reftable/system.h | 51 + reftable/test_framework.c | 68 ++ reftable/test_framework.h | 59 + reftable/tree.c | 63 + reftable/tree.h | 34 + reftable/tree_test.c | 62 + reftable/update.sh | 22 + reftable/writer.c | 673 +++++++++++ reftable/writer.h | 51 + reftable/zlib-compat.c | 92 ++ t/helper/test-reftable.c | 20 + t/helper/test-tool.c | 2 + t/helper/test-tool.h | 2 + 56 files changed, 10474 insertions(+), 5 deletions(-) create mode 100644 reftable/.gitattributes create mode 100644 reftable/LICENSE create mode 100644 reftable/VERSION 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/blocksource.c create mode 100644 reftable/blocksource.h 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/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/publicbasics.c 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 t/helper/test-reftable.c
base-commit: 306ee63a703ad67c54ba1209dc11dd9ea500dc1f
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-847%2Fhanwen%2Flibreftable-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-847/hanwen/libreftable-v2
Pull-Request: https://github.com/git/git/pull/847
Range-diff vs v1:
1: f697eac228 = 1: 6228103b4a reftable: add LICENSE
2: e2dbc84833 = 2: 5d1b946ab5 reftable: define the public API
3: 0f6efc065c = 3: 01a669a731 vcxproj: adjust for the reftable changes
4: 7bf59d491d ! 4: b94c5f5c61 reftable: add a barebones unittest framework
@@ reftable/test_framework.c (new)
+#include "system.h"
+#include "basics.h"
+
-+struct test_case **test_cases;
-+int test_case_len;
-+int test_case_cap;
++static struct test_case **test_cases;
++static int test_case_len;
++static int test_case_cap;
+
-+struct test_case *new_test_case(const char *name, void (*testfunc)(void))
++static struct test_case *new_test_case(const char *name, void (*testfunc)(void))
+{
+ struct test_case *tc = reftable_malloc(sizeof(struct test_case));
+ tc->name = name;
@@ reftable/test_framework.c (new)
+ reftable_free(test_cases[i]);
+ }
+ reftable_free(test_cases);
-+ test_cases = 0;
++ test_cases = NULL;
+ test_case_len = 0;
+ test_case_cap = 0;
+ return 0;
@@ reftable/test_framework.h (new)
+ void (*testfunc)(void);
+};
+
-+struct test_case *new_test_case(const char *name, void (*testfunc)(void));
+struct test_case *add_test_case(const char *name, void (*testfunc)(void));
+int test_main(int argc, const char *argv[]);
+
5: 570a8c4bca ! 5: 4190da597e reftable: utility functions
@@ reftable/blocksource.c (new)
+ return ((struct strbuf *)b)->len;
+}
+
-+struct reftable_block_source_vtable strbuf_vtable = {
++static struct reftable_block_source_vtable strbuf_vtable = {
+ .size = &strbuf_size,
+ .read_block = &strbuf_read_block,
+ .return_block = &strbuf_return_block,
@@ reftable/blocksource.c (new)
+ reftable_free(dest->data);
+}
+
-+struct reftable_block_source_vtable malloc_vtable = {
++static struct reftable_block_source_vtable malloc_vtable = {
+ .return_block = &malloc_return_block,
+};
+
-+struct reftable_block_source malloc_block_source_instance = {
++static struct reftable_block_source malloc_block_source_instance = {
+ .ops = &malloc_vtable,
+};
+
@@ reftable/blocksource.c (new)
+ return size;
+}
+
-+struct reftable_block_source_vtable file_vtable = {
++static struct reftable_block_source_vtable file_vtable = {
+ .size = &file_size,
+ .read_block = &file_read_block,
+ .return_block = &file_return_block,
@@ reftable/publicbasics.c (new)
+ }
+}
+
-+void *(*reftable_malloc_ptr)(size_t sz) = &malloc;
-+void *(*reftable_realloc_ptr)(void *, size_t) = &realloc;
-+void (*reftable_free_ptr)(void *) = &free;
++static void *(*reftable_malloc_ptr)(size_t sz) = &malloc;
++static void *(*reftable_realloc_ptr)(void *, size_t) = &realloc;
++static void (*reftable_free_ptr)(void *) = &free;
+
+void *reftable_malloc(size_t sz)
+{
6: 791f69c000 ! 6: 8eb944ea9b reftable: (de)serialization for the polymorphic record type.
@@ reftable/record.c (new)
+ (const struct reftable_ref_record *)p);
+}
+
-+struct reftable_record_vtable reftable_ref_record_vtable = {
++static struct reftable_record_vtable reftable_ref_record_vtable = {
+ .key = &reftable_ref_record_key,
+ .type = BLOCK_TYPE_REF,
+ .copy_from = &reftable_ref_record_copy_from,
@@ reftable/record.c (new)
+ return 0;
+}
+
-+struct reftable_record_vtable reftable_obj_record_vtable = {
++static struct reftable_record_vtable reftable_obj_record_vtable = {
+ .key = &reftable_obj_record_key,
+ .type = BLOCK_TYPE_OBJ,
+ .copy_from = &reftable_obj_record_copy_from,
@@ reftable/record.c (new)
+ (const struct reftable_log_record *)p);
+}
+
-+struct reftable_record_vtable reftable_log_record_vtable = {
++static struct reftable_record_vtable reftable_log_record_vtable = {
+ .key = &reftable_log_record_key,
+ .type = BLOCK_TYPE_LOG,
+ .copy_from = &reftable_log_record_copy_from,
@@ reftable/record.c (new)
+ return rec;
+}
+
-+void *reftable_record_yield(struct reftable_record *rec)
++/* clear out the record, yielding the reftable_record data that was
++ * encapsulated. */
++static void *reftable_record_yield(struct reftable_record *rec)
+{
+ void *p = rec->data;
+ rec->data = NULL;
@@ reftable/record.c (new)
+ return start.len - in.len;
+}
+
-+struct reftable_record_vtable reftable_index_record_vtable = {
++static struct reftable_record_vtable reftable_index_record_vtable = {
+ .key = &reftable_index_record_key,
+ .type = BLOCK_TYPE_INDEX,
+ .copy_from = &reftable_index_record_copy_from,
@@ reftable/record.h (new)
+/* creates a malloced record of the given type. Dispose with record_destroy */
+struct reftable_record reftable_new_record(uint8_t typ);
+
-+extern struct reftable_record_vtable reftable_ref_record_vtable;
-+
+/* Encode `key` into `dest`. Sets `is_restart` to indicate a restart. Returns
+ number of bytes written. */
+int reftable_encode_key(int *is_restart, struct string_view dest,
@@ reftable/record.h (new)
+/* zeroes out the embedded record */
+void reftable_record_clear(struct reftable_record *rec);
+
-+/* clear out the record, yielding the reftable_record data that was
-+ * encapsulated. */
-+void *reftable_record_yield(struct reftable_record *rec);
-+
+/* clear and deallocate embedded record, and zero `rec`. */
+void reftable_record_destroy(struct reftable_record *rec);
+
@@ reftable/record_test.c (new)
+ int i = 0;
+
+ for (i = 0; i <= 3; i++) {
-+ struct reftable_ref_record in = { 0 };
++ struct reftable_ref_record in = { NULL };
+ struct reftable_ref_record out = {
+ .refname = xstrdup("old name"),
+ .value = reftable_calloc(SHA1_SIZE),
+ .target_value = reftable_calloc(SHA1_SIZE),
+ .target = xstrdup("old value"),
+ };
-+ struct reftable_record rec_out = { 0 };
++ struct reftable_record rec_out = { NULL };
+ struct strbuf key = STRBUF_INIT;
-+ struct reftable_record rec = { 0 };
++ struct reftable_record rec = { NULL };
+ uint8_t buffer[1024] = { 0 };
+ struct string_view dest = {
+ .buf = buffer,
@@ reftable/record_test.c (new)
+ set_test_hash(in[0].new_hash, 1);
+ set_test_hash(in[0].old_hash, 2);
+ for (int i = 0; i < ARRAY_SIZE(in); i++) {
-+ struct reftable_record rec = { 0 };
++ struct reftable_record rec = { NULL };
+ struct strbuf key = STRBUF_INIT;
+ uint8_t buffer[1024] = { 0 };
+ struct string_view dest = {
@@ reftable/record_test.c (new)
+ .email = xstrdup("old@email"),
+ .message = xstrdup("old message"),
+ };
-+ struct reftable_record rec_out = { 0 };
++ struct reftable_record rec_out = { NULL };
+ int n, m, valtype;
+
+ reftable_record_from_log(&rec, &in[i]);
@@ reftable/record_test.c (new)
+ .buf = buffer,
+ .len = sizeof(buffer),
+ };
-+ struct reftable_record rec = { 0 };
++ struct reftable_record rec = { NULL };
+ struct strbuf key = STRBUF_INIT;
-+ struct reftable_obj_record out = { 0 };
-+ struct reftable_record rec_out = { 0 };
++ struct reftable_obj_record out = { NULL };
++ struct reftable_record rec_out = { NULL };
+ int n, m;
+ uint8_t extra;
+
@@ reftable/record_test.c (new)
+ .len = sizeof(buffer),
+ };
+ struct strbuf key = STRBUF_INIT;
-+ struct reftable_record rec = { 0 };
++ struct reftable_record rec = { NULL };
+ struct reftable_index_record out = { .last_key = STRBUF_INIT };
+ struct reftable_record out_rec = { NULL };
+ int n, m;
7: 1ba8e3eb30 ! 7: 757dd30fe2 reftable: reading/writing blocks
@@ reftable/block.c (new)
+ abort();
+}
+
-+int block_writer_register_restart(struct block_writer *w, int n, int restart,
-+ struct strbuf *key);
++static int block_writer_register_restart(struct block_writer *w, int n,
++ int is_restart, struct strbuf *key)
++{
++ int rlen = w->restart_len;
++ if (rlen >= MAX_RESTARTS) {
++ is_restart = 0;
++ }
++
++ if (is_restart) {
++ rlen++;
++ }
++ if (2 + 3 * rlen + n > w->block_size - w->next)
++ return -1;
++ if (is_restart) {
++ if (w->restart_len == w->restart_cap) {
++ w->restart_cap = w->restart_cap * 2 + 1;
++ w->restarts = reftable_realloc(
++ w->restarts, sizeof(uint32_t) * w->restart_cap);
++ }
++
++ w->restarts[w->restart_len++] = w->next;
++ }
++
++ w->next += n;
++
++ strbuf_reset(&w->last_key);
++ strbuf_addbuf(&w->last_key, key);
++ w->entries++;
++ return 0;
++}
+
+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)
@@ reftable/block.c (new)
+ return -1;
+}
+
-+int block_writer_register_restart(struct block_writer *w, int n, int is_restart,
-+ struct strbuf *key)
-+{
-+ int rlen = w->restart_len;
-+ if (rlen >= MAX_RESTARTS) {
-+ is_restart = 0;
-+ }
-+
-+ if (is_restart) {
-+ rlen++;
-+ }
-+ if (2 + 3 * rlen + n > w->block_size - w->next)
-+ return -1;
-+ if (is_restart) {
-+ if (w->restart_len == w->restart_cap) {
-+ w->restart_cap = w->restart_cap * 2 + 1;
-+ w->restarts = reftable_realloc(
-+ w->restarts, sizeof(uint32_t) * w->restart_cap);
-+ }
-+
-+ w->restarts[w->restart_len++] = w->next;
-+ }
-+
-+ w->next += n;
-+
-+ strbuf_reset(&w->last_key);
-+ strbuf_addbuf(&w->last_key, key);
-+ w->entries++;
-+ return 0;
-+}
+
+int block_writer_finish(struct block_writer *w)
+{
@@ reftable/block_test.c (new)
+ char *names[30];
+ const int N = ARRAY_SIZE(names);
+ const int block_size = 1024;
-+ struct reftable_block block = { 0 };
++ struct reftable_block block = { NULL };
+ struct block_writer bw = {
+ .last_key = STRBUF_INIT,
+ };
-+ struct reftable_ref_record ref = { 0 };
-+ struct reftable_record rec = { 0 };
++ struct reftable_ref_record ref = { NULL };
++ struct reftable_record rec = { NULL };
+ int i = 0;
+ int n;
+ struct block_reader br = { 0 };
8: 17fb8d050d ! 8: e30a7e0281 reftable: a generic binary tree implementation
@@ reftable/tree_test.c (new)
+{
+ struct tree_node *root = NULL;
+
-+ void *values[11] = { 0 };
-+ struct tree_node *nodes[11] = { 0 };
++ void *values[11] = { NULL };
++ struct tree_node *nodes[11] = { NULL };
+ int i = 1;
-+ struct curry c = { 0 };
++ struct curry c = { NULL };
+ do {
+ nodes[i] = tree_search(values + i, &root, &test_compare, 1);
+ i = (i * 7) % 11;
9: 4e40fc3b40 ! 9: 68aee16e60 reftable: write reftable files
@@ reftable/writer.c (new)
+#include "reftable.h"
+#include "tree.h"
+
++/* finishes a block, and writes it to storage */
++static int writer_flush_block(struct reftable_writer *w);
++
++/* deallocates memory related to the index */
++static void writer_clear_index(struct reftable_writer *w);
++
++/* finishes writing a 'r' (refs) or 'g' (reflogs) section */
++static int writer_finish_public_section(struct reftable_writer *w);
++
+static struct reftable_block_stats *
+writer_reftable_block_stats(struct reftable_writer *w, uint8_t typ)
+{
@@ reftable/writer.c (new)
+int reftable_writer_add_ref(struct reftable_writer *w,
+ struct reftable_ref_record *ref)
+{
-+ struct reftable_record rec = { 0 };
++ struct reftable_record rec = { NULL };
+ struct reftable_ref_record copy = *ref;
+ int err = 0;
+
@@ reftable/writer.c (new)
+int reftable_writer_add_log(struct reftable_writer *w,
+ struct reftable_log_record *log)
+{
-+ struct reftable_record rec = { 0 };
++ struct reftable_record rec = { NULL };
+ char *input_log_message = log->message;
+ struct strbuf cleaned_message = STRBUF_INIT;
+ int err;
@@ reftable/writer.c (new)
+ w->index_len = 0;
+ w->index_cap = 0;
+ for (i = 0; i < idx_len; i++) {
-+ struct reftable_record rec = { 0 };
++ struct reftable_record rec = { NULL };
+ reftable_record_from_index(&rec, idx + i);
+ if (block_writer_add(w->block_writer, &rec) == 0) {
+ continue;
@@ reftable/writer.c (new)
+ .offsets = entry->offsets,
+ .offset_len = entry->offset_len,
+ };
-+ struct reftable_record rec = { 0 };
++ struct reftable_record rec = { NULL };
+ if (arg->err < 0)
+ goto done;
+
@@ reftable/writer.c (new)
+static int writer_dump_object_index(struct reftable_writer *w)
+{
+ struct write_record_arg closure = { .w = w };
-+ struct common_prefix_arg common = { 0 };
++ struct common_prefix_arg common = { NULL };
+ if (w->obj_index_tree != NULL) {
+ infix_walk(w->obj_index_tree, &update_common, &common);
+ }
@@ reftable/writer.c (new)
+ return writer_finish_section(w);
+}
+
-+int writer_finish_public_section(struct reftable_writer *w)
++static int writer_finish_public_section(struct reftable_writer *w)
+{
+ uint8_t typ = 0;
+ int err = 0;
@@ reftable/writer.c (new)
+ return err;
+}
+
-+void writer_clear_index(struct reftable_writer *w)
++static void writer_clear_index(struct reftable_writer *w)
+{
+ int i = 0;
+ for (i = 0; i < w->index_len; i++) {
@@ reftable/writer.c (new)
+ w->index_cap = 0;
+}
+
-+const int debug = 0;
++static const int debug = 0;
+
+static int writer_flush_nonempty_block(struct reftable_writer *w)
+{
@@ reftable/writer.c (new)
+ return 0;
+}
+
-+int writer_flush_block(struct reftable_writer *w)
++static int writer_flush_block(struct reftable_writer *w)
+{
+ if (w->block_writer == NULL)
+ return 0;
@@ reftable/writer.h (new)
+ struct reftable_stats stats;
+};
+
-+/* finishes a block, and writes it to storage */
-+int writer_flush_block(struct reftable_writer *w);
-+
-+/* deallocates memory related to the index */
-+void writer_clear_index(struct reftable_writer *w);
-+
-+/* finishes writing a 'r' (refs) or 'g' (reflogs) section */
-+int writer_finish_public_section(struct reftable_writer *w);
-+
+#endif
10: 25259b683b ! 10: c196de7f06 reftable: read reftable files
@@ reftable/iter.c (new)
+{
+}
+
-+struct reftable_iterator_vtable empty_vtable = {
++static struct reftable_iterator_vtable empty_vtable = {
+ .next = &empty_iterator_next,
+ .close = &empty_iterator_close,
+};
@@ reftable/iter.c (new)
+ return err;
+}
+
-+struct reftable_iterator_vtable filtering_ref_iterator_vtable = {
++static struct reftable_iterator_vtable filtering_ref_iterator_vtable = {
+ .next = &filtering_ref_iterator_next,
+ .close = &filtering_ref_iterator_close,
+};
@@ reftable/iter.c (new)
+ return err;
+}
+
-+struct reftable_iterator_vtable indexed_table_ref_iter_vtable = {
++static struct reftable_iterator_vtable indexed_table_ref_iter_vtable = {
+ .next = &indexed_table_ref_iter_next,
+ .close = &indexed_table_ref_iter_close,
+};
@@ reftable/reader.c (new)
+ block_iter_close(&ti->bi);
+}
+
-+struct reftable_iterator_vtable table_iter_vtable = {
++static struct reftable_iterator_vtable table_iter_vtable = {
+ .next = &table_iter_next_void,
+ .close = &table_iter_close,
+};
11: 3448fcf828 ! 11: bf6b929b86 reftable: file level tests
@@ reftable/reftable_test.c (new)
+{
+ struct strbuf buf = STRBUF_INIT;
+ struct reftable_block_source source = { NULL };
-+ struct reftable_block out = { 0 };
++ struct reftable_block out = { NULL };
+ int n;
+ uint8_t in[] = "hello";
+ strbuf_add(&buf, in, sizeof(in));
@@ reftable/reftable_test.c (new)
+ };
+ struct reftable_writer *w =
+ reftable_new_writer(&strbuf_add_void, buf, &opts);
-+ struct reftable_ref_record ref = { 0 };
++ struct reftable_ref_record ref = { NULL };
+ int i = 0, n;
-+ struct reftable_log_record log = { 0 };
++ struct reftable_log_record log = { NULL };
+ const struct reftable_stats *stats = NULL;
+ *names = reftable_calloc(sizeof(char *) * (N + 1));
+ reftable_writer_set_limits(w, update_index, update_index);
@@ reftable/reftable_test.c (new)
+ struct reftable_write_options opts = {
+ .block_size = 256,
+ };
-+ struct reftable_ref_record ref = { 0 };
++ struct reftable_ref_record ref = { NULL };
+ int i = 0;
-+ struct reftable_log_record log = { 0 };
++ struct reftable_log_record log = { NULL };
+ int n;
-+ struct reftable_iterator it = { 0 };
-+ struct reftable_reader rd = { 0 };
-+ struct reftable_block_source source = { 0 };
++ struct reftable_iterator it = { NULL };
++ struct reftable_reader rd = { NULL };
++ struct reftable_block_source source = { NULL };
+ struct strbuf buf = STRBUF_INIT;
+ struct reftable_writer *w =
+ reftable_new_writer(&strbuf_add_void, &buf, &opts);
@@ reftable/reftable_test.c (new)
+ reftable_writer_set_limits(w, 0, N);
+ for (i = 0; i < N; i++) {
+ char name[256];
-+ struct reftable_ref_record ref = { 0 };
++ struct reftable_ref_record ref = { NULL };
+ snprintf(name, sizeof(name), "b%02d%0*d", i, 130, 7);
+ names[i] = xstrdup(name);
+ ref.refname = name;
@@ reftable/reftable_test.c (new)
+ }
+ for (i = 0; i < N; i++) {
+ uint8_t hash1[SHA1_SIZE], hash2[SHA1_SIZE];
-+ struct reftable_log_record log = { 0 };
++ struct reftable_log_record log = { NULL };
+ set_test_hash(hash1, i);
+ set_test_hash(hash2, i + 1);
+
@@ reftable/reftable_test.c (new)
+ char **names;
+ struct strbuf buf = STRBUF_INIT;
+ int N = 50;
-+ struct reftable_iterator it = { 0 };
-+ struct reftable_block_source source = { 0 };
-+ struct reftable_reader rd = { 0 };
++ struct reftable_iterator it = { NULL };
++ struct reftable_block_source source = { NULL };
++ struct reftable_reader rd = { NULL };
+ int err = 0;
+ int j = 0;
+
@@ reftable/reftable_test.c (new)
+ assert_err(err);
+
+ while (1) {
-+ struct reftable_ref_record ref = { 0 };
++ struct reftable_ref_record ref = { NULL };
+ int r = reftable_iterator_next_ref(&it, &ref);
+ assert(r >= 0);
+ if (r > 0) {
@@ reftable/reftable_test.c (new)
+ char **names;
+ struct strbuf buf = STRBUF_INIT;
+ int N = 50;
-+ struct reftable_reader rd = { 0 };
-+ struct reftable_block_source source = { 0 };
++ struct reftable_reader rd = { NULL };
++ struct reftable_block_source source = { NULL };
+ int err;
+ int i;
-+ struct reftable_log_record log = { 0 };
-+ struct reftable_iterator it = { 0 };
++ struct reftable_log_record log = { NULL };
++ struct reftable_iterator it = { NULL };
+
+ write_table(&names, &buf, N, 256, SHA1_ID);
+
@@ reftable/reftable_test.c (new)
+ char **names;
+ struct strbuf buf = STRBUF_INIT;
+ int N = 50;
-+ struct reftable_reader rd = { 0 };
-+ struct reftable_block_source source = { 0 };
++ struct reftable_reader rd = { NULL };
++ struct reftable_block_source source = { NULL };
+ int err;
+ int i = 0;
+
-+ struct reftable_iterator it = { 0 };
++ struct reftable_iterator it = { NULL };
+ struct strbuf pastLast = STRBUF_INIT;
-+ struct reftable_ref_record ref = { 0 };
++ struct reftable_ref_record ref = { NULL };
+
+ write_table(&names, &buf, N, 256, hash_id);
+
@@ reftable/reftable_test.c (new)
+
+ err = reftable_reader_seek_ref(&rd, &it, pastLast.buf);
+ if (err == 0) {
-+ struct reftable_ref_record ref = { 0 };
++ struct reftable_ref_record ref = { NULL };
+ int err = reftable_iterator_next_ref(&it, &ref);
+ assert(err > 0);
+ } else {
@@ reftable/reftable_test.c (new)
+ struct reftable_write_options opts = {
+ .block_size = 256,
+ };
-+ struct reftable_ref_record ref = { 0 };
++ struct reftable_ref_record ref = { NULL };
+ int i = 0;
+ int n;
+ int err;
+ struct reftable_reader rd;
-+ struct reftable_block_source source = { 0 };
++ struct reftable_block_source source = { NULL };
+
+ struct strbuf buf = STRBUF_INIT;
+ struct reftable_writer *w =
+ reftable_new_writer(&strbuf_add_void, &buf, &opts);
+
-+ struct reftable_iterator it = { 0 };
++ struct reftable_iterator it = { NULL };
+ int j;
+
+ set_test_hash(want_hash, 4);
@@ reftable/reftable_test.c (new)
+ char name[100];
+ uint8_t hash1[SHA1_SIZE];
+ uint8_t hash2[SHA1_SIZE];
-+ struct reftable_ref_record ref = { 0 };
++ struct reftable_ref_record ref = { NULL };
+
+ memset(hash, i, sizeof(hash));
+ memset(fill, 'x', 50);
@@ reftable/reftable_test.c (new)
+ struct strbuf buf = STRBUF_INIT;
+ struct reftable_writer *w =
+ reftable_new_writer(&strbuf_add_void, &buf, &opts);
-+ struct reftable_block_source source = { 0 };
++ struct reftable_block_source source = { NULL };
+ struct reftable_reader *rd = NULL;
-+ struct reftable_ref_record rec = { 0 };
-+ struct reftable_iterator it = { 0 };
++ struct reftable_ref_record rec = { NULL };
++ struct reftable_iterator it = { NULL };
+ int err;
+
+ reftable_writer_set_limits(w, 1, 1);
12: 64d98e60b2 ! 12: 4e38db7f48 reftable: rest of library
@@ Makefile: REFTABLE_OBJS += reftable/zlib-compat.o
## reftable/VERSION (new) ##
@@
-+b8ec0f74c74cb6752eb2033ad8e755a9c19aad15 C: add missing header
++7134eb9f8171a9759800f4187f9e6dde997335e7 C: NULL iso 0 for init
## reftable/dump.c (new) ##
@@
@@ reftable/dump.c (new)
+
+static int dump_table(const char *tablename)
+{
-+ struct reftable_block_source src = { 0 };
++ struct reftable_block_source src = { NULL };
+ int err = reftable_block_source_from_file(&src, tablename);
-+ struct reftable_iterator it = { 0 };
-+ struct reftable_ref_record ref = { 0 };
-+ struct reftable_log_record log = { 0 };
++ struct reftable_iterator it = { NULL };
++ struct reftable_ref_record ref = { NULL };
++ struct reftable_log_record log = { NULL };
+ struct reftable_reader *r = NULL;
+
+ if (err < 0)
@@ reftable/dump.c (new)
+{
+ struct reftable_stack *stack = NULL;
+ struct reftable_write_options cfg = {};
-+ struct reftable_iterator it = { 0 };
-+ struct reftable_ref_record ref = { 0 };
-+ struct reftable_log_record log = { 0 };
++ struct reftable_iterator it = { NULL };
++ struct reftable_ref_record ref = { NULL };
++ struct reftable_log_record log = { NULL };
+ struct reftable_merged_table *merged = NULL;
+
+ int err = reftable_new_stack(&stack, stackdir, cfg);
@@ reftable/merged.c (new)
+ return merged_iter_next(mi, rec);
+}
+
-+struct reftable_iterator_vtable merged_iter_vtable = {
++static struct reftable_iterator_vtable merged_iter_vtable = {
+ .next = &merged_iter_next_void,
+ .close = &merged_iter_close,
+};
@@ reftable/merged.c (new)
+ return mt->min;
+}
+
-+int merged_table_seek_record(struct reftable_merged_table *mt,
-+ struct reftable_iterator *it,
-+ struct reftable_record *rec)
++static int merged_table_seek_record(struct reftable_merged_table *mt,
++ struct reftable_iterator *it,
++ struct reftable_record *rec)
+{
+ struct reftable_iterator *iters = reftable_calloc(
+ sizeof(struct reftable_iterator) * mt->stack_len);
@@ reftable/merged.c (new)
+ struct reftable_ref_record ref = {
+ .refname = (char *)name,
+ };
-+ struct reftable_record rec = { 0 };
++ struct reftable_record rec = { NULL };
+ reftable_record_from_ref(&rec, &ref);
+ return merged_table_seek_record(mt, it, &rec);
+}
@@ reftable/merged.c (new)
+ .refname = (char *)name,
+ .update_index = update_index,
+ };
-+ struct reftable_record rec = { 0 };
++ struct reftable_record rec = { NULL };
+ reftable_record_from_log(&rec, &log);
+ return merged_table_seek_record(mt, it, &rec);
+}
@@ reftable/merged.h (new)
+};
+
+void merged_table_clear(struct reftable_merged_table *mt);
-+int merged_table_seek_record(struct reftable_merged_table *mt,
-+ struct reftable_iterator *it,
-+ struct reftable_record *rec);
+
+#endif
@@ reftable/merged_test.c (new)
+
+static void test_pq(void)
+{
-+ char *names[54] = { 0 };
++ char *names[54] = { NULL };
+ int N = ARRAY_SIZE(names) - 1;
+
-+ struct merged_iter_pqueue pq = { 0 };
++ struct merged_iter_pqueue pq = { NULL };
+ const char *last = NULL;
+
+ int i = 0;
@@ reftable/merged_test.c (new)
+ struct reftable_merged_table *mt =
+ merged_table_from_records(refs, &bs, &readers, sizes, bufs, 2);
+ int i;
-+ struct reftable_ref_record ref = { 0 };
-+ struct reftable_iterator it = { 0 };
++ struct reftable_ref_record ref = { NULL };
++ struct reftable_iterator it = { NULL };
+ int err = reftable_merged_table_seek_ref(mt, &it, "a");
+ assert_err(err);
+
@@ reftable/merged_test.c (new)
+ struct reftable_merged_table *mt =
+ merged_table_from_records(refs, &bs, &readers, sizes, bufs, 3);
+
-+ struct reftable_iterator it = { 0 };
++ struct reftable_iterator it = { NULL };
+ int err = reftable_merged_table_seek_ref(mt, &it, "a");
+ struct reftable_ref_record *out = NULL;
+ size_t len = 0;
@@ reftable/merged_test.c (new)
+
+ assert_err(err);
+ while (len < 100) { /* cap loops/recursion. */
-+ struct reftable_ref_record ref = { 0 };
++ struct reftable_ref_record ref = { NULL };
+ int err = reftable_iterator_next_ref(&it, &ref);
+ if (err > 0) {
+ break;
@@ reftable/merged_test.c (new)
+ .update_index = 1,
+ };
+ int err;
-+ struct reftable_block_source source = { 0 };
++ struct reftable_block_source source = { NULL };
+ struct reftable_table *tab = reftable_calloc(sizeof(*tab) * 1);
+ uint32_t hash_id;
+ struct reftable_reader *rd = NULL;
@@ reftable/pq.c (new)
+#include "system.h"
+#include "basics.h"
+
-+int pq_less(struct pq_entry a, struct pq_entry b)
++static int pq_less(struct pq_entry a, struct pq_entry b)
+{
+ struct strbuf ak = STRBUF_INIT;
+ struct strbuf bk = STRBUF_INIT;
@@ reftable/pq.h (new)
+ struct reftable_record rec;
+};
+
-+int pq_less(struct pq_entry a, struct pq_entry b);
-+
+struct merged_iter_pqueue {
+ struct pq_entry *heap;
+ size_t len;
@@ reftable/refname.c (new)
+ return strcmp(f_arg->names[k], f_arg->want) >= 0;
+}
+
-+int modification_has_ref(struct modification *mod, const char *name)
++static int modification_has_ref(struct modification *mod, const char *name)
+{
-+ struct reftable_ref_record ref = { 0 };
++ struct reftable_ref_record ref = { NULL };
+ int err = 0;
+
+ if (mod->add_len > 0) {
@@ reftable/refname.c (new)
+ mod->del_len = 0;
+}
+
-+int modification_has_ref_with_prefix(struct modification *mod,
-+ const char *prefix)
++static int modification_has_ref_with_prefix(struct modification *mod,
++ const char *prefix)
+{
+ struct reftable_iterator it = { NULL };
+ struct reftable_ref_record ref = { NULL };
@@ reftable/refname.c (new)
+ return err;
+}
+
-+int validate_refname(const char *name)
++static int validate_refname(const char *name)
+{
+ while (1) {
+ char *next = strchr(name, '/');
@@ reftable/refname.h (new)
+ size_t del_len;
+};
+
-+// -1 = error, 0 = found, 1 = not found
-+int modification_has_ref(struct modification *mod, const char *name);
-+
-+// -1 = error, 0 = found, 1 = not found.
-+int modification_has_ref_with_prefix(struct modification *mod,
-+ const char *prefix);
-+
-+// 0 = OK.
-+int validate_refname(const char *name);
-+
+int validate_ref_record_addition(struct reftable_table tab,
+ struct reftable_ref_record *recs, size_t sz);
+
@@ reftable/refname_test.c (new)
+ };
+ int err;
+ int i;
-+ struct reftable_block_source source = { 0 };
++ struct reftable_block_source source = { NULL };
+ struct reftable_reader *rd = NULL;
+ struct reftable_table tab = { NULL };
+ struct testcase cases[] = {
@@ reftable/refname_test.c (new)
+ return test_main(argc, argv);
+}
+ ## reftable/reftable.c ##
+@@ reftable/reftable.c: int reftable_table_seek_ref(struct reftable_table *tab,
+ struct reftable_ref_record ref = {
+ .refname = (char *)name,
+ };
+- struct reftable_record rec = { 0 };
++ struct reftable_record rec = { NULL };
+ reftable_record_from_ref(&rec, &ref);
+ return tab->ops->seek_record(tab->table_arg, it, &rec);
+ }
+@@ reftable/reftable.c: void reftable_table_from_reader(struct reftable_table *tab,
+ int reftable_table_read_ref(struct reftable_table *tab, const char *name,
+ struct reftable_ref_record *ref)
+ {
+- struct reftable_iterator it = { 0 };
++ struct reftable_iterator it = { NULL };
+ int err = reftable_table_seek_ref(tab, &it, name);
+ if (err)
+ goto done;
+
## reftable/stack.c (new) ##
@@
+/*
@@ reftable/stack.c (new)
+#include "reftable.h"
+#include "writer.h"
+
++static int stack_try_add(struct reftable_stack *st,
++ int (*write_table)(struct reftable_writer *wr,
++ void *arg),
++ void *arg);
++static int stack_write_compact(struct reftable_stack *st,
++ struct reftable_writer *wr, int first, int last,
++ struct reftable_log_expiry_config *config);
++static int stack_check_addition(struct reftable_stack *st,
++ const char *new_tab_name);
++static void reftable_addition_close(struct reftable_addition *add);
++static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
++ int reuse_open);
++
+int reftable_new_stack(struct reftable_stack **dest, const char *dir,
+ struct reftable_write_options config)
+{
@@ reftable/stack.c (new)
+ }
+
+ if (rd == NULL) {
-+ struct reftable_block_source src = { 0 };
++ struct reftable_block_source src = { NULL };
+ struct strbuf table_path = STRBUF_INIT;
+ strbuf_addstr(&table_path, st->reftable_dir);
+ strbuf_addstr(&table_path, "/");
@@ reftable/stack.c (new)
+ return udiff;
+}
+
-+int reftable_stack_reload_maybe_reuse(struct reftable_stack *st, int reuse_open)
++static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
++ int reuse_open)
+{
+ struct timeval deadline = { 0 };
+ int err = gettimeofday(&deadline, NULL);
@@ reftable/stack.c (new)
+ return err;
+}
+
-+void reftable_addition_close(struct reftable_addition *add)
++static void reftable_addition_close(struct reftable_addition *add)
+{
+ int i = 0;
+ struct strbuf nm = STRBUF_INIT;
@@ reftable/stack.c (new)
+ return err;
+}
+
-+int stack_try_add(struct reftable_stack *st,
-+ int (*write_table)(struct reftable_writer *wr, void *arg),
-+ void *arg)
++static int stack_try_add(struct reftable_stack *st,
++ int (*write_table)(struct reftable_writer *wr,
++ void *arg),
++ void *arg)
+{
+ struct reftable_addition add = REFTABLE_ADDITION_INIT;
+ int err = reftable_stack_init_addition(&add, st);
@@ reftable/stack.c (new)
+ return err;
+}
+
-+int stack_write_compact(struct reftable_stack *st, struct reftable_writer *wr,
-+ int first, int last,
-+ struct reftable_log_expiry_config *config)
++static int stack_write_compact(struct reftable_stack *st,
++ struct reftable_writer *wr, int first, int last,
++ struct reftable_log_expiry_config *config)
+{
+ int subtabs_len = last - first + 1;
+ struct reftable_table *subtabs = reftable_calloc(
+ sizeof(struct reftable_table) * (last - first + 1));
+ struct reftable_merged_table *mt = NULL;
+ int err = 0;
-+ struct reftable_iterator it = { 0 };
-+ struct reftable_ref_record ref = { 0 };
-+ struct reftable_log_record log = { 0 };
++ struct reftable_iterator it = { NULL };
++ struct reftable_ref_record ref = { NULL };
++ struct reftable_log_record log = { NULL };
+
+ uint64_t entries = 0;
+
@@ reftable/stack.c (new)
+int reftable_stack_read_log(struct reftable_stack *st, const char *refname,
+ struct reftable_log_record *log)
+{
-+ struct reftable_iterator it = { 0 };
++ struct reftable_iterator it = { NULL };
+ struct reftable_merged_table *mt = reftable_stack_merged_table(st);
+ int err = reftable_merged_table_seek_log(mt, &it, refname);
+ if (err)
@@ reftable/stack.c (new)
+ return err;
+}
+
-+int stack_check_addition(struct reftable_stack *st, const char *new_tab_name)
++static int stack_check_addition(struct reftable_stack *st,
++ const char *new_tab_name)
+{
+ int err = 0;
-+ struct reftable_block_source src = { 0 };
++ struct reftable_block_source src = { NULL };
+ struct reftable_reader *rd = NULL;
+ struct reftable_table tab = { NULL };
+ struct reftable_ref_record *refs = NULL;
@@ reftable/stack.c (new)
+ goto done;
+
+ while (1) {
-+ struct reftable_ref_record ref = { 0 };
++ struct reftable_ref_record ref = { NULL };
+ err = reftable_iterator_next_ref(&it, &ref);
+ if (err > 0) {
+ break;
@@ reftable/stack.h (new)
+};
+
+int read_lines(const char *filename, char ***lines);
-+int stack_try_add(struct reftable_stack *st,
-+ int (*write_table)(struct reftable_writer *wr, void *arg),
-+ void *arg);
-+int stack_write_compact(struct reftable_stack *st, struct reftable_writer *wr,
-+ int first, int last,
-+ struct reftable_log_expiry_config *config);
-+int fastlog2(uint64_t sz);
-+int stack_check_addition(struct reftable_stack *st, const char *new_tab_name);
-+void reftable_addition_close(struct reftable_addition *add);
-+int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
-+ int reuse_open);
+
+struct segment {
+ int start, end;
@@ reftable/stack.h (new)
+ uint64_t bytes;
+};
+
++int fastlog2(uint64_t sz);
+struct segment *sizes_to_segments(int *seglen, uint64_t *sizes, int n);
+struct segment suggest_compaction_segment(uint64_t *sizes, int n);
+
@@ reftable/stack_test.c (new)
+ .update_index = 1,
+ .target = "master",
+ };
-+ struct reftable_ref_record dest = { 0 };
++ struct reftable_ref_record dest = { NULL };
+
+ assert(mkdtemp(dir));
+
@@ reftable/stack_test.c (new)
+ .update_index = 1,
+ .target = "master",
+ };
-+ struct reftable_ref_record dest = { 0 };
++ struct reftable_ref_record dest = { NULL };
+
+ assert(mkdtemp(dir));
+
@@ reftable/stack_test.c (new)
+ };
+ struct reftable_stack *st = NULL;
+ char dir[256] = "/tmp/stack_test.XXXXXX";
-+ struct reftable_ref_record refs[2] = { { 0 } };
-+ struct reftable_log_record logs[2] = { { 0 } };
++ struct reftable_ref_record refs[2] = { { NULL } };
++ struct reftable_log_record logs[2] = { { NULL } };
+ int N = ARRAY_SIZE(refs);
+
+ assert(mkdtemp(dir));
@@ reftable/stack_test.c (new)
+ assert_err(err);
+
+ for (i = 0; i < N; i++) {
-+ struct reftable_ref_record dest = { 0 };
++ struct reftable_ref_record dest = { NULL };
++
+ int err = reftable_stack_read_ref(st, refs[i].refname, &dest);
+ assert_err(err);
+ assert(reftable_ref_record_equal(&dest, refs + i, SHA1_SIZE));
@@ reftable/stack_test.c (new)
+ }
+
+ for (i = 0; i < N; i++) {
-+ struct reftable_log_record dest = { 0 };
++ struct reftable_log_record dest = { NULL };
+ int err = reftable_stack_read_log(st, refs[i].refname, &dest);
+ assert_err(err);
+ assert(reftable_log_record_equal(&dest, logs + i, SHA1_SIZE));
@@ reftable/stack_test.c (new)
+ struct reftable_write_options cfg = { 0 };
+ struct reftable_stack *st = NULL;
+ int err;
-+ struct reftable_ref_record refs[2] = { { 0 } };
-+ struct reftable_log_record logs[2] = { { 0 } };
++ struct reftable_ref_record refs[2] = { { NULL } };
++ struct reftable_log_record logs[2] = { { NULL } };
+ int N = ARRAY_SIZE(refs);
-+ struct reftable_ref_record dest = { 0 };
-+ struct reftable_log_record log_dest = { 0 };
++ struct reftable_ref_record dest = { NULL };
++ struct reftable_log_record log_dest = { NULL };
+
+ assert(mkdtemp(dir));
+
@@ reftable/stack_test.c (new)
+ struct reftable_stack *st32 = NULL;
+ struct reftable_write_options cfg_default = { 0 };
+ struct reftable_stack *st_default = NULL;
-+ struct reftable_ref_record dest = { 0 };
++ struct reftable_ref_record dest = { NULL };
+
+ assert(mkdtemp(dir));
+ err = reftable_new_stack(&st, dir, cfg);
@@ reftable/stack_test.c (new)
+ char dir[256] = "/tmp/stack.test_reflog_expire.XXXXXX";
+ struct reftable_write_options cfg = { 0 };
+ struct reftable_stack *st = NULL;
-+ struct reftable_log_record logs[20] = { { 0 } };
++ struct reftable_log_record logs[20] = { { NULL } };
+ int N = ARRAY_SIZE(logs) - 1;
+ int i = 0;
+ int err;
+ struct reftable_log_expiry_config expiry = {
+ .time = 10,
+ };
-+ struct reftable_log_record log = { 0 };
++ struct reftable_log_record log = { NULL };
+
+ assert(mkdtemp(dir));
+ 13: b8729fee9e ! 13: c535f838d6 reftable: "test-tool dump-reftable" command.
@@ Makefile: REFTABLE_OBJS += reftable/writer.o
REFTABLE_TEST_OBJS += reftable/refname_test.o REFTABLE_TEST_OBJS += reftable/reftable_test.o
+ ## reftable/iter.c ##
+@@ reftable/iter.c: void reftable_iterator_destroy(struct reftable_iterator *it)
+ int reftable_iterator_next_ref(struct reftable_iterator *it,
+ struct reftable_ref_record *ref)
+ {
+- struct reftable_record rec = { 0 };
++ struct reftable_record rec = { NULL };
+ reftable_record_from_ref(&rec, ref);
+ return iterator_next(it, &rec);
+ }
+@@ reftable/iter.c: int reftable_iterator_next_ref(struct reftable_iterator *it,
+ int reftable_iterator_next_log(struct reftable_iterator *it,
+ struct reftable_log_record *log)
+ {
+- struct reftable_record rec = { 0 };
++ struct reftable_record rec = { NULL };
+ reftable_record_from_log(&rec, log);
+ return iterator_next(it, &rec);
+ }
+@@ reftable/iter.c: static int filtering_ref_iterator_next(void *iter_arg,
+ }
+
+ if (fri->double_check) {
+- struct reftable_iterator it = { 0 };
++ struct reftable_iterator it = { NULL };
+
+ err = reftable_table_seek_ref(&fri->tab, &it,
+ ref->refname);
+
+ ## reftable/reader.c ##
+@@ reftable/reader.c: static int parse_footer(struct reftable_reader *r, uint8_t *footer,
+ int init_reader(struct reftable_reader *r, struct reftable_block_source *source,
+ const char *name)
+ {
+- struct reftable_block footer = { 0 };
+- struct reftable_block header = { 0 };
++ struct reftable_block footer = { NULL };
++ struct reftable_block header = { NULL };
+ int err = 0;
+
+ memset(r, 0, sizeof(struct reftable_reader));
+@@ reftable/reader.c: int reader_init_block_reader(struct reftable_reader *r, struct block_reader *br,
+ {
+ int32_t guess_block_size = r->block_size ? r->block_size :
+ DEFAULT_BLOCK_SIZE;
+- struct reftable_block block = { 0 };
++ struct reftable_block block = { NULL };
+ uint8_t block_typ = 0;
+ int err = 0;
+ uint32_t header_off = next_off ? 0 : header_size(r->version);
+@@ reftable/reader.c: static int reader_seek_indexed(struct reftable_reader *r,
+ struct reftable_record *rec)
+ {
+ struct reftable_index_record want_index = { .last_key = STRBUF_INIT };
+- struct reftable_record want_index_rec = { 0 };
++ struct reftable_record want_index_rec = { NULL };
+ struct reftable_index_record index_result = { .last_key = STRBUF_INIT };
+- struct reftable_record index_result_rec = { 0 };
++ struct reftable_record index_result_rec = { NULL };
+ struct table_iter index_iter = TABLE_ITER_INIT;
+ struct table_iter next = TABLE_ITER_INIT;
+ int err = 0;
+@@ reftable/reader.c: int reftable_reader_seek_ref(struct reftable_reader *r,
+ struct reftable_ref_record ref = {
+ .refname = (char *)name,
+ };
+- struct reftable_record rec = { 0 };
++ struct reftable_record rec = { NULL };
+ reftable_record_from_ref(&rec, &ref);
+ return reader_seek(r, it, &rec);
+ }
+@@ reftable/reader.c: int reftable_reader_seek_log_at(struct reftable_reader *r,
+ .refname = (char *)name,
+ .update_index = update_index,
+ };
+- struct reftable_record rec = { 0 };
++ struct reftable_record rec = { NULL };
+ reftable_record_from_log(&rec, &log);
+ return reader_seek(r, it, &rec);
+ }
+@@ reftable/reader.c: static int reftable_reader_refs_for_indexed(struct reftable_reader *r,
+ .hash_prefix = oid,
+ .hash_prefix_len = r->object_id_len,
+ };
+- struct reftable_record want_rec = { 0 };
+- struct reftable_iterator oit = { 0 };
+- struct reftable_obj_record got = { 0 };
+- struct reftable_record got_rec = { 0 };
++ struct reftable_record want_rec = { NULL };
++ struct reftable_iterator oit = { NULL };
++ struct reftable_obj_record got = { NULL };
++ struct reftable_record got_rec = { NULL };
+ int err = 0;
+ struct indexed_table_ref_iter *itr = NULL;
+
+
## t/helper/test-reftable.c ##
@@ t/helper/test-reftable.c: int cmd__reftable(int argc, const char **argv)
tree_test_main(argc, argv);
Signature · 1 line
gitgitgadget
PATCH v2 1/13 reftable: add LICENSE
From: Han-Wen Nienhuys <[email protected]>
TODO: relicense?
--- reftable/LICENSE | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 reftable/LICENSE
reftable/LICENSE new file +31 -0
@@ -0,0 +1,31 @@ +BSD License + +Copyright (c) 2020, Google LLC +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +* Neither the name of Google LLC nor the names of its contributors may +be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Signature · 1 line
gitgitgadget
Re: [PATCH v2 01/13] reftable: add LICENSE
Hi,
Quoted 1 line · Han-Wen Nienhuys
TODO: relicense?
This line presumably doesn't want to be part of permanent history. :)
Instead, it can say a little about rationale. For example, does this make the code easier for other projects like libgit2 to share?
Quoted 5 lines
Signed-off-by Han-Wen Nienhuys <[email protected]>--- reftable/LICENSE | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 reftable/LICENSE
Should there be a reftable/README as well?
[...]
reftable/LICENSE line 4
Quoted 7 lines
reftable/LICENSE new file +4
-0@@ -0,0 +1,31 @@ +BSD License + +Copyright (c) 2020, Google LLC +All rights reserved.
nit: Google seems to prefer a slightly more spartan form of this license text:
https://opensource.google/docs/releasing/licenses/#bsd-license-file
Nits aside, except for the TODO line, this patch looks reasonable to me.
Thanks,
Jonathan
PATCH v2 2/13 reftable: define the public API
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- reftable/reftable.h | 585 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 585 insertions(+) create mode 100644 reftable/reftable.h
reftable/reftable.h new file +585 -0
@@ -0,0 +1,585 @@ +/* +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 REFTABLE_H +#define REFTABLE_H + +#include <stdint.h> +#include <stddef.h> + +void reftable_set_alloc (void *(*malloc)(size_t ), +void *(*realloc)(void *,size_t ),void (*free)(void *)); + +/**************************************************************** +Basic data types + +Reftables store the state of each ref in struct reftable_ref_record, and they +store a sequence of reflog updates in struct reftable_log_record. +****************************************************************/ + +/* reftable_ref_record holds a ref database entry target_value */ +struct reftable_ref_record { +char *refname;/* Name of the ref, malloced. */ +uint64_t update_index;/* Logical timestamp at which this value is +written */ +uint8_t *value;/* SHA1, or NULL. malloced. */ +uint8_t *target_value;/* peeled annotated tag, or NULL. malloced. */ +char *target;/* symref, or NULL. malloced. */ +}; + +/* returns whether 'ref' represents a deletion */ +int reftable_ref_record_is_deletion (const struct reftable_ref_record *ref); + +/* prints a reftable_ref_record onto stdout */ +void reftable_ref_record_print (struct reftable_ref_record *ref, +uint32_t hash_id); + +/* frees and nulls all pointer values. */ +void reftable_ref_record_clear (struct reftable_ref_record *ref); + +/* returns whether two reftable_ref_records are the same */ +int reftable_ref_record_equal (struct reftable_ref_record *a, +struct reftable_ref_record *b,int hash_size); + +/* reftable_log_record holds a reflog entry */ +struct reftable_log_record { +char *refname; +uint64_t update_index;/* logical timestamp of a transactional update. +*/ +uint8_t *new_hash; +uint8_t *old_hash; +char *name; +char *email; +uint64_t time; +int16_t tz_offset; +char *message; +}; + +/* returns whether 'ref' represents the deletion of a log record. */ +int reftable_log_record_is_deletion (const struct reftable_log_record *log); + +/* frees and nulls all pointer values. */ +void reftable_log_record_clear (struct reftable_log_record *log); + +/* returns whether two records are equal. */ +int reftable_log_record_equal (struct reftable_log_record *a, +struct reftable_log_record *b,int hash_size); + +/* dumps a reftable_log_record on stdout, for debugging/testing. */ +void reftable_log_record_print (struct reftable_log_record *log, +uint32_t hash_id); + +/**************************************************************** +Error handling + +Error are signaled with negative integer return values. 0 means success. +****************************************************************/ + +/* different types of errors */ +enum reftable_error { +/* Unexpected file system behavior */ +REFTABLE_IO_ERROR =-2 , + +/* Format inconsistency on reading data +*/ +REFTABLE_FORMAT_ERROR =-3 , + +/* File does not exist. Returned from block_source_from_file(), because +it needs special handling in stack. +*/ +REFTABLE_NOT_EXIST_ERROR =-4 , + +/* Trying to write out-of-date data. */ +REFTABLE_LOCK_ERROR =-5 , + +/* Misuse of the API: +- on writing a record with NULL refname. +- on writing a reftable_ref_record outside the table limits +- on writing a ref or log record before the stack's next_update_index +- on writing a log record with multiline message with +exact_log_message unset +- on reading a reftable_ref_record from log iterator, or vice versa. +*/ +REFTABLE_API_ERROR =-6 , + +/* Decompression error */ +REFTABLE_ZLIB_ERROR =-7 , + +/* Wrote a table without blocks. */ +REFTABLE_EMPTY_TABLE_ERROR =-8 , + +/* Dir/file conflict. */ +REFTABLE_NAME_CONFLICT =-9 , + +/* Illegal ref name. */ +REFTABLE_REFNAME_ERROR =-10 , +}; + +/* convert the numeric error code to a string. The string should not be +* deallocated. */ +const char *reftable_error_str (int err); + +/* +* Convert the numeric error code to an equivalent errno code. +*/ +int reftable_error_to_errno (int err); + +/**************************************************************** +Writing + +Writing single reftables +****************************************************************/ + +/* reftable_write_options sets options for writing a single reftable. */ +struct reftable_write_options { +/* boolean: do not pad out blocks to block size. */ +int unpadded; + +/* the blocksize. Should be less than 2^24. */ +uint32_t block_size; + +/* boolean: do not generate a SHA1 => ref index. */ +int skip_index_objects; + +/* how often to write complete keys in each block. */ +int restart_interval; + +/* 4-byte identifier ("sha1", "s256") of the hash. +* Defaults to SHA1 if unset +*/ +uint32_t hash_id; + +/* boolean: do not check ref names for validity or dir/file conflicts. +*/ +int skip_name_check; + +/* boolean: copy log messages exactly. If unset, check that the message +* is a single line, and add '\n' if missing. +*/ +int exact_log_message; +}; + +/* reftable_block_stats holds statistics for a single block type */ +struct reftable_block_stats { +/* total number of entries written */ +int entries; +/* total number of key restarts */ +int restarts; +/* total number of blocks */ +int blocks; +/* total number of index blocks */ +int index_blocks; +/* depth of the index */ +int max_index_level; + +/* offset of the first block for this type */ +uint64_t offset; +/* offset of the top level index block for this type, or 0 if not +* present */ +uint64_t index_offset; +}; + +/* stats holds overall statistics for a single reftable */ +struct reftable_stats { +/* total number of blocks written. */ +int blocks; +/* stats for ref data */ +struct reftable_block_stats ref_stats; +/* stats for the SHA1 to ref map. */ +struct reftable_block_stats obj_stats; +/* stats for index blocks */ +struct reftable_block_stats idx_stats; +/* stats for log blocks */ +struct reftable_block_stats log_stats; + +/* disambiguation length of shortened object IDs. */ +int object_id_len; +}; + +/* reftable_new_writer creates a new writer */ +struct reftable_writer * +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,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_stack_next_update_index(), or REFTABLE_API_ERROR is returned. + +For transactional updates, typically min==max. When converting an existing +ref database into a single reftable, this would be a range of update-index +timestamps. +*/ +void reftable_writer_set_limits (struct reftable_writer *w,uint64_t min, +uint64_t max); + +/* adds a reftable_ref_record. Must be called in ascending +order. The update_index must be within the limits set by +reftable_writer_set_limits(), or REFTABLE_API_ERROR is returned. + +It is an error to write a ref record after a log record. +*/ +int reftable_writer_add_ref (struct reftable_writer *w, +struct reftable_ref_record *ref); + +/* Convenience function to add multiple refs. Will sort the refs by +name before adding. */ +int reftable_writer_add_refs (struct reftable_writer *w, +struct reftable_ref_record *refs,int n); + +/* adds a reftable_log_record. Must be called in ascending order (with more +recent log entries first.) +*/ +int reftable_writer_add_log (struct reftable_writer *w, +struct reftable_log_record *log); + +/* Convenience function to add multiple logs. Will sort the records by +key before adding. */ +int reftable_writer_add_logs (struct reftable_writer *w, +struct reftable_log_record *logs,int n); + +/* reftable_writer_close finalizes the reftable. The writer is retained so +* statistics can be inspected. */ +int reftable_writer_close (struct reftable_writer *w); + +/* writer_stats returns the statistics on the reftable being written. + +This struct becomes invalid when the writer is freed. +*/ +const struct reftable_stats *writer_stats (struct reftable_writer *w); + +/* reftable_writer_free deallocates memory for the writer */ +void reftable_writer_free (struct reftable_writer *w); + +/**************************************************************** +* ITERATING +****************************************************************/ + +/* iterator is the generic interface for walking over data stored in a +reftable. It is generally passed around by value. +*/ +struct reftable_iterator { +struct reftable_iterator_vtable *ops; +void *iter_arg; +}; + +/* reads the next reftable_ref_record. Returns < 0 for error, 0 for OK and > 0: +end of iteration. +*/ +int reftable_iterator_next_ref (struct reftable_iterator *it, +struct reftable_ref_record *ref); + +/* reads the next reftable_log_record. Returns < 0 for error, 0 for OK and > 0: +end of iteration. +*/ +int reftable_iterator_next_log (struct reftable_iterator *it, +struct reftable_log_record *log); + +/* releases resources associated with an iterator. */ +void reftable_iterator_destroy (struct reftable_iterator *it); + +/**************************************************************** +Reading single tables + +The follow routines are for reading single files. For an application-level +interface, skip ahead to struct reftable_merged_table and struct +reftable_stack. +****************************************************************/ + +/* block_source is a generic wrapper for a seekable readable file. +It is generally passed around by value. +*/ +struct reftable_block_source { +struct reftable_block_source_vtable *ops; +void *arg; +}; + +/* a contiguous segment of bytes. It keeps track of its generating block_source +so it can return itself into the pool. +*/ +struct reftable_block { +uint8_t *data; +int len; +struct reftable_block_source source; +}; + +/* block_source_vtable are the operations that make up block_source */ +struct reftable_block_source_vtable { +/* returns the size of a block source */ +uint64_t (*size)(void *source); + +/* reads a segment from the block source. It is an error to read +beyond the end of the block */ +int (*read_block)(void *source,struct reftable_block *dest, +uint64_t off,uint32_t size); +/* mark the block as read; may return the data back to malloc */ +void (*return_block)(void *source,struct reftable_block *blockp); + +/* release all resources associated with the block source */ +void (*close)(void *source); +}; + +/* opens a file on the file system as a block_source */ +int reftable_block_source_from_file (struct reftable_block_source *block_src, +const char *name); + +/* The reader struct is a handle to an open reftable file. */ +struct reftable_reader ; + +/* reftable_new_reader opens a reftable for reading. If successful, returns 0 +* code and sets pp. The name is used for creating a stack. Typically, it is the +* basename of the file. The block source `src` is owned by the reader, and is +* closed on calling reftable_reader_destroy(). +*/ +int reftable_new_reader (struct reftable_reader **pp, +struct reftable_block_source *src,const char *name); + +/* reftable_reader_seek_ref returns an iterator where 'name' would be inserted +in the table. To seek to the start of the table, use name = "". + +example: + +struct reftable_reader *r = NULL; +int err = reftable_new_reader(&r, &src, "filename"); +if (err < 0) { ... } +struct reftable_iterator it = {0}; +err = reftable_reader_seek_ref(r, &it, "refs/heads/master"); +if (err < 0) { ... } +struct reftable_ref_record ref = {0}; +while (1) { +err = reftable_iterator_next_ref(&it, &ref); +if (err > 0) { +break; +} +if (err < 0) { +..error handling.. +} +..found.. +} +reftable_iterator_destroy(&it); +reftable_ref_record_clear(&ref); +*/ +int reftable_reader_seek_ref (struct reftable_reader *r, +struct reftable_iterator *it,const char *name); + +/* returns the hash ID used in this table. */ +uint32_t reftable_reader_hash_id (struct reftable_reader *r); + +/* seek to logs for the given name, older than update_index. To seek to the +start of the table, use name = "". +*/ +int reftable_reader_seek_log_at (struct reftable_reader *r, +struct reftable_iterator *it,const char *name, +uint64_t update_index); + +/* seek to newest log entry for given name. */ +int reftable_reader_seek_log (struct reftable_reader *r, +struct reftable_iterator *it,const char *name); + +/* closes and deallocates a reader. */ +void reftable_reader_free (struct reftable_reader *); + +/* return an iterator for the refs pointing to `oid`. */ +int reftable_reader_refs_for (struct reftable_reader *r, +struct reftable_iterator *it,uint8_t *oid); + +/* return the max_update_index for a table */ +uint64_t reftable_reader_max_update_index (struct reftable_reader *r); + +/* return the min_update_index for a table */ +uint64_t reftable_reader_min_update_index (struct reftable_reader *r); + +/**************************************************************** +Merged tables + +A ref database kept in a sequence of table files. The merged_table presents a +unified view to reading (seeking, iterating) a sequence of immutable tables. +****************************************************************/ + +/* A merged table is implements seeking/iterating over a stack of tables. */ +struct reftable_merged_table ; + +/* A generic reftable; see below. */ +struct reftable_table ; + +/* reftable_new_merged_table creates a new merged table. It takes ownership of +the stack array. +*/ +int reftable_new_merged_table (struct reftable_merged_table **dest, +struct reftable_table *stack,int n, +uint32_t hash_id); + +/* returns an iterator positioned just before 'name' */ +int reftable_merged_table_seek_ref (struct reftable_merged_table *mt, +struct reftable_iterator *it, +const char *name); + +/* returns an iterator for log entry, at given update_index */ +int reftable_merged_table_seek_log_at (struct reftable_merged_table *mt, +struct reftable_iterator *it, +const char *name,uint64_t update_index); + +/* like reftable_merged_table_seek_log_at but look for the newest entry. */ +int reftable_merged_table_seek_log (struct reftable_merged_table *mt, +struct reftable_iterator *it, +const char *name); + +/* returns the max update_index covered by this merged table. */ +uint64_t +reftable_merged_table_max_update_index (struct reftable_merged_table *mt); + +/* returns the min update_index covered by this merged table. */ +uint64_t +reftable_merged_table_min_update_index (struct reftable_merged_table *mt); + +/* releases memory for the merged_table */ +void reftable_merged_table_free (struct reftable_merged_table *m); + +/* return the hash ID of the merged table. */ +uint32_t reftable_merged_table_hash_id (struct reftable_merged_table *m); + +/**************************************************************** +Generic tables + +A unified API for reading tables, either merged tables, or single readers. +****************************************************************/ + +struct reftable_table { +struct reftable_table_vtable *ops; +void *table_arg; +}; + +int reftable_table_seek_ref (struct reftable_table *tab, +struct reftable_iterator *it,const char *name); + +void reftable_table_from_reader (struct reftable_table *tab, +struct reftable_reader *reader); + +/* returns the hash ID from a generic reftable_table */ +uint32_t reftable_table_hash_id (struct reftable_table *tab); + +/* create a generic table from reftable_merged_table */ +void reftable_table_from_merged_table (struct reftable_table *tab, +struct reftable_merged_table *table); + +/* returns the max update_index covered by this table. */ +uint64_t reftable_table_max_update_index (struct reftable_table *tab); + +/* returns the min update_index covered by this table. */ +uint64_t reftable_table_min_update_index (struct reftable_table *tab); + +/* convenience function to read a single ref. Returns < 0 for error, 0 +for success, and 1 if ref not found. */ +int reftable_table_read_ref (struct reftable_table *tab,const char *name, +struct reftable_ref_record *ref); + +/**************************************************************** +Mutable ref database + +The stack presents an interface to a mutable sequence of reftables. +****************************************************************/ + +/* a stack is a stack of reftables, which can be mutated by pushing a table to +* the top of the stack */ +struct reftable_stack ; + +/* open a new reftable stack. The tables along with the table list will be +stored in 'dir'. Typically, this should be .git/reftables. +*/ +int reftable_new_stack (struct reftable_stack **dest,const char *dir, +struct reftable_write_options config); + +/* returns the update_index at which a next table should be written. */ +uint64_t reftable_stack_next_update_index (struct reftable_stack *st); + +/* holds a transaction to add tables at the top of a stack. */ +struct reftable_addition ; + +/* +returns a new transaction to add reftables to the given stack. As a side +effect, the ref database is locked. +*/ +int reftable_stack_new_addition (struct reftable_addition **dest, +struct reftable_stack *st); + +/* Adds a reftable to transaction. */ +int reftable_addition_add (struct reftable_addition *add, +int (*write_table)(struct reftable_writer *wr, +void *arg), +void *arg); + +/* Commits the transaction, releasing the lock. */ +int reftable_addition_commit (struct reftable_addition *add); + +/* Release all non-committed data from the transaction, and deallocate the +transaction. Releases the lock if held. */ +void reftable_addition_destroy (struct reftable_addition *add); + +/* add a new table to the stack. The write_table function must call +reftable_writer_set_limits, add refs and return an error value. */ +int reftable_stack_add (struct reftable_stack *st, +int (*write_table)(struct reftable_writer *wr, +void *write_arg), +void *write_arg); + +/* returns the merged_table for seeking. This table is valid until the +next write or reload, and should not be closed or deleted. +*/ +struct reftable_merged_table * +reftable_stack_merged_table (struct reftable_stack *st); + +/* frees all resources associated with the stack. */ +void reftable_stack_destroy (struct reftable_stack *st); + +/* Reloads the stack if necessary. This is very cheap to run if the stack was up +* to date */ +int reftable_stack_reload (struct reftable_stack *st); + +/* Policy for expiring reflog entries. */ +struct reftable_log_expiry_config { +/* Drop entries older than this timestamp */ +uint64_t time; + +/* Drop older entries */ +uint64_t min_update_index; +}; + +/* compacts all reftables into a giant table. Expire reflog entries if config is +* non-NULL */ +int reftable_stack_compact_all (struct reftable_stack *st, +struct reftable_log_expiry_config *config); + +/* heuristically compact unbalanced table stack. */ +int reftable_stack_auto_compact (struct reftable_stack *st); + +/* convenience function to read a single ref. Returns < 0 for error, 0 +for success, and 1 if ref not found. */ +int reftable_stack_read_ref (struct reftable_stack *st,const char *refname, +struct reftable_ref_record *ref); + +/* convenience function to read a single log. Returns < 0 for error, 0 +for success, and 1 if ref not found. */ +int reftable_stack_read_log (struct reftable_stack *st,const char *refname, +struct reftable_log_record *log); + +/* statistics on past compactions. */ +struct reftable_compaction_stats { +uint64_t bytes;/* total number of bytes written */ +uint64_t entries_written;/* total number of entries written, including +failures. */ +int attempts;/* how often we tried to compact */ +int failures;/* failures happen on concurrent updates */ +}; + +/* return statistics for compaction up till now. */ +struct reftable_compaction_stats * +reftable_stack_compaction_stats (struct reftable_stack *st); + +#endif
Signature · 1 line
gitgitgadget
Re: [PATCH v2 02/13] reftable: define the public API
Hi,
Quoted 3 lines · Han-Wen Nienhuys
reftable/reftable.h | 585 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 585 insertions(+) create mode 100644 reftable/reftable.h
Adding a header in a separate patch from the implementation doesn't match the usual practice. Can we add declarations in the same patch as the functions being declared instead?
We could still introduce the header early in its own patch if we want, but it would be a skeleton of a header that gets filled out by later patches.
[...]
Quoted 3 lines
reftable/reftable.h new file +0
-0@@ -0,0 +1,585 @@
[...]
reftable/reftable.h line 13
Quoted 5 lines
reftable/reftable.h new file +5
-0+#ifndef REFTABLE_H +#define REFTABLE_H + +#include <stdint.h> +#include <stddef.h>
Git tends to prefer to rely on the caller to have #include-ed git-compat-util.h instead. That helps avoid having to worry about portability gotchas around what feature test macros need to have been set before the first #include of a standard header.
reftable/reftable.h line 23
Quoted 10 lines
reftable/reftable.h new file +10
-0+ +void reftable_set_alloc (void *(*malloc)(size_t ), +void *(*realloc)(void *,size_t ),void (*free)(void *)); + +/**************************************************************** +Basic data types + +Reftables store the state of each ref in struct reftable_ref_record, and they +store a sequence of reflog updates in struct reftable_log_record. +****************************************************************/
nit: Git style for comments would be
/*
* Basic data types
*
* Reftables store the state of each [etc]
*/
In other words:
- no lines of stars
- "/*" with no text after it as first line
- " *" leading subsequent lines
- " */" as last line
reftable/reftable.h line 29
Quoted 6 lines
reftable/reftable.h new file +6
-0+ +/* reftable_ref_record holds a ref database entry target_value */ +struct reftable_ref_record { +char *refname;/* Name of the ref, malloced. */ +uint64_t update_index;/* Logical timestamp at which this value is +written */
Likewise: Git doesn't have this style of multi-line comment, but this could be e.g. a single-line comment before
/* logical timestamp at which this value was written */ uint64_t update_index;
reftable/reftable.h line 30
Quoted 1 line
reftable/reftable.h new file +1
-0+uint8_t *value;/* SHA1, or NULL. malloced. */
What does "NULL. malloced" mean? Is the point that the reftable_ref_record owns the memory? (We don't do as good a job of documenting that as we should. Often a non-const pointer is owned, but in this example it's probably better to be explicit that this struct owns the memory pointed to by all its fields in the comment describing the struct as a whole.)
It would be more idiomatic for this to be a 'struct object_id' inline in the reftable_ref_record.
reftable/reftable.h line 36
Quoted 6 lines
reftable/reftable.h new file +6
-0+uint8_t *target_value;/* peeled annotated tag, or NULL. malloced. */ +char *target;/* symref, or NULL. malloced. */ +}; + +/* returns whether 'ref' represents a deletion */ +int reftable_ref_record_is_deletion (const struct reftable_ref_record *ref);
Nice, this looks pleasant to use.
reftable/reftable.h line 40
Quoted 4 lines
reftable/reftable.h new file +4
-0+ +/* prints a reftable_ref_record onto stdout */ +void reftable_ref_record_print (struct reftable_ref_record *ref, +uint32_t hash_id);
Probably worth being explicit about the purpose: is this for debugging?
What is a hash_id?
reftable/reftable.h line 50
Quoted 10 lines
reftable/reftable.h new file +10
-0+ +/* frees and nulls all pointer values. */ +void reftable_ref_record_clear (struct reftable_ref_record *ref); + +/* returns whether two reftable_ref_records are the same */ +int reftable_ref_record_equal (struct reftable_ref_record *a, +struct reftable_ref_record *b,int hash_size); + +/* reftable_log_record holds a reflog entry */ +struct reftable_log_record {
Should some of this documentation mention the reftable spec? E.g. an opening comment at the beginning of the header. That might help avoid having to duplicate too much explanation when terminology matches.
reftable/reftable.h line 53
Quoted 3 lines
reftable/reftable.h new file +3
-0+char *refname; +uint64_t update_index;/* logical timestamp of a transactional update. +*/
multiline comment (I won't mention the rest).
reftable/reftable.h line 63
Quoted 10 lines
reftable/reftable.h new file +10
-0+uint8_t *new_hash; +uint8_t *old_hash; +char *name; +char *email; +uint64_t time; +int16_t tz_offset; +char *message; +}; + +/* returns whether 'ref' represents the deletion of a log record. */
These are a bit subtle --- should the comment include a little more explanation (e.g. about how this is mostly for tools like "git stash")?
Quoted 11 lines
reftable/reftable.h new file +11
-0+int reftable_log_record_is_deletion (const struct reftable_log_record *log); + +/* frees and nulls all pointer values. */ +void reftable_log_record_clear (struct reftable_log_record *log); + +/* returns whether two records are equal. */ +int reftable_log_record_equal (struct reftable_log_record *a, +struct reftable_log_record *b,int hash_size); + +/* dumps a reftable_log_record on stdout, for debugging/testing. */ +void reftable_log_record_print(struct reftable_log_record *log,
reftable/reftable.h line 84
Quoted 10 lines
reftable/reftable.h new file +10
-0+ uint32_t hash_id); + +/**************************************************************** +Error handling + +Error are signaled with negative integer return values. 0 means success. +****************************************************************/ + +/* different types of errors */ +enum reftable_error {
Reminds me of zlib and liblzma. I like it.
Quoted 14 lines
reftable/reftable.h new file +14
-0+/* Unexpected file system behavior */ +REFTABLE_IO_ERROR =-2 , + +/* Format inconsistency on reading data +*/ +REFTABLE_FORMAT_ERROR =-3 , + +/* File does not exist. Returned from block_source_from_file(), because +it needs special handling in stack. +*/ +REFTABLE_NOT_EXIST_ERROR =-4 , + +/* Trying to write out-of-date data. */ + REFTABLE_LOCK_ERROR = -5,
reftable/reftable.h line 108
Quoted 10 lines
reftable/reftable.h new file +10
-0+ +/* Misuse of the API: +- on writing a record with NULL refname. +- on writing a reftable_ref_record outside the table limits +- on writing a ref or log record before the stack's next_update_index +- on writing a log record with multiline message with +exact_log_message unset +- on reading a reftable_ref_record from log iterator, or vice versa. +*/ + REFTABLE_API_ERROR = -6,
Should these call BUG()? Or is it useful for some callers to be able to recover from these errors?
reftable/reftable.h line 116
Quoted 8 lines
reftable/reftable.h new file +8
-0+ +/* Decompression error */ +REFTABLE_ZLIB_ERROR =-7 , + +/* Wrote a table without blocks. */ + REFTABLE_EMPTY_TABLE_ERROR = -8, + +/* Dir/file conflict. */
This is when adding a ref?
reftable/reftable.h line 125
Quoted 9 lines
reftable/reftable.h new file +9
-0+REFTABLE_NAME_CONFLICT =-9 , + +/* Illegal ref name. */ + REFTABLE_REFNAME_ERROR = -10, +}; + +/* convert the numeric error code to a string. The string should not be +* deallocated. */ +const char *reftable_error_str (int err);
In the ZLIB_ERROR case this would produce a frustratingly vague message. I think I don't mind, but it might be worth a NEEDSWORK comment to revisit in the future.
reftable/reftable.h line 130
Quoted 5 lines
reftable/reftable.h new file +5
-0+ +/* +* Convert the numeric error code to an equivalent errno code. +*/ +int reftable_error_to_errno (int err);
What is the intended use of this function?
[...]
reftable/reftable.h line 168
Quoted 2 lines
reftable/reftable.h new file +2
-0+/* reftable_block_stats holds statistics for a single block type */ +struct reftable_block_stats {
Are these for use in tracing, debugging, or some other purpose?
[...]
reftable/reftable.h line 207
Quoted 4 lines
reftable/reftable.h new file +4
-0+/* reftable_new_writer creates a new writer */ +struct reftable_writer * +reftable_new_writer (int (*writer_func)(void *,const void *,size_t ), +void *writer_arg,struct reftable_write_options *opts);
What is a writer? What is a writer_func? Is this a handle that allows me to determine where I write my reftable? (Sounds reasonable but the comment could be more explicit.)
[...]
reftable/reftable.h line 210
Quoted 3 lines
reftable/reftable.h new file +3
-0+ +/* write to a file descriptor. fdp should be an int* pointing to the fd. */ +int reftable_fd_write (void *fdp,const void *data,size_t size);
What does this do for interrupted writes?
reftable/reftable.h line 221
Quoted 11 lines
reftable/reftable.h new file +11
-0+ +/* 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_stack_next_update_index(), or REFTABLE_API_ERROR is returned. + +For transactional updates, typically min==max. When converting an existing +ref database into a single reftable, this would be a range of update-index +timestamps. +*/ +void reftable_writer_set_limits (struct reftable_writer *w,uint64_t min, +uint64_t max);
What happens if I write to a reftable_writer without setting limits first?
reftable/reftable.h line 230
Quoted 9 lines
reftable/reftable.h new file +9
-0+ +/* adds a reftable_ref_record. Must be called in ascending +order. The update_index must be within the limits set by +reftable_writer_set_limits(), or REFTABLE_API_ERROR is returned. + +It is an error to write a ref record after a log record. +*/ +int reftable_writer_add_ref (struct reftable_writer *w, +struct reftable_ref_record *ref);
optional: since it's called a writer, _write_ref() feels like a slightly more natural verb (likewise for the others).
[...]
reftable/reftable.h line 241
Quoted 5 lines
reftable/reftable.h new file +5
-0+/* adds a reftable_log_record. Must be called in ascending order (with more +recent log entries first.) +*/ +int reftable_writer_add_log (struct reftable_writer *w, +struct reftable_log_record *log);
If I want to write multiple log records with the same update_index to represent a multi-ref transaction, do I have to do anything careful?
reftable/reftable.h line 250
Quoted 9 lines
reftable/reftable.h new file +9
-0+ +/* Convenience function to add multiple logs. Will sort the records by +key before adding. */ +int reftable_writer_add_logs (struct reftable_writer *w, +struct reftable_log_record *logs,int n); + +/* reftable_writer_close finalizes the reftable. The writer is retained so +* statistics can be inspected. */ +int reftable_writer_close (struct reftable_writer *w);
Nice.
reftable/reftable.h line 256
Quoted 6 lines
reftable/reftable.h new file +6
-0+ +/* writer_stats returns the statistics on the reftable being written. + +This struct becomes invalid when the writer is freed. +*/ +const struct reftable_stats *writer_stats (struct reftable_writer *w);
Also nice.
[...]
reftable/reftable.h line 271
Quoted 7 lines
reftable/reftable.h new file +7
-0+/* iterator is the generic interface for walking over data stored in a +reftable. It is generally passed around by value. +*/ +struct reftable_iterator { +struct reftable_iterator_vtable *ops; +void *iter_arg; +};
Even small structs like this one tend not to be passed by value in the Git codebase. The only counterexample I found with a quick search (builtin/stash.c::save_untracked_files) appears to be a typo.
[...]
reftable/reftable.h line 311
Quoted 8 lines
reftable/reftable.h new file +8
-0+/* a contiguous segment of bytes. It keeps track of its generating block_source +so it can return itself into the pool. +*/ +struct reftable_block { +uint8_t *data; +int len; +struct reftable_block_source source; +};
Is this something that could use mem-pool.h?
Quoted 5 lines
reftable/reftable.h new file +5
-0+/* block_source_vtable are the operations that make up block_source */ +struct reftable_block_source_vtable { +/* returns the size of a block source */ +uint64_t (*size)(void *source); +
reftable/reftable.h line 327
Quoted 10 lines
reftable/reftable.h new file +10
-0+/* reads a segment from the block source. It is an error to read +beyond the end of the block */ +int (*read_block)(void *source,struct reftable_block *dest, +uint64_t off,uint32_t size); +/* mark the block as read; may return the data back to malloc */ +void (*return_block)(void *source,struct reftable_block *blockp); + +/* release all resources associated with the block source */ +void (*close)(void *source); +};
This is abstracting file input? I wonder if some name emphasizing the 'read' operation like block_reader might be clearer.
Do I pass in the 'struct block_source *' as the source arg? If so, why are these declared as void *?
Is the reason this manages the buffer instead of requiring a caller-supplied buffer to support zero-copy?
[...]
reftable/reftable.h line 334
Quoted 7 lines
reftable/reftable.h new file +7
-0+ +/* opens a file on the file system as a block_source */ +int reftable_block_source_from_file (struct reftable_block_source *block_src, +const char *name); + +/* The reader struct is a handle to an open reftable file. */ +struct reftable_reader ;
... ah, this is why the underlying file handle isn't called a reader. Hmm.
[...]
reftable/reftable.h line 345
Quoted 2 lines
reftable/reftable.h new file +2
-0+/* reftable_reader_seek_ref returns an iterator where 'name' would be inserted +in the table. To seek to the start of the table, use name = "".
nit: s/iterator where/iterator pointing where/
Quoted 12 lines
reftable/reftable.h new file +12
-0+ + example: + +struct reftable_reader *r =NULL ; +int err =reftable_new_reader (&r, &src,"filename" ); + if (err < 0) { ... } +struct reftable_iterator it = {0 }; + err =reftable_reader_seek_ref (r, &it,"refs/heads/master" ); + if (err < 0) { ... } +struct reftable_ref_record ref = {0 }; +while (1 ) { + err =reftable_iterator_next_ref (&it, &ref);
reftable/reftable.h line 367
Quoted 10 lines
reftable/reftable.h new file +10
-0+if (err >0 ) { +break ; + } +if (err <0 ) { + ..error handling.. + } + ..found.. + } +reftable_iterator_destroy (&it); +reftable_ref_record_clear (&ref);
Nice.
reftable/reftable.h line 373
Quoted 6 lines
+ */
reftable/reftable.h new file +5
-0+int reftable_reader_seek_ref (struct reftable_reader *r, +struct reftable_iterator *it,const char *name); + +/* returns the hash ID used in this table. */ +uint32_t reftable_reader_hash_id (struct reftable_reader *r);
What is a hash id?
[...]
Quoted 4 lines
+ Merged tables
+
+ A ref database kept in a sequence of table files. Themerged_tablepresents a
+ unified view to reading (seeking, iterating) a sequence of immutable tables.
Nice as well.
[...]
reftable/reftable.h line 436
Quoted 3 lines
reftable/reftable.h new file +3
-0+/* returns the max update_index covered by this merged table. */ +uint64_t +reftable_merged_table_max_update_index (struct reftable_merged_table *mt);
nit: return type should be on the same line as the function name
uint64_t reftable_merged_table_max_update_index( struct reftable_merged_table *mt);
[...]
Quoted 3 lines
+ Generic tables
+
+ A unified API for reading tables, either merged tables, or single readers.
Are there callers/helpers that don't know whether they want one or the other?
[...]
Quoted 2 lines
+ * a stack is a stack of reftables, which can be mutated by pushing a table to
+ * the top of the stack
Nice.
[...]
reftable/reftable.h line 503
Quoted 2 lines
reftable/reftable.h new file +2
-0+/* holds a transaction to add tables at the top of a stack. */ +struct reftable_addition ;
When would I add multiple tables at once to the top? Is this used during compacting, for example?
[...]
reftable/reftable.h line 560
Quoted 2 lines
reftable/reftable.h new file +2
-0+/* heuristically compact unbalanced table stack. */ +int reftable_stack_auto_compact (struct reftable_stack *st);
When do I call this? What are the semantics? (By this I don't mean what heuristic does it use, but what expectation should I have as a caller?)
reftable/reftable.h line 570
Quoted 4 lines
reftable/reftable.h new file +4
-0+/* convenience function to read a single log. Returns < 0 for error, 0 +for success, and 1 if ref not found. */ +int reftable_stack_read_log (struct reftable_stack *st,const char *refname, +struct reftable_log_record *log);
nit: single log *record*?
reftable/reftable.h line 583
Quoted 12 lines
reftable/reftable.h new file +12
-0+/* statistics on past compactions. */ +struct reftable_compaction_stats { +uint64_t bytes;/* total number of bytes written */ +uint64_t entries_written;/* total number of entries written, including +failures. */ +int attempts;/* how often we tried to compact */ +int failures;/* failures happen on concurrent updates */ +}; + +/* return statistics for compaction up till now. */ +struct reftable_compaction_stats * +reftable_stack_compaction_stats (struct reftable_stack *st);
All in all, I like this API. Thanks for putting it together thoughtfully.
The API builds up in layers (blocks, reftables, merged reftables, etc), suggesting a natural division for the patch series. I think the rest of the series follows that --- let's see.
Thanks,
Jonathan
Re: [PATCH v2 02/13] reftable: define the public API
Quoted 9 lines · Jonathan Nieder
Hi,
Quoted 3 lines · Han-Wen Nienhuys
reftable/reftable.h | 585 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 585 insertions(+) create mode 100644 reftable/reftable.h
Quoted 7 lines
Adding a header in a separate patch from the implementation doesn't match the usual practice. Can we add declarations in the same patch as the functions being declared instead?
We could still introduce the header early in its own patch if we want, but it would be a skeleton of a header that gets filled out by later patches.
To poke a little deeper into what I think Jonathan is trying to say:
When we reviewed this for review club, the general feeling was that A) this commit structure is still pretty difficult to review, and B) we want to make sure you aren't spending a ton of time reorganizing the code only to find that people still don't like the structure (as may have happened with the current iteration).
One point I think Jonathan is making is that each commit should stand
alone: compile, be tested, and do something succinct. Another point (or maybe the same point from a different angle) is that a series of commits should tell a progressive story. That is, something like
add infrastructure to support reftable library
reftable: read/write blocks
reftable: write files
reftable: read files
reftable: generate binary tree from file
...
is much more compelling (and easier to review) than
reftable: LICENSE
reftable: headers
reftable: tests
...
Towards the latter end of your series it seems like you started to take that approach; but some commits, like this one (reftable: define the public API) are not quite so. That is, this commit is hard to review without the context of the rest of the series: I read it, I say, "well WHY do we need these functions?", and then I cannot continue my review of this patch until I've completed my review of the rest of the series.
Of course, taking a completed project - like your initial reftable submission - and then chopping it up into a cute story of commits is a pain in the ***. Doing it twice - or more - is just aggravating. So I wonder whether we can bikeshed what story would look nice before you even pick up your 'git rebase -i'? Doing that bikeshedding here on list means that we also have a chance for someone to interrupt and say, "no, that organization doesn't make sense" - or even for someone to say "Emily, there is no need to reorganize these commits, go sit down" ;)
Quoted 6 lines
All in all, I like this API. Thanks for putting it together thoughtfully.
The API builds up in layers (blocks, reftables, merged reftables, etc), suggesting a natural division for the patch series. I think the rest of the series follows that --- let's see.
Or maybe what I said runs contrary to what Jonathan was actually saying. Please, correct me :)
- Emily
Re: [PATCH v2 02/13] reftable: define the public API
Quoted 7 lines · Emily Shaffer
Of course, taking a completed project - like your initial reftable submission - and then chopping it up into a cute story of commits is a pain in the ***. Doing it twice - or more - is just aggravating. So I wonder whether we can bikeshed what story would look nice before you even pick up your 'git rebase -i'? Doing that bikeshedding here on list means that we also have a chance for someone to interrupt and say, "no, that organization doesn't make sense" - or even for someone to say
It would be great if you could bikeshed about this while I'm on holiday :)
That said, I think it's worthwhile to first look at the commit that uses the library to implement a ref backend, because that will show better how the reftable library fits with the way that Git likes to consume its data.
Signature · 11 lines
Han-Wen Nienhuys - Google Munich I work 80%. Don't expect answers from me on Fridays. -- Google Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Re: [PATCH v2 02/13] reftable: define the public API
In reply to Emily ShafferQuoted 19 lines · Emily Shaffer
Quoted 9 lines · Jonathan Nieder
Hi,
Quoted 3 lines · Han-Wen Nienhuys
reftable/reftable.h | 585 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 585 insertions(+) create mode 100644 reftable/reftable.hQuoted 7 lines
Adding a header in a separate patch from the implementation doesn't match the usual practice. Can we add declarations in the same patch as the functions being declared instead?
We could still introduce the header early in its own patch if we want, but it would be a skeleton of a header that gets filled out by later patches.
Quoted 7 lines
To poke a little deeper into what I think Jonathan is trying to say:
Of course, taking a completed project - like your initial reftable submission - and then chopping it up into a cute story of commits is a pain in the ***. Doing it twice - or more - is just aggravating. So I wonder whether we can bikeshed what story would look nice before you even pick up your 'git rebase -i'? Doing that bikeshedding here on list
Looks like this didn't happen. I went with Jonathan's suggestion, and chopped the public API header in smaller bits. PTAL.
Signature · 11 lines
Han-Wen Nienhuys - Google Munich I work 80%. Don't expect answers from me on Fridays. -- Google Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Re: [PATCH v2 02/13] reftable: define the public API
In reply to Jonathan NiederQuoted 3 lines · Jonathan Nieder
Adding a header in a separate patch from the implementation doesn't match the usual practice. Can we add declarations in the same patch as the functions being declared instead?
We could, but it would considerably complicate work on this patch series, as the commit boundary then doesn't fall on file boundaries anymore. Would you be open to having multiple headers for the public interface? eg. reftable-record.h, reftable-reader.h etc. ?
Quoted 10 lines
reftable/reftable.h line 108
Quoted 9 lines
reftable/reftable.h new file +9
-0+/* Misuse of the API: +- on writing a record with NULL refname. +- on writing a reftable_ref_record outside the table limits +- on writing a ref or log record before the stack's next_update_index +- on writing a log record with multiline message with +exact_log_message unset +- on reading a reftable_ref_record from log iterator, or vice versa. +*/ + REFTABLE_API_ERROR = -6,
Quoted 2 lines
Should these call
BUG()? Or is it useful for some callers to be able to recover from these errors?
Since this was written as a standalone library, it's up to the caller to decide what should be done. It also simplifies testing: you can verify that incorrect API usage returns a certain error code, but a BUG() that prints an error or crashes the program is much harder to verify.
Quoted 3 lines
Quoted 1 line
+ /* Dir/file conflict. */
This is when adding a ref?
yes.
Quoted 7 lines
reftable/reftable.h line 130
Quoted 5 lines
reftable/reftable.h new file +5
-0+ +/* +* Convert the numeric error code to an equivalent errno code. +*/ +int reftable_error_to_errno (int err);What is the intended use of this function?
The read_raw_ref method in the ref backend API uses errno values as out-of-band communication mechanism.
Quoted 5 lines
Quoted 5 lines
reftable/reftable.h new file +5
-0+/* 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_stack_next_update_index(), or REFTABLE_API_ERROR is returned. + +For transactional updates, typically min==max. When converting an existing
Quoted 8 lines
reftable/reftable.h line 221
Quoted 5 lines
reftable/reftable.h new file +5
-0+ref database into a single reftable, this would be a range of update-index +timestamps. +*/ +void reftable_writer_set_limits (struct reftable_writer *w,uint64_t min, +uint64_t max);What happens if I write to a
reftable_writerwithout setting limits first?
you get an REFTABLE_API_ERROR.
Quoted 19 lines
Quoted 5 lines
reftable/reftable.h new file +5
-0+/* block_source_vtable are the operations that make up block_source */ +struct reftable_block_source_vtable { +/* returns the size of a block source */ +uint64_t (*size)(void *source); +reftable/reftable.h line 327
Quoted 10 lines
reftable/reftable.h new file +10
-0+/* reads a segment from the block source. It is an error to read +beyond the end of the block */ +int (*read_block)(void *source,struct reftable_block *dest, +uint64_t off,uint32_t size); +/* mark the block as read; may return the data back to malloc */ +void (*return_block)(void *source,struct reftable_block *blockp); + +/* release all resources associated with the block source */ +void (*close)(void *source); +};This is abstracting file input? I wonder if some name emphasizing the 'read' operation like
block_readermight be clearer.
Quoted 2 lines
Do I pass in the 'struct
block_source*' as the source arg? If so, why are these declared as void *?
you pass in block_source->arg. Should struct implementations of polymorphic types carry a pointer to their own vtable instead?
Quoted 2 lines
Is the reason this manages the buffer instead of requiring a caller-supplied buffer to support zero-copy?
Log blocks are compressed, so the caller doesn't know the correct size to supply. By letting the block source handle the management, we can swap out the block read from the file for a block managed by malloc on decompressing a log block.
Quoted 4 lines
reftable/reftable.h line 373
Quoted 2 lines
reftable/reftable.h new file +2
-0+/* returns the hash ID used in this table. */ +uint32_t reftable_reader_hash_id (struct reftable_reader *r);What is a hash id?
The hash identifier, eg. "sha1" for SHA1 and "s256" for SHA-256.
Quoted 7 lines
[...]
Quoted 3 lines
+ Generic tables
+
+ A unified API for reading tables, either merged tables, or single readers.Are there callers/helpers that don't know whether they want one or the other?
The setup with per-worktree refs means that there are two reftable stacks in a .git repo. In order to iterate over the entire ref space, you have to merge two merged tables, but the stack itslef merges a set of simple reftables.
Quoted 6 lines
[...]
reftable/reftable.h line 503
Quoted 2 lines
reftable/reftable.h new file +2
-0+/* holds a transaction to add tables at the top of a stack. */ +struct reftable_addition ;When would I add multiple tables at once to the top? Is this used during compacting, for example?
No. You'd usually add only one table, but at the same time, it seemed kind of arbitrary to allow adding only one table. FWIW, I added this because libgit2 wants to be able to lock a ref database: creating the reftable_addition institutes the lock.
Signature · 11 lines
Han-Wen Nienhuys - Google Munich I work 80%. Don't expect answers from me on Fridays. -- Google Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Re: [PATCH v2 02/13] reftable: define the public API
Quoted 10 lines · Han-Wen Nienhuys
Quoted 3 lines · Jonathan Nieder
Adding a header in a separate patch from the implementation doesn't match the usual practice. Can we add declarations in the same patch as the functions being declared instead?
We could, but it would considerably complicate work on this patch series, as the commit boundary then doesn't fall on file boundaries anymore. Would you be open to having multiple headers for the public interface? eg.
reftable-record.h,reftable-reader.hetc. ?
I'd be more than open to it: I think that would make for a clearer API.
For example, a caller like refs.c wouldn't have any need to use reftable-block.h because that's a lower-level detail that operates behind the scenes when operating on a stack of reftables. So I think this would be a good step toward addressing the feedback Jonathan gave about the set of functions exposed by the API being overwhelming.
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
reftable/reftable.h new file +9
-0+/* Misuse of the API: +- on writing a record with NULL refname. +- on writing a reftable_ref_record outside the table limits +- on writing a ref or log record before the stack's next_update_index +- on writing a log record with multiline message with +exact_log_message unset +- on reading a reftable_ref_record from log iterator, or vice versa. +*/ + REFTABLE_API_ERROR = -6,
Quoted 5 lines
Quoted 2 lines
Should these call
BUG()? Or is it useful for some callers to be able to recover from these errors?Since this was written as a standalone library, it's up to the caller to decide what should be done.
I'm not strongly opinionated about this, but just a quick note: this implies that the library would need to make sure it is producing a valid state in error cases.
Does the API documentation describe what state a handle is in after an error --- e.g., what operations are permitted after that?
Quoted 10 lines
Quoted 7 lines
reftable/reftable.h line 130
Quoted 5 lines
reftable/reftable.h new file +5
-0+ +/* +* Convert the numeric error code to an equivalent errno code. +*/ +int reftable_error_to_errno (int err);What is the intended use of this function?
The
read_raw_refmethod in the ref backend API uses errno values as out-of-band communication mechanism.
Could we change that? It sounds error-prone.
[...]
Quoted 14 lines
Quoted 5 lines
Quoted 5 lines
reftable/reftable.h new file +5
-0+/* 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_stack_next_update_index(), or REFTABLE_API_ERROR is returned. + +For transactional updates, typically min==max. When converting an existing Quoted 8 lines
reftable/reftable.h line 221
Quoted 5 lines
reftable/reftable.h new file +5
-0+ref database into a single reftable, this would be a range of update-index +timestamps. +*/ +void reftable_writer_set_limits (struct reftable_writer *w,uint64_t min, +uint64_t max);What happens if I write to a
reftable_writerwithout setting limits first?
Quoted 1 line
you get an
REFTABLE_API_ERROR.
(To be clear, with all of these questions, I am asking not only for my own curiosity but so that the documentation, naming, etc can be cleared up to help the next person who wonders the same thing.)
[...]
Quoted 5 lines
Quoted 2 lines
Do I pass in the 'struct
block_source*' as the source arg? If so, why are these declared as void *?you pass in
block_source->arg. Should struct implementations of polymorphic types carry a pointer to their own vtable instead?
I think a pointer to block_source would make it more self-explanatory, yes.
(Aside: this is a difference between how Go and e.g. Java and C++ implement polymorphism. In Go, an interface object contains a vtable and a pointer to the object. In Java and C++, the object contains a vptr.)
Quoted 7 lines
Quoted 2 lines
Is the reason this manages the buffer instead of requiring a caller-supplied buffer to support zero-copy?
Log blocks are compressed, so the caller doesn't know the correct size to supply. By letting the block source handle the management, we can swap out the block read from the file for a block managed by malloc on decompressing a log block.
Hm, my naive assumption would have been that we'd use different buffers for the compressed and uncompressed data.
Quoted 11 lines
Quoted 6 lines
Quoted 3 lines
+ Generic tables
+
+ A unified API for reading tables, either merged tables, or single readers.Are there callers/helpers that don't know whether they want one or the other?
The setup with per-worktree refs means that there are two reftable stacks in a .git repo. In order to iterate over the entire ref space, you have to merge two merged tables, but the stack itslef merges a set of simple reftables.
I see. That's subtle, so it seems worth documenting for the next person reading this documentation and wondering why.
Thanks,
Jonathan
Re: [PATCH v2 02/13] reftable: define the public API
Quoted 6 lines · Jonathan Nieder
Quoted 4 lines
We could, but it would considerably complicate work on this patch series, as the commit boundary then doesn't fall on file boundaries anymore. Would you be open to having multiple headers for the public interface? eg.
reftable-record.h,reftable-reader.hetc. ?I'd be more than open to it: I think that would make for a clearer API.
Done.
Quoted 9 lines
Quoted 5 lines
Quoted 2 lines
Should these call
BUG()? Or is it useful for some callers to be able to recover from these errors?Since this was written as a standalone library, it's up to the caller to decide what should be done.
I'm not strongly opinionated about this, but just a quick note: this implies that the library would need to make sure it is producing a valid state in error cases.
This is what it already does. I tried to clarify this in some places.
Quoted 2 lines
Does the API documentation describe what state a handle is in after an error --- e.g., what operations are permitted after that?
API_ERROR should not result in state changes.
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
Quoted 1 line
+int
reftable_error_to_errno(int err);What is the intended use of this function?
The
read_raw_refmethod in the ref backend API uses errno values as out-of-band communication mechanism.Could we change that? It sounds error-prone.
I moved it into refs/reftable-backend.c; I'd rather not also have to clean up the files backend as part of this work, though.
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
Do I pass in the 'struct
block_source*' as the source arg? If so, why are these declared as void *?you pass in
block_source->arg. Should struct implementations of polymorphic types carry a pointer to their own vtable instead?I think a pointer to
block_sourcewould make it more self-explanatory, yes.
This has a number of downsides, though. It means I have to introduce separate structs strbuf_blocksource and file_blocksource, along with functions to create and deallocate them, so it makes the whole thing more unwieldy to use.
Quoted 10 lines
Quoted 7 lines
Quoted 2 lines
Is the reason this manages the buffer instead of requiring a caller-supplied buffer to support zero-copy?
Log blocks are compressed, so the caller doesn't know the correct size to supply. By letting the block source handle the management, we can swap out the block read from the file for a block managed by malloc on decompressing a log block.
Hm, my naive assumption would have been that we'd use different buffers for the compressed and uncompressed data.
Maybe you can comment on how to solve this differently when we get to the implementation of block reading/writing in the commit series.
Quoted 7 lines
Quoted 7 lines
Quoted 6 lines
Quoted 3 lines
+ Generic tables
+
+ A unified API for reading tables, either merged tables, or single readers.Are there callers/helpers that don't know whether they want one or the other?
Quoted 7 lines
Quoted 4 lines
The setup with per-worktree refs means that there are two reftable stacks in a .git repo. In order to iterate over the entire ref space, you have to merge two merged tables, but the stack itslef merges a set of simple reftables.
I see. That's subtle, so it seems worth documenting for the next person reading this documentation and wondering why.
Done.
Signature · 11 lines
Han-Wen Nienhuys - Google Munich I work 80%. Don't expect answers from me on Fridays. -- Google Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Re: [PATCH v2 02/13] reftable: define the public API
In reply to Han-Wen Nienhuys via GitGitGadgetMy initial impression is that this public API is much larger than I would expect from reading the reftable spec. Let me look at it in detail...
Quoted 6 lines
reftable/reftable.h new file +6
-0+/**************************************************************** +Basic data types + +Reftables store the state of each ref in struct reftable_ref_record, and they +store a sequence of reflog updates in struct reftable_log_record. +****************************************************************/
reftable/reftable.h line 33
Quoted 10 lines
reftable/reftable.h new file +10
-0+ +/* reftable_ref_record holds a ref database entry target_value */ +struct reftable_ref_record { +char *refname;/* Name of the ref, malloced. */ +uint64_t update_index;/* Logical timestamp at which this value is +written */ +uint8_t *value;/* SHA1, or NULL. malloced. */ +uint8_t *target_value;/* peeled annotated tag, or NULL. malloced. */ +char *target;/* symref, or NULL. malloced. */ +};
A typical user would just want to do refname->hash, refname->log, or possibly hash->refname lookups, so a ref record would be meant for low-level users. I can't think of a typical use case, but even if such existed, I wouldn't expect this API - in particular, the refname is not written as such on disk (it is composed from a prefix and a suffix, if I remember correctly), so I would expect a function that one can call to write the refname, but not for it to appear in a data structure.
reftable/reftable.h line 36
Quoted 2 lines
reftable/reftable.h new file +2
-0+/* returns whether 'ref' represents a deletion */ +int reftable_ref_record_is_deletion (const struct reftable_ref_record *ref);
This looks unorthogonal - looking at the spec, the "value type" can represent a deletion, one object name (value of the ref), two object names (value of the ref + peeled target), or symbolic reference. If we're willing to allocate memory for a ref record, I think we can afford the extra 4 bytes (maybe 8) and use a tagged union instead.
reftable/reftable.h line 40
Quoted 3 lines
reftable/reftable.h new file +3
-0+/* prints a reftable_ref_record onto stdout */ +void reftable_ref_record_print (struct reftable_ref_record *ref, +uint32_t hash_id);
Not sure if this belongs here.
reftable/reftable.h line 43
Quoted 2 lines
reftable/reftable.h new file +2
-0+/* frees and nulls all pointer values. */ +void reftable_ref_record_clear (struct reftable_ref_record *ref);
Usually we call this "release" in Git.
reftable/reftable.h line 47
Quoted 3 lines
reftable/reftable.h new file +3
-0+/* returns whether two reftable_ref_records are the same */ +int reftable_ref_record_equal (struct reftable_ref_record *a, +struct reftable_ref_record *b,int hash_size);
Not sure what this will be used for.
reftable/reftable.h line 61
Quoted 13 lines
reftable/reftable.h new file +13
-0+/* reftable_log_record holds a reflog entry */ +struct reftable_log_record { +char *refname; +uint64_t update_index;/* logical timestamp of a transactional update. +*/ +uint8_t *new_hash; +uint8_t *old_hash; +char *name; +char *email; +uint64_t time; +int16_t tz_offset; +char *message; +};
Mostly same comments as above.
[snip]
reftable/reftable.h line 81
Quoted 5 lines
reftable/reftable.h new file +5
-0+/**************************************************************** +Error handling + +Error are signaled with negative integer return values. 0 means success. +****************************************************************/
I didn't look at this section deeply, but this looks OK at first glance.
reftable/reftable.h line 136
Quoted 5 lines
reftable/reftable.h new file +5
-0+/**************************************************************** +Writing + +Writing single reftables +****************************************************************/
OK - writing a single reftable makes sense.
Quoted 18 lines
reftable/reftable.h new file +18
-0+/* reftable_write_options sets options for writing a single reftable. */ +struct reftable_write_options { +/* boolean: do not pad out blocks to block size. */ +int unpadded; + +/* the blocksize. Should be less than 2^24. */ +uint32_t block_size; + +/* boolean: do not generate a SHA1 => ref index. */ +int skip_index_objects; + +/* how often to write complete keys in each block. */ +int restart_interval; + +/* 4-byte identifier ("sha1", "s256") of the hash. +* Defaults to SHA1 if unset +*/ +uint32_t hash_id;
reftable/reftable.h line 165
Quoted 10 lines
reftable/reftable.h new file +10
-0+ +/* boolean: do not check ref names for validity or dir/file conflicts. +*/ +int skip_name_check; + +/* boolean: copy log messages exactly. If unset, check that the message +* is a single line, and add '\n' if missing. +*/ +int exact_log_message; +};
I'm not sure we need so many options, but nothing jumps out to me off-hand. Some minor things - "hash_id" should probably be an enum and the booleans should be "unsigned variable_name : 1".
[snip statistics]
Statistics look useful.
reftable/reftable.h line 210
Quoted 7 lines
reftable/reftable.h new file +7
-0+/* reftable_new_writer creates a new writer */ +struct reftable_writer * +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,const void *data,size_t size);
Not sure if we'll need to write to things other than an fd, but if we do, this makes sense.
reftable/reftable.h line 221
Quoted 10 lines
reftable/reftable.h new file +10
-0+/* 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_stack_next_update_index(), or REFTABLE_API_ERROR is returned. + +For transactional updates, typically min==max. When converting an existing +ref database into a single reftable, this would be a range of update-index +timestamps. +*/ +void reftable_writer_set_limits (struct reftable_writer *w,uint64_t min, +uint64_t max);
This seems to be here because we want to write the file in a single pass, and the update index maximum and minimum appear in the header. If we were allowed to seek while writing, could the update index maximum and minimum be deduced instead? That might be a compelling reason to only support FDs, and require that the FDs be seekable.
reftable/reftable.h line 235
Quoted 13 lines
reftable/reftable.h new file +13
-0+/* adds a reftable_ref_record. Must be called in ascending +order. The update_index must be within the limits set by +reftable_writer_set_limits(), or REFTABLE_API_ERROR is returned. + +It is an error to write a ref record after a log record. +*/ +int reftable_writer_add_ref (struct reftable_writer *w, +struct reftable_ref_record *ref); + +/* Convenience function to add multiple refs. Will sort the refs by +name before adding. */ +int reftable_writer_add_refs (struct reftable_writer *w, +struct reftable_ref_record *refs,int n);
Since refs is an array of objects and not an array of pointers, these two functions could be combined - the first is the same as calling the second with n=1.
Also, ascending order of what?
The user will also need to know where to get the update index from - I presume that this will be the maximum update index of any record with the given refname + 1.
[snip similar functions for log]
reftable/reftable.h line 259
Quoted 12 lines
reftable/reftable.h new file +12
-0+/* reftable_writer_close finalizes the reftable. The writer is retained so +* statistics can be inspected. */ +int reftable_writer_close (struct reftable_writer *w); + +/* writer_stats returns the statistics on the reftable being written. + +This struct becomes invalid when the writer is freed. +*/ +const struct reftable_stats *writer_stats (struct reftable_writer *w); + +/* reftable_writer_free deallocates memory for the writer */ +void reftable_writer_free (struct reftable_writer *w);
OK.
Quoted 3 lines
+/****************************************************************
+ * ITERATING
+ ****************************************************************/
[snip]
reftable/reftable.h line 283
Quoted 11 lines
reftable/reftable.h new file +11
-0+/* reads the next reftable_ref_record. Returns < 0 for error, 0 for OK and > 0: +end of iteration. +*/ +int reftable_iterator_next_ref (struct reftable_iterator *it, +struct reftable_ref_record *ref); + +/* reads the next reftable_log_record. Returns < 0 for error, 0 for OK and > 0: +end of iteration. +*/ +int reftable_iterator_next_log (struct reftable_iterator *it, +struct reftable_log_record *log);
From my recollection, in Git, we typically use foreach functions with a callback that is invoked once for each result. I think that's preferable to this approach.
reftable/reftable.h line 294
Quoted 7 lines
reftable/reftable.h new file +7
-0+/**************************************************************** +Reading single tables + +The follow routines are for reading single files. For an application-level +interface, skip ahead to struct reftable_merged_table and struct +reftable_stack. +****************************************************************/
I think that this whole section could be skipped - if we really wanted to read a single table, we could just use the merged-table interface with one table.
[snip]
reftable/reftable.h line 410
Quoted 12 lines
reftable/reftable.h new file +12
-0+/**************************************************************** +Merged tables + +A ref database kept in a sequence of table files. The merged_table presents a +unified view to reading (seeking, iterating) a sequence of immutable tables. +****************************************************************/ + +/* A merged table is implements seeking/iterating over a stack of tables. */ +struct reftable_merged_table ; + +/* A generic reftable; see below. */ +struct reftable_table ;
Why would we need to see an individual reftable? Could we just represent a merged table as a virtual concatenation of blocks (as if all the blocks were in the same file)?
reftable/reftable.h line 417
Quoted 6 lines
reftable/reftable.h new file +6
-0+/* reftable_new_merged_table creates a new merged table. It takes ownership of +the stack array. +*/ +int reftable_new_merged_table (struct reftable_merged_table **dest, +struct reftable_table *stack,int n, +uint32_t hash_id);
I presume this would be used for things like compacting a few reftables together? In which case, I would expect this function to just take a list of filenames.
Quoted 4 lines
reftable/reftable.h new file +4
-0+/* returns an iterator positioned just before 'name' */ +int reftable_merged_table_seek_ref (struct reftable_merged_table *mt, +struct reftable_iterator *it, +const char *name);
reftable/reftable.h line 432
Quoted 10 lines
reftable/reftable.h new file +10
-0+ +/* returns an iterator for log entry, at given update_index */ +int reftable_merged_table_seek_log_at (struct reftable_merged_table *mt, +struct reftable_iterator *it, +const char *name,uint64_t update_index); + +/* like reftable_merged_table_seek_log_at but look for the newest entry. */ +int reftable_merged_table_seek_log (struct reftable_merged_table *mt, +struct reftable_iterator *it, +const char *name);
Why do iterators need to be precisely positioned if this merged table is immutable?
reftable/reftable.h line 446
Quoted 13 lines
reftable/reftable.h new file +13
-0+/* returns the max update_index covered by this merged table. */ +uint64_t +reftable_merged_table_max_update_index (struct reftable_merged_table *mt); + +/* returns the min update_index covered by this merged table. */ +uint64_t +reftable_merged_table_min_update_index (struct reftable_merged_table *mt); + +/* releases memory for the merged_table */ +void reftable_merged_table_free (struct reftable_merged_table *m); + +/* return the hash ID of the merged table. */ +uint32_t reftable_merged_table_hash_id (struct reftable_merged_table *m);
OK - makes sense.
reftable/reftable.h line 452
Quoted 5 lines
reftable/reftable.h new file +5
-0+/**************************************************************** +Generic tables + +A unified API for reading tables, either merged tables, or single readers. +****************************************************************/
Again, we could just use the merged table API.
[snip]
reftable/reftable.h line 481
Quoted 4 lines
reftable/reftable.h new file +4
-0+/* convenience function to read a single ref. Returns < 0 for error, 0 +for success, and 1 if ref not found. */ +int reftable_table_read_ref (struct reftable_table *tab,const char *name, +struct reftable_ref_record *ref);
I presume this returns the most up-to-date record in the (possibly merged) table? So we can just read the hash off the record to know what this ref points to.
reftable/reftable.h line 487
Quoted 5 lines
reftable/reftable.h new file +5
-0+/**************************************************************** +Mutable ref database + +The stack presents an interface to a mutable sequence of reftables. +****************************************************************/
So I would expect ref mutations to be done by opening the existing reftables as a merged table, open a reftable writer, write all the changes that need to be written, and update the tables.list file...
reftable/reftable.h line 500
Quoted 12 lines
reftable/reftable.h new file +12
-0+/* a stack is a stack of reftables, which can be mutated by pushing a table to +* the top of the stack */ +struct reftable_stack ; + +/* open a new reftable stack. The tables along with the table list will be +stored in 'dir'. Typically, this should be .git/reftables. +*/ +int reftable_new_stack (struct reftable_stack **dest,const char *dir, +struct reftable_write_options config); + +/* returns the update_index at which a next table should be written. */ +uint64_t reftable_stack_next_update_index (struct reftable_stack *st);
...so I don't see why we need a stack.
Quoted 12 lines
reftable/reftable.h new file +12
-0+/* holds a transaction to add tables at the top of a stack. */ +struct reftable_addition ; + +/* +returns a new transaction to add reftables to the given stack. As a side +effect, the ref database is locked. +*/ +int reftable_stack_new_addition (struct reftable_addition **dest, +struct reftable_stack *st); + +/* Adds a reftable to transaction. */ +int reftable_addition_add(struct reftable_addition *add,
reftable/reftable.h line 523
Quoted 10 lines
reftable/reftable.h new file +10
-0+int (*write_table)(struct reftable_writer *wr, +void *arg), + void *arg); + +/* Commits the transaction, releasing the lock. */ +int reftable_addition_commit (struct reftable_addition *add); + +/* Release all non-committed data from the transaction, and deallocate the +transaction. Releases the lock if held. */ +void reftable_addition_destroy (struct reftable_addition *add);
We do need a transaction to write the new tables.list and then atomically update the repository with it, but I don't think we need one just to add a reftable to the stack.
[snip rest of stack functions]
reftable/reftable.h line 557
Quoted 13 lines
reftable/reftable.h new file +13
-0+/* Policy for expiring reflog entries. */ +struct reftable_log_expiry_config { +/* Drop entries older than this timestamp */ +uint64_t time; + +/* Drop older entries */ +uint64_t min_update_index; +}; + +/* compacts all reftables into a giant table. Expire reflog entries if config is +* non-NULL */ +int reftable_stack_compact_all (struct reftable_stack *st, +struct reftable_log_expiry_config *config);
Ah, the stack is used for compacting as well. I don't think compacting belongs here though - it should be its own thing that can make use of the merged-table reader and the single-table writer.
[snip]
reftable/reftable.h line 570
Quoted 9 lines
reftable/reftable.h new file +9
-0+/* convenience function to read a single ref. Returns < 0 for error, 0 +for success, and 1 if ref not found. */ +int reftable_stack_read_ref (struct reftable_stack *st,const char *refname, +struct reftable_ref_record *ref); + +/* convenience function to read a single log. Returns < 0 for error, 0 +for success, and 1 if ref not found. */ +int reftable_stack_read_log (struct reftable_stack *st,const char *refname, +struct reftable_log_record *log);
Other things that belong to the merged table reader instead of the stack.
Some things that I think are missing:
- foreach all latest record of all refs (in any order) (I see some functions above that can set the position of the iterator, but I don't think that the iterator skips over irrelevant refs? So we can't use it for iterating over all refs if we don't care about the history of refs)
- foreach all log entries of one ref (or all refs)
In summary, I would have expected a mechanism to read multiple (possibly one) reftable and perform queries on it, a mechanism to write a single reftable, and some functions to update tables.list.
Re: [PATCH v2 02/13] reftable: define the public API
Hi Jonathan,
thanks for a detailed look at the API. I'm replying to some queries here, but I won't have time for another 2 weeks to revisit the code.
Quoted 2 lines · Jonathan Tan
A typical user would just want to do
refname->hash,refname->log, or possiblyhash->refnamelookups, so a ref record would be meant for low-level
You are forgetting refname -> symref target and refname -> peeled tag.
Quoted 5 lines
users. I can't think of a typical use case, but even if such existed, I wouldn't expect this API - in particular, the refname is not written as such on disk (it is composed from a prefix and a suffix, if I remember correctly), so I would expect a function that one can call to write the refname, but not for it to appear in a data structure.
It works with records, because it is more practical for the low level code, but also because it obviates defining many functions for the different types of lookups and seeks.
Quoted 8 lines
reftable/reftable.h line 36
Quoted 2 lines
reftable/reftable.h new file +2
-0+/* returns whether 'ref' represents a deletion */ +int reftable_ref_record_is_deletion (const struct reftable_ref_record *ref);This looks unorthogonal - looking at the spec, the "value type" can represent a deletion, one object name (value of the ref), two object names (value of the ref + peeled target), or symbolic reference. If we're willing to allocate memory for a ref record, I think we can afford the extra 4 bytes (maybe 8) and use a tagged union instead.
I guess you want something like
struct {
enum TYPE { REF_DEL, REF_1VAL, REF_2VAL, REF_SYM } record_type;
char *ref_name;
uint64_t update_index;
union {
uint_8 *val1;
struct { uint8_t *val1, *val2; } val2;
char *sym;
};
};I suspect it makes initialization more verbose, but yes, I could do that.
Quoted 5 lines
reftable/reftable.h line 47
Quoted 3 lines
reftable/reftable.h new file +3
-0+/* returns whether two reftable_ref_records are the same */ +int reftable_ref_record_equal (struct reftable_ref_record *a, +struct reftable_ref_record *b,int hash_size);Not sure what this will be used for.
This is useful for testing.
Quoted 7 lines
Quoted 7 lines
reftable/reftable.h new file +7
-0+/* 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_stack_next_update_index(), or REFTABLE_API_ERROR is returned. + +For transactional updates, typically min==max. When converting an existing +ref database into a single reftable, this would be a range of update-index +timestamps.
Quoted 8 lines
reftable/reftable.h line 221
Quoted 3 lines
+ */
reftable/reftable.h new file +2
-0+void reftable_writer_set_limits (struct reftable_writer *w,uint64_t min, +uint64_t max);This seems to be here because we want to write the file in a single pass, and the update index maximum and minimum appear in the header. If we were allowed to seek while writing, could the update index maximum and minimum be deduced instead?
No. The ref record update_index is delta encoded against the min update_index, so you have to know it upfront.
If I could redesign the format, I'd leave out the max update_index from the header (because it can be determined afterwards.), but it's too late now.
Quoted 19 lines
Quoted 4 lines
reftable/reftable.h new file +3
-0+/* adds a reftable_ref_record. Must be called in ascending +order. The update_index must be within the limits set by +reftable_writer_set_limits(), or REFTABLE_API_ERROR is returned. reftable/reftable.h line 235
Quoted 10 lines
reftable/reftable.h new file +10
-0+ +It is an error to write a ref record after a log record. +*/ +int reftable_writer_add_ref (struct reftable_writer *w, +struct reftable_ref_record *ref); + +/* Convenience function to add multiple refs. Will sort the refs by +name before adding. */ +int reftable_writer_add_refs (struct reftable_writer *w, +struct reftable_ref_record *refs,int n);Since refs is an array of objects and not an array of pointers, these two functions could be combined - the first is the same as calling the second with n=1.
Quoted 1 line
Also, ascending order of what?
Of record keys, ie. refname in case of ref records.
Quoted 3 lines
The user will also need to know where to get the update index from - I presume that this will be the maximum update index of any record with the given refname + 1.
there is a reftable_stack_next_update_index() for that.
Quoted 7 lines
Quoted 7 lines
reftable/reftable.h new file +7
-0+/* reads the next reftable_ref_record. Returns < 0 for error, 0 for OK and > 0: +end of iteration. +*/ +int reftable_iterator_next_ref (struct reftable_iterator *it, +struct reftable_ref_record *ref); + +/* reads the next reftable_log_record. Returns < 0 for error, 0 for OK and > 0:
Quoted 8 lines
reftable/reftable.h line 283
Quoted 4 lines
reftable/reftable.h new file +4
-0+end of iteration. +*/ +int reftable_iterator_next_log (struct reftable_iterator *it, +struct reftable_log_record *log);From my recollection, in Git, we typically use foreach functions with a callback that is invoked once for each result. I think that's preferable to this approach.
The iterator interface has the advantage that it captures reading both individual entries (read_raw_ref) and iteration. It's also a natural structure, because internally the iteration state has to be stored (iterating merged tables stores the iterators in a priority queue.)
Quoted 16 lines
[snip]
reftable/reftable.h line 410
Quoted 12 lines
reftable/reftable.h new file +12
-0+/**************************************************************** +Merged tables + +A ref database kept in a sequence of table files. The merged_table presents a +unified view to reading (seeking, iterating) a sequence of immutable tables. +****************************************************************/ + +/* A merged table is implements seeking/iterating over a stack of tables. */ +struct reftable_merged_table ; + +/* A generic reftable; see below. */ +struct reftable_table ;Why would we need to see an individual reftable?
It could be useful diagnostics and troubleshooting. For example, if you are debugging or trying to troubleshoot/fsck a repo, it might be interesting to read a single file from .git/reftable/
Philosophically, since we have to expose the functionality to write a single table, it seems reasonable to read single tables for symmetry as well.
Quoted 3 lines
Could we just represent
a merged table as a virtual concatenation of blocks (as if all the blocks were in the same file)?
No. The format doesn't work like that.
Quoted 10 lines
reftable/reftable.h line 417
Quoted 6 lines
reftable/reftable.h new file +6
-0+/* reftable_new_merged_table creates a new merged table. It takes ownership of +the stack array. +*/ +int reftable_new_merged_table (struct reftable_merged_table **dest, +struct reftable_table *stack,int n, +uint32_t hash_id);I presume this would be used for things like compacting a few reftables together? In which case, I would expect this function to just take a list of filenames.
The merging of tables has nothing to do with the filesystem, and could be done with reftables represented as in memory structure. This is also how the unittests work.
Quoted 9 lines
Quoted 9 lines
reftable/reftable.h new file +9
-0+/* returns an iterator positioned just before 'name' */ +int reftable_merged_table_seek_ref (struct reftable_merged_table *mt, +struct reftable_iterator *it, +const char *name); + +/* returns an iterator for log entry, at given update_index */ +int reftable_merged_table_seek_log_at (struct reftable_merged_table *mt, +struct reftable_iterator *it, +const char *name,uint64_t update_index);
Quoted 8 lines
reftable/reftable.h line 432
Quoted 5 lines
reftable/reftable.h new file +5
-0+ +/* like reftable_merged_table_seek_log_at but look for the newest entry. */ +int reftable_merged_table_seek_log (struct reftable_merged_table *mt, +struct reftable_iterator *it, +const char *name);Why do iterators need to be precisely positioned if this merged table is immutable?
I don't understand the question. How would you read a single ref from a merged table without a seek function?
Quoted 8 lines
reftable/reftable.h line 481
Quoted 4 lines
reftable/reftable.h new file +4
-0+/* convenience function to read a single ref. Returns < 0 for error, 0 +for success, and 1 if ref not found. */ +int reftable_table_read_ref (struct reftable_table *tab,const char *name, +struct reftable_ref_record *ref);I presume this returns the most up-to-date record in the (possibly merged) table? So we can just read the hash off the record to know what this ref points to.
correct.
Quoted 9 lines
reftable/reftable.h line 487
Quoted 5 lines
reftable/reftable.h new file +5
-0+/**************************************************************** +Mutable ref database + +The stack presents an interface to a mutable sequence of reftables. +****************************************************************/ So I would expect ref mutations to be done by opening the existing reftables as a merged table, open a reftable writer, write all the changes that need to be written, and update the tables.list file...
Roughly, yes, but there are other considerations: you have to check for concurrent updates (by the time you finish reading tables.list, a compaction may have deleted some of the tables referenced, so you have to retry). If there are any failures, the transaction fails and you have to clean up already written tables. If the write succeeds, but the stack becomes unbalanced, you have to do a compaction. If you have to reload data, you want to avoid closing and opening the same file.
If you are curious what the stack does, have a look at stack.c.
Also, this is for separation of logic. The merged table is composed of reftables that have no particular type of storage, but support the read interface. The stack is what ties the merged table to a posix-like file system, and provides mutations.
Quoted 18 lines
Quoted 18 lines
reftable/reftable.h new file +18
-0+/* holds a transaction to add tables at the top of a stack. */ +struct reftable_addition ; + +/* +returns a new transaction to add reftables to the given stack. As a side +effect, the ref database is locked. +*/ +int reftable_stack_new_addition (struct reftable_addition **dest, +struct reftable_stack *st); + +/* Adds a reftable to transaction. */ +int reftable_addition_add (struct reftable_addition *add, +int (*write_table)(struct reftable_writer *wr, +void *arg), +void *arg); + +/* Commits the transaction, releasing the lock. */ +int reftable_addition_commit (struct reftable_addition *add);
Quoted 8 lines
reftable/reftable.h line 523
Quoted 4 lines
reftable/reftable.h new file +4
-0+ +/* Release all non-committed data from the transaction, and deallocate the +transaction. Releases the lock if held. */ +void reftable_addition_destroy (struct reftable_addition *add);We do need a transaction to write the new tables.list and then atomically update the repository with it, but I don't think we need one just to add a reftable to the stack.
see above.
Quoted 8 lines
reftable/reftable.h line 557
Quoted 4 lines
reftable/reftable.h new file +4
-0+/* compacts all reftables into a giant table. Expire reflog entries if config is +* non-NULL */ +int reftable_stack_compact_all (struct reftable_stack *st, +struct reftable_log_expiry_config *config);Ah, the stack is used for compacting as well. I don't think compacting belongs here though - it should be its own thing that can make use of the merged-table reader and the single-table writer.
Why should it be like that, and where should compaction live?
Quoted 3 lines
Some things that I think are missing:
- foreach all latest record of all refs (in any order) (I see some
Ref records are keyed by name, so the iteration produces each ref only once.
Quoted 2 lines
functions above that can set the position of the iterator, but I don't think that the iterator skips over irrelevant refs?
What makes you think that?
Quoted 3 lines
So we can't
use it for iterating over all refs if we don't care about the history of refs)
I think you might be confusing the refs with reflogs here. The history of a ref is kept in the reflog. This is why functions that want to seek to a log record need an update_index timestamp.
Quoted 1 line
- foreach all log entries of one ref (or all refs)
Before reviewing the API header here, I think it might be useful to also look at the commit in the other series that uses the API to implement the git ref backend. It shows how the foreach functions can be implemented using the iterator interface.
Quoted 3 lines
In summary, I would have expected a mechanism to read multiple (possibly one) reftable and perform queries on it, a mechanism to write a single reftable, and some functions to update tables.list.
I understand your expectation, but I think that expectation glosses over important details of how it actually works.
Signature · 7 lines
Han-Wen Nienhuys - Google Munich I work 80%. Don't expect answers from me on Fridays. -- Google Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
PATCH v2 3/13 vcxproj: adjust for the reftable changes
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
This allows Git to be compiled via Visual Studio again after integrating the hn/reftable branch.
--- config.mak.uname | 2 +- contrib/buildsystems/Generators/Vcxproj.pm | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-)
config.mak.uname +1 -1
@@ -709,7 +709,7 @@ vcxproj: # Make .vcxproj files and add them unset QUIET_GEN QUIET_BUILT_IN; \ perl contrib/buildsystems/generate -g Vcxproj - git add -f git.sln {*,*/lib,t/helper/*}/*.vcxproj + git add -f git.sln {*,*/lib,*/libreftable,t/helper/*}/*.vcxproj # Generate the LinkOrCopyBuiltins.targets and LinkOrCopyRemoteHttp.targets file (echo '<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">' && \
contrib/buildsystems/Generators/Vcxproj.pm +10 -1
@@ -77,7 +77,7 @@ sub createProject { my $libs_release = "\n "; my $libs_debug = "\n "; if (!$static_library) { - $libs_release = join(";", sort(grep /^(?!libgit\.lib|xdiff\/lib\.lib|vcs-svn\/lib\.lib)/, @{$$build_structure{"$prefix${name}_LIBS"}})); + $libs_release = join(";", sort(grep /^(?!libgit\.lib|xdiff\/lib\.lib|vcs-svn\/lib\.lib|reftable\/libreftable\.lib)/, @{$$build_structure{"$prefix${name}_LIBS"}})); $libs_debug = $libs_release; $libs_debug =~ s/zlib\.lib/zlibd\.lib/g; $libs_debug =~ s/libexpat\.lib/libexpatd\.lib/g;
@@ -232,6 +232,7 @@ sub createProject { EOM if (!$static_library || $target =~ 'vcs-svn' || $target =~ 'xdiff') { my $uuid_libgit = $$build_structure{"LIBS_libgit_GUID"}; + my $uuid_libreftable = $$build_structure{"LIBS_reftable/libreftable_GUID"}; my $uuid_xdiff_lib = $$build_structure{"LIBS_xdiff/lib_GUID"}; print F << "EOM";
@@ -241,6 +242,14 @@ sub createProject { <ReferenceOutputAssembly>false</ReferenceOutputAssembly> </ProjectReference> EOM + if (!($name =~ /xdiff|libreftable/)) { + print F << "EOM"; + <ProjectReference Include="$cdup\\reftable\\libreftable\\libreftable.vcxproj"> + <Project>$uuid_libreftable</Project> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + </ProjectReference> +EOM + } if (!($name =~ 'xdiff')) { print F << "EOM"; <ProjectReference Include="$cdup\\xdiff\\lib\\xdiff_lib.vcxproj">
Signature · 1 line
gitgitgadget
Re: [PATCH v2 03/13] vcxproj: adjust for the reftable changes
Quoted 4 lines · Han-Wen Nienhuys
From: Johannes Schindelin <[email protected]>
This allows Git to be compiled via Visual Studio again after integrating the
hn/reftablebranch.
nit: This branch name is no longer meaningful to the primary audience for the commit message (people discovering this commit in git history later).
Quoted 5 lines
Signed-off-by Johannes Schindelin <[email protected]>--- config.mak.uname | 2 +- contrib/buildsystems/Generators/Vcxproj.pm | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-)
Can this be squashed into or put immediately after patch 5 which introduces the Makefile?
Thanks,
Jonathan
Re: [PATCH v2 03/13] vcxproj: adjust for the reftable changes
Hi Jonathan,
Quoted 11 lines · Jonathan Nieder
Quoted 4 lines · Han-Wen Nienhuys
From: Johannes Schindelin <[email protected]>
This allows Git to be compiled via Visual Studio again after integrating the
hn/reftablebranch.nit: This branch name is no longer meaningful to the primary audience for the commit message (people discovering this commit in git history later).
Quoted 8 lines
Quoted 5 lines
Signed-off-by Johannes Schindelin <[email protected]>--- config.mak.uname | 2 +- contrib/buildsystems/Generators/Vcxproj.pm | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-)Can this be squashed into or put immediately after patch 5 which introduces the
Makefile?
I never intended this to be a stand-alone patch. So yes, I would be very much in favor of squashing these changes into the appropriate commit.
Ciao,
Dscho
PATCH v2 4/13 reftable: add a barebones unittest framework
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- reftable/test_framework.c | 68 +++++++++++++++++++++++++++++++++++++++ reftable/test_framework.h | 59 +++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 reftable/test_framework.c create mode 100644 reftable/test_framework.h
reftable/test_framework.c new file +68 -0
@@ -0,0 +1,68 @@ +/* +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 "test_framework.h" + +#include "system.h" +#include "basics.h" + +static struct test_case **test_cases; +static int test_case_len; +static int test_case_cap; + +static struct test_case *new_test_case (const char *name,void (*testfunc)(void )) +{ +struct test_case *tc =reftable_malloc (sizeof (struct test_case )); + tc->name = name; + tc->testfunc = testfunc; +return tc; +} + +struct test_case *add_test_case (const char *name,void (*testfunc)(void )) +{ +struct test_case *tc =new_test_case (name, testfunc); +if (test_case_len == test_case_cap) { + test_case_cap =2 * test_case_cap +1 ; + test_cases =reftable_realloc ( + test_cases,sizeof (struct test_case ) * test_case_cap); + } + + test_cases[test_case_len++] = tc; +return tc; +} + +int test_main (int argc,const char *argv[]) +{ +const char *filter =NULL ; +int i =0 ; +if (argc >1 ) { + filter = argv[1 ]; + } + +for (i =0 ; i < test_case_len; i++) { +const char *name = test_cases[i]->name; +if (filter ==NULL ||strstr (name, filter) !=NULL ) { +printf ("case %s\n" , name); + test_cases[i]->testfunc (); + }else { +printf ("skip %s\n" , name); + } + +reftable_free (test_cases[i]); + } +reftable_free (test_cases); + test_cases =NULL ; + test_case_len =0 ; + test_case_cap =0 ; +return 0 ; +} + +void set_test_hash (uint8_t *p,int i) +{ +memset (p, (uint8_t )i,hash_size (SHA1_ID )); +}
reftable/test_framework.h new file +59 -0
59 lines not on this page · raw message
Signature · 1 line
gitgitgadget
Re: [PATCH v2 04/13] reftable: add a barebones unittest framework
Hi,
Quoted 7 lines · Han-Wen Nienhuys
Signed-off-by Han-Wen Nienhuys <[email protected]>--- reftable/test_framework.c | 68 +++++++++++++++++++++++++++++++++++++++ reftable/test_framework.h | 59 +++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 reftable/test_framework.c create mode 100644 reftable/test_framework.h
Hm, can the commit message say a little about the motivation for this test framework and what it allows?
For example, does it allow unit tests to write output in TAP format? How does it compare to existing frameworks like
https://www.eyrie.org/~eagle/software/c-tap-harness/? How does this approach compare to what Git's existing unit-style tests with helpers in t/helper/ driven by scripts in t/ do?
Thanks,
Jonathan
Re: [PATCH v2 04/13] reftable: add a barebones unittest framework
In reply to Han-Wen Nienhuys via GitGitGadgetQuoted 6 lines
From: Han-Wen Nienhuys <[email protected]>
Signed-off-by Han-Wen Nienhuys <[email protected]>--- reftable/test_framework.c | 68 +++++++++++++++++++++++++++++++++++++++ reftable/test_framework.h | 59 +++++++++++++++++++++++++++++++++
Even if reftable will be a project maintained by the Git project but independent enough to be used by other projects, I don't think it's worth creating a separate test framework. E.g. as far as I can tell, when we import sha1dc, we don't import any tests, so I don't think they need to import tests either - they can run the tests in the Git repo themselves if they need to.
Re: [PATCH v2 04/13] reftable: add a barebones unittest framework
Quoted 7 lines · Jonathan Tan
Quoted 6 lines
From: Han-Wen Nienhuys <[email protected]>
Signed-off-by Han-Wen Nienhuys <[email protected]>--- reftable/test_framework.c | 68 +++++++++++++++++++++++++++++++++++++++ reftable/test_framework.h | 59 +++++++++++++++++++++++++++++++++
Quoted 6 lines
Even if reftable will be a project maintained by the Git project but independent enough to be used by other projects, I don't think it's worth creating a separate test framework. E.g. as far as I can tell, when we import sha1dc, we don't import any tests, so I don't think they need to import tests either - they can run the tests in the Git repo themselves if they need to.
I agree with Jonathan here, I think we should stick with the existing test framework.
PATCH v2 5/13 reftable: utility functions
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
This commit provides basic utility classes for the reftable library.
Since the reftable library must compile standalone, there may be some overlap with git-core utility functions.
--- Makefile | 26 ++++++- reftable/basics.c | 131 +++++++++++++++++++++++++++++++++ reftable/basics.h | 48 +++++++++++++ reftable/blocksource.c | 148 ++++++++++++++++++++++++++++++++++++++ reftable/blocksource.h | 22 ++++++ reftable/compat.c | 110 ++++++++++++++++++++++++++++ reftable/compat.h | 48 +++++++++++++ reftable/publicbasics.c | 100 ++++++++++++++++++++++++++ reftable/reftable-tests.h | 22 ++++++ reftable/strbuf.c | 142 ++++++++++++++++++++++++++++++++++++ reftable/strbuf.h | 80 +++++++++++++++++++++ reftable/strbuf_test.c | 37 ++++++++++ reftable/system.h | 51 +++++++++++++ t/helper/test-reftable.c | 8 +++ t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 + 16 files changed, 972 insertions(+), 3 deletions(-) create mode 100644 reftable/basics.c create mode 100644 reftable/basics.h create mode 100644 reftable/blocksource.c create mode 100644 reftable/blocksource.h create mode 100644 reftable/compat.c create mode 100644 reftable/compat.h create mode 100644 reftable/publicbasics.c create mode 100644 reftable/reftable-tests.h 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 t/helper/test-reftable.c
Makefile +23 -3
@@ -727,6 +727,7 @@ TEST_BUILTINS_OBJS += test-read-cache.o TEST_BUILTINS_OBJS += test-read-graph.o TEST_BUILTINS_OBJS += test-read-midx.o TEST_BUILTINS_OBJS += test-ref-store.o +TEST_BUILTINS_OBJS += test-reftable.o TEST_BUILTINS_OBJS += test-regex.o TEST_BUILTINS_OBJS += test-repository.o TEST_BUILTINS_OBJS += test-revision-walking.o
@@ -806,6 +807,8 @@ TEST_SHELL_PATH = $(SHELL_PATH) LIB_FILE = libgit.a XDIFF_LIB = xdiff/lib.a +REFTABLE_LIB = reftable/libreftable.a +REFTABLE_TEST_LIB = reftable/libreftable_test.a GENERATED_H += config-list.h GENERATED_H += command-list.h
@@ -1167,7 +1170,7 @@ THIRD_PARTY_SOURCES += compat/regex/% THIRD_PARTY_SOURCES += sha1collisiondetection/% THIRD_PARTY_SOURCES += sha1dc/% -GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) +GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) EXTLIBS = GIT_USER_AGENT = git/$(GIT_VERSION)
@@ -2369,10 +2372,21 @@ XDIFF_OBJS += xdiff/xpatience.o XDIFF_OBJS += xdiff/xprepare.o XDIFF_OBJS += xdiff/xutils.o +REFTABLE_OBJS += reftable/basics.o +REFTABLE_OBJS += reftable/blocksource.o +REFTABLE_OBJS += reftable/publicbasics.o +REFTABLE_OBJS += reftable/compat.o +REFTABLE_OBJS += reftable/strbuf.o + +REFTABLE_TEST_OBJS += reftable/strbuf_test.o +REFTABLE_TEST_OBJS += reftable/test_framework.o + TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \ $(XDIFF_OBJS) \ $(FUZZ_OBJS) \ + $(REFTABLE_OBJS) \ + $(REFTABLE_TEST_OBJS) \ common-main.o \ git.o ifndef NO_CURL
@@ -2524,6 +2538,12 @@ $(LIB_FILE): $(LIB_OBJS) $(XDIFF_LIB): $(XDIFF_OBJS) $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^ +$(REFTABLE_LIB): $(REFTABLE_OBJS) + $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^ + +$(REFTABLE_TEST_LIB): $(REFTABLE_TEST_OBJS) + $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^ + export DEFAULT_EDITOR DEFAULT_PAGER Documentation/GIT-EXCLUDED-PROGRAMS: FORCE
@@ -2802,7 +2822,7 @@ perf: all t/helper/test-tool$X: $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) -t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS) +t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS) $(REFTABLE_TEST_LIB) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS) check-sha1:: t/helper/test-tool$X
@@ -3133,7 +3153,7 @@ cocciclean: clean: profile-clean coverage-clean cocciclean $(RM) *.res $(RM) $(OBJECTS) - $(RM) $(LIB_FILE) $(XDIFF_LIB) + $(RM) $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(REFTABLE_TEST_LIB) $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X $(RM) $(TEST_PROGRAMS) $(RM) $(FUZZ_PROGRAMS)
reftable/basics.c new file +131 -0
131 lines not on this page · raw message
reftable/basics.h new file +48 -0
48 lines not on this page · raw message
reftable/blocksource.c new file +148 -0
148 lines not on this page · raw message
reftable/blocksource.h new file +22 -0
@@ -0,0 +1,22 @@ +/* +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 BLOCKSOURCE_H +#define BLOCKSOURCE_H + +#include "strbuf.h" + +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/compat.c new file +110 -0
110 lines not on this page · raw message
reftable/compat.h new file +48 -0
48 lines not on this page · raw message
reftable/publicbasics.c new file +100 -0
100 lines not on this page · raw message
reftable/reftable-tests.h new file +22 -0
@@ -0,0 +1,22 @@ +/* +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 REFTABLE_TESTS_H +#define REFTABLE_TESTS_H + +int block_test_main (int argc,const char **argv); +int merged_test_main (int argc,const char **argv); +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 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); + +#endif
reftable/strbuf.c new file +142 -0
142 lines not on this page · raw message
reftable/strbuf.h new file +80 -0
80 lines not on this page · raw message
reftable/strbuf_test.c new file +37 -0
@@ -0,0 +1,37 @@ +/* +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 "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 file +51 -0
51 lines not on this page · raw message
t/helper/test-reftable.c new file +8 -0
@@ -0,0 +1,8 @@ +#include "reftable/reftable-tests.h" +#include "test-tool.h" + +int cmd__reftable (int argc,const char **argv) +{ +strbuf_test_main (argc, argv); +return 0 ; +}
t/helper/test-tool.c +1 -0
@@ -53,6 +53,7 @@ static struct test_cmd cmds[] = { { "read-graph", cmd__read_graph }, { "read-midx", cmd__read_midx }, { "ref-store", cmd__ref_store }, + { "reftable", cmd__reftable }, { "regex", cmd__regex }, { "repository", cmd__repository }, { "revision-walking", cmd__revision_walking },
t/helper/test-tool.h +1 -0
@@ -42,6 +42,7 @@ int cmd__read_cache(int argc, const char **argv);int cmd__read_graph (int argc,const char **argv);int cmd__read_midx (int argc,const char **argv);int cmd__ref_store (int argc,const char **argv); +int cmd__reftable (int argc,const char **argv);int cmd__regex (int argc,const char **argv);int cmd__repository (int argc,const char **argv);int cmd__revision_walking (int argc,const char **argv);
Signature · 1 line
gitgitgadget
Re: [PATCH v2 05/13] reftable: utility functions
reftable/basics.c line 9
Quoted 12 lines · Han-Wen Nienhuys
reftable/basics.c new file +9
-0@@ -0,0 +1,131 @@ +/* +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 "basics.h"
Git's source files start with #include-ing "git-compat-util.h"; how do we approach that here?
E.g. would we pass -include on the command line, or could we #include some kind of compat-util.h that #include-s "git-compat-util.h" in Git and does the appropriate steps for enabling feature test macros and including system headers when used outside?
[...]
reftable/basics.c line 30
Quoted 1 line
reftable/basics.c new file +1
-0+int binsearch (size_t sz,int (*f)(size_t k,void *args),void *args)
How does this compare to stdlib's bsearch?
[...]
reftable/basics.c line 71
Quoted 12 lines
reftable/basics.c new file +12
-0+void free_names (char **a) +{ +char **p = a; +if (p ==NULL ) { +return ; + } +while (*p) { +reftable_free (*p); + p++; + } +reftable_free (a); +}
Are there other callers that need custom free?
[...]
reftable/basics.c line 82
Quoted 10 lines
reftable/basics.c new file +10
-0+int names_length (char **names) +{ +int len =0 ; +char **p = names; +while (*p) { + p++; + len++; + } +return len; +}
The rest are probably easier to evaluate when I look at the callers, and it's time for me to go to sleep now. I'll try to find some time soon to pick the review back up.
Thanks for a thoughtfully put together library so far.
Jonathan
Re: [PATCH v2 05/13] reftable: utility functions
Quoted 4 lines · Jonathan Nieder
[...]
Quoted 1 line
+int
binsearch(size_t sz, int (*f)(size_t k, void *args), void *args)How does this compare to stdlib's bsearch?
bsearch gives you back NULL if it doesn't find an exact match.
Quoted 4 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ reftable_free(a); +}Are there other callers that need custom free?
The libgit2 folks requested the ability to set memory allocation routines, hence reftable_free().
Signature · 11 lines
Han-Wen Nienhuys - Google Munich I work 80%. Don't expect answers from me on Fridays. -- Google Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Re: [PATCH v2 05/13] reftable: utility functions
(+cc: Patrick Steinhardt from libgit2)
Hi,
Han-Wen Nienhuys wrote[1]:
Quoted 10 lines · Jonathan Nieder
Quoted 6 lines
Review
Quoted 2 lines · Han-Wen Nienhuys
(quoted diff) +2
-0+ reftable_free(a); +}Are there other callers that need custom free?
The libgit2 folks requested the ability to set memory allocation routines, hence
reftable_free().
Thanks. Patrick or Han-Wen, can you say a little more about this use case? That would help with making sure we are making an API that meets its needs.
For example, is a custom allocator something that would be set globally or something attached to a handle? If the former, would code that uses xmalloc and free and gets #define-d away when used in libgit2 work? If the latter, what does the handle look like?
Thanks,
Jonathan
[1] context: https://lore.kernel.org/git/[email protected]/T/#r2337ddc80848c80f6c9699d904ab64d4297d2c54
Re: [PATCH v2 05/13] reftable: utility functions
Quoted 15 lines · Jonathan Nieder
(+cc: Patrick Steinhardt from libgit2)
Hi,Han-Wen Nienhuys wrote[1]:
Quoted 10 lines · Jonathan Nieder
Quoted 6 lines
Review
Quoted 2 lines · Han-Wen Nienhuys
(quoted diff) +2
-0+ reftable_free(a); +}Are there other callers that need custom free?
The libgit2 folks requested the ability to set memory allocation routines, hence
reftable_free().
Quoted 8 lines
Thanks. Patrick or Han-Wen, can you say a little more about this use case? That would help with making sure we are making an API that meets its needs.
For example, is a custom allocator something that would be set globally or something attached to a handle? If the former, would code that uses xmalloc and free and gets #define-d away when used in libgit2 work? If the latter, what does the handle look like?
We have global pluggable allocators in libgit2 which can be set up before calling git_libgit2_init(). The user of libgit2 can fill a git_allocator structure with a set of funtcion pointers, most importantly with implementations of free and malloc. Those then get used across all of libgit2 for all subsequent allocations.
In order to be as thorough as possible, we thus also need to replace these function pointers for libgit2's dependencies. As registration of the allocator happens at runtime, we need to also be able to replace function pointers of dependencies at runtime. E.g. for OpenSSL, it provides an API CRYPTO_set_mem_functions(malloc, realloc, free) which we call on global initialization of the libgit2 library.
So, to answer your questions:
- The allocator is global and cannot be changed after initialization of libgit2.
- It is pluggable, users can set up their own allocators by filling a structure with function pointers for free, malloc, realloc etc.
- Due to the pluggable nature, we need to be able to set up those pointers at runtime. We can provide a set of static wrappers though which then call into the pluggable functions, so defines would probably work for us, too.
I hope that answers all of your questions.
Patrick
Re: [PATCH v2 05/13] reftable: utility functions
Quoted 16 lines · Patrick Steinhardt
In order to be as thorough as possible, we thus also need to replace these function pointers for libgit2's dependencies. As registration of the allocator happens at runtime, we need to also be able to replace function pointers of dependencies at runtime. E.g. for OpenSSL, it provides an API
CRYPTO_set_mem_functions(malloc, realloc, free)which we call on global initialization of the libgit2 library.So, to answer your questions:
- The allocator is global and cannot be changed after initialization of libgit2.
- It is pluggable, users can set up their own allocators by filling a structure with function pointers for
free,malloc,reallocetc.
Quoted 6 lines
- Due to the pluggable nature, we need to be able to set up those pointers at runtime. We can provide a set of static wrappers though which then call into the pluggable functions, so defines would probably work for us, too.
I hope that answers all of your questions.
Thanks! Yes, that's very helpful.
Jonathan
Re: [PATCH v2 05/13] reftable: utility functions
In reply to Patrick SteinhardtHi Patrick,
Quoted 25 lines · Patrick Steinhardt
Quoted 15 lines · Jonathan Nieder
(+cc: Patrick Steinhardt from libgit2)
Hi,Han-Wen Nienhuys wrote[1]:
Quoted 10 lines · Jonathan Nieder
Quoted 6 lines
Review
Quoted 2 lines · Han-Wen Nienhuys
(quoted diff) +2
-0+ reftable_free(a); +}Are there other callers that need custom free?
The libgit2 folks requested the ability to set memory allocation routines, hence
reftable_free().Quoted 8 lines
Thanks. Patrick or Han-Wen, can you say a little more about this use case? That would help with making sure we are making an API that meets its needs.
For example, is a custom allocator something that would be set globally or something attached to a handle? If the former, would code that uses xmalloc and free and gets #define-d away when used in libgit2 work? If the latter, what does the handle look like?
Quoted 5 lines
We have global pluggable allocators in libgit2 which can be set up before calling
git_libgit2_init(). The user of libgit2 can fill agit_allocatorstructure with a set of funtcion pointers, most importantly with implementations offreeandmalloc. Those then get used across all of libgit2 for all subsequent allocations.
I did not find out how those are used in the deps/ part of libgit2's source code. For example, I see a couple of instances where malloc() is used in ntlmclient and in pcre.
The thing I was looking for would have been something like
#define malloc(size) git__malloc(size) ...
This would also have been what I imagined to be the best strategy to integrate the reftable code once it is properly embedded in libgit.a (and of course using libgit.a's API as much as it can).
Somewhat related: I was wondering whether it would make sense for git.git to rename strbuf to git_buf? Would that make it easier to exchange code between the two projects? Or would it just be unnecessary churn?
Ciao,
Dscho
Re: [PATCH v2 05/13] reftable: utility functions
Quoted 3 lines · Johannes Schindelin
Somewhat related: I was wondering whether it would make sense for git.git to rename
strbuftogit_buf? Would that make it easier to exchange code between the two projects? Or would it just be unnecessary churn?
To us, "git_buf" is just as descriptive as "buf" and does not say anything about the nature of 'buf' (other than apparently it was invented and widely used here). "git_strbuf" I can understand, but why should we?
Re: [PATCH v2 05/13] reftable: utility functions
Hi Junio,
Quoted 10 lines · Junio C Hamano
Quoted 3 lines · Johannes Schindelin
Somewhat related: I was wondering whether it would make sense for git.git to rename
strbuftogit_buf? Would that make it easier to exchange code between the two projects? Or would it just be unnecessary churn?To us, "
git_buf" is just as descriptive as "buf" and does not say anything about the nature of 'buf' (other than apparently it was invented and widely used here). "git_strbuf" I can understand, but why should we?
If it makes code sharing between git.git and libgit2 easier, why shouldn't we ;-)
Obviously, if it doesn't make life easier, we shouldn't bother.
Ciao,
Dscho
Re: [PATCH v2 05/13] reftable: utility functions
Quoted 10 lines · Johannes Schindelin
Hi Junio,
Quoted 6 lines · Junio C Hamano
Quoted 3 lines · Johannes Schindelin
Somewhat related: I was wondering whether it would make sense for git.git to rename
strbuftogit_buf? Would that make it easier to exchange code between the two projects? Or would it just be unnecessary churn?
Quoted 7 lines
Quoted 4 lines
To us, "
git_buf" is just as descriptive as "buf" and does not say anything about the nature of 'buf' (other than apparently it was invented and widely used here). "git_strbuf" I can understand, but why should we?If it makes code sharing between git.git and libgit2 easier, why shouldn't we ;-)
I see no reasonably explanation why libgit2 is not the one who uses "#define strbuf git_buf" to make "sharing easier", though.
Re: [PATCH v2 05/13] reftable: utility functions
Hi Junio,
Quoted 17 lines · Junio C Hamano
Quoted 15 lines · Johannes Schindelin
Hi Junio,
Quoted 10 lines · Junio C Hamano
Quoted 3 lines · Johannes Schindelin
Somewhat related: I was wondering whether it would make sense for git.git to rename
strbuftogit_buf? Would that make it easier to exchange code between the two projects? Or would it just be unnecessary churn?To us, "
git_buf" is just as descriptive as "buf" and does not say anything about the nature of 'buf' (other than apparently it was invented and widely used here). "git_strbuf" I can understand, but why should we?
Quoted 5 lines
Quoted 2 lines
If it makes code sharing between git.git and libgit2 easier, why shouldn't we ;-)
I see no reasonably explanation why libgit2 is not the one who uses "#define strbuf
git_buf" to make "sharing easier", though.
That's a fair point, thanks for bringing it up!
Ciao,
Dscho
Re: [PATCH v2 05/13] reftable: utility functions
In reply to Junio C HamanoQuoted 17 lines · Junio C Hamano
Quoted 15 lines · Johannes Schindelin
Hi Junio,
Quoted 10 lines · Junio C Hamano
Quoted 3 lines · Johannes Schindelin
Somewhat related: I was wondering whether it would make sense for git.git to rename
strbuftogit_buf? Would that make it easier to exchange code between the two projects? Or would it just be unnecessary churn?To us, "
git_buf" is just as descriptive as "buf" and does not say anything about the nature of 'buf' (other than apparently it was invented and widely used here). "git_strbuf" I can understand, but why should we?
Quoted 5 lines
Quoted 2 lines
If it makes code sharing between git.git and libgit2 easier, why shouldn't we ;-)
I see no reasonably explanation why libgit2 is not the one who uses "#define strbuf
git_buf" to make "sharing easier", though.
It probably wouldn't help much anyway. We already have our own buffer type which we can't easily replace with yours as it's part of the public interface. If the need arises, providing a compatibility interface for it shouldn't be too hard.
Patrick
Re: [PATCH v2 05/13] reftable: utility functions
In reply to Han-Wen Nienhuys via GitGitGadgetHi Han-Wen,
Quoted 6 lines · Han-Wen Nienhuys via GitGitGadget
From: Han-Wen Nienhuys <[email protected]>
This commit provides basic utility classes for the reftable library.
Since the reftable library must compile standalone, there may be some overlap with git-core utility functions.
My position on this idea to duplicate functionality in order to somehow pretend that the reftable code is independent of Git's source code has not changed.
Be that as it may, the CI build failures impacted my notifications' signal/noise ratio too much, so here goes (Junio, I would be delighted if you could apply this on top of your branch):
-- snipsnap --
From 1698c3450b5e47927c2e8fe35cad2634963bad89 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <[email protected]>
Date: Wed, 30 Sep 2020 16:53:53 +0200
Subject: [PATCH] fixup??? reftable: utility functions
Let's not forget our CMake configuration.
--- contrib/buildsystems/CMakeLists.txt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)
contrib/buildsystems/CMakeLists.txt +12 -2
@@ -591,6 +591,12 @@ parse_makefile_for_sources(libxdiff_SOURCES "XDIFF_OBJS") list(TRANSFORM libxdiff_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/") add_library(xdiff STATIC ${libxdiff_SOURCES}) +#reftable +parse_makefile_for_sources(reftable_SOURCES "REFTABLE_OBJS") + +list(TRANSFORM reftable_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/") +add_library(reftable STATIC ${reftable_SOURCES}) + if(WIN32) if(NOT MSVC)#use windres when compiling with gcc and clang add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/git.res
@@ -613,7 +619,7 @@ endif() #link all required libraries to common-main add_library(common-main OBJECT ${CMAKE_SOURCE_DIR}/common-main.c) -target_link_libraries(common-main libgit xdiff ${ZLIB_LIBRARIES}) +target_link_libraries(common-main libgit xdiff reftable ${ZLIB_LIBRARIES}) if(Intl_FOUND) target_link_libraries(common-main ${Intl_LIBRARIES}) endif()
@@ -848,11 +854,15 @@ if(BUILD_TESTING) add_executable(test-fake-ssh ${CMAKE_SOURCE_DIR}/t/helper/test-fake-ssh.c) target_link_libraries(test-fake-ssh common-main) +#reftable-tests +parse_makefile_for_sources(test-reftable_SOURCES "REFTABLE_TEST_OBJS") +list(TRANSFORM test-reftable_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/") + #test-tool parse_makefile_for_sources(test-tool_SOURCES "TEST_BUILTINS_OBJS") list(TRANSFORM test-tool_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/t/helper/") -add_executable(test-tool ${CMAKE_SOURCE_DIR}/t/helper/test-tool.c ${test-tool_SOURCES}) +add_executable(test-tool ${CMAKE_SOURCE_DIR}/t/helper/test-tool.c ${test-tool_SOURCES} ${test-reftable_SOURCES}) target_link_libraries(test-tool common-main) set_target_properties(test-fake-ssh test-tool
--
2.28.0.windows.1.18.g5300e52e185
Re: [PATCH v2 05/13] reftable: utility functions
Quoted 8 lines · Johannes Schindelin
Quoted 4 lines
This commit provides basic utility classes for the reftable library.
Since the reftable library must compile standalone, there may be some overlap with git-core utility functions.
My position on this idea to duplicate functionality in order to somehow pretend that the reftable code is independent of Git's source code has not changed.
The above may be sufficient between you and Han-Wen, and a few selected others who still remember, but please do not forget that we are collaborating in the open. It would help those who are learning open source interactions by watching from sidelines, if you spelled out what your position is or at least left a pointer to a previous discussion.
FWIW, I think it is a mistake to try to make this directory so that it can be lifted out of our code base and used independently, as it has to create unnecessary friction to the code when used here. It is not like other code that we are not their primary intended audience and we simply "borrow" from them (e.g. xdiff/ & sha1dc/).
The previous paragraph agrees with my guess of your position, but perhaps you have something else in mind. I dunno.
Quoted 3 lines
Be that as it may, the CI build failures impacted my notifications' signal/noise ratio too much, so here goes (Junio, I would be delighted if you could apply this on top of your branch):
Quite honestly, this close to the first preview release for 2.29, I'd rather drop this round from 'seen' and expect an updated topic, than piling fixup on top of fixups.
Thanks.
Re: [PATCH v2 05/13] reftable: utility functions
Hi Junio,
Quoted 11 lines · Junio C Hamano
Quoted 8 lines · Johannes Schindelin
Quoted 4 lines
This commit provides basic utility classes for the reftable library.
Since the reftable library must compile standalone, there may be some overlap with git-core utility functions.
My position on this idea to duplicate functionality in order to somehow pretend that the reftable code is independent of Git's source code has not changed.
Quoted 6 lines
The above may be sufficient between you and Han-Wen, and a few selected others who still remember, but please do not forget that we are collaborating in the open. It would help those who are learning open source interactions by watching from sidelines, if you spelled out what your position is or at least left a pointer to a previous discussion.
You're right, sorry.
Quoted 8 lines
FWIW, I think it is a mistake to try to make this directory so that it can be lifted out of our code base and used independently, as it has to create unnecessary friction to the code when used here. It is not like other code that we are not their primary intended audience and we simply "borrow" from them (e.g. xdiff/ & sha1dc/).
The previous paragraph agrees with my guess of your position, but perhaps you have something else in mind. I dunno.
Yes, you were spot on.
I am rather strongly opposed to introducing duplicated functionality. Rather than spending time on finding half a dozen links to conversations in the past, let me try to make the case afresh:
I have a very concrete example of the undesirable consequences: the commit-graph breakage Stolee and I debugged yesterday, where a bug hid in in_merge_bases_many() for two years, hidden by the fact that there was _duplicate logic_ in get_reachable_subset().
Pretty much _all_ of the code in reftable/ is _very_ new, has not seen any real-world testing, and as such cannot really be trusted, in particular the code that duplicates functionality already present in libgit.a. Note that I do not claim that Han-Wen cannot be trusted: to the contrary, I trust him very much (we have a history going all the way back to Lilypond, the musical typesetter). The code, however, cannot be trusted, and needs to be reviewed and tested in practice.
The simple fact is that even the thorough review that the commit-graph patches received did not prevent the min_generation/max_generation bug in in_merge_bases_many() from entering the code base, and if we had not had _another_ function doing very, very similar things, that bug would most likely not have lingered for _this_ long.
Likewise for the reimplementation of the convenient functionality like strbuf. This kind of duplication (in the case of struct strbuf, quite _literally_: there are now confusingly _two_ strbuf.h files that even try to implement the _exact same_ API) is _prone_ to lead to stale, or long undiscovered, bugs in one of the duplicated implementations.
Which means that we're likely to introduce bugs with those new functions introduced in reftable/. For the reftable functionality, that has to be accepted. For functions that do the same as equivalents in libgit.a, we do not have to accept it.
In any case, duplicated functionality is a maintenance burden, and especially in this case, an unnecessary one at that: unlike xdiff or compat/regex/, the reftable functionality is co-dependent with libgit.a. Yes, you can implement libreftable as a stand-alone library (including duplicated functionality from libgit.a, as I pointed out), but you _cannot use it independently of Git_.
The reftable concerns _Git_ refs.
This is not a compressor that could come in handy in a messenger app, or a diff engine that could potentially help with a semantic merging tool.
It is code whose entire purpose is to manage boatloads of Git refs efficiently. Therefore, I see no convincing reason to insist on maintaining this code outside of git/git.
It would make more sense to maintain parse-options or strbuf or t/test-lib.sh outside of git/git (because at least that functionality is independent enough of Git to be potentially useful to other projects) and we don't. We maintain them inside git/git, and for good reason: it makes development and maintenance a hell of a lot easier.
And once we agree that reftable is intended to be so integral a part of Git that it should absolutely have its home inside git/git, all of that duplicated functionality (with almost totally untested implementations, at least when it comes to "battle testing in production") can totally evaporate and does not need to worry us any more.
And then we can start to spend reviewers' time on the actual code revolving around the actual reftables rather than having to bother with reviewing, say, a close, but non-identical strbuf that might even have "borrowed" parts of the strbuf code and put it under a more permissive license (I don't know, and I don't want to know, that's part of the reason why I don't want to review the reftable patches in their current form).
In other words, we can then finally start to actually review the reftable code on its merits of teaching Git a valuable new feature.
Quoted 7 lines
Quoted 3 lines
Be that as it may, the CI build failures impacted my notifications' signal/noise ratio too much, so here goes (Junio, I would be delighted if you could apply this on top of your branch):
Quite honestly, this close to the first preview release for 2.29, I'd rather drop this round from 'seen' and expect an updated topic, than piling fixup on top of fixups.
Sounds good to me.
Ciao,
Dscho
Re: [PATCH v2 05/13] reftable: utility functions
In reply to Han-Wen Nienhuys via GitGitGadgetQuoted 17 lines
From: Han-Wen Nienhuys <[email protected]>
This commit provides basic utility classes for the reftable library.
Since the reftable library must compile standalone, there may be some overlap with git-core utility functions.
Signed-off-by Han-Wen Nienhuys <[email protected]>--- Makefile | 26 ++++++- reftable/basics.c | 131 +++++++++++++++++++++++++++++++++ reftable/basics.h | 48 +++++++++++++ reftable/blocksource.c | 148 ++++++++++++++++++++++++++++++++++++++ reftable/blocksource.h | 22 ++++++ reftable/compat.c | 110 ++++++++++++++++++++++++++++ reftable/compat.h | 48 +++++++++++++ reftable/publicbasics.c | 100 ++++++++++++++++++++++++++
Quoted 8 lines
reftable/reftable-tests.h | 22 ++++++ reftable/strbuf.c | 142 ++++++++++++++++++++++++++++++++++++ reftable/strbuf.h | 80 +++++++++++++++++++++ reftable/strbuf_test.c | 37 ++++++++++ reftable/system.h | 51 +++++++++++++ t/helper/test-reftable.c | 8 +++ t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 +
I think duplicating things like strbuf is an unnecessary burden if Git is to maintain this library. Something like "reftable will only import git-compat-util.h and strbuf.h, and any project that wants to use reftable must make sure that these functions and data structures are available" would be more plausible.
Re: [PATCH v2 05/13] reftable: utility functions
Quoted 5 lines · Jonathan Tan
Quoted 5 lines
From: Han-Wen Nienhuys <[email protected]>
This commit provides basic utility classes for the reftable library.
Quoted 8 lines
Quoted 2 lines
Since the reftable library must compile standalone, there may be some overlap with git-core utility functions.
I think duplicating things like strbuf is an unnecessary burden if Git is to maintain this library. Something like "reftable will only import
git-compat-util.handstrbuf.h, and any project that wants to use reftable must make sure that these functions and data structures are available" would be more plausible.
Sure, but how do we ensure that the directory won't take on dependencies beyond these headers? I am worried that I will be involved in a tedious back & forth process to keep updates going into libgit2 and/or also have to keep maintaining github.com/google/reftable.
FWIW, the duplication is really tiny: according to
$ wc $(grep -l REFTABLE_STANDALONE *[ch])
it's just 431 lines of code.
--
Han-Wen Nienhuys - Google Munich
I work 80%. Don't expect answers from me on Fridays. --
Google Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg
Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Re: [PATCH v2 05/13] reftable: utility functions
Hi Han-Wen,
Quoted 21 lines · Han-Wen Nienhuys
Quoted 5 lines · Jonathan Tan
Quoted 5 lines
From: Han-Wen Nienhuys <[email protected]>
This commit provides basic utility classes for the reftable library.
Quoted 8 lines
Quoted 2 lines
Since the reftable library must compile standalone, there may be some overlap with git-core utility functions.
I think duplicating things like strbuf is an unnecessary burden if Git is to maintain this library. Something like "reftable will only import
git-compat-util.handstrbuf.h, and any project that wants to use reftable must make sure that these functions and data structures are available" would be more plausible.Sure, but how do we ensure that the directory won't take on dependencies beyond these headers? I am worried that I will be involved in a tedious back & forth process to keep updates going into libgit2 and/or also have to keep maintaining github.com/google/reftable.
Quoted 5 lines
FWIW, the duplication is really tiny: according to
$ wc $(grep -l REFTABLE_STANDALONE *[ch])it's just 431 lines of code.
The merge_bases_many() function has only 33 lines of code, partially duplicating get_reachable_subset(). Yet, it had a bug in it for two years that was not found.
How much worse will the situation be with your 431 lines of code.
Even more so when you consider the fact that you intend to shove the same duplication down libgit2's throat. It's "triplicating" code.
So I find the argument you made above quite unconvincing.
Ciao,
Dscho
Re: [PATCH v2 05/13] reftable: utility functions
Hi,
Quoted 8 lines · Johannes Schindelin
The
merge_bases_many()function has only 33 lines of code, partially duplicatingget_reachable_subset(). Yet, it had a bug in it for two years that was not found.How much worse will the situation be with your 431 lines of code.
Even more so when you consider the fact that you intend to shove the same duplication down libgit2's throat. It's "triplicating" code.
Careful: you seem to be making a bunch of assumptions here (for example, around Han-Wen having some intent around shoving things down libgit2's throat), and you seem to be focusing on the person instead of the contribution.
Would you mind restating your point in a way that is a little easier to process, for example by focusing on the effect that this patch would have on you as a Git developer?
Thanks,
Jonathan
Quoted 1 line
So I find the argument you made above quite unconvincing.
Re: [PATCH v2 05/13] reftable: utility functions
Hi Jonathan,
Quoted 11 lines · Jonathan Nieder
Quoted 8 lines · Johannes Schindelin
The
merge_bases_many()function has only 33 lines of code, partially duplicatingget_reachable_subset(). Yet, it had a bug in it for two years that was not found.How much worse will the situation be with your 431 lines of code.
Even more so when you consider the fact that you intend to shove the same duplication down libgit2's throat. It's "triplicating" code.
Quoted 8 lines
Careful: you seem to be making a bunch of assumptions here (for example, around Han-Wen having some intent around shoving things down libgit2's throat), and you seem to be focusing on the person instead of the contribution.
Would you mind restating your point in a way that is a little easier to process, for example by focusing on the effect that this patch would have on you as a Git developer?
I really hope that Han-Wen did not take this as a personal attack.
In case he did, I'll gladly try to re-phrase my point: It does not matter how much code is duplicated. The fact that we run into bugs even in unintentionally duplicated code is reason enough to not lightly accept this design decision as being set in stone.
Which is why I, you, Emily and Josh pointed that out, and it is the same reason why Peff and Junio had made the same point in the past.
I was quite unpleasantly surprised to see that all of those objections seemed to be insufficient, hence my rather forceful reply.
As to the libgit2 angle: libgit2 also has strbuf (which it calls git_buf IIRC), and I am certain that it also has those other helper functions à la put_be32(). So yes, the triplication I mentioned is a very real, undesirable feature of this patch series.
To be honest, I would much rather spend my time reviewing patches that added reftable support using as much of libgit.a's functionality as possible rather than repeating the same point over and over again: that it makes very little sense _not_ to use that functionality. Of course, at this stage I understand that my feedback is not very welcome, so I will try to refrain from commenting on this patch series further (I do reserve the right to send patches that fix CI failures, just like I've done quite a few times up to this point).
Ciao,
Dscho
Re: [PATCH v2 05/13] reftable: utility functions
Quoted 2 lines · Johannes Schindelin
Of course, at
this stage I understand that my feedback is not very welcome,
Do you mean because you don't feel that your feedback has been sufficiently acted upon, or is there something else you are alluding to?
Puzzled,
Jonathan
Re: [PATCH v2 05/13] reftable: utility functions
Hi Jonathan,
Quoted 7 lines · Jonathan Nieder
Quoted 2 lines · Johannes Schindelin
Of course, at this stage I understand that my feedback is not very welcome,
Do you mean because you don't feel that your feedback has been sufficiently acted upon, or is there something else you are alluding to?
My feedback has not been acted upon, and my arguments have been dismissed (or there were attempts to dismiss them, I should say, that did not convince me). At least I saw that when gitster repeated some of my concerns in #git-devel, there were attempts to address them. While I feel bad about leaving the burden on gitster's shoulders, at least I am glad that those concerns are being addressed.
Ciao,
Dscho
Re: [PATCH v2 05/13] reftable: utility functions
In reply to Johannes SchindelinQuoted 9 lines · Johannes Schindelin
Quoted 5 lines
FWIW, the duplication is really tiny: according to
$ wc $(grep -l REFTABLE_STANDALONE *[ch])it's just 431 lines of code.
The
merge_bases_many()function has only 33 lines of code, partially duplicatingget_reachable_subset(). Yet, it had a bug in it for two years that was not found.
It does not affect the current discussion, but what you are giving is a revisionist view of the world. The latter function came MUCH later to do a bit more than the former. The bug was caused by the fact that those that added the latter neglected the responsibility of maintaining the former to the same degree when new feature like commit-graph were added to the latter.
The root cause was that the latter one did not share the code with the former one when it was introduced. That does make it appear similar to the situation we have at hand with duplicated utility functions.
Quoted 9 lines
How much worse will the situation be with your 431 lines of code.
Even more so when you consider the fact that you intend to shove the same duplication down libgit2's throat. It's "triplicating" code.
So I find the argument you made above quite unconvincing.
Ciao,
Dscho
Re: [PATCH v2 05/13] reftable: utility functions
Hi Junio,
Quoted 12 lines · Junio C Hamano
Quoted 9 lines · Johannes Schindelin
Quoted 5 lines
FWIW, the duplication is really tiny: according to
$ wc $(grep -l REFTABLE_STANDALONE *[ch])it's just 431 lines of code.
The
merge_bases_many()function has only 33 lines of code, partially duplicatingget_reachable_subset(). Yet, it had a bug in it for two years that was not found.
Quoted 6 lines
It does not affect the current discussion, but what you are giving is a revisionist view of the world. The latter function came MUCH later to do a bit more than the former. The bug was caused by the fact that those that added the latter neglected the responsibility of maintaining the former to the same degree when new feature like commit-graph were added to the latter.
You're right, I mixed up the direction: by introducing new code, the old code became under-tested and a bug creeped in that was not detected for two years.
Quoted 4 lines
The root cause was that the latter one did not share the code with the former one when it was introduced. That does make it appear similar to the situation we have at hand with duplicated utility functions.
Indeed, and even if this is just one concrete example, experience (and teachers and instructors and mentors) repeat the lesson that code duplication should be avoided because it _does_ cause problems.
Thanks for setting the record straight,
Dscho
Quoted 11 lines
Quoted 10 lines
How much worse will the situation be with your 431 lines of code.
Even more so when you consider the fact that you intend to shove the same duplication down libgit2's throat. It's "triplicating" code.
So I find the argument you made above quite unconvincing.
Ciao,
Dscho
Re: [PATCH v2 05/13] reftable: utility functions
In reply to Han-Wen NienhuysQuoted 21 lines · Han-Wen Nienhuys
Quoted 5 lines · Jonathan Tan
Quoted 5 lines
From: Han-Wen Nienhuys <[email protected]>
This commit provides basic utility classes for the reftable library.
Quoted 8 lines
Quoted 2 lines
Since the reftable library must compile standalone, there may be some overlap with git-core utility functions.
I think duplicating things like strbuf is an unnecessary burden if Git is to maintain this library. Something like "reftable will only import
git-compat-util.handstrbuf.h, and any project that wants to use reftable must make sure that these functions and data structures are available" would be more plausible.Sure, but how do we ensure that the directory won't take on dependencies beyond these headers? I am worried that I will be involved in a tedious back & forth process to keep updates going into libgit2 and/or also have to keep maintaining github.com/google/reftable.
Quoted 5 lines
FWIW, the duplication is really tiny: according to
$ wc $(grep -l REFTABLE_STANDALONE *[ch])it's just 431 lines of code.
Why import the dead code at all? If I add this to your update script:
# Not a general solution, but works for the specific code here.
perl -0777 -pi -e 's[(#ifndef REFTABLE_STANDALONE\n.*?\n#else\n).*?(?=\n#endif)][$1/* Removed upstream code for git.git import */]gs' reftable/system.h perl -0777 -pi -e 's[(?<=#ifdef REFTABLE_STANDALONE\n).*?(?=\n#endif)][/* Removed upstream code for git.git import */]gs' reftable/strbuf.c reftable/compat.h perl -0777 -pi -e 's[(?<=#ifdef REFTABLE_STANDALONE\n).*?(?=\n#else)][/* Removed upstream code for git.git import */]gs' reftable/compat.c reftable/strbuf.h
It's now 157 lines instead of 431.
I think doing that with a tiny bit more complexity in the update.sh script is a much lower maintenance burden.
It's not just about the number of lines, but things coming up in grep, and now unique code really stands out (e.g. strbuf_add_void, should probably be just added to the main strbuf.h if it's needed...), and of course the cost of attention of eyeballing an already big series on the ML & the churn every time we have updates.
Overall I'm all for having this carved out in its own directory at a cost of a bit more maintenance burden if it can be shared with libgit2 & be a standalone library.
I am concerned that it seems this code can't be maintained in git.git by anyone not willing to sign a contract with Google. I sent a tiny PR for a typo fix at [1] and got directed to sign [2] before someone at Google could look at it. I see brian raised this before in [3] but there wasn't a reply to that point.
Is there some summary of how this part of integrating it is supposed to work going forward?
At first glance that seems like a recipe for perma-forking this pretty much from day one, i.e.:
A. Upstream changes happen
B. We get a patch to the bundled library to this ML ==> Google employees maintaining A can't even look at B ==> Patch B can't be integrated into A
1. https://github.com/google/reftable/pull/2
2. https://cla.developers.google.com/about/google-individual
3. https://lore.kernel.org/git/[email protected]/
Re: [PATCH v2 05/13] reftable: utility functions
Quoted 5 lines · Ævar Arnfjörð Bjarmason
It's not just about the number of lines, but things coming up in grep, and now unique code really stands out (e.g.
strbuf_add_void, should probably be just added to the mainstrbuf.hif it's needed...), and of course the cost of attention of eyeballing an already big series on the ML & the churn every time we have updates.
These are all valid concerns.
Quoted 3 lines
Overall I'm all for having this carved out in its own directory at a cost of a bit more maintenance burden if it can be shared with libgit2 & be a standalone library.
Do you mean by "this" the reftable stuff as a whole, or only the duplicated support library part? If the latter is split out of what we import as our reftable/ directory, and stored in a separate directory that we do not have to import (because we have strbuf and other stuff) but other people may choose to use (because they may not have strbuf and other stuff), that may work. Also, if the contents of reftable/ directory wants a pluggable allocator, the main code can be written to call reftable_malloc (or whatever), with a thin shim to interface with us (i.e. reftable_malloc() would be implemented as a thin wrapper around xmalloc() for us) which is stored in a separate directory just for us to interface with the main reftable library. For libgit2, there will be a separate directory that uses a different implementation of reftable_malloc() that would let them plug their preferred allocator. An arrangement like that might work.
I do not offhand know if that kind of overhead is worth the trouble or if there are better ways, though.
Quoted 2 lines
I am concerned that it seems this code can't be maintained in git.git by anyone not willing to sign a contract with Google.
It can be maintained in git.git; the trouble comes when they want to update us.
I however suspect that, as the primary intended audience, it is hard to imagine that the reftable library as a standalone project will be successful without going through the usual reviews and testing in git.git. So even though there exists github.com/google/reftable repository, its contents may not matter very much in practice, unless they come here and beg for the change we make ourselves anyway. Perhaps I am being naive. I dunno.
Quoted 7 lines
I sent a tiny PR for
a typo fix at [1] and got directed to sign [2] before someone at Google could look at it. I see brian raised this before in [3] but there wasn't a reply to that point.Is there some summary of how this part of integrating it is supposed to work going forward?
Yeah, thanks for raising a good point. We definitely need to figure this part out.
Quoted 11 lines
At first glance that seems like a recipe for perma-forking this pretty much from day one, i.e.:
A. Upstream changes happen
B. We get a patch to the bundled library to this ML ==> Google employees maintaining A can't even look at B ==> Patch B can't be integrated into A1. https://github.com/google/reftable/pull/2
2. https://cla.developers.google.com/about/google-individual
3. https://lore.kernel.org/git/[email protected]/
PATCH v2 6/13 reftable: (de)serialization for the polymorphic record type.
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- Makefile | 2 + reftable/constants.h | 21 + reftable/record.c | 1116 ++++++++++++++++++++++++++++++++++++++ reftable/record.h | 137 +++++ reftable/record_test.c | 410 ++++++++++++++ t/helper/test-reftable.c | 1 + 6 files changed, 1687 insertions(+) create mode 100644 reftable/constants.h create mode 100644 reftable/record.c create mode 100644 reftable/record.h create mode 100644 reftable/record_test.c
Makefile +2 -0
@@ -2376,8 +2376,10 @@ REFTABLE_OBJS += reftable/basics.o REFTABLE_OBJS += reftable/blocksource.o REFTABLE_OBJS += reftable/publicbasics.o REFTABLE_OBJS += reftable/compat.o +REFTABLE_OBJS += reftable/record.o REFTABLE_OBJS += reftable/strbuf.o +REFTABLE_TEST_OBJS += reftable/record_test.o REFTABLE_TEST_OBJS += reftable/strbuf_test.o REFTABLE_TEST_OBJS += reftable/test_framework.o
reftable/constants.h new file +21 -0
@@ -0,0 +1,21 @@ +/* +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 CONSTANTS_H +#define CONSTANTS_H + +#define BLOCK_TYPE_LOG 'g' +#define BLOCK_TYPE_INDEX 'i' +#define BLOCK_TYPE_REF 'r' +#define BLOCK_TYPE_OBJ 'o' +#define BLOCK_TYPE_ANY 0 + +#define MAX_RESTARTS ((1 << 16) - 1) +#define DEFAULT_BLOCK_SIZE 4096 + +#endif
reftable/record.c new file +1116 -0
1,116 lines not on this page · raw message
reftable/record.h new file +137 -0
137 lines not on this page · raw message
reftable/record_test.c new file +410 -0
410 lines not on this page · raw message
t/helper/test-reftable.c +1 -0
@@ -3,6 +3,7 @@int cmd__reftable (int argc,const char **argv) { +record_test_main (argc, argv);strbuf_test_main (argc, argv);return 0 ; }
Signature · 1 line
gitgitgadget
Re: [PATCH v2 06/13] reftable: (de)serialization for the polymorphic record type.
reftable/record.c line 837
Quoted 3 lines · Han-Wen Nienhuys via GitGitGadget
reftable/record.c new file +3
-0+struct reftable_record reftable_new_record (uint8_t typ) +{ +struct reftable_record rec = {NULL };
I can see a lot of "sparse" inspired work went into this, but we would want to take the previous discussion into account, as nothing has changed since then.
https://lore.kernel.org/git/[email protected]/
https://lore.kernel.org/git/[email protected]/
Unfortunately we didn't reach a clear consensus back then; I thought we saw a fix to make sparse silent when we use the "= { 0 }" idiom in recent versions of sparse, but the above threads do not have any mention of it, so either I am misremembering or there were other discussions on the same topic where it was also mentioned.
Review
Quoted 1 line
(quoted diff) +1
-0+ switch (typ) {
Need blank before this line.
Thanks.
Re: [PATCH v2 06/13] reftable: (de)serialization for the polymorphic record type.
Quoted 10 lines · Junio C Hamano
reftable/record.c line 837
Quoted 3 lines · Han-Wen Nienhuys via GitGitGadget
reftable/record.c new file +3
-0+struct reftable_record reftable_new_record (uint8_t typ) +{ +struct reftable_record rec = {NULL };I can see a lot of "sparse" inspired work went into this, but we would want to take the previous discussion into account, as nothing has changed since then.
Quoted 3 lines
yep, sparse is equally happy with = { 0 }; here - at least any sparse version after 'v0.6.1-246-g41f651b4'. ;-) So, as I said yesterday, the last official release v0.6.2 should be fine, and I _think_ Debian Testing has a suitable version. (or you could build from source).
Quoted 5 lines
Unfortunately we didn't reach a clear consensus back then; I thought we saw a fix to make sparse silent when we use the "= { 0 }" idiom in recent versions of sparse, but the above threads do not have any mention of it, so either I am misremembering or there were other discussions on the same topic where it was also mentioned.
See commit 1c96642326 (sparse: allow '{ 0 }' to be used without warnings, 22-05-2020). We could actually remove that now, since '-Wno-universal-initializer' is now the default (but it doesn't hurt).
ATB,
Ramsay Jones
PATCH v2 7/13 reftable: reading/writing blocks
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
Includes a code snippet copied from zlib
--- Makefile | 3 + reftable/.gitattributes | 1 + reftable/block.c | 441 +++++++++++++++++++++++++++++++++++++++ reftable/block.h | 129 ++++++++++++ reftable/block_test.c | 158 ++++++++++++++ reftable/zlib-compat.c | 92 ++++++++ t/helper/test-reftable.c | 1 + 7 files changed, 825 insertions(+) create mode 100644 reftable/.gitattributes create mode 100644 reftable/block.c create mode 100644 reftable/block.h create mode 100644 reftable/block_test.c create mode 100644 reftable/zlib-compat.c
Makefile +3 -0
@@ -2373,12 +2373,15 @@ XDIFF_OBJS += xdiff/xprepare.o XDIFF_OBJS += xdiff/xutils.o REFTABLE_OBJS += reftable/basics.o +REFTABLE_OBJS += reftable/block.o REFTABLE_OBJS += reftable/blocksource.o REFTABLE_OBJS += reftable/publicbasics.o REFTABLE_OBJS += reftable/compat.o REFTABLE_OBJS += reftable/record.o REFTABLE_OBJS += reftable/strbuf.o +REFTABLE_OBJS += reftable/zlib-compat.o +REFTABLE_TEST_OBJS += reftable/block_test.o REFTABLE_TEST_OBJS += reftable/record_test.o REFTABLE_TEST_OBJS += reftable/strbuf_test.o REFTABLE_TEST_OBJS += reftable/test_framework.o
reftable/.gitattributes new file +1 -0
@@ -0,0 +1 @@ +/zlib-compat.c whitespace=-indent-with-non-tab,-trailing-space
reftable/block.c new file +441 -0
441 lines not on this page · raw message
reftable/block.h new file +129 -0
129 lines not on this page · raw message
reftable/block_test.c new file +158 -0
158 lines not on this page · raw message
reftable/zlib-compat.c new file +92 -0
92 lines not on this page · raw message
t/helper/test-reftable.c +1 -0
@@ -3,6 +3,7 @@int cmd__reftable (int argc,const char **argv) { +block_test_main (argc, argv);record_test_main (argc, argv);strbuf_test_main (argc, argv);return 0 ;
Signature · 1 line
gitgitgadget
PATCH v2 8/13 reftable: a generic binary tree implementation
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
This is necessary for building a OID => ref map on write
--- Makefile | 2 ++ reftable/tree.c | 63 ++++++++++++++++++++++++++++++++++++++++ reftable/tree.h | 34 ++++++++++++++++++++++ reftable/tree_test.c | 62 +++++++++++++++++++++++++++++++++++++++ t/helper/test-reftable.c | 1 + 5 files changed, 162 insertions(+) create mode 100644 reftable/tree.c create mode 100644 reftable/tree.h create mode 100644 reftable/tree_test.c
Makefile +2 -0
@@ -2379,12 +2379,14 @@ REFTABLE_OBJS += reftable/publicbasics.o REFTABLE_OBJS += reftable/compat.o REFTABLE_OBJS += reftable/record.o REFTABLE_OBJS += reftable/strbuf.o +REFTABLE_OBJS += reftable/tree.o REFTABLE_OBJS += reftable/zlib-compat.o REFTABLE_TEST_OBJS += reftable/block_test.o REFTABLE_TEST_OBJS += reftable/record_test.o REFTABLE_TEST_OBJS += reftable/strbuf_test.o REFTABLE_TEST_OBJS += reftable/test_framework.o +REFTABLE_TEST_OBJS += reftable/tree_test.o TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
reftable/tree.c new file +63 -0
63 lines not on this page · raw message
reftable/tree.h new file +34 -0
@@ -0,0 +1,34 @@ +/* +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 TREE_H +#define TREE_H + +/* tree_node is a generic binary search tree. */ +struct tree_node { +void *key; +struct tree_node *left, *right; +}; + +/* looks for `key` in `rootp` using `compare` as comparison function. If insert +is set, insert the key if it's not found. Else, return NULL. +*/ +struct tree_node *tree_search (void *key,struct tree_node **rootp, +int (*compare)(const void *,const void *), +int insert); + +/* performs an infix walk of the tree. */ +void infix_walk (struct tree_node *t,void (*action)(void *arg,void *key), +void *arg); + +/* +deallocates the tree nodes recursively. Keys should be deallocated separately +by walking over the tree. */ +void tree_free (struct tree_node *t); + +#endif
reftable/tree_test.c new file +62 -0
62 lines not on this page · raw message
t/helper/test-reftable.c +1 -0
@@ -6,5 +6,6 @@ int cmd__reftable(int argc, const char **argv)block_test_main (argc, argv);record_test_main (argc, argv);strbuf_test_main (argc, argv); +tree_test_main (argc, argv);return 0 ; }
Signature · 1 line
gitgitgadget
PATCH v2 9/13 reftable: write reftable files
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- Makefile | 1 + reftable/writer.c | 673 ++++++++++++++++++++++++++++++++++++++++++++++ reftable/writer.h | 51 ++++ 3 files changed, 725 insertions(+) create mode 100644 reftable/writer.c create mode 100644 reftable/writer.h
Makefile +1 -0
@@ -2380,6 +2380,7 @@ REFTABLE_OBJS += reftable/compat.o REFTABLE_OBJS += reftable/record.o REFTABLE_OBJS += reftable/strbuf.o REFTABLE_OBJS += reftable/tree.o +REFTABLE_OBJS += reftable/writer.o REFTABLE_OBJS += reftable/zlib-compat.o REFTABLE_TEST_OBJS += reftable/block_test.o
reftable/writer.c new file +673 -0
673 lines not on this page · raw message
reftable/writer.h new file +51 -0
51 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH v2 10/13 reftable: read reftable files
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- Makefile | 3 + reftable/iter.c | 242 +++++++++++++++ reftable/iter.h | 72 +++++ reftable/reader.c | 733 ++++++++++++++++++++++++++++++++++++++++++++ reftable/reader.h | 78 +++++ reftable/reftable.c | 104 +++++++ 6 files changed, 1232 insertions(+) create mode 100644 reftable/iter.c create mode 100644 reftable/iter.h create mode 100644 reftable/reader.c create mode 100644 reftable/reader.h create mode 100644 reftable/reftable.c
Makefile +3 -0
@@ -2377,7 +2377,10 @@ REFTABLE_OBJS += reftable/block.o REFTABLE_OBJS += reftable/blocksource.o REFTABLE_OBJS += reftable/publicbasics.o REFTABLE_OBJS += reftable/compat.o +REFTABLE_OBJS += reftable/iter.o +REFTABLE_OBJS += reftable/reader.o REFTABLE_OBJS += reftable/record.o +REFTABLE_OBJS += reftable/reftable.o REFTABLE_OBJS += reftable/strbuf.o REFTABLE_OBJS += reftable/tree.o REFTABLE_OBJS += reftable/writer.o
reftable/iter.c new file +242 -0
242 lines not on this page · raw message
reftable/iter.h new file +72 -0
72 lines not on this page · raw message
reftable/reader.c new file +733 -0
733 lines not on this page · raw message
reftable/reader.h new file +78 -0
78 lines not on this page · raw message
reftable/reftable.c new file +104 -0
104 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH v2 11/13 reftable: file level tests
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- Makefile | 1 + reftable/reftable_test.c | 585 +++++++++++++++++++++++++++++++++++++++ t/helper/test-reftable.c | 1 + 3 files changed, 587 insertions(+) create mode 100644 reftable/reftable_test.c
Makefile +1 -0
@@ -2388,6 +2388,7 @@ REFTABLE_OBJS += reftable/zlib-compat.o REFTABLE_TEST_OBJS += reftable/block_test.o REFTABLE_TEST_OBJS += reftable/record_test.o +REFTABLE_TEST_OBJS += reftable/reftable_test.o REFTABLE_TEST_OBJS += reftable/strbuf_test.o REFTABLE_TEST_OBJS += reftable/test_framework.o REFTABLE_TEST_OBJS += reftable/tree_test.o
reftable/reftable_test.c new file +585 -0
585 lines not on this page · raw message
t/helper/test-reftable.c +1 -0
@@ -5,6 +5,7 @@ int cmd__reftable(int argc, const char **argv) {block_test_main (argc, argv);record_test_main (argc, argv); +reftable_test_main (argc, argv);strbuf_test_main (argc, argv);tree_test_main (argc, argv);return 0 ;
Signature · 1 line
gitgitgadget
PATCH v2 12/13 reftable: rest of library
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
This will be further split up once preceding commits have passed review.
--- Makefile | 8 +- reftable/VERSION | 1 + reftable/dump.c | 212 +++++++ reftable/merged.c | 358 +++++++++++ reftable/merged.h | 36 ++ reftable/merged_test.c | 331 ++++++++++ reftable/pq.c | 115 ++++ reftable/pq.h | 32 + reftable/refname.c | 209 +++++++ reftable/refname.h | 28 + reftable/refname_test.c | 100 +++ reftable/reftable.c | 4 +- reftable/stack.c | 1240 ++++++++++++++++++++++++++++++++++++++ reftable/stack.h | 40 ++ reftable/stack_test.c | 788 ++++++++++++++++++++++++ reftable/update.sh | 22 + t/helper/test-reftable.c | 3 + 17 files changed, 3524 insertions(+), 3 deletions(-) create mode 100644 reftable/VERSION create mode 100644 reftable/dump.c 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/refname.c create mode 100644 reftable/refname.h create mode 100644 reftable/refname_test.c create mode 100644 reftable/stack.c create mode 100644 reftable/stack.h create mode 100644 reftable/stack_test.c create mode 100755 reftable/update.sh
Makefile +7 -1
@@ -2375,12 +2375,16 @@ XDIFF_OBJS += xdiff/xutils.o REFTABLE_OBJS += reftable/basics.o REFTABLE_OBJS += reftable/block.o REFTABLE_OBJS += reftable/blocksource.o -REFTABLE_OBJS += reftable/publicbasics.o REFTABLE_OBJS += reftable/compat.o REFTABLE_OBJS += reftable/iter.o +REFTABLE_OBJS += reftable/merged.o +REFTABLE_OBJS += reftable/pq.o +REFTABLE_OBJS += reftable/publicbasics.o REFTABLE_OBJS += reftable/reader.o REFTABLE_OBJS += reftable/record.o +REFTABLE_OBJS += reftable/refname.o REFTABLE_OBJS += reftable/reftable.o +REFTABLE_OBJS += reftable/stack.o REFTABLE_OBJS += reftable/strbuf.o REFTABLE_OBJS += reftable/tree.o REFTABLE_OBJS += reftable/writer.o
@@ -2388,7 +2392,9 @@ REFTABLE_OBJS += reftable/zlib-compat.o REFTABLE_TEST_OBJS += reftable/block_test.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/stack_test.o REFTABLE_TEST_OBJS += reftable/strbuf_test.o REFTABLE_TEST_OBJS += reftable/test_framework.o REFTABLE_TEST_OBJS += reftable/tree_test.o
reftable/VERSION new file +1 -0
@@ -0,0 +1 @@ +7134eb9f8171a9759800f4187f9e6dde997335e7 C: NULL iso 0 for init
reftable/dump.c new file +212 -0
212 lines not on this page · raw message
reftable/merged.c new file +358 -0
358 lines not on this page · raw message
reftable/merged.h new file +36 -0
@@ -0,0 +1,36 @@ +/* +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 MERGED_H +#define MERGED_H + +#include "pq.h" +#include "reftable.h" + +struct reftable_merged_table { +struct reftable_table *stack; +size_t stack_len; +uint32_t hash_id; +int suppress_deletions; + +uint64_t min; +uint64_t max; +}; + +struct merged_iter { +struct reftable_iterator *stack; +uint32_t hash_id; +size_t stack_len; +uint8_t typ; +int suppress_deletions; +struct merged_iter_pqueue pq; +}; + +void merged_table_clear (struct reftable_merged_table *mt); + +#endif
reftable/merged_test.c new file +331 -0
331 lines not on this page · raw message
reftable/pq.c new file +115 -0
115 lines not on this page · raw message
reftable/pq.h new file +32 -0
@@ -0,0 +1,32 @@ +/* +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 PQ_H +#define PQ_H + +#include "record.h" + +struct pq_entry { +int index; +struct reftable_record rec; +}; + +struct merged_iter_pqueue { +struct pq_entry *heap; +size_t len; +size_t cap; +}; + +struct pq_entry merged_iter_pqueue_top (struct merged_iter_pqueue pq); +int merged_iter_pqueue_is_empty (struct merged_iter_pqueue pq); +void merged_iter_pqueue_check (struct merged_iter_pqueue pq); +struct pq_entry merged_iter_pqueue_remove (struct merged_iter_pqueue *pq); +void merged_iter_pqueue_add (struct merged_iter_pqueue *pq,struct pq_entry e); +void merged_iter_pqueue_clear (struct merged_iter_pqueue *pq); + +#endif
reftable/refname.c new file +209 -0
209 lines not on this page · raw message
reftable/refname.h new file +28 -0
@@ -0,0 +1,28 @@ +/* +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 REFNAME_H +#define REFNAME_H + +#include "reftable.h" + +struct modification { +struct reftable_table tab; + +char **add; +size_t add_len; + +char **del; +size_t del_len; +}; + +int validate_ref_record_addition (struct reftable_table tab, +struct reftable_ref_record *recs,size_t sz); + +int modification_validate (struct modification *mod); + +#endif
reftable/refname_test.c new file +100 -0
100 lines not on this page · raw message
reftable/reftable.c +2 -2
@@ -44,7 +44,7 @@ int reftable_table_seek_ref(struct reftable_table *tab,struct reftable_ref_record ref = { .refname = (char *)name, }; -struct reftable_record rec = {0 }; +struct reftable_record rec = {NULL };reftable_record_from_ref (&rec, &ref);return tab->ops->seek_record (tab->table_arg, it, &rec); }
@@ -60,7 +60,7 @@ void reftable_table_from_reader(struct reftable_table *tab,int reftable_table_read_ref (struct reftable_table *tab,const char *name,struct reftable_ref_record *ref) { -struct reftable_iterator it = {0 }; +struct reftable_iterator it = {NULL };int err =reftable_table_seek_ref (tab, &it, name);if (err) goto done;
reftable/stack.c new file +1240 -0
1,240 lines not on this page · raw message
reftable/stack.h new file +40 -0
@@ -0,0 +1,40 @@ +/* +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 STACK_H +#define STACK_H + +#include "reftable.h" +#include "system.h" + +struct reftable_stack { +char *list_file; +char *reftable_dir; +int disable_auto_compact; + +struct reftable_write_options config; + +struct reftable_reader **readers; +size_t readers_len; +struct reftable_merged_table *merged; +struct reftable_compaction_stats stats; +}; + +int read_lines (const char *filename,char ***lines); + +struct segment { +int start, end; +int log; +uint64_t bytes; +}; + +int fastlog2 (uint64_t sz); +struct segment *sizes_to_segments (int *seglen,uint64_t *sizes,int n); +struct segment suggest_compaction_segment (uint64_t *sizes,int n); + +#endif
reftable/stack_test.c new file +788 -0
256 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Signature · 1 line
gitgitgadget
Part of the diff is not shown here. View the raw message.
Re: [PATCH v2 12/13] reftable: rest of library
Hi Han-Wen,
Quoted 3 lines · Han-Wen Nienhuys via GitGitGadget
From: Han-Wen Nienhuys <[email protected]>
This will be further split up once preceding commits have passed review.
Before you further split it up, I encourage you to include these patches without which the CI builds will continue to fail (Junio, could I ask you to either cherry-pick them from https://github.com/git-for-windows/git's
shears/seen branch, or apply them from the mbox?):
-- snipsnap --
From e485e006f34922439f2e971a1c5c38b8ca56c011 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <[email protected]>
Date: Wed, 30 Sep 2020 14:46:59 +0200
Subject: [PATCH 1/3] fixup??? reftable: rest of library
struct abc x = {}is a GNUism.
--- reftable/dump.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
reftable/dump.c +2 -2
@@ -75,7 +75,7 @@ static int dump_table(const char *tablename)static int compact_stack (const char *stackdir) {struct reftable_stack *stack =NULL ; -struct reftable_write_options cfg = {}; +struct reftable_write_options cfg = {0 };int err =reftable_new_stack (&stack, stackdir, cfg);if (err <0 )
@@ -94,7 +94,7 @@ static int compact_stack(const char *stackdir)static int dump_stack (const char *stackdir) {struct reftable_stack *stack =NULL ; -struct reftable_write_options cfg = {}; +struct reftable_write_options cfg = {0 };struct reftable_iterator it = {NULL };struct reftable_ref_record ref = {NULL };struct reftable_log_record log = {NULL };
--
2.28.0.windows.1.18.g5300e52e185
From d5faa818a1bc00016e310d27602551127db620fb Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <[email protected]>
Date: Wed, 30 Sep 2020 14:55:28 +0200
Subject: [PATCH 2/3] fixup??? reftable: rest of library
0-sized arrays are actually not portable.
--- reftable/stack_test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
reftable/stack_test.c +2 -2
@@ -572,11 +572,11 @@ static void test_sizes_to_segments(void)static void test_sizes_to_segments_empty (void ) { -uint64_t sizes[0 ]; +uint64_t sizes[1 ];int seglen =0 ;struct segment *segs = -sizes_to_segments (&seglen, sizes,ARRAY_SIZE (sizes)); +sizes_to_segments (&seglen, sizes,0 );assert (seglen ==0 );reftable_free (segs); }
--
2.28.0.windows.1.18.g5300e52e185
From d446e1a7354c676d60114b50ba96a6ea083441ae Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <[email protected]>
Date: Wed, 30 Sep 2020 15:19:28 +0200
Subject: [PATCH 3/3] fixup??? reftable: rest of library
Avoid using getopt(): it might be POSIX, but Git's audience is much larger than POSIX. MSVC, for example, does not support getopt().
--- reftable/dump.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-)
reftable/dump.c +13 -19
@@ -160,40 +160,34 @@ static void print_help(void)int reftable_dump_main (int argc,char *const *argv) {int err =0 ; -int opt;int opt_dump_table =0 ;int opt_dump_stack =0 ;int opt_compact =0 ; -const char *arg =NULL ; -while ((opt =getopt (argc, argv,"2chts" )) !=-1 ) { -switch (opt) { -case '2' : - hash_id =0x73323536 ; +const char *arg =NULL , *argv0 = argv[0 ]; + +for (; argc >1 ; argv++, argc--) +if (*argv[1 ] !='-' )break ; -case 't' : +else if (!strcmp ("-2" , argv[1 ])) + hash_id =0x73323536 ; +else if (!strcmp ("-t" , argv[1 ])) opt_dump_table =1 ; -break ; -case 's' : +else if (!strcmp ("-s" , argv[1 ])) opt_dump_stack =1 ; -break ; -case 'c' : +else if (!strcmp ("-c" , argv[1 ])) opt_compact =1 ; -break ; -case '?' : -case 'h' : +else if (!strcmp ("-?" , argv[1 ]) || !strcmp ("-h" , argv[1 ])) {print_help ();return 2 ; -break ; } - } -if (argv[optind] ==NULL ) { +if (argc !=2 ) {fprintf (stderr,"need argument\n" );print_help ();return 2 ; } - arg = argv[optind]; + arg = argv[1 ];if (opt_dump_table) { err =dump_table (arg);
@@ -204,7 +198,7 @@ int reftable_dump_main(int argc, char *const *argv) }if (err <0 ) { -fprintf (stderr,"%s: %s: %s\n" , argv[0 ], arg, +fprintf (stderr,"%s: %s: %s\n" , argv0, arg,reftable_error_str (err));return 1 ; }
--
2.28.0.windows.1.18.g5300e52e185
Re: [PATCH v2 12/13] reftable: rest of library
Quoted 1 line · Johannes Schindelin
Subject: [PATCH 1/3] fixup??? reftable: rest of library
This is unambiguously good.
Quoted 6 lines
Subject: [PATCH 2/3] fixup??? reftable: rest of library
0-sized arrays are actually not portable.
...
static voidtest_sizes_to_segments_empty(void)
{
reftable/stack_test.c static void test_sizes_to_segments(void) · line 581
Quoted 9 lines
reftable/stack_test.c +2
-2-uint64_t sizes[0 ]; +uint64_t sizes[1 ];int seglen =0 ;struct segment *segs = -sizes_to_segments (&seglen, sizes,ARRAY_SIZE (sizes)); +sizes_to_segments (&seglen, sizes,0 );assert (seglen ==0 );reftable_free (segs);
Question to Han-Wen.
It is unclear what this test wants to test. Do we even need sizes[] array if we know we are passing a hardcoded 0? IOW, I would understand if the test were
sizes_to_segments(&seglen, NULL, 0);
to ensure that sizes_to_segments do not even attempt to look at sizes[] array when the number of elements is 0.
Quoted 4 lines
Subject: [PATCH 3/3] fixup??? reftable: rest of library
Avoid using
getopt(): it might be POSIX, but Git's audience is much larger than POSIX. MSVC, for example, does not supportgetopt().
Either that, or we could use parse-options(). I do not care either way, as this seems to be purely for debugging?
Re: [PATCH v2 12/13] reftable: rest of library
Hi Junio,
Quoted 9 lines · Junio C Hamano
Quoted 4 lines · Johannes Schindelin
Subject: [PATCH 3/3] fixup??? reftable: rest of library
Avoid using
getopt(): it might be POSIX, but Git's audience is much larger than POSIX. MSVC, for example, does not supportgetopt().Either that, or we could use parse-options(). I do not care either way, as this seems to be purely for debugging?
I actually wanted to use parse_options(), but due to the way reftable/ insists on being completely separate from libgit.a, it is not really trivial, as #include "parse-options.h" fails (because git-compat-util.h was not included, and IIRC including _that_ resulted in more compile errors, at which stage I opted for a more minimal solution).
So yeah, I would strongly expect reftable/ to become considerably simpler once it starts using libgit.a's goodies.
Ciao,
Dscho
PATCH v2 13/13 reftable: "test-tool dump-reftable" command.
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
This command dumps individual tables or a stack of of tables.
--- Makefile | 2 ++ reftable/iter.c | 6 +++--- reftable/reader.c | 22 +++++++++++----------- t/helper/test-reftable.c | 5 +++++ t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 + 6 files changed, 23 insertions(+), 14 deletions(-)
Makefile +2 -0
@@ -2391,6 +2391,8 @@ REFTABLE_OBJS += reftable/writer.o REFTABLE_OBJS += reftable/zlib-compat.o REFTABLE_TEST_OBJS += reftable/block_test.o +REFTABLE_TEST_OBJS += reftable/dump.o +REFTABLE_TEST_OBJS += reftable/merged_test.o REFTABLE_TEST_OBJS += reftable/record_test.o REFTABLE_TEST_OBJS += reftable/refname_test.o REFTABLE_TEST_OBJS += reftable/reftable_test.o
reftable/iter.c +3 -3
@@ -59,7 +59,7 @@ void reftable_iterator_destroy(struct reftable_iterator *it)int reftable_iterator_next_ref (struct reftable_iterator *it,struct reftable_ref_record *ref) { -struct reftable_record rec = {0 }; +struct reftable_record rec = {NULL };reftable_record_from_ref (&rec, ref);return iterator_next (it, &rec); }
@@ -67,7 +67,7 @@ int reftable_iterator_next_ref(struct reftable_iterator *it,int reftable_iterator_next_log (struct reftable_iterator *it,struct reftable_log_record *log) { -struct reftable_record rec = {0 }; +struct reftable_record rec = {NULL };reftable_record_from_log (&rec, log);return iterator_next (it, &rec); }
@@ -95,7 +95,7 @@ static int filtering_ref_iterator_next(void *iter_arg, }if (fri->double_check) { -struct reftable_iterator it = {0 }; +struct reftable_iterator it = {NULL }; err =reftable_table_seek_ref (&fri->tab, &it, ref->refname);
reftable/reader.c +11 -11
@@ -161,8 +161,8 @@ static int parse_footer(struct reftable_reader *r, uint8_t *footer,int init_reader (struct reftable_reader *r,struct reftable_block_source *source,const char *name) { -struct reftable_block footer = {0 }; -struct reftable_block header = {0 }; +struct reftable_block footer = {NULL }; +struct reftable_block header = {NULL };int err =0 ;memset (r,0 ,sizeof (struct reftable_reader ));
@@ -270,7 +270,7 @@ int reader_init_block_reader(struct reftable_reader *r, struct block_reader *br, {int32_t guess_block_size = r->block_size ? r->block_size :DEFAULT_BLOCK_SIZE ; -struct reftable_block block = {0 }; +struct reftable_block block = {NULL };uint8_t block_typ =0 ;int err =0 ;uint32_t header_off = next_off ?0 :header_size (r->version);
@@ -477,9 +477,9 @@ static int reader_seek_indexed(struct reftable_reader *r, struct reftable_record *rec) {struct reftable_index_record want_index = { .last_key =STRBUF_INIT }; -struct reftable_record want_index_rec = {0 }; +struct reftable_record want_index_rec = {NULL };struct reftable_index_record index_result = { .last_key =STRBUF_INIT }; -struct reftable_record index_result_rec = {0 }; +struct reftable_record index_result_rec = {NULL };struct table_iter index_iter =TABLE_ITER_INIT ;struct table_iter next =TABLE_ITER_INIT ;int err =0 ;
@@ -584,7 +584,7 @@ int reftable_reader_seek_ref(struct reftable_reader *r,struct reftable_ref_record ref = { .refname = (char *)name, }; -struct reftable_record rec = {0 }; +struct reftable_record rec = {NULL };reftable_record_from_ref (&rec, &ref);return reader_seek (r, it, &rec); }
@@ -597,7 +597,7 @@ int reftable_reader_seek_log_at(struct reftable_reader *r, .refname = (char *)name, .update_index = update_index, }; -struct reftable_record rec = {0 }; +struct reftable_record rec = {NULL };reftable_record_from_log (&rec, &log);return reader_seek (r, it, &rec); }
@@ -644,10 +644,10 @@ static int reftable_reader_refs_for_indexed(struct reftable_reader *r, .hash_prefix = oid, .hash_prefix_len = r->object_id_len, }; -struct reftable_record want_rec = {0 }; -struct reftable_iterator oit = {0 }; -struct reftable_obj_record got = {0 }; -struct reftable_record got_rec = {0 }; +struct reftable_record want_rec = {NULL }; +struct reftable_iterator oit = {NULL }; +struct reftable_obj_record got = {NULL }; +struct reftable_record got_rec = {NULL };int err =0 ;struct indexed_table_ref_iter *itr =NULL ;
t/helper/test-reftable.c +5 -0
@@ -13,3 +13,8 @@ int cmd__reftable(int argc, const char **argv)tree_test_main (argc, argv);return 0 ; } + +int cmd__dump_reftable (int argc,const char **argv) +{ +return reftable_dump_main (argc, (char *const *)argv); +}
t/helper/test-tool.c +1 -0
@@ -54,6 +54,7 @@ static struct test_cmd cmds[] = { { "read-midx", cmd__read_midx }, { "ref-store", cmd__ref_store }, { "reftable", cmd__reftable }, + { "dump-reftable", cmd__dump_reftable }, { "regex", cmd__regex }, { "repository", cmd__repository }, { "revision-walking", cmd__revision_walking },
t/helper/test-tool.h +1 -0
@@ -17,6 +17,7 @@ int cmd__dump_cache_tree(int argc, const char **argv);int cmd__dump_fsmonitor (int argc,const char **argv);int cmd__dump_split_index (int argc,const char **argv);int cmd__dump_untracked_cache (int argc,const char **argv); +int cmd__dump_reftable (int argc,const char **argv);int cmd__example_decorate (int argc,const char **argv);int cmd__genrandom (int argc,const char **argv);int cmd__genzeros (int argc,const char **argv);
Signature · 1 line
gitgitgadget
v3 cover reftable library
In reply to Han-Wen Nienhuys via GitGitGadgetThis splits the giant commit from
https://github.com/gitgitgadget/git/pull/539 into a series of smaller commits, which build and have unittests.
The final commit should also be split up, but I want to wait until we have consensus that the bottom commits look good.
version 26 Nov 2020:
* split public API header into multiple headers
* minor adaptions in response to review feedback
* filter out code for standalone compile
* get rid of test framework
Han-Wen Nienhuys (15):
move sleep_millisec to git-compat-util.h
init-db: set the_repository->hash_algo early on
reftable: add LICENSE
reftable: add error related functionality
reftable: utility functions
reftable: add blocksource, an abstraction for random access reads
reftable: (de)serialization for the polymorphic record type.
reftable: reading/writing blocks
reftable: a generic binary tree implementation
reftable: write reftable files
reftable: read reftable files
reftable: reftable file level tests
reftable: rest of library
Reftable support for git-core
Add "test-tool dump-reftable" command.
SZEDER Gábor (1):
git-prompt: prepare for reftable refs backend
.../technical/repository-version.txt | 7 + Makefile | 46 +- builtin/clone.c | 5 +- builtin/init-db.c | 56 +- builtin/worktree.c | 27 +- cache.h | 9 +- config.mak.uname | 2 +- contrib/buildsystems/Generators/Vcxproj.pm | 11 +- contrib/completion/git-prompt.sh | 7 +- git-compat-util.h | 2 + refs.c | 27 +- refs.h | 3 + refs/refs-internal.h | 1 + refs/reftable-backend.c | 1418 +++++++++++++++++ reftable/.gitattributes | 1 + reftable/LICENSE | 31 + reftable/VERSION | 1 + reftable/basics.c | 144 ++ reftable/basics.h | 56 + reftable/basics_test.c | 56 + reftable/block.c | 440 +++++ reftable/block.h | 129 ++ reftable/block_test.c | 119 ++ reftable/blocksource.c | 148 ++ reftable/blocksource.h | 22 + reftable/compat.h | 48 + reftable/constants.h | 21 + reftable/dump.c | 219 +++ reftable/error.c | 39 + reftable/iter.c | 242 +++ reftable/iter.h | 75 + reftable/merged.c | 366 +++++ reftable/merged.h | 35 + reftable/merged_test.c | 333 ++++ reftable/pq.c | 115 ++ reftable/pq.h | 32 + reftable/publicbasics.c | 58 + reftable/reader.c | 733 +++++++++ reftable/reader.h | 75 + reftable/record.c | 1116 +++++++++++++ reftable/record.h | 139 ++ reftable/record_test.c | 404 +++++ reftable/refname.c | 209 +++ reftable/refname.h | 29 + reftable/refname_test.c | 100 ++ reftable/reftable-blocksource.h | 50 + reftable/reftable-error.h | 62 + reftable/reftable-generic.h | 49 + reftable/reftable-iterator.h | 37 + reftable/reftable-malloc.h | 20 + reftable/reftable-merged.h | 68 + reftable/reftable-reader.h | 89 ++ reftable/reftable-record.h | 73 + reftable/reftable-stack.h | 116 ++ reftable/reftable-tests.h | 22 + reftable/reftable-writer.h | 149 ++ reftable/reftable.c | 98 ++ reftable/reftable_test.c | 577 +++++++ reftable/stack.c | 1247 +++++++++++++++ reftable/stack.h | 41 + reftable/stack_test.c | 781 +++++++++ reftable/system.h | 32 + reftable/test_framework.c | 23 + reftable/test_framework.h | 48 + reftable/tree.c | 63 + reftable/tree.h | 34 + reftable/tree_test.c | 61 + reftable/writer.c | 676 ++++++++ reftable/writer.h | 50 + reftable/zlib-compat.c | 92 ++ repository.c | 2 + repository.h | 3 + setup.c | 9 +- t/helper/test-reftable.c | 20 + t/helper/test-tool.c | 4 +- t/helper/test-tool.h | 2 + t/t0031-reftable.sh | 199 +++ t/t0032-reftable-unittest.sh | 14 + t/t1409-avoid-packing-refs.sh | 6 + t/t1450-fsck.sh | 6 + t/t3210-pack-refs.sh | 6 + t/test-lib.sh | 5 + 82 files changed, 11951 insertions(+), 39 deletions(-) create mode 100644 refs/reftable-backend.c create mode 100644 reftable/.gitattributes create mode 100644 reftable/LICENSE create mode 100644 reftable/VERSION create mode 100644 reftable/basics.c create mode 100644 reftable/basics.h create mode 100644 reftable/basics_test.c create mode 100644 reftable/block.c create mode 100644 reftable/block.h create mode 100644 reftable/block_test.c create mode 100644 reftable/blocksource.c create mode 100644 reftable/blocksource.h create mode 100644 reftable/compat.h create mode 100644 reftable/constants.h create mode 100644 reftable/dump.c create mode 100644 reftable/error.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/publicbasics.c 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-blocksource.h create mode 100644 reftable/reftable-error.h create mode 100644 reftable/reftable-generic.h create mode 100644 reftable/reftable-iterator.h create mode 100644 reftable/reftable-malloc.h create mode 100644 reftable/reftable-merged.h create mode 100644 reftable/reftable-reader.h create mode 100644 reftable/reftable-record.h create mode 100644 reftable/reftable-stack.h create mode 100644 reftable/reftable-tests.h create mode 100644 reftable/reftable-writer.h create mode 100644 reftable/reftable.c 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/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 100644 reftable/writer.c create mode 100644 reftable/writer.h create mode 100644 reftable/zlib-compat.c create mode 100644 t/helper/test-reftable.c create mode 100755 t/t0031-reftable.sh create mode 100755 t/t0032-reftable-unittest.sh
base-commit: faefdd61ec7c7f6f3c8c9907891465ac9a2a1475
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-847%2Fhanwen%2Flibreftable-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-847/hanwen/libreftable-v3
Pull-Request: https://github.com/git/git/pull/847
Range-diff vs v2:
-: ---------- > 1: 030998a732 move sleep_millisec to git-compat-util.h
-: ---------- > 2: cf667653df init-db: set the_repository->hash_algo early on
1: 6228103b4a = 3: 91c3ac2449 reftable: add LICENSE
2: 5d1b946ab5 ! 4: 2aa30f536f reftable: define the public API
@@ Metadata
Author: Han-Wen Nienhuys <[email protected]>
## Commit message ##
- reftable: define the public API
+ reftable: add error related functionality
+
+ The reftable/ directory is structured as a library, so it cannot
+ crash on misuse. Instead, it returns an error codes.
+
+ In addition, the error code can be used to signal conditions from lower levels
+ of the library to be handled by higher levels of the library. For example, a
+ transaction might legitimately write an empty reftable file, but in that case,
+ we'd want to shortcut the transaction overhead.
Signed-off-by: Han-Wen Nienhuys <[email protected]>
- ## reftable/reftable.h (new) ##
+ ## reftable/error.c (new) ##
@@
+/*
+Copyright 2020 Google LLC
@@ reftable/reftable.h (new)
+https://developers.google.com/open-source/licenses/bsd
+*/
+
-+#ifndef REFTABLE_H
-+#define REFTABLE_H
-+
-+#include <stdint.h>
-+#include <stddef.h>
-+
-+void reftable_set_alloc(void *(*malloc)(size_t),
-+ void *(*realloc)(void *, size_t), void (*free)(void *));
-+
-+/****************************************************************
-+ Basic data types
-+
-+ Reftables store the state of each ref in struct reftable_ref_record, and they
-+ store a sequence of reflog updates in struct reftable_log_record.
-+ ****************************************************************/
-+
-+/* reftable_ref_record holds a ref database entry target_value */
-+struct reftable_ref_record {
-+ char *refname; /* Name of the ref, malloced. */
-+ uint64_t update_index; /* Logical timestamp at which this value is
-+ written */
-+ uint8_t *value; /* SHA1, or NULL. malloced. */
-+ uint8_t *target_value; /* peeled annotated tag, or NULL. malloced. */
-+ char *target; /* symref, or NULL. malloced. */
-+};
-+
-+/* returns whether 'ref' represents a deletion */
-+int reftable_ref_record_is_deletion(const struct reftable_ref_record *ref);
-+
-+/* prints a reftable_ref_record onto stdout */
-+void reftable_ref_record_print(struct reftable_ref_record *ref,
-+ uint32_t hash_id);
-+
-+/* frees and nulls all pointer values. */
-+void reftable_ref_record_clear(struct reftable_ref_record *ref);
-+
-+/* returns whether two reftable_ref_records are the same */
-+int reftable_ref_record_equal(struct reftable_ref_record *a,
-+ struct reftable_ref_record *b, int hash_size);
-+
-+/* reftable_log_record holds a reflog entry */
-+struct reftable_log_record {
-+ char *refname;
-+ uint64_t update_index; /* logical timestamp of a transactional update.
-+ */
-+ uint8_t *new_hash;
-+ uint8_t *old_hash;
-+ char *name;
-+ char *email;
-+ uint64_t time;
-+ int16_t tz_offset;
-+ char *message;
-+};
-+
-+/* returns whether 'ref' represents the deletion of a log record. */
-+int reftable_log_record_is_deletion(const struct reftable_log_record *log);
-+
-+/* frees and nulls all pointer values. */
-+void reftable_log_record_clear(struct reftable_log_record *log);
-+
-+/* returns whether two records are equal. */
-+int reftable_log_record_equal(struct reftable_log_record *a,
-+ struct reftable_log_record *b, int hash_size);
-+
-+/* dumps a reftable_log_record on stdout, for debugging/testing. */
-+void reftable_log_record_print(struct reftable_log_record *log,
-+ uint32_t hash_id);
++#include "reftable-error.h"
++
++#include <stdio.h>
++
++const char *reftable_error_str(int err)
++{
++ static char buf[250];
++ switch (err) {
++ case REFTABLE_IO_ERROR:
++ return "I/O error";
++ case REFTABLE_FORMAT_ERROR:
++ return "corrupt reftable file";
++ case REFTABLE_NOT_EXIST_ERROR:
++ return "file does not exist";
++ case REFTABLE_LOCK_ERROR:
++ return "data is outdated";
++ case REFTABLE_API_ERROR:
++ return "misuse of the reftable API";
++ case REFTABLE_ZLIB_ERROR:
++ return "zlib failure";
++ case REFTABLE_NAME_CONFLICT:
++ return "file/directory conflict";
++ case REFTABLE_REFNAME_ERROR:
++ return "invalid refname";
++ case -1:
++ return "general error";
++ default:
++ snprintf(buf, sizeof(buf), "unknown error code %d", err);
++ return buf;
++ }
++}
+
+ ## reftable/reftable-error.h (new) ##
+@@
++/*
++Copyright 2020 Google LLC
+
-+/****************************************************************
-+ Error handling
++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
++*/
+
-+ Error are signaled with negative integer return values. 0 means success.
-+ ****************************************************************/
++#ifndef REFTABLE_ERROR_H
++#define REFTABLE_ERROR_H
+
-+/* different types of errors */
++/*
++ Errors in reftable calls are signaled with negative integer return values. 0
++ means success.
++*/
+enum reftable_error {
+ /* Unexpected file system behavior */
+ REFTABLE_IO_ERROR = -2,
@@ reftable/reftable.h (new)
+ - on writing a log record with multiline message with
+ exact_log_message unset
+ - on reading a reftable_ref_record from log iterator, or vice versa.
++
++ When a call misuses the API, the internal state of the library is kept
++ unchanged.
+ */
+ REFTABLE_API_ERROR = -6,
+
@@ reftable/reftable.h (new)
+ * deallocated. */
+const char *reftable_error_str(int err);
+
-+/*
-+ * Convert the numeric error code to an equivalent errno code.
-+ */
-+int reftable_error_to_errno(int err);
-+
-+/****************************************************************
-+ Writing
-+
-+ Writing single reftables
-+ ****************************************************************/
-+
-+/* reftable_write_options sets options for writing a single reftable. */
-+struct reftable_write_options {
-+ /* boolean: do not pad out blocks to block size. */
-+ int unpadded;
-+
-+ /* the blocksize. Should be less than 2^24. */
-+ uint32_t block_size;
-+
-+ /* boolean: do not generate a SHA1 => ref index. */
-+ int skip_index_objects;
-+
-+ /* how often to write complete keys in each block. */
-+ int restart_interval;
-+
-+ /* 4-byte identifier ("sha1", "s256") of the hash.
-+ * Defaults to SHA1 if unset
-+ */
-+ uint32_t hash_id;
-+
-+ /* boolean: do not check ref names for validity or dir/file conflicts.
-+ */
-+ int skip_name_check;
-+
-+ /* boolean: copy log messages exactly. If unset, check that the message
-+ * is a single line, and add '\n' if missing.
-+ */
-+ int exact_log_message;
-+};
-+
-+/* reftable_block_stats holds statistics for a single block type */
-+struct reftable_block_stats {
-+ /* total number of entries written */
-+ int entries;
-+ /* total number of key restarts */
-+ int restarts;
-+ /* total number of blocks */
-+ int blocks;
-+ /* total number of index blocks */
-+ int index_blocks;
-+ /* depth of the index */
-+ int max_index_level;
-+
-+ /* offset of the first block for this type */
-+ uint64_t offset;
-+ /* offset of the top level index block for this type, or 0 if not
-+ * present */
-+ uint64_t index_offset;
-+};
-+
-+/* stats holds overall statistics for a single reftable */
-+struct reftable_stats {
-+ /* total number of blocks written. */
-+ int blocks;
-+ /* stats for ref data */
-+ struct reftable_block_stats ref_stats;
-+ /* stats for the SHA1 to ref map. */
-+ struct reftable_block_stats obj_stats;
-+ /* stats for index blocks */
-+ struct reftable_block_stats idx_stats;
-+ /* stats for log blocks */
-+ struct reftable_block_stats log_stats;
-+
-+ /* disambiguation length of shortened object IDs. */
-+ int object_id_len;
-+};
-+
-+/* reftable_new_writer creates a new writer */
-+struct reftable_writer *
-+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, 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_stack_next_update_index(), or REFTABLE_API_ERROR is returned.
-+
-+ For transactional updates, typically min==max. When converting an existing
-+ ref database into a single reftable, this would be a range of update-index
-+ timestamps.
-+ */
-+void reftable_writer_set_limits(struct reftable_writer *w, uint64_t min,
-+ uint64_t max);
-+
-+/* adds a reftable_ref_record. Must be called in ascending
-+ order. The update_index must be within the limits set by
-+ reftable_writer_set_limits(), or REFTABLE_API_ERROR is returned.
-+
-+ It is an error to write a ref record after a log record.
-+ */
-+int reftable_writer_add_ref(struct reftable_writer *w,
-+ struct reftable_ref_record *ref);
-+
-+/* Convenience function to add multiple refs. Will sort the refs by
-+ name before adding. */
-+int reftable_writer_add_refs(struct reftable_writer *w,
-+ struct reftable_ref_record *refs, int n);
-+
-+/* adds a reftable_log_record. Must be called in ascending order (with more
-+ recent log entries first.)
-+ */
-+int reftable_writer_add_log(struct reftable_writer *w,
-+ struct reftable_log_record *log);
-+
-+/* Convenience function to add multiple logs. Will sort the records by
-+ key before adding. */
-+int reftable_writer_add_logs(struct reftable_writer *w,
-+ struct reftable_log_record *logs, int n);
-+
-+/* reftable_writer_close finalizes the reftable. The writer is retained so
-+ * statistics can be inspected. */
-+int reftable_writer_close(struct reftable_writer *w);
-+
-+/* writer_stats returns the statistics on the reftable being written.
-+
-+ This struct becomes invalid when the writer is freed.
-+ */
-+const struct reftable_stats *writer_stats(struct reftable_writer *w);
-+
-+/* reftable_writer_free deallocates memory for the writer */
-+void reftable_writer_free(struct reftable_writer *w);
-+
-+/****************************************************************
-+ * ITERATING
-+ ****************************************************************/
-+
-+/* iterator is the generic interface for walking over data stored in a
-+ reftable. It is generally passed around by value.
-+*/
-+struct reftable_iterator {
-+ struct reftable_iterator_vtable *ops;
-+ void *iter_arg;
-+};
-+
-+/* reads the next reftable_ref_record. Returns < 0 for error, 0 for OK and > 0:
-+ end of iteration.
-+*/
-+int reftable_iterator_next_ref(struct reftable_iterator *it,
-+ struct reftable_ref_record *ref);
-+
-+/* reads the next reftable_log_record. Returns < 0 for error, 0 for OK and > 0:
-+ end of iteration.
-+*/
-+int reftable_iterator_next_log(struct reftable_iterator *it,
-+ struct reftable_log_record *log);
-+
-+/* releases resources associated with an iterator. */
-+void reftable_iterator_destroy(struct reftable_iterator *it);
-+
-+/****************************************************************
-+ Reading single tables
-+
-+ The follow routines are for reading single files. For an application-level
-+ interface, skip ahead to struct reftable_merged_table and struct
-+ reftable_stack.
-+ ****************************************************************/
-+
-+/* block_source is a generic wrapper for a seekable readable file.
-+ It is generally passed around by value.
-+ */
-+struct reftable_block_source {
-+ struct reftable_block_source_vtable *ops;
-+ void *arg;
-+};
-+
-+/* a contiguous segment of bytes. It keeps track of its generating block_source
-+ so it can return itself into the pool.
-+*/
-+struct reftable_block {
-+ uint8_t *data;
-+ int len;
-+ struct reftable_block_source source;
-+};
-+
-+/* block_source_vtable are the operations that make up block_source */
-+struct reftable_block_source_vtable {
-+ /* returns the size of a block source */
-+ uint64_t (*size)(void *source);
-+
-+ /* reads a segment from the block source. It is an error to read
-+ beyond the end of the block */
-+ int (*read_block)(void *source, struct reftable_block *dest,
-+ uint64_t off, uint32_t size);
-+ /* mark the block as read; may return the data back to malloc */
-+ void (*return_block)(void *source, struct reftable_block *blockp);
-+
-+ /* release all resources associated with the block source */
-+ void (*close)(void *source);
-+};
-+
-+/* opens a file on the file system as a block_source */
-+int reftable_block_source_from_file(struct reftable_block_source *block_src,
-+ const char *name);
-+
-+/* The reader struct is a handle to an open reftable file. */
-+struct reftable_reader;
-+
-+/* reftable_new_reader opens a reftable for reading. If successful, returns 0
-+ * code and sets pp. The name is used for creating a stack. Typically, it is the
-+ * basename of the file. The block source `src` is owned by the reader, and is
-+ * closed on calling reftable_reader_destroy().
-+ */
-+int reftable_new_reader(struct reftable_reader **pp,
-+ struct reftable_block_source *src, const char *name);
-+
-+/* reftable_reader_seek_ref returns an iterator where 'name' would be inserted
-+ in the table. To seek to the start of the table, use name = "".
-+
-+ example:
-+
-+ struct reftable_reader *r = NULL;
-+ int err = reftable_new_reader(&r, &src, "filename");
-+ if (err < 0) { ... }
-+ struct reftable_iterator it = {0};
-+ err = reftable_reader_seek_ref(r, &it, "refs/heads/master");
-+ if (err < 0) { ... }
-+ struct reftable_ref_record ref = {0};
-+ while (1) {
-+ err = reftable_iterator_next_ref(&it, &ref);
-+ if (err > 0) {
-+ break;
-+ }
-+ if (err < 0) {
-+ ..error handling..
-+ }
-+ ..found..
-+ }
-+ reftable_iterator_destroy(&it);
-+ reftable_ref_record_clear(&ref);
-+ */
-+int reftable_reader_seek_ref(struct reftable_reader *r,
-+ struct reftable_iterator *it, const char *name);
-+
-+/* returns the hash ID used in this table. */
-+uint32_t reftable_reader_hash_id(struct reftable_reader *r);
-+
-+/* seek to logs for the given name, older than update_index. To seek to the
-+ start of the table, use name = "".
-+ */
-+int reftable_reader_seek_log_at(struct reftable_reader *r,
-+ struct reftable_iterator *it, const char *name,
-+ uint64_t update_index);
-+
-+/* seek to newest log entry for given name. */
-+int reftable_reader_seek_log(struct reftable_reader *r,
-+ struct reftable_iterator *it, const char *name);
-+
-+/* closes and deallocates a reader. */
-+void reftable_reader_free(struct reftable_reader *);
-+
-+/* return an iterator for the refs pointing to `oid`. */
-+int reftable_reader_refs_for(struct reftable_reader *r,
-+ struct reftable_iterator *it, uint8_t *oid);
-+
-+/* return the max_update_index for a table */
-+uint64_t reftable_reader_max_update_index(struct reftable_reader *r);
-+
-+/* return the min_update_index for a table */
-+uint64_t reftable_reader_min_update_index(struct reftable_reader *r);
-+
-+/****************************************************************
-+ Merged tables
-+
-+ A ref database kept in a sequence of table files. The merged_table presents a
-+ unified view to reading (seeking, iterating) a sequence of immutable tables.
-+ ****************************************************************/
-+
-+/* A merged table is implements seeking/iterating over a stack of tables. */
-+struct reftable_merged_table;
-+
-+/* A generic reftable; see below. */
-+struct reftable_table;
-+
-+/* reftable_new_merged_table creates a new merged table. It takes ownership of
-+ the stack array.
-+*/
-+int reftable_new_merged_table(struct reftable_merged_table **dest,
-+ struct reftable_table *stack, int n,
-+ uint32_t hash_id);
-+
-+/* returns an iterator positioned just before 'name' */
-+int reftable_merged_table_seek_ref(struct reftable_merged_table *mt,
-+ struct reftable_iterator *it,
-+ const char *name);
-+
-+/* returns an iterator for log entry, at given update_index */
-+int reftable_merged_table_seek_log_at(struct reftable_merged_table *mt,
-+ struct reftable_iterator *it,
-+ const char *name, uint64_t update_index);
-+
-+/* like reftable_merged_table_seek_log_at but look for the newest entry. */
-+int reftable_merged_table_seek_log(struct reftable_merged_table *mt,
-+ struct reftable_iterator *it,
-+ const char *name);
-+
-+/* returns the max update_index covered by this merged table. */
-+uint64_t
-+reftable_merged_table_max_update_index(struct reftable_merged_table *mt);
-+
-+/* returns the min update_index covered by this merged table. */
-+uint64_t
-+reftable_merged_table_min_update_index(struct reftable_merged_table *mt);
-+
-+/* releases memory for the merged_table */
-+void reftable_merged_table_free(struct reftable_merged_table *m);
-+
-+/* return the hash ID of the merged table. */
-+uint32_t reftable_merged_table_hash_id(struct reftable_merged_table *m);
-+
-+/****************************************************************
-+ Generic tables
-+
-+ A unified API for reading tables, either merged tables, or single readers.
-+ ****************************************************************/
-+
-+struct reftable_table {
-+ struct reftable_table_vtable *ops;
-+ void *table_arg;
-+};
-+
-+int reftable_table_seek_ref(struct reftable_table *tab,
-+ struct reftable_iterator *it, const char *name);
-+
-+void reftable_table_from_reader(struct reftable_table *tab,
-+ struct reftable_reader *reader);
-+
-+/* returns the hash ID from a generic reftable_table */
-+uint32_t reftable_table_hash_id(struct reftable_table *tab);
-+
-+/* create a generic table from reftable_merged_table */
-+void reftable_table_from_merged_table(struct reftable_table *tab,
-+ struct reftable_merged_table *table);
-+
-+/* returns the max update_index covered by this table. */
-+uint64_t reftable_table_max_update_index(struct reftable_table *tab);
-+
-+/* returns the min update_index covered by this table. */
-+uint64_t reftable_table_min_update_index(struct reftable_table *tab);
-+
-+/* convenience function to read a single ref. Returns < 0 for error, 0
-+ for success, and 1 if ref not found. */
-+int reftable_table_read_ref(struct reftable_table *tab, const char *name,
-+ struct reftable_ref_record *ref);
-+
-+/****************************************************************
-+ Mutable ref database
-+
-+ The stack presents an interface to a mutable sequence of reftables.
-+ ****************************************************************/
-+
-+/* a stack is a stack of reftables, which can be mutated by pushing a table to
-+ * the top of the stack */
-+struct reftable_stack;
-+
-+/* open a new reftable stack. The tables along with the table list will be
-+ stored in 'dir'. Typically, this should be .git/reftables.
-+*/
-+int reftable_new_stack(struct reftable_stack **dest, const char *dir,
-+ struct reftable_write_options config);
-+
-+/* returns the update_index at which a next table should be written. */
-+uint64_t reftable_stack_next_update_index(struct reftable_stack *st);
-+
-+/* holds a transaction to add tables at the top of a stack. */
-+struct reftable_addition;
-+
-+/*
-+ returns a new transaction to add reftables to the given stack. As a side
-+ effect, the ref database is locked.
-+*/
-+int reftable_stack_new_addition(struct reftable_addition **dest,
-+ struct reftable_stack *st);
-+
-+/* Adds a reftable to transaction. */
-+int reftable_addition_add(struct reftable_addition *add,
-+ int (*write_table)(struct reftable_writer *wr,
-+ void *arg),
-+ void *arg);
-+
-+/* Commits the transaction, releasing the lock. */
-+int reftable_addition_commit(struct reftable_addition *add);
-+
-+/* Release all non-committed data from the transaction, and deallocate the
-+ transaction. Releases the lock if held. */
-+void reftable_addition_destroy(struct reftable_addition *add);
-+
-+/* add a new table to the stack. The write_table function must call
-+ reftable_writer_set_limits, add refs and return an error value. */
-+int reftable_stack_add(struct reftable_stack *st,
-+ int (*write_table)(struct reftable_writer *wr,
-+ void *write_arg),
-+ void *write_arg);
-+
-+/* returns the merged_table for seeking. This table is valid until the
-+ next write or reload, and should not be closed or deleted.
-+*/
-+struct reftable_merged_table *
-+reftable_stack_merged_table(struct reftable_stack *st);
-+
-+/* frees all resources associated with the stack. */
-+void reftable_stack_destroy(struct reftable_stack *st);
-+
-+/* Reloads the stack if necessary. This is very cheap to run if the stack was up
-+ * to date */
-+int reftable_stack_reload(struct reftable_stack *st);
-+
-+/* Policy for expiring reflog entries. */
-+struct reftable_log_expiry_config {
-+ /* Drop entries older than this timestamp */
-+ uint64_t time;
-+
-+ /* Drop older entries */
-+ uint64_t min_update_index;
-+};
-+
-+/* compacts all reftables into a giant table. Expire reflog entries if config is
-+ * non-NULL */
-+int reftable_stack_compact_all(struct reftable_stack *st,
-+ struct reftable_log_expiry_config *config);
-+
-+/* heuristically compact unbalanced table stack. */
-+int reftable_stack_auto_compact(struct reftable_stack *st);
-+
-+/* convenience function to read a single ref. Returns < 0 for error, 0
-+ for success, and 1 if ref not found. */
-+int reftable_stack_read_ref(struct reftable_stack *st, const char *refname,
-+ struct reftable_ref_record *ref);
-+
-+/* convenience function to read a single log. Returns < 0 for error, 0
-+ for success, and 1 if ref not found. */
-+int reftable_stack_read_log(struct reftable_stack *st, const char *refname,
-+ struct reftable_log_record *log);
-+
-+/* statistics on past compactions. */
-+struct reftable_compaction_stats {
-+ uint64_t bytes; /* total number of bytes written */
-+ uint64_t entries_written; /* total number of entries written, including
-+ failures. */
-+ int attempts; /* how often we tried to compact */
-+ int failures; /* failures happen on concurrent updates */
-+};
-+
-+/* return statistics for compaction up till now. */
-+struct reftable_compaction_stats *
-+reftable_stack_compaction_stats(struct reftable_stack *st);
-+
+#endif
3: 01a669a731 < -: ---------- vcxproj: adjust for the reftable changes
5: 4190da597e ! 5: 987d1d186f reftable: utility functions
@@ Commit message
This commit provides basic utility classes for the reftable library.
- Since the reftable library must compile standalone, there may be some overlap
- with git-core utility functions.
-
Signed-off-by: Han-Wen Nienhuys <[email protected]>
## Makefile ##
@@ Makefile: TEST_SHELL_PATH = $(SHELL_PATH)
+REFTABLE_LIB = reftable/libreftable.a
+REFTABLE_TEST_LIB = reftable/libreftable_test.a
- GENERATED_H += config-list.h
GENERATED_H += command-list.h
+ GENERATED_H += config-list.h
@@ Makefile: THIRD_PARTY_SOURCES += compat/regex/%
THIRD_PARTY_SOURCES += sha1collisiondetection/%
THIRD_PARTY_SOURCES += sha1dc/%
@@ Makefile: XDIFF_OBJS += xdiff/xpatience.o
XDIFF_OBJS += xdiff/xutils.o
+REFTABLE_OBJS += reftable/basics.o
-+REFTABLE_OBJS += reftable/blocksource.o
++REFTABLE_OBJS += reftable/error.o
+REFTABLE_OBJS += reftable/publicbasics.o
-+REFTABLE_OBJS += reftable/compat.o
-+REFTABLE_OBJS += reftable/strbuf.o
+
-+REFTABLE_TEST_OBJS += reftable/strbuf_test.o
+REFTABLE_TEST_OBJS += reftable/test_framework.o
++REFTABLE_TEST_OBJS += reftable/basics_test.o
+
TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
@@ reftable/basics.c (new)
+ }
+
+ return *a == *b;
++}
++
++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;
+}
## reftable/basics.h (new) ##
@@ reftable/basics.h (new)
+#ifndef BASICS_H
+#define BASICS_H
+
++/*
++ * miscellaneous utilities that are not provided by Git.
++ */
++
+#include "system.h"
+
+/* Bigendian en/decoding of integers */
@@ reftable/basics.h (new)
+void reftable_free(void *p);
+void *reftable_calloc(size_t sz);
+
++/* Find the longest shared prefix size of `a` and `b` */
++struct strbuf;
++int common_prefix_size(struct strbuf *a, struct strbuf *b);
++
+#endif
- ## reftable/blocksource.c (new) ##
+ ## reftable/basics_test.c (new) ##
@@
+/*
+Copyright 2020 Google LLC
@@ reftable/blocksource.c (new)
+#include "system.h"
+
+#include "basics.h"
-+#include "blocksource.h"
-+#include "strbuf.h"
-+#include "reftable.h"
-+
-+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;
-+}
-+
-+static uint64_t strbuf_size(void *b)
-+{
-+ return ((struct strbuf *)b)->len;
-+}
-+
-+static 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);
-+}
-+
-+static struct reftable_block_source_vtable malloc_vtable = {
-+ .return_block = &malloc_return_block,
-+};
-+
-+static struct reftable_block_source malloc_block_source_instance = {
-+ .ops = &malloc_vtable,
-+};
-+
-+struct reftable_block_source malloc_block_source(void)
-+{
-+ return malloc_block_source_instance;
-+}
++#include "test_framework.h"
++#include "reftable-tests.h"
+
-+struct file_block_source {
-+ int fd;
-+ uint64_t size;
++struct binsearch_args {
++ int key;
++ int *arr;
+};
+
-+static uint64_t file_size(void *b)
++static int binsearch_func(size_t i, void *void_args)
+{
-+ return ((struct file_block_source *)b)->size;
-+}
++ struct binsearch_args *args = (struct binsearch_args *)void_args;
+
-+static void file_return_block(void *b, struct reftable_block *dest)
-+{
-+ memset(dest->data, 0xff, dest->len);
-+ reftable_free(dest->data);
++ return args->key < args->arr[i];
+}
+
-+static void file_close(void *b)
-+{
-+ int fd = ((struct file_block_source *)b)->fd;
-+ if (fd > 0) {
-+ close(fd);
-+ ((struct file_block_source *)b)->fd = 0;
-+ }
-+
-+ reftable_free(b);
-+}
-+
-+static int file_read_block(void *v, struct reftable_block *dest, uint64_t off,
-+ uint32_t size)
-+{
-+ struct file_block_source *b = (struct file_block_source *)v;
-+ assert(off + size <= b->size);
-+ dest->data = reftable_malloc(size);
-+ if (pread(b->fd, dest->data, size, off) != size)
-+ return -1;
-+ dest->len = size;
-+ return size;
-+}
-+
-+static struct reftable_block_source_vtable file_vtable = {
-+ .size = &file_size,
-+ .read_block = &file_read_block,
-+ .return_block = &file_return_block,
-+ .close = &file_close,
-+};
-+
-+int reftable_block_source_from_file(struct reftable_block_source *bs,
-+ const char *name)
++static void test_binsearch(void)
+{
-+ struct stat st = { 0 };
-+ int err = 0;
-+ int fd = open(name, O_RDONLY);
-+ struct file_block_source *p = NULL;
-+ if (fd < 0) {
-+ if (errno == ENOENT) {
-+ return REFTABLE_NOT_EXIST_ERROR;
-+ }
-+ return -1;
-+ }
-+
-+ err = fstat(fd, &st);
-+ if (err < 0)
-+ return -1;
-+
-+ p = reftable_calloc(sizeof(struct file_block_source));
-+ p->size = st.st_size;
-+ p->fd = fd;
-+
-+ assert(bs->ops == NULL);
-+ bs->ops = &file_vtable;
-+ bs->arg = p;
-+ return 0;
-+}
-
- ## reftable/blocksource.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 BLOCKSOURCE_H
-+#define BLOCKSOURCE_H
-+
-+#include "strbuf.h"
-+
-+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);
++ int arr[] = { 2, 4, 6, 8, 10 };
++ size_t sz = ARRAY_SIZE(arr);
++ struct binsearch_args args = {
++ .arr = arr,
++ };
+
-+struct reftable_block_source malloc_block_source(void);
-+
-+#endif
-
- ## reftable/compat.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
-+
-+*/
-+
-+/* compat.c - compatibility functions for standalone compilation */
-+
-+#include "system.h"
-+#include "basics.h"
-+
-+#ifdef REFTABLE_STANDALONE
-+
-+#include <dirent.h>
-+
-+void put_be32(void *p, uint32_t i)
-+{
-+ uint8_t *out = (uint8_t *)p;
-+
-+ out[0] = (uint8_t)((i >> 24) & 0xff);
-+ out[1] = (uint8_t)((i >> 16) & 0xff);
-+ out[2] = (uint8_t)((i >> 8) & 0xff);
-+ out[3] = (uint8_t)((i)&0xff);
-+}
-+
-+uint32_t get_be32(uint8_t *in)
-+{
-+ return (uint32_t)(in[0]) << 24 | (uint32_t)(in[1]) << 16 |
-+ (uint32_t)(in[2]) << 8 | (uint32_t)(in[3]);
-+}
-+
-+void put_be64(void *p, uint64_t v)
-+{
-+ uint8_t *out = (uint8_t *)p;
-+ int i = sizeof(uint64_t);
-+ while (i--) {
-+ out[i] = (uint8_t)(v & 0xff);
-+ v >>= 8;
-+ }
-+}
-+
-+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)(bytes[i] & 0xff);
-+ }
-+ return v;
-+}
-+
-+uint16_t get_be16(uint8_t *in)
-+{
-+ return (uint32_t)(in[0]) << 8 | (uint32_t)(in[1]);
-+}
-+
-+char *xstrdup(const char *s)
-+{
-+ int l = strlen(s);
-+ char *dest = (char *)reftable_malloc(l + 1);
-+ strncpy(dest, s, l + 1);
-+ return dest;
-+}
-+
-+void sleep_millisec(int millisecs)
-+{
-+ usleep(millisecs * 1000);
-+}
-+
-+void reftable_clear_dir(const char *dirname)
-+{
-+ DIR *dir = opendir(dirname);
-+ struct dirent *ent = NULL;
-+ assert(dir);
-+ while ((ent = readdir(dir)) != NULL) {
-+ unlinkat(dirfd(dir), ent->d_name, 0);
++ for (i = 1; i < 11; i++) {
++ int res;
++ args.key = i;
++ res = binsearch(sz, &binsearch_func, &args);
++
++ if (res < sz) {
++ EXPECT(args.key < arr[res]);
++ if (res > 0) {
++ EXPECT(args.key >= arr[res - 1]);
++ }
++ } else {
++ EXPECT(args.key == 10 || args.key == 11);
++ }
+ }
-+ closedir(dir);
-+ rmdir(dirname);
+}
+
-+#else
-+
-+#include "../dir.h"
-+
-+void reftable_clear_dir(const char *dirname)
++int basics_test_main(int argc, const char *argv[])
+{
-+ struct strbuf path = STRBUF_INIT;
-+ strbuf_addstr(&path, dirname);
-+ remove_dir_recursively(&path, 0);
-+ strbuf_release(&path);
-+}
-+
-+#endif
-+
-+int hash_size(uint32_t id)
-+{
-+ switch (id) {
-+ case 0:
-+ case SHA1_ID:
-+ return SHA1_SIZE;
-+ case SHA256_ID:
-+ return SHA256_SIZE;
-+ }
-+ abort();
++ test_binsearch();
++ return 0;
+}
## reftable/compat.h (new) ##
@@ reftable/publicbasics.c (new)
+https://developers.google.com/open-source/licenses/bsd
+*/
+
-+#include "reftable.h"
++#include "reftable-malloc.h"
+
+#include "basics.h"
+#include "system.h"
+
-+const char *reftable_error_str(int err)
-+{
-+ static char buf[250];
-+ switch (err) {
-+ case REFTABLE_IO_ERROR:
-+ return "I/O error";
-+ case REFTABLE_FORMAT_ERROR:
-+ return "corrupt reftable file";
-+ case REFTABLE_NOT_EXIST_ERROR:
-+ return "file does not exist";
-+ case REFTABLE_LOCK_ERROR:
-+ return "data is outdated";
-+ case REFTABLE_API_ERROR:
-+ return "misuse of the reftable API";
-+ case REFTABLE_ZLIB_ERROR:
-+ return "zlib failure";
-+ case REFTABLE_NAME_CONFLICT:
-+ return "file/directory conflict";
-+ case REFTABLE_REFNAME_ERROR:
-+ return "invalid refname";
-+ case -1:
-+ return "general error";
-+ default:
-+ snprintf(buf, sizeof(buf), "unknown error code %d", err);
-+ return buf;
-+ }
-+}
-+
-+int reftable_error_to_errno(int err)
-+{
-+ switch (err) {
-+ case REFTABLE_IO_ERROR:
-+ return EIO;
-+ case REFTABLE_FORMAT_ERROR:
-+ return EFAULT;
-+ case REFTABLE_NOT_EXIST_ERROR:
-+ return ENOENT;
-+ case REFTABLE_LOCK_ERROR:
-+ return EBUSY;
-+ case REFTABLE_API_ERROR:
-+ return EINVAL;
-+ case REFTABLE_ZLIB_ERROR:
-+ return EDOM;
-+ default:
-+ return ERANGE;
-+ }
-+}
-+
+static void *(*reftable_malloc_ptr)(size_t sz) = &malloc;
+static void *(*reftable_realloc_ptr)(void *, size_t) = &realloc;
+static void (*reftable_free_ptr)(void *) = &free;
@@ reftable/publicbasics.c (new)
+ reftable_free_ptr = free;
+}
+
-+int reftable_fd_write(void *arg, const void *data, size_t sz)
++int hash_size(uint32_t id)
+{
-+ int *fdp = (int *)arg;
-+ return write(*fdp, data, sz);
++ switch (id) {
++ case 0:
++ case SHA1_ID:
++ return SHA1_SIZE;
++ case SHA256_ID:
++ return SHA256_SIZE;
++ }
++ abort();
+}
- ## reftable/reftable-tests.h (new) ##
+ ## reftable/reftable-malloc.h (new) ##
@@
+/*
+Copyright 2020 Google LLC
@@ reftable/reftable-tests.h (new)
+https://developers.google.com/open-source/licenses/bsd
+*/
+
-+#ifndef REFTABLE_TESTS_H
-+#define REFTABLE_TESTS_H
++#ifndef REFTABLE_H
++#define REFTABLE_H
+
-+int block_test_main(int argc, const char **argv);
-+int merged_test_main(int argc, const char **argv);
-+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 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);
++#include <stddef.h>
++
++/*
++ Overrides the functions to use for memory management.
++ */
++void reftable_set_alloc(void *(*malloc)(size_t),
++ void *(*realloc)(void *, size_t), void (*free)(void *));
+
+#endif
- ## reftable/strbuf.c (new) ##
+ ## reftable/reftable-tests.h (new) ##
@@
+/*
+Copyright 2020 Google LLC
@@ reftable/strbuf.c (new)
+https://developers.google.com/open-source/licenses/bsd
+*/
+
-+#include "strbuf.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;
-+}
++#ifndef REFTABLE_TESTS_H
++#define REFTABLE_TESTS_H
+
-+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;
-+}
++int basics_test_main(int argc, const char **argv);
++int block_test_main(int argc, const char **argv);
++int merged_test_main(int argc, const char **argv);
++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 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);
+
+#endif
-+
-+int strbuf_add_void(void *b, const void *data, size_t sz)
-+{
-+ strbuf_add((struct strbuf *)b, data, sz);
-+ return sz;
-+}
-+
-+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) ##
+ ## reftable/system.h (new) ##
@@
+/*
+Copyright 2020 Google LLC
@@ reftable/strbuf.h (new)
+https://developers.google.com/open-source/licenses/bsd
+*/
+
-+#ifndef SLICE_H
-+#define SLICE_H
-+
-+#ifdef REFTABLE_STANDALONE
-+
-+#include "basics.h"
-+
-+/*
-+ Provides a bounds-checked, growable byte ranges. To use, initialize as "strbuf
-+ x = STRBUF_INIT;"
-+ */
-+struct strbuf {
-+ size_t len;
-+ size_t 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);
++#ifndef SYSTEM_H
++#define SYSTEM_H
+
-+/* Append `add` to `dest. */
-+void strbuf_addbuf(struct strbuf *dest, struct strbuf *add);
++#include "git-compat-util.h"
++#include "strbuf.h"
+
-+#else
++#include <zlib.h>
+
-+#include "../git-compat-util.h"
-+#include "../strbuf.h"
++struct strbuf;
++/* In git, this is declared in dir.h */
++int remove_dir_recursively(struct strbuf *path, int flags);
+
-+#endif
-+
-+extern struct strbuf reftable_empty_strbuf;
++#define SHA1_ID 0x73686131
++#define SHA256_ID 0x73323536
++#define SHA1_SIZE 20
++#define SHA256_SIZE 32
+
-+/* Like strbuf_add, but suitable for passing to reftable_new_writer
++/* This is uncompress2, which is only available in zlib as of 2017.
+ */
-+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);
++int uncompress_return_consumed(Bytef *dest, uLongf *destLen,
++ const Bytef *source, uLong *sourceLen);
++int hash_size(uint32_t id);
+
+#endif
- ## reftable/strbuf_test.c (new) ##
+ ## reftable/test_framework.c (new) ##
@@
+/*
+Copyright 2020 Google LLC
@@ reftable/strbuf_test.c (new)
+https://developers.google.com/open-source/licenses/bsd
+*/
+
-+#include "strbuf.h"
-+
+#include "system.h"
++#include "test_framework.h"
+
+#include "basics.h"
-+#include "test_framework.h"
-+#include "reftable-tests.h"
+
-+static void test_strbuf(void)
++void set_test_hash(uint8_t *p, int i)
+{
-+ 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);
++ memset(p, (uint8_t)i, hash_size(SHA1_ID));
+}
+
-+int strbuf_test_main(int argc, const char *argv[])
++int strbuf_add_void(void *b, const void *data, size_t sz)
+{
-+ add_test_case("test_strbuf", &test_strbuf);
-+ return test_main(argc, argv);
++ strbuf_add((struct strbuf *)b, data, sz);
++ return sz;
+}
- ## reftable/system.h (new) ##
+ ## reftable/test_framework.h (new) ##
@@
+/*
+Copyright 2020 Google LLC
@@ reftable/system.h (new)
+https://developers.google.com/open-source/licenses/bsd
+*/
+
-+#ifndef SYSTEM_H
-+#define SYSTEM_H
-+
-+#ifndef REFTABLE_STANDALONE
-+
-+#include "git-compat-util.h"
-+#include "cache.h"
-+#include <zlib.h>
-+
-+#else
-+
-+#include <assert.h>
-+#include <errno.h>
-+#include <fcntl.h>
-+#include <inttypes.h>
-+#include <stdint.h>
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+#include <sys/stat.h>
-+#include <sys/time.h>
-+#include <sys/types.h>
-+#include <unistd.h>
-+#include <zlib.h>
++#ifndef TEST_FRAMEWORK_H
++#define TEST_FRAMEWORK_H
+
-+#include "compat.h"
++#include "system.h"
++#include "reftable-error.h"
++
++#define EXPECT_ERR(c) \
++ if (c != 0) { \
++ fflush(stderr); \
++ fflush(stdout); \
++ fprintf(stderr, "%s: %d: error == %d (%s), want 0\n", \
++ __FILE__, __LINE__, c, reftable_error_str(c)); \
++ abort(); \
++ }
+
-+#endif /* REFTABLE_STANDALONE */
++#define EXPECT_STREQ(a, b) \
++ if (strcmp(a, b)) { \
++ fflush(stderr); \
++ fflush(stdout); \
++ fprintf(stderr, "%s:%d: %s (%s) != %s (%s)\n", __FILE__, \
++ __LINE__, #a, a, #b, b); \
++ abort(); \
++ }
+
-+void reftable_clear_dir(const char *dirname);
++#define EXPECT(c) \
++ if (!(c)) { \
++ fflush(stderr); \
++ fflush(stdout); \
++ fprintf(stderr, "%s: %d: failed assertion %s\n", __FILE__, \
++ __LINE__, #c); \
++ abort(); \
++ }
+
-+#define SHA1_ID 0x73686131
-+#define SHA256_ID 0x73323536
-+#define SHA1_SIZE 20
-+#define SHA256_SIZE 32
++void set_test_hash(uint8_t *p, int i);
+
-+/* This is uncompress2, which is only available in zlib as of 2017.
++/* Like strbuf_add, but suitable for passing to reftable_new_writer
+ */
-+int uncompress_return_consumed(Bytef *dest, uLongf *destLen,
-+ const Bytef *source, uLong *sourceLen);
-+int hash_size(uint32_t id);
++int strbuf_add_void(void *b, const void *data, size_t sz);
+
+#endif
@@ t/helper/test-reftable.c (new)
+
+int cmd__reftable(int argc, const char **argv)
+{
-+ strbuf_test_main(argc, argv);
++ basics_test_main(argc, argv);
++
+ return 0;
+}
## t/helper/test-tool.c ##
@@ t/helper/test-tool.c: static struct test_cmd cmds[] = {
+ { "path-utils", cmd__path_utils },
+ { "pkt-line", cmd__pkt_line },
+ { "prio-queue", cmd__prio_queue },
+- { "proc-receive", cmd__proc_receive},
++ { "proc-receive", cmd__proc_receive },
+ { "progress", cmd__progress },
+ { "reach", cmd__reach },
+ { "read-cache", cmd__read_cache },
{ "read-graph", cmd__read_graph },
{ "read-midx", cmd__read_midx },
{ "ref-store", cmd__ref_store },
@@ t/helper/test-tool.h: int cmd__read_cache(int argc, const char **argv);
int cmd__regex(int argc, const char **argv);
int cmd__repository(int argc, const char **argv);
int cmd__revision_walking(int argc, const char **argv);
+
+ ## t/t0032-reftable-unittest.sh (new) ##
+@@
++#!/bin/sh
++#
++# Copyright (c) 2020 Google LLC
++#
++
++test_description='reftable unittests'
++
++. ./test-lib.sh
++
++test_expect_success 'unittests' '
++ test-tool reftable
++'
++
++test_done
4: b94c5f5c61 ! 6: f71e82b995 reftable: add a barebones unittest framework
@@ Metadata
Author: Han-Wen Nienhuys <[email protected]>
## Commit message ##
- reftable: add a barebones unittest framework
+ reftable: add blocksource, an abstraction for random access reads
+
+ The reftable format is usually used with files for storage. However, we abstract
+ away this using the blocksource data structure. This has two advantages:
+
+ * log blocks are zlib compressed, and handling them is simplified if we can
+ discard byte segments from within the block layer.
+
+ * for unittests, it is useful to read and write in-memory. The blocksource
+ allows us to abstract the data away from on-disk files.
Signed-off-by: Han-Wen Nienhuys <[email protected]>
- ## reftable/test_framework.c (new) ##
+ ## Makefile ##
+@@ Makefile: XDIFF_OBJS += xdiff/xutils.o
+
+ REFTABLE_OBJS += reftable/basics.o
+ REFTABLE_OBJS += reftable/error.o
++REFTABLE_OBJS += reftable/blocksource.o
+ REFTABLE_OBJS += reftable/publicbasics.o
+
+ REFTABLE_TEST_OBJS += reftable/test_framework.o
+
+ ## reftable/blocksource.c (new) ##
@@
+/*
+Copyright 2020 Google LLC
@@ reftable/test_framework.c (new)
+https://developers.google.com/open-source/licenses/bsd
+*/
+
-+#include "test_framework.h"
-+
+#include "system.h"
++
+#include "basics.h"
++#include "blocksource.h"
++#include "reftable-blocksource.h"
++#include "reftable-error.h"
+
-+static struct test_case **test_cases;
-+static int test_case_len;
-+static int test_case_cap;
++static void strbuf_return_block(void *b, struct reftable_block *dest)
++{
++ memset(dest->data, 0xff, dest->len);
++ reftable_free(dest->data);
++}
+
-+static struct test_case *new_test_case(const char *name, void (*testfunc)(void))
++static void strbuf_close(void *b)
+{
-+ struct test_case *tc = reftable_malloc(sizeof(struct test_case));
-+ tc->name = name;
-+ tc->testfunc = testfunc;
-+ return tc;
+}
+
-+struct test_case *add_test_case(const char *name, void (*testfunc)(void))
++static int strbuf_read_block(void *v, struct reftable_block *dest, uint64_t off,
++ uint32_t size)
+{
-+ struct test_case *tc = new_test_case(name, testfunc);
-+ if (test_case_len == test_case_cap) {
-+ test_case_cap = 2 * test_case_cap + 1;
-+ test_cases = reftable_realloc(
-+ test_cases, sizeof(struct test_case) * test_case_cap);
-+ }
++ 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;
++}
+
-+ test_cases[test_case_len++] = tc;
-+ return tc;
++static uint64_t strbuf_size(void *b)
++{
++ return ((struct strbuf *)b)->len;
+}
+
-+int test_main(int argc, const char *argv[])
++static 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)
+{
-+ const char *filter = NULL;
-+ int i = 0;
-+ if (argc > 1) {
-+ filter = argv[1];
-+ }
++ assert(bs->ops == NULL);
++ bs->ops = &strbuf_vtable;
++ bs->arg = buf;
++}
+
-+ for (i = 0; i < test_case_len; i++) {
-+ const char *name = test_cases[i]->name;
-+ if (filter == NULL || strstr(name, filter) != NULL) {
-+ printf("case %s\n", name);
-+ test_cases[i]->testfunc();
-+ } else {
-+ printf("skip %s\n", name);
-+ }
++static void malloc_return_block(void *b, struct reftable_block *dest)
++{
++ memset(dest->data, 0xff, dest->len);
++ reftable_free(dest->data);
++}
++
++static struct reftable_block_source_vtable malloc_vtable = {
++ .return_block = &malloc_return_block,
++};
++
++static struct reftable_block_source malloc_block_source_instance = {
++ .ops = &malloc_vtable,
++};
+
-+ reftable_free(test_cases[i]);
++struct reftable_block_source malloc_block_source(void)
++{
++ return malloc_block_source_instance;
++}
++
++struct file_block_source {
++ int fd;
++ uint64_t size;
++};
++
++static uint64_t file_size(void *b)
++{
++ return ((struct file_block_source *)b)->size;
++}
++
++static void file_return_block(void *b, struct reftable_block *dest)
++{
++ memset(dest->data, 0xff, dest->len);
++ reftable_free(dest->data);
++}
++
++static void file_close(void *b)
++{
++ int fd = ((struct file_block_source *)b)->fd;
++ if (fd > 0) {
++ close(fd);
++ ((struct file_block_source *)b)->fd = 0;
+ }
-+ reftable_free(test_cases);
-+ test_cases = NULL;
-+ test_case_len = 0;
-+ test_case_cap = 0;
-+ return 0;
++
++ reftable_free(b);
+}
+
-+void set_test_hash(uint8_t *p, int i)
++static int file_read_block(void *v, struct reftable_block *dest, uint64_t off,
++ uint32_t size)
+{
-+ memset(p, (uint8_t)i, hash_size(SHA1_ID));
++ struct file_block_source *b = (struct file_block_source *)v;
++ assert(off + size <= b->size);
++ dest->data = reftable_malloc(size);
++ if (pread(b->fd, dest->data, size, off) != size)
++ return -1;
++ dest->len = size;
++ return size;
++}
++
++static struct reftable_block_source_vtable file_vtable = {
++ .size = &file_size,
++ .read_block = &file_read_block,
++ .return_block = &file_return_block,
++ .close = &file_close,
++};
++
++int reftable_block_source_from_file(struct reftable_block_source *bs,
++ const char *name)
++{
++ struct stat st = { 0 };
++ int err = 0;
++ int fd = open(name, O_RDONLY);
++ struct file_block_source *p = NULL;
++ if (fd < 0) {
++ if (errno == ENOENT) {
++ return REFTABLE_NOT_EXIST_ERROR;
++ }
++ return -1;
++ }
++
++ err = fstat(fd, &st);
++ if (err < 0)
++ return -1;
++
++ p = reftable_calloc(sizeof(struct file_block_source));
++ p->size = st.st_size;
++ p->fd = fd;
++
++ assert(bs->ops == NULL);
++ bs->ops = &file_vtable;
++ bs->arg = p;
++ return 0;
+}
- ## reftable/test_framework.h (new) ##
+ ## reftable/blocksource.h (new) ##
@@
+/*
+Copyright 2020 Google LLC
@@ reftable/test_framework.h (new)
+https://developers.google.com/open-source/licenses/bsd
+*/
+
-+#ifndef TEST_FRAMEWORK_H
-+#define TEST_FRAMEWORK_H
++#ifndef BLOCKSOURCE_H
++#define BLOCKSOURCE_H
+
+#include "system.h"
+
-+#ifdef NDEBUG
-+#undef NDEBUG
-+#endif
++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);
+
-+#ifdef assert
-+#undef assert
+#endif
+
+ ## reftable/reftable-blocksource.h (new) ##
+@@
++/*
++Copyright 2020 Google LLC
+
-+#define assert_err(c) \
-+ if (c != 0) { \
-+ fflush(stderr); \
-+ fflush(stdout); \
-+ fprintf(stderr, "%s: %d: error == %d (%s), want 0\n", \
-+ __FILE__, __LINE__, c, reftable_error_str(c)); \
-+ abort(); \
-+ }
++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
++*/
+
-+#define assert_streq(a, b) \
-+ if (strcmp(a, b)) { \
-+ fflush(stderr); \
-+ fflush(stdout); \
-+ fprintf(stderr, "%s:%d: %s (%s) != %s (%s)\n", __FILE__, \
-+ __LINE__, #a, a, #b, b); \
-+ abort(); \
-+ }
++#ifndef REFTABLE_BLOCKSOURCE_H
++#define REFTABLE_BLOCKSOURCE_H
+
-+#define assert(c) \
-+ if (!(c)) { \
-+ fflush(stderr); \
-+ fflush(stdout); \
-+ fprintf(stderr, "%s: %d: failed assertion %s\n", __FILE__, \
-+ __LINE__, #c); \
-+ abort(); \
-+ }
++#include <stdint.h>
++
++/* block_source is a generic wrapper for a seekable readable file.
++ */
++struct reftable_block_source {
++ struct reftable_block_source_vtable *ops;
++ void *arg;
++};
+
-+struct test_case {
-+ const char *name;
-+ void (*testfunc)(void);
++/* a contiguous segment of bytes. It keeps track of its generating block_source
++ so it can return itself into the pool.
++*/
++struct reftable_block {
++ uint8_t *data;
++ int len;
++ struct reftable_block_source source;
+};
+
-+struct test_case *add_test_case(const char *name, void (*testfunc)(void));
-+int test_main(int argc, const char *argv[]);
++/* block_source_vtable are the operations that make up block_source */
++struct reftable_block_source_vtable {
++ /* returns the size of a block source */
++ uint64_t (*size)(void *source);
++
++ /* reads a segment from the block source. It is an error to read
++ beyond the end of the block */
++ int (*read_block)(void *source, struct reftable_block *dest,
++ uint64_t off, uint32_t size);
++ /* mark the block as read; may return the data back to malloc */
++ void (*return_block)(void *source, struct reftable_block *blockp);
++
++ /* release all resources associated with the block source */
++ void (*close)(void *source);
++};
+
-+void set_test_hash(uint8_t *p, int i);
++/* opens a file on the file system as a block_source */
++int reftable_block_source_from_file(struct reftable_block_source *block_src,
++ const char *name);
+
+#endif
6: 8eb944ea9b ! 7: bbe2ed7173 reftable: (de)serialization for the polymorphic record type.
@@ Metadata
## Commit message ##
reftable: (de)serialization for the polymorphic record type.
+ The reftable format is structured as a sequence of blocks, and each block
+ contains a sequence of prefix-compressed key-value records. There are 4 types of
+ records, and they have similarities in how they must be handled. This is
+ achieved by introducing a polymorphic 'record' type that encapsulates ref, log,
+ index and object records.
+
Signed-off-by: Han-Wen Nienhuys <[email protected]>
## Makefile ##
@@ Makefile: REFTABLE_OBJS += reftable/basics.o
+ REFTABLE_OBJS += reftable/error.o
REFTABLE_OBJS += reftable/blocksource.o
REFTABLE_OBJS += reftable/publicbasics.o
- REFTABLE_OBJS += reftable/compat.o
+REFTABLE_OBJS += reftable/record.o
- REFTABLE_OBJS += reftable/strbuf.o
+REFTABLE_TEST_OBJS += reftable/record_test.o
- REFTABLE_TEST_OBJS += reftable/strbuf_test.o
REFTABLE_TEST_OBJS += reftable/test_framework.o
+ REFTABLE_TEST_OBJS += reftable/basics_test.o
## reftable/constants.h (new) ##
@@ reftable/record.c (new)
+
+#include "system.h"
+#include "constants.h"
-+#include "reftable.h"
++#include "reftable-error.h"
+#include "basics.h"
+
+int get_var_int(uint64_t *dest, struct string_view *in)
@@ reftable/record.c (new)
+
+ /* This is simple and correct, but we could probably reuse the hash
+ fields. */
-+ reftable_ref_record_clear(ref);
++ reftable_ref_record_release(ref);
+ if (src->refname != NULL) {
+ ref->refname = xstrdup(src->refname);
+ }
@@ reftable/record.c (new)
+ printf("}\n");
+}
+
-+static void reftable_ref_record_clear_void(void *rec)
++static void reftable_ref_record_release_void(void *rec)
+{
-+ reftable_ref_record_clear((struct reftable_ref_record *)rec);
++ reftable_ref_record_release((struct reftable_ref_record *)rec);
+}
+
-+void reftable_ref_record_clear(struct reftable_ref_record *ref)
++void reftable_ref_record_release(struct reftable_ref_record *ref)
+{
+ reftable_free(ref->refname);
+ reftable_free(ref->target);
@@ reftable/record.c (new)
+ .val_type = &reftable_ref_record_val_type,
+ .encode = &reftable_ref_record_encode,
+ .decode = &reftable_ref_record_decode,
-+ .clear = &reftable_ref_record_clear_void,
++ .release = &reftable_ref_record_release_void,
+ .is_deletion = &reftable_ref_record_is_deletion_void,
+};
+
@@ reftable/record.c (new)
+ strbuf_add(dest, rec->hash_prefix, rec->hash_prefix_len);
+}
+
-+static void reftable_obj_record_clear(void *rec)
++static void reftable_obj_record_release(void *rec)
+{
+ struct reftable_obj_record *obj = (struct reftable_obj_record *)rec;
+ FREE_AND_NULL(obj->hash_prefix);
@@ reftable/record.c (new)
+ (const struct reftable_obj_record *)src_rec;
+ int olen;
+
-+ reftable_obj_record_clear(obj);
++ reftable_obj_record_release(obj);
+ *obj = *src;
+ obj->hash_prefix = reftable_malloc(obj->hash_prefix_len);
+ memcpy(obj->hash_prefix, src->hash_prefix, obj->hash_prefix_len);
@@ reftable/record.c (new)
+ .val_type = &reftable_obj_record_val_type,
+ .encode = &reftable_obj_record_encode,
+ .decode = &reftable_obj_record_decode,
-+ .clear = &reftable_obj_record_clear,
++ .release = &reftable_obj_record_release,
+ .is_deletion = not_a_deletion,
+};
+
@@ reftable/record.c (new)
+ const struct reftable_log_record *src =
+ (const struct reftable_log_record *)src_rec;
+
-+ reftable_log_record_clear(dst);
++ reftable_log_record_release(dst);
+ *dst = *src;
+ if (dst->refname != NULL) {
+ dst->refname = xstrdup(dst->refname);
@@ reftable/record.c (new)
+ }
+}
+
-+static void reftable_log_record_clear_void(void *rec)
++static void reftable_log_record_release_void(void *rec)
+{
+ struct reftable_log_record *r = (struct reftable_log_record *)rec;
-+ reftable_log_record_clear(r);
++ reftable_log_record_release(r);
+}
+
-+void reftable_log_record_clear(struct reftable_log_record *r)
++void reftable_log_record_release(struct reftable_log_record *r)
+{
+ reftable_free(r->refname);
+ reftable_free(r->new_hash);
@@ reftable/record.c (new)
+ .val_type = &reftable_log_record_val_type,
+ .encode = &reftable_log_record_encode,
+ .decode = &reftable_log_record_decode,
-+ .clear = &reftable_log_record_clear_void,
++ .release = &reftable_log_record_release_void,
+ .is_deletion = &reftable_log_record_is_deletion_void,
+};
+
@@ reftable/record.c (new)
+
+void reftable_record_destroy(struct reftable_record *rec)
+{
-+ reftable_record_clear(rec);
++ reftable_record_release(rec);
+ reftable_free(reftable_record_yield(rec));
+}
+
@@ reftable/record.c (new)
+ dst->offset = src->offset;
+}
+
-+static void reftable_index_record_clear(void *rec)
++static void reftable_index_record_release(void *rec)
+{
+ struct reftable_index_record *idx = (struct reftable_index_record *)rec;
+ strbuf_release(&idx->last_key);
@@ reftable/record.c (new)
+ .val_type = &reftable_index_record_val_type,
+ .encode = &reftable_index_record_encode,
+ .decode = &reftable_index_record_decode,
-+ .clear = &reftable_index_record_clear,
++ .release = &reftable_index_record_release,
+ .is_deletion = ¬_a_deletion,
+};
+
@@ reftable/record.c (new)
+ return rec->ops->decode(rec->data, key, extra, src, hash_size);
+}
+
-+void reftable_record_clear(struct reftable_record *rec)
++void reftable_record_release(struct reftable_record *rec)
+{
-+ rec->ops->clear(rec->data);
++ rec->ops->release(rec->data);
+}
+
+int reftable_record_is_deletion(struct reftable_record *rec)
@@ reftable/record.h (new)
+#ifndef RECORD_H
+#define RECORD_H
+
-+#include "reftable.h"
-+#include "strbuf.h"
+#include "system.h"
+
++#include <stdint.h>
++
++#include "reftable-record.h"
++
+/*
+ A substring of existing string data. This structure takes no responsibility
+ for the lifetime of the data it points to.
@@ reftable/record.h (new)
+ struct string_view src, int hash_size);
+
+ /* deallocate and null the record. */
-+ void (*clear)(void *rec);
++ void (*release)(void *rec);
+
+ /* is this a tombstone? */
+ int (*is_deletion)(const void *rec);
@@ reftable/record.h (new)
+int reftable_record_is_deletion(struct reftable_record *rec);
+
+/* zeroes out the embedded record */
-+void reftable_record_clear(struct reftable_record *rec);
++void reftable_record_release(struct reftable_record *rec);
+
+/* clear and deallocate embedded record, and zero `rec`. */
+void reftable_record_destroy(struct reftable_record *rec);
@@ reftable/record_test.c (new)
+#include "system.h"
+#include "basics.h"
+#include "constants.h"
-+#include "reftable.h"
+#include "test_framework.h"
+#include "reftable-tests.h"
+
@@ reftable/record_test.c (new)
+ reftable_record_copy_from(©, rec, SHA1_SIZE);
+ switch (reftable_record_type(©)) {
+ case BLOCK_TYPE_REF:
-+ assert(reftable_ref_record_equal(reftable_record_as_ref(©),
++ EXPECT(reftable_ref_record_equal(reftable_record_as_ref(©),
+ reftable_record_as_ref(rec),
+ SHA1_SIZE));
+ break;
+ case BLOCK_TYPE_LOG:
-+ assert(reftable_log_record_equal(reftable_record_as_log(©),
++ EXPECT(reftable_log_record_equal(reftable_record_as_log(©),
+ reftable_record_as_log(rec),
+ SHA1_SIZE));
+ break;
@@ reftable/record_test.c (new)
+ int n = put_var_int(&out, in);
+ uint64_t got = 0;
+
-+ assert(n > 0);
++ EXPECT(n > 0);
+ out.len = n;
+ n = get_var_int(&got, &out);
-+ assert(n > 0);
++ EXPECT(n > 0);
+
-+ assert(got == in);
++ EXPECT(got == in);
+ }
+}
+
@@ reftable/record_test.c (new)
+ 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);
++ EXPECT(common_prefix_size(&a, &b) == cases[i].want);
+
+ strbuf_release(&a);
+ strbuf_release(&b);
@@ reftable/record_test.c (new)
+ reftable_record_from_ref(&rec, &in);
+ test_copy(&rec);
+
-+ assert(reftable_record_val_type(&rec) == i);
++ EXPECT(reftable_record_val_type(&rec) == i);
+
+ reftable_record_key(&rec, &key);
+ n = reftable_record_encode(&rec, dest, SHA1_SIZE);
-+ assert(n > 0);
++ EXPECT(n > 0);
+
+ /* decode into a non-zero reftable_record to test for leaks. */
+
+ reftable_record_from_ref(&rec_out, &out);
+ m = reftable_record_decode(&rec_out, key, i, dest, SHA1_SIZE);
-+ assert(n == m);
++ EXPECT(n == m);
+
-+ assert((out.value != NULL) == (in.value != NULL));
-+ assert((out.target_value != NULL) == (in.target_value != NULL));
-+ assert((out.target != NULL) == (in.target != NULL));
-+ reftable_record_clear(&rec_out);
++ EXPECT((out.value != NULL) == (in.value != NULL));
++ EXPECT((out.target_value != NULL) == (in.target_value != NULL));
++ EXPECT((out.target != NULL) == (in.target != NULL));
++ reftable_record_release(&rec_out);
+
+ strbuf_release(&key);
-+ reftable_ref_record_clear(&in);
++ reftable_ref_record_release(&in);
+ }
+}
+
@@ reftable/record_test.c (new)
+ }
+ };
+
-+ assert(!reftable_log_record_equal(&in[0], &in[1], SHA1_SIZE));
++ EXPECT(!reftable_log_record_equal(&in[0], &in[1], SHA1_SIZE));
+ in[1].update_index = in[0].update_index;
-+ assert(reftable_log_record_equal(&in[0], &in[1], SHA1_SIZE));
-+ reftable_log_record_clear(&in[0]);
-+ reftable_log_record_clear(&in[1]);
++ EXPECT(reftable_log_record_equal(&in[0], &in[1], SHA1_SIZE));
++ reftable_log_record_release(&in[0]);
++ reftable_log_record_release(&in[1]);
+}
+
+static void test_reftable_log_record_roundtrip(void)
@@ reftable/record_test.c (new)
+ reftable_record_key(&rec, &key);
+
+ n = reftable_record_encode(&rec, dest, SHA1_SIZE);
-+ assert(n >= 0);
++ EXPECT(n >= 0);
+ reftable_record_from_log(&rec_out, &out);
+ valtype = reftable_record_val_type(&rec);
+ m = reftable_record_decode(&rec_out, key, valtype, dest,
+ SHA1_SIZE);
-+ assert(n == m);
++ EXPECT(n == m);
+
-+ assert(reftable_log_record_equal(&in[i], &out, SHA1_SIZE));
-+ reftable_log_record_clear(&in[i]);
++ EXPECT(reftable_log_record_equal(&in[i], &out, SHA1_SIZE));
++ reftable_log_record_release(&in[i]);
+ strbuf_release(&key);
-+ reftable_record_clear(&rec_out);
++ reftable_record_release(&rec_out);
+ }
+}
+
@@ reftable/record_test.c (new)
+ uint32_t out;
+ put_be24(dest, in);
+ out = get_be24(dest);
-+ assert(in == out);
++ EXPECT(in == out);
+}
+
+static void test_key_roundtrip(void)
@@ reftable/record_test.c (new)
+ strbuf_addstr(&key, "refs/tags/bla");
+ extra = 6;
+ n = reftable_encode_key(&restart, dest, last_key, key, extra);
-+ assert(!restart);
-+ assert(n > 0);
++ EXPECT(!restart);
++ EXPECT(n > 0);
+
+ m = reftable_decode_key(&roundtrip, &rt_extra, last_key, dest);
-+ assert(n == m);
-+ assert(0 == strbuf_cmp(&key, &roundtrip));
-+ assert(rt_extra == extra);
++ EXPECT(n == m);
++ EXPECT(0 == strbuf_cmp(&key, &roundtrip));
++ EXPECT(rt_extra == extra);
+
+ strbuf_release(&last_key);
+ strbuf_release(&key);
@@ reftable/record_test.c (new)
+ test_copy(&rec);
+ reftable_record_key(&rec, &key);
+ n = reftable_record_encode(&rec, dest, SHA1_SIZE);
-+ assert(n > 0);
++ EXPECT(n > 0);
+ extra = reftable_record_val_type(&rec);
+ reftable_record_from_obj(&rec_out, &out);
+ m = reftable_record_decode(&rec_out, key, extra, dest,
+ SHA1_SIZE);
-+ assert(n == m);
++ EXPECT(n == m);
+
-+ assert(in.hash_prefix_len == out.hash_prefix_len);
-+ assert(in.offset_len == out.offset_len);
++ EXPECT(in.hash_prefix_len == out.hash_prefix_len);
++ EXPECT(in.offset_len == out.offset_len);
+
-+ assert(!memcmp(in.hash_prefix, out.hash_prefix,
++ EXPECT(!memcmp(in.hash_prefix, out.hash_prefix,
+ in.hash_prefix_len));
-+ assert(0 == memcmp(in.offsets, out.offsets,
++ EXPECT(0 == memcmp(in.offsets, out.offsets,
+ sizeof(uint64_t) * in.offset_len));
+ strbuf_release(&key);
-+ reftable_record_clear(&rec_out);
++ reftable_record_release(&rec_out);
+ }
+}
+
@@ reftable/record_test.c (new)
+ reftable_record_key(&rec, &key);
+ test_copy(&rec);
+
-+ assert(0 == strbuf_cmp(&key, &in.last_key));
++ EXPECT(0 == strbuf_cmp(&key, &in.last_key));
+ n = reftable_record_encode(&rec, dest, SHA1_SIZE);
-+ assert(n > 0);
++ EXPECT(n > 0);
+
+ extra = reftable_record_val_type(&rec);
+ reftable_record_from_index(&out_rec, &out);
+ m = reftable_record_decode(&out_rec, key, extra, dest, SHA1_SIZE);
-+ assert(m == n);
++ EXPECT(m == n);
+
-+ assert(in.offset == out.offset);
++ EXPECT(in.offset == out.offset);
+
-+ reftable_record_clear(&out_rec);
++ reftable_record_release(&out_rec);
+ strbuf_release(&key);
+ strbuf_release(&in.last_key);
+}
+
+int record_test_main(int argc, const char *argv[])
+{
-+ add_test_case("test_reftable_log_record_equal",
-+ &test_reftable_log_record_equal);
-+ add_test_case("test_reftable_log_record_roundtrip",
-+ &test_reftable_log_record_roundtrip);
-+ add_test_case("test_reftable_ref_record_roundtrip",
-+ &test_reftable_ref_record_roundtrip);
-+ add_test_case("test_varint_roundtrip", &test_varint_roundtrip);
-+ add_test_case("test_key_roundtrip", &test_key_roundtrip);
-+ add_test_case("test_common_prefix", &test_common_prefix);
-+ add_test_case("test_reftable_obj_record_roundtrip",
-+ &test_reftable_obj_record_roundtrip);
-+ add_test_case("test_reftable_index_record_roundtrip",
-+ &test_reftable_index_record_roundtrip);
-+ add_test_case("test_u24_roundtrip", &test_u24_roundtrip);
-+ return test_main(argc, argv);
++ test_reftable_log_record_equal();
++ test_reftable_log_record_roundtrip();
++ test_reftable_ref_record_roundtrip();
++ test_varint_roundtrip();
++ test_key_roundtrip();
++ test_common_prefix();
++ test_reftable_obj_record_roundtrip();
++ test_reftable_index_record_roundtrip();
++ test_u24_roundtrip();
++ return 0;
+}
+ ## reftable/reftable-record.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 REFTABLE_RECORD_H
++#define REFTABLE_RECORD_H
++
++#include <stdint.h>
++
++/*
++ Basic data types
++
++ Reftables store the state of each ref in struct reftable_ref_record, and they
++ store a sequence of reflog updates in struct reftable_log_record.
++*/
++
++/* reftable_ref_record holds a ref database entry target_value */
++struct reftable_ref_record {
++ char *refname; /* Name of the ref, malloced. */
++ uint64_t update_index; /* Logical timestamp at which this value is
++ written */
++ uint8_t *value; /* SHA1, or NULL. malloced. */
++ uint8_t *target_value; /* peeled annotated tag, or NULL. malloced. */
++ char *target; /* symref, or NULL. malloced. */
++};
++
++/* returns whether 'ref' represents a deletion */
++int reftable_ref_record_is_deletion(const struct reftable_ref_record *ref);
++
++/* prints a reftable_ref_record onto stdout */
++void reftable_ref_record_print(struct reftable_ref_record *ref,
++ uint32_t hash_id);
++
++/* frees and nulls all pointer values. */
++void reftable_ref_record_release(struct reftable_ref_record *ref);
++
++/* returns whether two reftable_ref_records are the same */
++int reftable_ref_record_equal(struct reftable_ref_record *a,
++ struct reftable_ref_record *b, int hash_size);
++
++/* reftable_log_record holds a reflog entry */
++struct reftable_log_record {
++ char *refname;
++ uint64_t update_index; /* logical timestamp of a transactional update.
++ */
++ uint8_t *new_hash;
++ uint8_t *old_hash;
++ char *name;
++ char *email;
++ uint64_t time;
++ int16_t tz_offset;
++ char *message;
++};
++
++/* returns whether 'ref' represents the deletion of a log record. */
++int reftable_log_record_is_deletion(const struct reftable_log_record *log);
++
++/* frees and nulls all pointer values. */
++void reftable_log_record_release(struct reftable_log_record *log);
++
++/* returns whether two records are equal. Useful for testing. */
++int reftable_log_record_equal(struct reftable_log_record *a,
++ struct reftable_log_record *b, int hash_size);
++
++/* dumps a reftable_log_record on stdout, for debugging/testing. */
++void reftable_log_record_print(struct reftable_log_record *log,
++ uint32_t hash_id);
++
++#endif
+
## t/helper/test-reftable.c ##
@@
-
int cmd__reftable(int argc, const char **argv)
{
+ basics_test_main(argc, argv);
+-
+ record_test_main(argc, argv);
- strbuf_test_main(argc, argv);
return 0;
}
7: 757dd30fe2 ! 8: a358b052d5 reftable: reading/writing blocks
@@ Metadata
## Commit message ##
reftable: reading/writing blocks
+ The reftable format is structured as a sequence of block. Within a block,
+ records are prefix compressed, with an index of offsets for fully expand keys to
+ enable binary search within blocks.
+
+ This commit provides the logic to read and write these blocks.
+
Includes a code snippet copied from zlib
Signed-off-by: Han-Wen Nienhuys <[email protected]>
## Makefile ##
-@@ Makefile: XDIFF_OBJS += xdiff/xprepare.o
- XDIFF_OBJS += xdiff/xutils.o
+@@ Makefile: XDIFF_OBJS += xdiff/xutils.o
REFTABLE_OBJS += reftable/basics.o
+ REFTABLE_OBJS += reftable/error.o
+REFTABLE_OBJS += reftable/block.o
REFTABLE_OBJS += reftable/blocksource.o
REFTABLE_OBJS += reftable/publicbasics.o
- REFTABLE_OBJS += reftable/compat.o
REFTABLE_OBJS += reftable/record.o
- REFTABLE_OBJS += reftable/strbuf.o
+REFTABLE_OBJS += reftable/zlib-compat.o
+REFTABLE_TEST_OBJS += reftable/block_test.o
REFTABLE_TEST_OBJS += reftable/record_test.o
- REFTABLE_TEST_OBJS += reftable/strbuf_test.o
REFTABLE_TEST_OBJS += reftable/test_framework.o
+ REFTABLE_TEST_OBJS += reftable/basics_test.o
## reftable/.gitattributes (new) ##
@@
@@ reftable/block.c (new)
+#include "blocksource.h"
+#include "constants.h"
+#include "record.h"
-+#include "reftable.h"
++#include "reftable-error.h"
+#include "system.h"
+#include "zlib.h"
+
@@ reftable/block.c (new)
+ return -1;
+}
+
-+
+int block_writer_finish(struct block_writer *w)
+{
+ int i = 0;
@@ reftable/block.c (new)
+ return err;
+}
+
-+void block_writer_clear(struct block_writer *bw)
++void block_writer_release(struct block_writer *bw)
+{
+ FREE_AND_NULL(bw->restarts);
+ strbuf_release(&bw->last_key);
@@ reftable/block.h (new)
+
+#include "basics.h"
+#include "record.h"
-+#include "reftable.h"
++#include "reftable-blocksource.h"
+
+/*
+ Writes reftable blocks. The block_writer is reused across blocks to minimize
@@ reftable/block.h (new)
+int block_writer_finish(struct block_writer *w);
+
+/* clears out internally allocated block_writer members. */
-+void block_writer_clear(struct block_writer *bw);
++void block_writer_release(struct block_writer *bw);
+
+/* Read a block. */
+struct block_reader {
@@ reftable/block_test.c (new)
+#include "basics.h"
+#include "constants.h"
+#include "record.h"
-+#include "reftable.h"
+#include "test_framework.h"
+#include "reftable-tests.h"
+
-+struct binsearch_args {
-+ int key;
-+ int *arr;
-+};
-+
-+static int binsearch_func(size_t i, void *void_args)
-+{
-+ struct binsearch_args *args = (struct binsearch_args *)void_args;
-+
-+ return args->key < args->arr[i];
-+}
-+
-+static void test_binsearch(void)
-+{
-+ int arr[] = { 2, 4, 6, 8, 10 };
-+ size_t sz = ARRAY_SIZE(arr);
-+ struct binsearch_args args = {
-+ .arr = arr,
-+ };
-+
-+ int i = 0;
-+ for (i = 1; i < 11; i++) {
-+ int res;
-+ args.key = i;
-+ res = binsearch(sz, &binsearch_func, &args);
-+
-+ if (res < sz) {
-+ assert(args.key < arr[res]);
-+ if (res > 0) {
-+ assert(args.key >= arr[res - 1]);
-+ }
-+ } else {
-+ assert(args.key == 10 || args.key == 11);
-+ }
-+ }
-+}
-+
+static void test_block_read_write(void)
+{
+ const int header_off = 21; /* random */
@@ reftable/block_test.c (new)
+ n = block_writer_add(&bw, &rec);
+ ref.refname = NULL;
+ ref.value = NULL;
-+ assert(n == 0);
++ EXPECT(n == 0);
+ }
+
+ n = block_writer_finish(&bw);
-+ assert(n > 0);
++ EXPECT(n > 0);
+
-+ block_writer_clear(&bw);
++ block_writer_release(&bw);
+
+ block_reader_init(&br, &block, header_off, block_size, SHA1_SIZE);
+
@@ reftable/block_test.c (new)
+
+ while (1) {
+ int r = block_iter_next(&it, &rec);
-+ assert(r >= 0);
++ EXPECT(r >= 0);
+ if (r > 0) {
+ break;
+ }
-+ assert_streq(names[j], ref.refname);
++ EXPECT_STREQ(names[j], ref.refname);
+ j++;
+ }
+
-+ reftable_record_clear(&rec);
++ reftable_record_release(&rec);
+ block_iter_close(&it);
+
+ for (i = 0; i < N; i++) {
@@ reftable/block_test.c (new)
+ strbuf_addstr(&want, names[i]);
+
+ n = block_reader_seek(&br, &it, &want);
-+ assert(n == 0);
++ EXPECT(n == 0);
+
+ n = block_iter_next(&it, &rec);
-+ assert(n == 0);
++ EXPECT(n == 0);
+
-+ assert_streq(names[i], ref.refname);
++ EXPECT_STREQ(names[i], ref.refname);
+
+ want.len--;
+ n = block_reader_seek(&br, &it, &want);
-+ assert(n == 0);
++ EXPECT(n == 0);
+
+ n = block_iter_next(&it, &rec);
-+ assert(n == 0);
-+ assert_streq(names[10 * (i / 10)], ref.refname);
++ EXPECT(n == 0);
++ EXPECT_STREQ(names[10 * (i / 10)], ref.refname);
+
+ block_iter_close(&it);
+ }
+
-+ reftable_record_clear(&rec);
++ reftable_record_release(&rec);
+ reftable_block_done(&br.block);
+ strbuf_release(&want);
+ for (i = 0; i < N; i++) {
@@ reftable/block_test.c (new)
+
+int block_test_main(int argc, const char *argv[])
+{
-+ add_test_case("binsearch", &test_binsearch);
-+ add_test_case("block_read_write", &test_block_read_write);
-+ return test_main(argc, argv);
++ test_block_read_write();
++ return 0;
+}
## reftable/zlib-compat.c (new) ##
@@ reftable/zlib-compat.c (new)
## t/helper/test-reftable.c ##
@@
-
int cmd__reftable(int argc, const char **argv)
{
+ basics_test_main(argc, argv);
+ block_test_main(argc, argv);
record_test_main(argc, argv);
- strbuf_test_main(argc, argv);
return 0;
+ }
8: e30a7e0281 ! 9: 24afac9c91 reftable: a generic binary tree implementation
@@ Metadata
## Commit message ##
reftable: a generic binary tree implementation
- This is necessary for building a OID => ref map on write
+ The reftable format includes support for an (OID => ref) map. This map can speed
+ up visibility and reachability checks. In particular, various operations along
+ the fetch/push path within Gerrit have ben sped up by using this structure.
+
+ The map is constructed with help of a binary tree. Object IDs are hashes, so
+ they are uniformly distributed. Hence, the tree does not attempt forced
+ rebalancing.
Signed-off-by: Han-Wen Nienhuys <[email protected]>
## Makefile ##
-@@ Makefile: REFTABLE_OBJS += reftable/publicbasics.o
- REFTABLE_OBJS += reftable/compat.o
+@@ Makefile: REFTABLE_OBJS += reftable/block.o
+ REFTABLE_OBJS += reftable/blocksource.o
+ REFTABLE_OBJS += reftable/publicbasics.o
REFTABLE_OBJS += reftable/record.o
- REFTABLE_OBJS += reftable/strbuf.o
+REFTABLE_OBJS += reftable/tree.o
REFTABLE_OBJS += reftable/zlib-compat.o
++REFTABLE_TEST_OBJS += reftable/basics_test.o
REFTABLE_TEST_OBJS += reftable/block_test.o
REFTABLE_TEST_OBJS += reftable/record_test.o
- REFTABLE_TEST_OBJS += reftable/strbuf_test.o
REFTABLE_TEST_OBJS += reftable/test_framework.o
+-REFTABLE_TEST_OBJS += reftable/basics_test.o
+REFTABLE_TEST_OBJS += reftable/tree_test.o
TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
@@ reftable/tree_test.c (new)
+
+#include "basics.h"
+#include "record.h"
-+#include "reftable.h"
+#include "test_framework.h"
+#include "reftable-tests.h"
+
@@ reftable/tree_test.c (new)
+
+int tree_test_main(int argc, const char *argv[])
+{
-+ add_test_case("test_tree", &test_tree);
-+ return test_main(argc, argv);
++ test_tree();
++ return 0;
+}
## t/helper/test-reftable.c ##
@@ t/helper/test-reftable.c: int cmd__reftable(int argc, const char **argv)
+ basics_test_main(argc, argv);
block_test_main(argc, argv);
record_test_main(argc, argv);
- strbuf_test_main(argc, argv);
+ tree_test_main(argc, argv);
return 0;
}
9: 68aee16e60 ! 10: 5f0b7c5592 reftable: write reftable files
@@ Commit message
Signed-off-by: Han-Wen Nienhuys <[email protected]>
## Makefile ##
-@@ Makefile: REFTABLE_OBJS += reftable/compat.o
+@@ Makefile: REFTABLE_OBJS += reftable/blocksource.o
+ REFTABLE_OBJS += reftable/publicbasics.o
REFTABLE_OBJS += reftable/record.o
- REFTABLE_OBJS += reftable/strbuf.o
REFTABLE_OBJS += reftable/tree.o
+REFTABLE_OBJS += reftable/writer.o
REFTABLE_OBJS += reftable/zlib-compat.o
- REFTABLE_TEST_OBJS += reftable/block_test.o
+ REFTABLE_TEST_OBJS += reftable/basics_test.o
+
+ ## reftable/reftable-writer.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 REFTABLE_WRITER_H
++#define REFTABLE_WRITER_H
++
++#include <stdint.h>
++
++#include "reftable-record.h"
++
++/*
++ Writing single reftables
++*/
++
++/* reftable_write_options sets options for writing a single reftable. */
++struct reftable_write_options {
++ /* boolean: do not pad out blocks to block size. */
++ unsigned unpadded : 1;
++
++ /* the blocksize. Should be less than 2^24. */
++ uint32_t block_size;
++
++ /* boolean: do not generate a SHA1 => ref index. */
++ unsigned skip_index_objects : 1;
++
++ /* how often to write complete keys in each block. */
++ int restart_interval;
++
++ /* 4-byte identifier ("sha1", "s256") of the hash.
++ * Defaults to SHA1 if unset
++ */
++ uint32_t hash_id;
++
++ /* boolean: do not check ref names for validity or dir/file conflicts.
++ */
++ unsigned skip_name_check : 1;
++
++ /* boolean: copy log messages exactly. If unset, check that the message
++ * is a single line, and add '\n' if missing.
++ */
++ unsigned exact_log_message : 1;
++};
++
++/* reftable_block_stats holds statistics for a single block type */
++struct reftable_block_stats {
++ /* total number of entries written */
++ int entries;
++ /* total number of key restarts */
++ int restarts;
++ /* total number of blocks */
++ int blocks;
++ /* total number of index blocks */
++ int index_blocks;
++ /* depth of the index */
++ int max_index_level;
++
++ /* offset of the first block for this type */
++ uint64_t offset;
++ /* offset of the top level index block for this type, or 0 if not
++ * present */
++ uint64_t index_offset;
++};
++
++/* stats holds overall statistics for a single reftable */
++struct reftable_stats {
++ /* total number of blocks written. */
++ int blocks;
++ /* stats for ref data */
++ struct reftable_block_stats ref_stats;
++ /* stats for the SHA1 to ref map. */
++ struct reftable_block_stats obj_stats;
++ /* stats for index blocks */
++ struct reftable_block_stats idx_stats;
++ /* stats for log blocks */
++ struct reftable_block_stats log_stats;
++
++ /* disambiguation length of shortened object IDs. */
++ int object_id_len;
++};
++
++/* reftable_new_writer creates a new writer */
++struct reftable_writer *
++reftable_new_writer(int (*writer_func)(void *, const void *, size_t),
++ void *writer_arg, struct reftable_write_options *opts);
++
++/* 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_stack_next_update_index(), or REFTABLE_API_ERROR is returned.
++
++ For transactional updates to a stack, typically min==max, and the
++ update_index can be obtained by inspeciting the stack. When converting an
++ existing ref database into a single reftable, this would be a range of
++ update-index timestamps.
++ */
++void reftable_writer_set_limits(struct reftable_writer *w, uint64_t min,
++ uint64_t max);
++
++/*
++ Add a reftable_ref_record. The record should have names that come after
++ already added records.
++
++ The update_index must be within the limits set by
++ reftable_writer_set_limits(), or REFTABLE_API_ERROR is returned. It is an
++ REFTABLE_API_ERROR error to write a ref record after a log record.
++*/
++int reftable_writer_add_ref(struct reftable_writer *w,
++ struct reftable_ref_record *ref);
++
++/*
++ Convenience function to add multiple reftable_ref_records; the function sorts
++ the records before adding them, reordering the records array passed in.
++*/
++int reftable_writer_add_refs(struct reftable_writer *w,
++ struct reftable_ref_record *refs, int n);
++
++/*
++ adds reftable_log_records. Log records are keyed by (refname, decreasing
++ update_index). The key for the record added must come after the already added
++ log records.
++*/
++int reftable_writer_add_log(struct reftable_writer *w,
++ struct reftable_log_record *log);
++
++/*
++ Convenience function to add multiple reftable_log_records; the function sorts
++ the records before adding them, reordering records array passed in.
++*/
++int reftable_writer_add_logs(struct reftable_writer *w,
++ struct reftable_log_record *logs, int n);
++
++/* reftable_writer_close finalizes the reftable. The writer is retained so
++ * statistics can be inspected. */
++int reftable_writer_close(struct reftable_writer *w);
++
++/* writer_stats returns the statistics on the reftable being written.
++
++ This struct becomes invalid when the writer is freed.
++ */
++const struct reftable_stats *writer_stats(struct reftable_writer *w);
++
++/* reftable_writer_free deallocates memory for the writer */
++void reftable_writer_free(struct reftable_writer *w);
++
++#endif
## reftable/writer.c (new) ##
@@
@@ reftable/writer.c (new)
+#include "block.h"
+#include "constants.h"
+#include "record.h"
-+#include "reftable.h"
+#include "tree.h"
++#include "reftable-error.h"
+
+/* finishes a block, and writes it to storage */
+static int writer_flush_block(struct reftable_writer *w);
@@ reftable/writer.c (new)
+ w->block_writer->restart_interval = w->opts.restart_interval;
+}
+
++static struct strbuf reftable_empty_strbuf = STRBUF_INIT;
++
+struct reftable_writer *
+reftable_new_writer(int (*writer_func)(void *, const void *, size_t),
+ void *writer_arg, struct reftable_write_options *opts)
@@ reftable/writer.c (new)
+ int err = 0;
+ int i = 0;
+ QSORT(logs, n, reftable_log_record_compare_key);
++
+ for (i = 0; err == 0 && i < n; i++) {
+ err = reftable_writer_add_log(w, &logs[i]);
+ }
@@ reftable/writer.c (new)
+
+done:
+ /* free up memory. */
-+ block_writer_clear(&w->block_writer_data);
++ block_writer_release(&w->block_writer_data);
+ writer_clear_index(w);
+ strbuf_release(&w->last_key);
+ return err;
@@ reftable/writer.h (new)
+
+#include "basics.h"
+#include "block.h"
-+#include "reftable.h"
-+#include "strbuf.h"
+#include "tree.h"
++#include "reftable-writer.h"
+
+struct reftable_writer {
+ int (*write)(void *, const void *, size_t);
10: c196de7f06 ! 11: 9aa2caade8 reftable: read reftable files
@@ Metadata
## Commit message ##
reftable: read reftable files
+ This supports reading a single reftable file.
+
+ The commit introduces an abstract iterator type, which captures the usecases
+ both of reading individual refs, and iterating over a segment of the ref
+ namespace.
+
Signed-off-by: Han-Wen Nienhuys <[email protected]>
## Makefile ##
-@@ Makefile: REFTABLE_OBJS += reftable/block.o
+@@ Makefile: REFTABLE_OBJS += reftable/basics.o
+ REFTABLE_OBJS += reftable/error.o
+ REFTABLE_OBJS += reftable/block.o
REFTABLE_OBJS += reftable/blocksource.o
- REFTABLE_OBJS += reftable/publicbasics.o
- REFTABLE_OBJS += reftable/compat.o
+REFTABLE_OBJS += reftable/iter.o
+ REFTABLE_OBJS += reftable/publicbasics.o
+REFTABLE_OBJS += reftable/reader.o
REFTABLE_OBJS += reftable/record.o
+REFTABLE_OBJS += reftable/reftable.o
- REFTABLE_OBJS += reftable/strbuf.o
REFTABLE_OBJS += reftable/tree.o
REFTABLE_OBJS += reftable/writer.o
+ REFTABLE_OBJS += reftable/zlib-compat.o
## reftable/iter.c (new) ##
@@
@@ reftable/iter.c (new)
+#include "block.h"
+#include "constants.h"
+#include "reader.h"
-+#include "reftable.h"
++#include "reftable-error.h"
+
+int iterator_is_null(struct reftable_iterator *it)
+{
@@ reftable/iter.c (new)
+int reftable_iterator_next_ref(struct reftable_iterator *it,
+ struct reftable_ref_record *ref)
+{
-+ struct reftable_record rec = { 0 };
++ struct reftable_record rec = { NULL };
+ reftable_record_from_ref(&rec, ref);
+ return iterator_next(it, &rec);
+}
@@ reftable/iter.c (new)
+int reftable_iterator_next_log(struct reftable_iterator *it,
+ struct reftable_log_record *log)
+{
-+ struct reftable_record rec = { 0 };
++ struct reftable_record rec = { NULL };
+ reftable_record_from_log(&rec, log);
+ return iterator_next(it, &rec);
+}
@@ reftable/iter.c (new)
+ }
+
+ if (fri->double_check) {
-+ struct reftable_iterator it = { 0 };
++ struct reftable_iterator it = { NULL };
+
+ err = reftable_table_seek_ref(&fri->tab, &it,
+ ref->refname);
@@ reftable/iter.c (new)
+ }
+ }
+
-+ reftable_ref_record_clear(ref);
++ reftable_ref_record_release(ref);
+ return err;
+}
+
@@ reftable/iter.h (new)
+#ifndef ITER_H
+#define ITER_H
+
++#include "system.h"
+#include "block.h"
+#include "record.h"
-+#include "strbuf.h"
++
++#include "reftable-iterator.h"
++#include "reftable-generic.h"
+
+struct reftable_iterator_vtable {
+ int (*next)(void *iter_arg, struct reftable_record *rec);
@@ reftable/reader.c (new)
+#include "constants.h"
+#include "iter.h"
+#include "record.h"
-+#include "reftable.h"
++#include "reftable-error.h"
+#include "tree.h"
+
+uint64_t block_source_size(struct reftable_block_source *source)
@@ reftable/reader.c (new)
+int init_reader(struct reftable_reader *r, struct reftable_block_source *source,
+ const char *name)
+{
-+ struct reftable_block footer = { 0 };
-+ struct reftable_block header = { 0 };
++ struct reftable_block footer = { NULL };
++ struct reftable_block header = { NULL };
+ int err = 0;
+
+ memset(r, 0, sizeof(struct reftable_reader));
@@ reftable/reader.c (new)
+{
+ int32_t guess_block_size = r->block_size ? r->block_size :
+ DEFAULT_BLOCK_SIZE;
-+ struct reftable_block block = { 0 };
++ struct reftable_block block = { NULL };
+ uint8_t block_typ = 0;
+ int err = 0;
+ uint32_t header_off = next_off ? 0 : header_size(r->version);
@@ reftable/reader.c (new)
+ struct reftable_record *rec)
+{
+ struct reftable_index_record want_index = { .last_key = STRBUF_INIT };
-+ struct reftable_record want_index_rec = { 0 };
++ struct reftable_record want_index_rec = { NULL };
+ struct reftable_index_record index_result = { .last_key = STRBUF_INIT };
-+ struct reftable_record index_result_rec = { 0 };
++ struct reftable_record index_result_rec = { NULL };
+ struct table_iter index_iter = TABLE_ITER_INIT;
+ struct table_iter next = TABLE_ITER_INIT;
+ int err = 0;
@@ reftable/reader.c (new)
+done:
+ block_iter_close(&next.bi);
+ table_iter_close(&index_iter);
-+ reftable_record_clear(&want_index_rec);
-+ reftable_record_clear(&index_result_rec);
++ reftable_record_release(&want_index_rec);
++ reftable_record_release(&index_result_rec);
+ return err;
+}
+
@@ reftable/reader.c (new)
+ struct reftable_ref_record ref = {
+ .refname = (char *)name,
+ };
-+ struct reftable_record rec = { 0 };
++ struct reftable_record rec = { NULL };
+ reftable_record_from_ref(&rec, &ref);
+ return reader_seek(r, it, &rec);
+}
@@ reftable/reader.c (new)
+ .refname = (char *)name,
+ .update_index = update_index,
+ };
-+ struct reftable_record rec = { 0 };
++ struct reftable_record rec = { NULL };
+ reftable_record_from_log(&rec, &log);
+ return reader_seek(r, it, &rec);
+}
@@ reftable/reader.c (new)
+ .hash_prefix = oid,
+ .hash_prefix_len = r->object_id_len,
+ };
-+ struct reftable_record want_rec = { 0 };
-+ struct reftable_iterator oit = { 0 };
-+ struct reftable_obj_record got = { 0 };
-+ struct reftable_record got_rec = { 0 };
++ struct reftable_record want_rec = { NULL };
++ struct reftable_iterator oit = { NULL };
++ struct reftable_obj_record got = { NULL };
++ struct reftable_record got_rec = { NULL };
+ int err = 0;
+ struct indexed_table_ref_iter *itr = NULL;
+
@@ reftable/reader.c (new)
+
+done:
+ reftable_iterator_destroy(&oit);
-+ reftable_record_clear(&got_rec);
++ reftable_record_release(&got_rec);
+ return err;
+}
+
@@ reftable/reader.h (new)
+
+#include "block.h"
+#include "record.h"
-+#include "reftable.h"
++#include "reftable-iterator.h"
++#include "reftable-reader.h"
+
+uint64_t block_source_size(struct reftable_block_source *source);
+
@@ reftable/reader.h (new)
+ uint64_t (*max_update_index)(void *tab);
+};
+
-+int reftable_table_seek_record(struct reftable_table *tab,
-+ struct reftable_iterator *it,
-+ struct reftable_record *rec);
-+
+#endif
- ## reftable/reftable.c (new) ##
+ ## reftable/reftable-iterator.h (new) ##
@@
+/*
+Copyright 2020 Google LLC
@@ reftable/reftable.c (new)
+https://developers.google.com/open-source/licenses/bsd
+*/
+
-+#include "reftable.h"
-+#include "record.h"
-+#include "reader.h"
++#ifndef REFTABLE_ITERATOR_H
++#define REFTABLE_ITERATOR_H
+
-+static int reftable_reader_seek_void(void *tab, struct reftable_iterator *it,
-+ struct reftable_record *rec)
-+{
-+ return reader_seek((struct reftable_reader *)tab, it, rec);
-+}
++#include "reftable-record.h"
+
-+static uint32_t reftable_reader_hash_id_void(void *tab)
-+{
-+ return reftable_reader_hash_id((struct reftable_reader *)tab);
-+}
++/* iterator is the generic interface for walking over data stored in a
++ reftable.
++*/
++struct reftable_iterator {
++ struct reftable_iterator_vtable *ops;
++ void *iter_arg;
++};
+
-+static uint64_t reftable_reader_min_update_index_void(void *tab)
-+{
-+ return reftable_reader_min_update_index((struct reftable_reader *)tab);
-+}
++/* reads the next reftable_ref_record. Returns < 0 for error, 0 for OK and > 0:
++ end of iteration.
++*/
++int reftable_iterator_next_ref(struct reftable_iterator *it,
++ struct reftable_ref_record *ref);
+
-+static uint64_t reftable_reader_max_update_index_void(void *tab)
-+{
-+ return reftable_reader_max_update_index((struct reftable_reader *)tab);
-+}
++/* reads the next reftable_log_record. Returns < 0 for error, 0 for OK and > 0:
++ end of iteration.
++*/
++int reftable_iterator_next_log(struct reftable_iterator *it,
++ struct reftable_log_record *log);
+
-+static struct reftable_table_vtable reader_vtable = {
-+ .seek_record = reftable_reader_seek_void,
-+ .hash_id = reftable_reader_hash_id_void,
-+ .min_update_index = reftable_reader_min_update_index_void,
-+ .max_update_index = reftable_reader_max_update_index_void,
-+};
++/* releases resources associated with an iterator. */
++void reftable_iterator_destroy(struct reftable_iterator *it);
+
-+int reftable_table_seek_ref(struct reftable_table *tab,
-+ struct reftable_iterator *it, const char *name)
-+{
-+ struct reftable_ref_record ref = {
-+ .refname = (char *)name,
-+ };
-+ struct reftable_record rec = { 0 };
-+ reftable_record_from_ref(&rec, &ref);
-+ return tab->ops->seek_record(tab->table_arg, it, &rec);
-+}
++#endif
+
+ ## reftable/reftable-reader.h (new) ##
+@@
++/*
++Copyright 2020 Google LLC
+
-+void reftable_table_from_reader(struct reftable_table *tab,
-+ struct reftable_reader *reader)
-+{
-+ assert(tab->ops == NULL);
-+ tab->ops = &reader_vtable;
-+ tab->table_arg = reader;
-+}
++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
++*/
+
-+int reftable_table_read_ref(struct reftable_table *tab, const char *name,
-+ struct reftable_ref_record *ref)
-+{
-+ struct reftable_iterator it = { 0 };
-+ int err = reftable_table_seek_ref(tab, &it, name);
-+ if (err)
-+ goto done;
++#ifndef REFTABLE_READER_H
++#define REFTABLE_READER_H
+
-+ err = reftable_iterator_next_ref(&it, ref);
-+ if (err)
-+ goto done;
++#include "reftable-iterator.h"
++#include "reftable-blocksource.h"
+
-+ if (strcmp(ref->refname, name) ||
-+ reftable_ref_record_is_deletion(ref)) {
-+ reftable_ref_record_clear(ref);
-+ err = 1;
-+ goto done;
-+ }
++/*
++ Reading single tables
+
-+done:
-+ reftable_iterator_destroy(&it);
-+ return err;
-+}
++ The follow routines are for reading single files. For an application-level
++ interface, skip ahead to struct reftable_merged_table and struct
++ reftable_stack.
++*/
+
-+int reftable_table_seek_record(struct reftable_table *tab,
-+ struct reftable_iterator *it,
-+ struct reftable_record *rec)
-+{
-+ return tab->ops->seek_record(tab->table_arg, it, rec);
-+}
++/* The reader struct is a handle to an open reftable file. */
++struct reftable_reader;
+
-+uint64_t reftable_table_max_update_index(struct reftable_table *tab)
-+{
-+ return tab->ops->max_update_index(tab->table_arg);
-+}
++/* reftable_new_reader opens a reftable for reading. If successful, returns 0
++ * code and sets pp. The name is used for creating a stack. Typically, it is the
++ * basename of the file. The block source `src` is owned by the reader, and is
++ * closed on calling reftable_reader_destroy().
++ */
++int reftable_new_reader(struct reftable_reader **pp,
++ struct reftable_block_source *src, const char *name);
++
++/* reftable_reader_seek_ref returns an iterator where 'name' would be inserted
++ in the table. To seek to the start of the table, use name = "".
++
++ example:
++
++ struct reftable_reader *r = NULL;
++ int err = reftable_new_reader(&r, &src, "filename");
++ if (err < 0) { ... }
++ struct reftable_iterator it = {0};
++ err = reftable_reader_seek_ref(r, &it, "refs/heads/master");
++ if (err < 0) { ... }
++ struct reftable_ref_record ref = {0};
++ while (1) {
++ err = reftable_iterator_next_ref(&it, &ref);
++ if (err > 0) {
++ break;
++ }
++ if (err < 0) {
++ ..error handling..
++ }
++ ..found..
++ }
++ reftable_iterator_destroy(&it);
++ reftable_ref_record_release(&ref);
++ */
++int reftable_reader_seek_ref(struct reftable_reader *r,
++ struct reftable_iterator *it, const char *name);
+
-+uint64_t reftable_table_min_update_index(struct reftable_table *tab)
-+{
-+ return tab->ops->min_update_index(tab->table_arg);
-+}
++/* returns the hash ID used in this table. */
++uint32_t reftable_reader_hash_id(struct reftable_reader *r);
+
-+uint32_t reftable_table_hash_id(struct reftable_table *tab)
-+{
-+ return tab->ops->hash_id(tab->table_arg);
-+}
++/* seek to logs for the given name, older than update_index. To seek to the
++ start of the table, use name = "".
++ */
++int reftable_reader_seek_log_at(struct reftable_reader *r,
++ struct reftable_iterator *it, const char *name,
++ uint64_t update_index);
++
++/* seek to newest log entry for given name. */
++int reftable_reader_seek_log(struct reftable_reader *r,
++ struct reftable_iterator *it, const char *name);
++
++/* closes and deallocates a reader. */
++void reftable_reader_free(struct reftable_reader *);
++
++/* return an iterator for the refs pointing to `oid`. */
++int reftable_reader_refs_for(struct reftable_reader *r,
++ struct reftable_iterator *it, uint8_t *oid);
++
++/* return the max_update_index for a table */
++uint64_t reftable_reader_max_update_index(struct reftable_reader *r);
++
++/* return the min_update_index for a table */
++uint64_t reftable_reader_min_update_index(struct reftable_reader *r);
++
++#endif
11: bf6b929b86 ! 12: 581a4200d6 reftable: file level tests
@@ Metadata
Author: Han-Wen Nienhuys <[email protected]>
## Commit message ##
- reftable: file level tests
+ reftable: reftable file level tests
+
+ With support for reading and writing files in place, we can construct files (in
+ memory) and attempt to read them back.
+
+ Because some sections of the format are optional (eg. indices, log entries), we
+ have to exercise this code using multiple sizes of input data
Signed-off-by: Han-Wen Nienhuys <[email protected]>
## Makefile ##
@@ Makefile: REFTABLE_OBJS += reftable/zlib-compat.o
-
+ REFTABLE_TEST_OBJS += reftable/basics_test.o
REFTABLE_TEST_OBJS += reftable/block_test.o
REFTABLE_TEST_OBJS += reftable/record_test.o
+REFTABLE_TEST_OBJS += reftable/reftable_test.o
- REFTABLE_TEST_OBJS += reftable/strbuf_test.o
REFTABLE_TEST_OBJS += reftable/test_framework.o
REFTABLE_TEST_OBJS += reftable/tree_test.o
+
## reftable/reftable_test.c (new) ##
@@
@@ reftable/reftable_test.c (new)
+https://developers.google.com/open-source/licenses/bsd
+*/
+
-+#include "reftable.h"
-+
+#include "system.h"
+
+#include "basics.h"
@@ reftable/reftable_test.c (new)
+#include "record.h"
+#include "test_framework.h"
+#include "reftable-tests.h"
++#include "reftable-stack.h"
+
+static const int update_index = 5;
+
@@ reftable/reftable_test.c (new)
+ uint8_t in[] = "hello";
+ strbuf_add(&buf, in, sizeof(in));
+ block_source_from_strbuf(&source, &buf);
-+ assert(block_source_size(&source) == 6);
++ EXPECT(block_source_size(&source) == 6);
+ n = block_source_read_block(&source, &out, 0, sizeof(in));
-+ assert(n == sizeof(in));
-+ assert(!memcmp(in, out.data, n));
++ EXPECT(n == sizeof(in));
++ EXPECT(!memcmp(in, out.data, n));
+ reftable_block_done(&out);
+
+ n = block_source_read_block(&source, &out, 1, 2);
-+ assert(n == 2);
-+ assert(!memcmp(out.data, "el", 2));
++ EXPECT(n == 2);
++ EXPECT(!memcmp(out.data, "el", 2));
+
+ reftable_block_done(&out);
+ block_source_close(&source);
@@ reftable/reftable_test.c (new)
+ (*names)[i] = xstrdup(name);
+
+ n = reftable_writer_add_ref(w, &ref);
-+ assert(n == 0);
++ EXPECT(n == 0);
+ }
+
+ for (i = 0; i < N; i++) {
@@ reftable/reftable_test.c (new)
+ log.message = "message";
+
+ n = reftable_writer_add_log(w, &log);
-+ assert(n == 0);
++ EXPECT(n == 0);
+ }
+
+ n = reftable_writer_close(w);
-+ assert(n == 0);
++ EXPECT(n == 0);
+
+ stats = writer_stats(w);
+ for (i = 0; i < stats->ref_stats.blocks; i++) {
@@ reftable/reftable_test.c (new)
+ if (off == 0) {
+ off = header_size((hash_id == SHA256_ID) ? 2 : 1);
+ }
-+ assert(buf->buf[off] == 'r');
++ EXPECT(buf->buf[off] == 'r');
+ }
+
-+ assert(stats->log_stats.blocks > 0);
++ EXPECT(stats->log_stats.blocks > 0);
+ reftable_writer_free(w);
+}
+
@@ reftable/reftable_test.c (new)
+ log.new_hash = hash2;
+ reftable_writer_set_limits(w, update_index, update_index);
+ err = reftable_writer_add_log(w, &log);
-+ assert_err(err);
++ EXPECT_ERR(err);
+ err = reftable_writer_close(w);
-+ assert_err(err);
++ EXPECT_ERR(err);
+ reftable_writer_free(w);
+ strbuf_release(&buf);
+}
@@ reftable/reftable_test.c (new)
+ ref.update_index = i;
+
+ err = reftable_writer_add_ref(w, &ref);
-+ assert_err(err);
++ EXPECT_ERR(err);
+ }
+ for (i = 0; i < N; i++) {
+ uint8_t hash1[SHA1_SIZE], hash2[SHA1_SIZE];
@@ reftable/reftable_test.c (new)
+ log.new_hash = hash2;
+
+ err = reftable_writer_add_log(w, &log);
-+ assert_err(err);
++ EXPECT_ERR(err);
+ }
+
+ n = reftable_writer_close(w);
-+ assert(n == 0);
++ EXPECT(n == 0);
+
+ stats = writer_stats(w);
-+ assert(stats->log_stats.blocks > 0);
++ EXPECT(stats->log_stats.blocks > 0);
+ reftable_writer_free(w);
+ w = NULL;
+
+ block_source_from_strbuf(&source, &buf);
+
+ err = init_reader(&rd, &source, "file.log");
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_reader_seek_ref(&rd, &it, names[N - 1]);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_iterator_next_ref(&it, &ref);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ /* end of iteration. */
+ err = reftable_iterator_next_ref(&it, &ref);
-+ assert(0 < err);
++ EXPECT(0 < err);
+
+ reftable_iterator_destroy(&it);
-+ reftable_ref_record_clear(&ref);
++ reftable_ref_record_release(&ref);
+
+ err = reftable_reader_seek_log(&rd, &it, "");
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ i = 0;
+ while (1) {
@@ reftable/reftable_test.c (new)
+ break;
+ }
+
-+ assert_err(err);
-+ assert_streq(names[i], log.refname);
-+ assert(i == log.update_index);
++ EXPECT_ERR(err);
++ EXPECT_STREQ(names[i], log.refname);
++ EXPECT(i == log.update_index);
+ i++;
-+ reftable_log_record_clear(&log);
++ reftable_log_record_release(&log);
+ }
+
-+ assert(i == N);
++ EXPECT(i == N);
+ reftable_iterator_destroy(&it);
+
+ /* cleanup. */
@@ reftable/reftable_test.c (new)
+ block_source_from_strbuf(&source, &buf);
+
+ err = init_reader(&rd, &source, "file.ref");
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_reader_seek_ref(&rd, &it, "");
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ while (1) {
+ struct reftable_ref_record ref = { NULL };
+ int r = reftable_iterator_next_ref(&it, &ref);
-+ assert(r >= 0);
++ EXPECT(r >= 0);
+ if (r > 0) {
+ break;
+ }
-+ assert(0 == strcmp(names[j], ref.refname));
-+ assert(update_index == ref.update_index);
++ EXPECT(0 == strcmp(names[j], ref.refname));
++ EXPECT(update_index == ref.update_index);
+
+ j++;
-+ reftable_ref_record_clear(&ref);
++ reftable_ref_record_release(&ref);
+ }
-+ assert(j == N);
++ EXPECT(j == N);
+ reftable_iterator_destroy(&it);
+ strbuf_release(&buf);
+ free_names(names);
@@ reftable/reftable_test.c (new)
+ struct strbuf buf = STRBUF_INIT;
+ int N = 1;
+ write_table(&names, &buf, N, 4096, SHA1_ID);
-+ assert(buf.len < 200);
++ EXPECT(buf.len < 200);
+ strbuf_release(&buf);
+ free_names(names);
+}
@@ reftable/reftable_test.c (new)
+ block_source_from_strbuf(&source, &buf);
+
+ err = init_reader(&rd, &source, "file.ref");
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_reader_seek_ref(&rd, &it, names[0]);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_iterator_next_log(&it, &log);
-+ assert(err == REFTABLE_API_ERROR);
++ EXPECT(err == REFTABLE_API_ERROR);
+
+ strbuf_release(&buf);
+ for (i = 0; i < N; i++) {
@@ reftable/reftable_test.c (new)
+ block_source_from_strbuf(&source, &buf);
+
+ err = init_reader(&rd, &source, "file.ref");
-+ assert_err(err);
-+ assert(hash_id == reftable_reader_hash_id(&rd));
++ EXPECT_ERR(err);
++ EXPECT(hash_id == reftable_reader_hash_id(&rd));
+
+ if (!index) {
+ rd.ref_offsets.index_offset = 0;
+ } else {
-+ assert(rd.ref_offsets.index_offset > 0);
++ EXPECT(rd.ref_offsets.index_offset > 0);
+ }
+
+ for (i = 1; i < N; i++) {
+ int err = reftable_reader_seek_ref(&rd, &it, names[i]);
-+ assert_err(err);
++ EXPECT_ERR(err);
+ err = reftable_iterator_next_ref(&it, &ref);
-+ assert_err(err);
-+ assert(0 == strcmp(names[i], ref.refname));
-+ assert(i == ref.value[0]);
++ EXPECT_ERR(err);
++ EXPECT(0 == strcmp(names[i], ref.refname));
++ EXPECT(i == ref.value[0]);
+
-+ reftable_ref_record_clear(&ref);
++ reftable_ref_record_release(&ref);
+ reftable_iterator_destroy(&it);
+ }
+
@@ reftable/reftable_test.c (new)
+ if (err == 0) {
+ struct reftable_ref_record ref = { NULL };
+ int err = reftable_iterator_next_ref(&it, &ref);
-+ assert(err > 0);
++ EXPECT(err > 0);
+ } else {
-+ assert(err > 0);
++ EXPECT(err > 0);
+ }
+
+ strbuf_release(&pastLast);
@@ reftable/reftable_test.c (new)
+ */
+ /* blocks. */
+ n = reftable_writer_add_ref(w, &ref);
-+ assert(n == 0);
++ EXPECT(n == 0);
+
+ if (!memcmp(hash1, want_hash, SHA1_SIZE) ||
+ !memcmp(hash2, want_hash, SHA1_SIZE)) {
@@ reftable/reftable_test.c (new)
+ }
+
+ n = reftable_writer_close(w);
-+ assert(n == 0);
++ EXPECT(n == 0);
+
+ reftable_writer_free(w);
+ w = NULL;
@@ reftable/reftable_test.c (new)
+ block_source_from_strbuf(&source, &buf);
+
+ err = init_reader(&rd, &source, "file.ref");
-+ assert_err(err);
++ EXPECT_ERR(err);
+ if (!indexed) {
+ rd.obj_offsets.is_present = 0;
+ }
+
+ err = reftable_reader_seek_ref(&rd, &it, "");
-+ assert_err(err);
++ EXPECT_ERR(err);
+ reftable_iterator_destroy(&it);
+
+ err = reftable_reader_refs_for(&rd, &it, want_hash);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ j = 0;
+ while (1) {
+ int err = reftable_iterator_next_ref(&it, &ref);
-+ assert(err >= 0);
++ EXPECT(err >= 0);
+ if (err > 0) {
+ break;
+ }
+
-+ assert(j < want_names_len);
-+ assert(0 == strcmp(ref.refname, want_names[j]));
++ EXPECT(j < want_names_len);
++ EXPECT(0 == strcmp(ref.refname, want_names[j]));
+ j++;
-+ reftable_ref_record_clear(&ref);
++ reftable_ref_record_release(&ref);
+ }
-+ assert(j == want_names_len);
++ EXPECT(j == want_names_len);
+
+ strbuf_release(&buf);
+ free_names(want_names);
@@ reftable/reftable_test.c (new)
+ reftable_writer_set_limits(w, 1, 1);
+
+ err = reftable_writer_close(w);
-+ assert(err == REFTABLE_EMPTY_TABLE_ERROR);
++ EXPECT(err == REFTABLE_EMPTY_TABLE_ERROR);
+ reftable_writer_free(w);
+
-+ assert(buf.len == header_size(1) + footer_size(1));
++ EXPECT(buf.len == header_size(1) + footer_size(1));
+
+ block_source_from_strbuf(&source, &buf);
+
+ err = reftable_new_reader(&rd, &source, "filename");
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_reader_seek_ref(rd, &it, "");
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_iterator_next_ref(&it, &rec);
-+ assert(err > 0);
++ EXPECT(err > 0);
+
+ reftable_iterator_destroy(&it);
+ reftable_reader_free(rd);
@@ reftable/reftable_test.c (new)
+
+int reftable_test_main(int argc, const char *argv[])
+{
-+ add_test_case("test_log_write_read", test_log_write_read);
-+ add_test_case("test_table_read_write_seek_linear_sha256",
-+ &test_table_read_write_seek_linear_sha256);
-+ add_test_case("test_log_buffer_size", test_log_buffer_size);
-+ add_test_case("test_table_write_small_table",
-+ &test_table_write_small_table);
-+ add_test_case("test_buffer", &test_buffer);
-+ add_test_case("test_table_read_api", &test_table_read_api);
-+ add_test_case("test_table_read_write_sequential",
-+ &test_table_read_write_sequential);
-+ add_test_case("test_table_read_write_seek_linear",
-+ &test_table_read_write_seek_linear);
-+ add_test_case("test_table_read_write_seek_index",
-+ &test_table_read_write_seek_index);
-+ add_test_case("test_table_read_write_refs_for_no_index",
-+ &test_table_refs_for_no_index);
-+ add_test_case("test_table_read_write_refs_for_obj_index",
-+ &test_table_refs_for_obj_index);
-+ add_test_case("test_table_empty", &test_table_empty);
-+ return test_main(argc, argv);
++ test_log_write_read();
++ test_table_read_write_seek_linear_sha256();
++ test_log_buffer_size();
++ test_table_write_small_table();
++ test_buffer();
++ test_table_read_api();
++ test_table_read_write_sequential();
++ test_table_read_write_seek_linear();
++ test_table_read_write_seek_index();
++ test_table_refs_for_no_index();
++ test_table_refs_for_obj_index();
++ test_table_empty();
++ return 0;
+}
## t/helper/test-reftable.c ##
@@ t/helper/test-reftable.c: int cmd__reftable(int argc, const char **argv)
- {
+ basics_test_main(argc, argv);
block_test_main(argc, argv);
record_test_main(argc, argv);
+ reftable_test_main(argc, argv);
- strbuf_test_main(argc, argv);
tree_test_main(argc, argv);
return 0;
+ }
12: 4e38db7f48 ! 13: a26fb180ad reftable: rest of library
@@ Metadata
## Commit message ##
reftable: rest of library
- This will be further split up once preceding commits have passed review.
-
Signed-off-by: Han-Wen Nienhuys <[email protected]>
## Makefile ##
-@@ Makefile: XDIFF_OBJS += xdiff/xutils.o
- REFTABLE_OBJS += reftable/basics.o
+@@ Makefile: REFTABLE_OBJS += reftable/error.o
REFTABLE_OBJS += reftable/block.o
REFTABLE_OBJS += reftable/blocksource.o
--REFTABLE_OBJS += reftable/publicbasics.o
- REFTABLE_OBJS += reftable/compat.o
REFTABLE_OBJS += reftable/iter.o
+REFTABLE_OBJS += reftable/merged.o
+REFTABLE_OBJS += reftable/pq.o
-+REFTABLE_OBJS += reftable/publicbasics.o
+ REFTABLE_OBJS += reftable/publicbasics.o
REFTABLE_OBJS += reftable/reader.o
REFTABLE_OBJS += reftable/record.o
+REFTABLE_OBJS += reftable/refname.o
REFTABLE_OBJS += reftable/reftable.o
+REFTABLE_OBJS += reftable/stack.o
- REFTABLE_OBJS += reftable/strbuf.o
REFTABLE_OBJS += reftable/tree.o
REFTABLE_OBJS += reftable/writer.o
-@@ Makefile: REFTABLE_OBJS += reftable/zlib-compat.o
-
- REFTABLE_TEST_OBJS += reftable/block_test.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/stack_test.o
- REFTABLE_TEST_OBJS += reftable/strbuf_test.o
- REFTABLE_TEST_OBJS += reftable/test_framework.o
- REFTABLE_TEST_OBJS += reftable/tree_test.o
+ REFTABLE_OBJS += reftable/zlib-compat.o
## reftable/VERSION (new) ##
@@
-+7134eb9f8171a9759800f4187f9e6dde997335e7 C: NULL iso 0 for init
++b8ec0f74c74cb6752eb2033ad8e755a9c19aad15 C: add missing header
## reftable/dump.c (new) ##
@@
@@ reftable/dump.c (new)
+
+static int dump_table(const char *tablename)
+{
-+ struct reftable_block_source src = { NULL };
++ struct reftable_block_source src = { 0 };
+ int err = reftable_block_source_from_file(&src, tablename);
-+ struct reftable_iterator it = { NULL };
-+ struct reftable_ref_record ref = { NULL };
-+ struct reftable_log_record log = { NULL };
++ struct reftable_iterator it = { 0 };
++ struct reftable_ref_record ref = { 0 };
++ struct reftable_log_record log = { 0 };
+ struct reftable_reader *r = NULL;
+
+ if (err < 0)
@@ reftable/dump.c (new)
+{
+ struct reftable_stack *stack = NULL;
+ struct reftable_write_options cfg = {};
-+ struct reftable_iterator it = { NULL };
-+ struct reftable_ref_record ref = { NULL };
-+ struct reftable_log_record log = { NULL };
++ struct reftable_iterator it = { 0 };
++ struct reftable_ref_record ref = { 0 };
++ struct reftable_log_record log = { 0 };
+ struct reftable_merged_table *merged = NULL;
+
+ int err = reftable_new_stack(&stack, stackdir, cfg);
@@ reftable/merged.c (new)
+#include "pq.h"
+#include "reader.h"
+#include "record.h"
-+#include "reftable.h"
++#include "reftable-merged.h"
++#include "reftable-error.h"
+#include "system.h"
+
+static int merged_iter_init(struct merged_iter *mi)
@@ reftable/merged.c (new)
+{
+ struct merged_iter *mi = (struct merged_iter *)p;
+ int i = 0;
-+ merged_iter_pqueue_clear(&mi->pq);
++ merged_iter_pqueue_release(&mi->pq);
+ for (i = 0; i < mi->stack_len; i++) {
+ reftable_iterator_destroy(&mi->stack[i]);
+ }
@@ reftable/merged.c (new)
+}
+
+/* clears the list of subtable, without affecting the readers themselves. */
-+void merged_table_clear(struct reftable_merged_table *mt)
++void merged_table_release(struct reftable_merged_table *mt)
+{
+ FREE_AND_NULL(mt->stack);
+ mt->stack_len = 0;
@@ reftable/merged.c (new)
+ if (mt == NULL) {
+ return;
+ }
-+ merged_table_clear(mt);
++ merged_table_release(mt);
+ reftable_free(mt);
+}
+
@@ reftable/merged.c (new)
+ return mt->min;
+}
+
++static int reftable_table_seek_record(struct reftable_table *tab,
++ struct reftable_iterator *it,
++ struct reftable_record *rec)
++{
++ return tab->ops->seek_record(tab->table_arg, it, rec);
++}
++
+static int merged_table_seek_record(struct reftable_merged_table *mt,
+ struct reftable_iterator *it,
+ struct reftable_record *rec)
@@ reftable/merged.h (new)
+#define MERGED_H
+
+#include "pq.h"
-+#include "reftable.h"
+
+struct reftable_merged_table {
+ struct reftable_table *stack;
@@ reftable/merged.h (new)
+ struct merged_iter_pqueue pq;
+};
+
-+void merged_table_clear(struct reftable_merged_table *mt);
++void merged_table_release(struct reftable_merged_table *mt);
+
+#endif
@@ reftable/merged_test.c (new)
+#include "pq.h"
+#include "reader.h"
+#include "record.h"
-+#include "reftable.h"
+#include "test_framework.h"
++#include "reftable-merged.h"
+#include "reftable-tests.h"
++#include "reftable-generic.h"
++#include "reftable-stack.h"
+
+static void test_pq(void)
+{
@@ reftable/merged_test.c (new)
+ reftable_free(names[i]);
+ }
+
-+ merged_iter_pqueue_clear(&pq);
++ merged_iter_pqueue_release(&pq);
+}
+
+static void write_test_table(struct strbuf *buf,
@@ reftable/merged_test.c (new)
+ }
+
+ err = reftable_writer_close(w);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ reftable_writer_free(w);
+}
@@ reftable/merged_test.c (new)
+
+ err = reftable_new_reader(&(*readers)[i], &(*source)[i],
+ "name");
-+ assert_err(err);
++ EXPECT_ERR(err);
+ reftable_table_from_reader(&tabs[i], (*readers)[i]);
+ }
+
+ err = reftable_new_merged_table(&mt, tabs, n, SHA1_ID);
-+ assert_err(err);
++ EXPECT_ERR(err);
+ return mt;
+}
+
@@ reftable/merged_test.c (new)
+ struct reftable_ref_record ref = { NULL };
+ struct reftable_iterator it = { NULL };
+ int err = reftable_merged_table_seek_ref(mt, &it, "a");
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_iterator_next_ref(&it, &ref);
-+ assert_err(err);
-+ assert(ref.update_index == 2);
-+ reftable_ref_record_clear(&ref);
++ EXPECT_ERR(err);
++ EXPECT(ref.update_index == 2);
++ reftable_ref_record_release(&ref);
+ reftable_iterator_destroy(&it);
+ readers_destroy(readers, 2);
+ reftable_merged_table_free(mt);
@@ reftable/merged_test.c (new)
+ size_t cap = 0;
+ int i = 0;
+
-+ assert_err(err);
++ EXPECT_ERR(err);
+ while (len < 100) { /* cap loops/recursion. */
+ struct reftable_ref_record ref = { NULL };
+ int err = reftable_iterator_next_ref(&it, &ref);
@@ reftable/merged_test.c (new)
+ assert(reftable_ref_record_equal(&want[i], &out[i], SHA1_SIZE));
+ }
+ for (i = 0; i < len; i++) {
-+ reftable_ref_record_clear(&out[i]);
++ reftable_ref_record_release(&out[i]);
+ }
+ reftable_free(out);
+
@@ reftable/merged_test.c (new)
+ reftable_writer_set_limits(w, 1, 1);
+
+ err = reftable_writer_add_ref(w, &rec);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_writer_close(w);
-+ assert_err(err);
++ EXPECT_ERR(err);
+ reftable_writer_free(w);
+
+ block_source_from_strbuf(&source, &buf);
+
+ err = reftable_new_reader(&rd, &source, "filename");
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ hash_id = reftable_reader_hash_id(rd);
+ assert(hash_id == SHA1_ID);
+
+ reftable_table_from_reader(&tab[0], rd);
+ err = reftable_new_merged_table(&merged, tab, 1, SHA1_ID);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ reftable_reader_free(rd);
+ reftable_merged_table_free(merged);
@@ reftable/merged_test.c (new)
+
+int merged_test_main(int argc, const char *argv[])
+{
-+ add_test_case("test_merged_between", &test_merged_between);
-+ add_test_case("test_pq", &test_pq);
-+ add_test_case("test_merged", &test_merged);
-+ add_test_case("test_default_write_opts", &test_default_write_opts);
-+ return test_main(argc, argv);
++ test_merged_between();
++ test_pq();
++ test_merged();
++ test_default_write_opts();
++ return 0;
+}
## reftable/pq.c (new) ##
@@ reftable/pq.c (new)
+
+#include "pq.h"
+
-+#include "reftable.h"
++#include "reftable-record.h"
+#include "system.h"
+#include "basics.h"
+
@@ reftable/pq.c (new)
+ }
+}
+
-+void merged_iter_pqueue_clear(struct merged_iter_pqueue *pq)
++void merged_iter_pqueue_release(struct merged_iter_pqueue *pq)
+{
+ int i = 0;
+ for (i = 0; i < pq->len; i++) {
@@ reftable/pq.h (new)
+void merged_iter_pqueue_check(struct merged_iter_pqueue pq);
+struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq);
+void merged_iter_pqueue_add(struct merged_iter_pqueue *pq, struct pq_entry e);
-+void merged_iter_pqueue_clear(struct merged_iter_pqueue *pq);
++void merged_iter_pqueue_release(struct merged_iter_pqueue *pq);
+
+#endif
@@ reftable/refname.c (new)
+*/
+
+#include "system.h"
-+#include "reftable.h"
++#include "reftable-error.h"
+#include "basics.h"
+#include "refname.h"
-+#include "strbuf.h"
++#include "reftable-iterator.h"
+
+struct find_arg {
+ char **names;
@@ reftable/refname.c (new)
+ }
+
+ err = reftable_table_read_ref(&mod->tab, name, &ref);
-+ reftable_ref_record_clear(&ref);
++ reftable_ref_record_release(&ref);
+ return err;
+}
+
-+static void modification_clear(struct modification *mod)
++static void modification_release(struct modification *mod)
+{
+ /* don't delete the strings themselves; they're owned by ref records.
+ */
@@ reftable/refname.c (new)
+ }
+
+done:
-+ reftable_ref_record_clear(&ref);
++ reftable_ref_record_release(&ref);
+ reftable_iterator_destroy(&it);
+ return err;
+}
@@ reftable/refname.c (new)
+ }
+
+ err = modification_validate(&mod);
-+ modification_clear(&mod);
++ modification_release(&mod);
+ return err;
+}
+
@@ reftable/refname.h (new)
+#ifndef REFNAME_H
+#define REFNAME_H
+
-+#include "reftable.h"
++#include "reftable-record.h"
++#include "reftable-generic.h"
+
+struct modification {
+ struct reftable_table tab;
@@ reftable/refname_test.c (new)
+https://developers.google.com/open-source/licenses/bsd
+*/
+
-+#include "reftable.h"
-+
+#include "basics.h"
+#include "block.h"
+#include "blocksource.h"
@@ reftable/refname_test.c (new)
+#include "reader.h"
+#include "record.h"
+#include "refname.h"
++#include "reftable-error.h"
++#include "reftable-writer.h"
+#include "system.h"
+
+#include "test_framework.h"
@@ reftable/refname_test.c (new)
+ reftable_writer_set_limits(w, 1, 1);
+
+ err = reftable_writer_add_ref(w, &rec);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_writer_close(w);
-+ assert_err(err);
++ EXPECT_ERR(err);
+ reftable_writer_free(w);
+
+ block_source_from_strbuf(&source, &buf);
+ err = reftable_new_reader(&rd, &source, "filename");
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ reftable_table_from_reader(&tab, rd);
+
@@ reftable/refname_test.c (new)
+ }
+
+ err = modification_validate(&mod);
-+ assert(err == cases[i].error_code);
++ EXPECT(err == cases[i].error_code);
+ }
+
+ reftable_reader_free(rd);
@@ reftable/refname_test.c (new)
+
+int refname_test_main(int argc, const char *argv[])
+{
-+ add_test_case("test_conflict", &test_conflict);
-+ return test_main(argc, argv);
++ test_conflict();
++ return 0;
+}
- ## reftable/reftable.c ##
-@@ reftable/reftable.c: int reftable_table_seek_ref(struct reftable_table *tab,
- struct reftable_ref_record ref = {
- .refname = (char *)name,
- };
-- struct reftable_record rec = { 0 };
+ ## reftable/reftable-generic.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 REFTABLE_GENERIC_H
++#define REFTABLE_GENERIC_H
++
++#include "reftable-iterator.h"
++#include "reftable-reader.h"
++#include "reftable-merged.h"
++
++/*
++ Provides a unified API for reading tables, either merged tables, or single
++ readers.
++*/
++struct reftable_table {
++ struct reftable_table_vtable *ops;
++ void *table_arg;
++};
++
++int reftable_table_seek_ref(struct reftable_table *tab,
++ struct reftable_iterator *it, const char *name);
++
++void reftable_table_from_reader(struct reftable_table *tab,
++ struct reftable_reader *reader);
++
++/* returns the hash ID from a generic reftable_table */
++uint32_t reftable_table_hash_id(struct reftable_table *tab);
++
++/* create a generic table from reftable_merged_table */
++void reftable_table_from_merged_table(struct reftable_table *tab,
++ struct reftable_merged_table *table);
++
++/* returns the max update_index covered by this table. */
++uint64_t reftable_table_max_update_index(struct reftable_table *tab);
++
++/* returns the min update_index covered by this table. */
++uint64_t reftable_table_min_update_index(struct reftable_table *tab);
++
++/* convenience function to read a single ref. Returns < 0 for error, 0
++ for success, and 1 if ref not found. */
++int reftable_table_read_ref(struct reftable_table *tab, const char *name,
++ struct reftable_ref_record *ref);
++
++#endif
+
+ ## reftable/reftable-merged.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 REFTABLE_MERGED_H
++#define REFTABLE_MERGED_H
++
++#include "reftable-iterator.h"
++
++/*
++ Merged tables
++
++ A ref database kept in a sequence of table files. The merged_table presents a
++ unified view to reading (seeking, iterating) a sequence of immutable tables.
++
++ The merged tables are on purpose kept disconnected from their actual storage
++ (eg. files on disk), because it is useful to merge tables aren't files. For
++ example, the per-workspace and global ref namespace can be implemented as a
++ merged table of two stacks of file-backed reftables.
++*/
++
++/* A merged table is implements seeking/iterating over a stack of tables. */
++struct reftable_merged_table;
++
++/* A generic reftable; see below. */
++struct reftable_table;
++
++/* reftable_new_merged_table creates a new merged table. It takes ownership of
++ the stack array.
++*/
++int reftable_new_merged_table(struct reftable_merged_table **dest,
++ struct reftable_table *stack, int n,
++ uint32_t hash_id);
++
++/* returns an iterator positioned just before 'name' */
++int reftable_merged_table_seek_ref(struct reftable_merged_table *mt,
++ struct reftable_iterator *it,
++ const char *name);
++
++/* returns an iterator for log entry, at given update_index */
++int reftable_merged_table_seek_log_at(struct reftable_merged_table *mt,
++ struct reftable_iterator *it,
++ const char *name, uint64_t update_index);
++
++/* like reftable_merged_table_seek_log_at but look for the newest entry. */
++int reftable_merged_table_seek_log(struct reftable_merged_table *mt,
++ struct reftable_iterator *it,
++ const char *name);
++
++/* returns the max update_index covered by this merged table. */
++uint64_t
++reftable_merged_table_max_update_index(struct reftable_merged_table *mt);
++
++/* returns the min update_index covered by this merged table. */
++uint64_t
++reftable_merged_table_min_update_index(struct reftable_merged_table *mt);
++
++/* releases memory for the merged_table */
++void reftable_merged_table_free(struct reftable_merged_table *m);
++
++/* return the hash ID of the merged table. */
++uint32_t reftable_merged_table_hash_id(struct reftable_merged_table *m);
++
++#endif
+
+ ## reftable/reftable-stack.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 REFTABLE_STACK_H
++#define REFTABLE_STACK_H
++
++#include "reftable-writer.h"
++
++/*
++ The stack presents an interface to a mutable sequence of reftables.
++
++ A stack can be mutated by pushing a table to the top of the stack.
++
++ The reftable_stack automatically compacts files on disk to ensure good
++ amortized performance.
++*/
++struct reftable_stack;
++
++/* open a new reftable stack. The tables along with the table list will be
++ stored in 'dir'. Typically, this should be .git/reftables.
++*/
++int reftable_new_stack(struct reftable_stack **dest, const char *dir,
++ struct reftable_write_options config);
++
++/* returns the update_index at which a next table should be written. */
++uint64_t reftable_stack_next_update_index(struct reftable_stack *st);
++
++/* holds a transaction to add tables at the top of a stack. */
++struct reftable_addition;
++
++/*
++ returns a new transaction to add reftables to the given stack. As a side
++ effect, the ref database is locked.
++*/
++int reftable_stack_new_addition(struct reftable_addition **dest,
++ struct reftable_stack *st);
++
++/* Adds a reftable to transaction. */
++int reftable_addition_add(struct reftable_addition *add,
++ int (*write_table)(struct reftable_writer *wr,
++ void *arg),
++ void *arg);
++
++/* Commits the transaction, releasing the lock. */
++int reftable_addition_commit(struct reftable_addition *add);
++
++/* Release all non-committed data from the transaction, and deallocate the
++ transaction. Releases the lock if held. */
++void reftable_addition_destroy(struct reftable_addition *add);
++
++/* add a new table to the stack. The write_table function must call
++ reftable_writer_set_limits, add refs and return an error value. */
++int reftable_stack_add(struct reftable_stack *st,
++ int (*write_table)(struct reftable_writer *wr,
++ void *write_arg),
++ void *write_arg);
++
++/* returns the merged_table for seeking. This table is valid until the
++ next write or reload, and should not be closed or deleted.
++*/
++struct reftable_merged_table *
++reftable_stack_merged_table(struct reftable_stack *st);
++
++/* frees all resources associated with the stack. */
++void reftable_stack_destroy(struct reftable_stack *st);
++
++/* Reloads the stack if necessary. This is very cheap to run if the stack was up
++ * to date */
++int reftable_stack_reload(struct reftable_stack *st);
++
++/* Policy for expiring reflog entries. */
++struct reftable_log_expiry_config {
++ /* Drop entries older than this timestamp */
++ uint64_t time;
++
++ /* Drop older entries */
++ uint64_t min_update_index;
++};
++
++/* compacts all reftables into a giant table. Expire reflog entries if config is
++ * non-NULL */
++int reftable_stack_compact_all(struct reftable_stack *st,
++ struct reftable_log_expiry_config *config);
++
++/* heuristically compact unbalanced table stack. */
++int reftable_stack_auto_compact(struct reftable_stack *st);
++
++/* convenience function to read a single ref. Returns < 0 for error, 0
++ for success, and 1 if ref not found. */
++int reftable_stack_read_ref(struct reftable_stack *st, const char *refname,
++ struct reftable_ref_record *ref);
++
++/* convenience function to read a single log. Returns < 0 for error, 0
++ for success, and 1 if ref not found. */
++int reftable_stack_read_log(struct reftable_stack *st, const char *refname,
++ struct reftable_log_record *log);
++
++/* statistics on past compactions. */
++struct reftable_compaction_stats {
++ uint64_t bytes; /* total number of bytes written */
++ uint64_t entries_written; /* total number of entries written, including
++ failures. */
++ int attempts; /* how often we tried to compact */
++ int failures; /* failures happen on concurrent updates */
++};
++
++/* return statistics for compaction up till now. */
++struct reftable_compaction_stats *
++reftable_stack_compaction_stats(struct reftable_stack *st);
++
++#endif
+
+ ## reftable/reftable.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 "record.h"
++#include "reader.h"
++#include "reftable-iterator.h"
++#include "reftable-generic.h"
++
++static int reftable_reader_seek_void(void *tab, struct reftable_iterator *it,
++ struct reftable_record *rec)
++{
++ return reader_seek((struct reftable_reader *)tab, it, rec);
++}
++
++static uint32_t reftable_reader_hash_id_void(void *tab)
++{
++ return reftable_reader_hash_id((struct reftable_reader *)tab);
++}
++
++static uint64_t reftable_reader_min_update_index_void(void *tab)
++{
++ return reftable_reader_min_update_index((struct reftable_reader *)tab);
++}
++
++static uint64_t reftable_reader_max_update_index_void(void *tab)
++{
++ return reftable_reader_max_update_index((struct reftable_reader *)tab);
++}
++
++static struct reftable_table_vtable reader_vtable = {
++ .seek_record = reftable_reader_seek_void,
++ .hash_id = reftable_reader_hash_id_void,
++ .min_update_index = reftable_reader_min_update_index_void,
++ .max_update_index = reftable_reader_max_update_index_void,
++};
++
++int reftable_table_seek_ref(struct reftable_table *tab,
++ struct reftable_iterator *it, const char *name)
++{
++ struct reftable_ref_record ref = {
++ .refname = (char *)name,
++ };
+ struct reftable_record rec = { NULL };
- reftable_record_from_ref(&rec, &ref);
- return tab->ops->seek_record(tab->table_arg, it, &rec);
- }
-@@ reftable/reftable.c: void reftable_table_from_reader(struct reftable_table *tab,
- int reftable_table_read_ref(struct reftable_table *tab, const char *name,
- struct reftable_ref_record *ref)
- {
-- struct reftable_iterator it = { 0 };
++ reftable_record_from_ref(&rec, &ref);
++ return tab->ops->seek_record(tab->table_arg, it, &rec);
++}
++
++void reftable_table_from_reader(struct reftable_table *tab,
++ struct reftable_reader *reader)
++{
++ assert(tab->ops == NULL);
++ tab->ops = &reader_vtable;
++ tab->table_arg = reader;
++}
++
++int reftable_table_read_ref(struct reftable_table *tab, const char *name,
++ struct reftable_ref_record *ref)
++{
+ struct reftable_iterator it = { NULL };
- int err = reftable_table_seek_ref(tab, &it, name);
- if (err)
- goto done;
++ int err = reftable_table_seek_ref(tab, &it, name);
++ if (err)
++ goto done;
++
++ err = reftable_iterator_next_ref(&it, ref);
++ if (err)
++ goto done;
++
++ if (strcmp(ref->refname, name) ||
++ reftable_ref_record_is_deletion(ref)) {
++ reftable_ref_record_release(ref);
++ err = 1;
++ goto done;
++ }
++
++done:
++ reftable_iterator_destroy(&it);
++ return err;
++}
++
++uint64_t reftable_table_max_update_index(struct reftable_table *tab)
++{
++ return tab->ops->max_update_index(tab->table_arg);
++}
++
++uint64_t reftable_table_min_update_index(struct reftable_table *tab)
++{
++ return tab->ops->min_update_index(tab->table_arg);
++}
++
++uint32_t reftable_table_hash_id(struct reftable_table *tab)
++{
++ return tab->ops->hash_id(tab->table_arg);
++}
## reftable/stack.c (new) ##
@@
@@ reftable/stack.c (new)
+#include "merged.h"
+#include "reader.h"
+#include "refname.h"
-+#include "reftable.h"
++#include "reftable-error.h"
++#include "reftable-record.h"
+#include "writer.h"
+
+static int stack_try_add(struct reftable_stack *st,
@@ reftable/stack.c (new)
+static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
+ int reuse_open);
+
++static int reftable_fd_write(void *arg, const void *data, size_t sz)
++{
++ int *fdp = (int *)arg;
++ return write(*fdp, data, sz);
++}
++
+int reftable_new_stack(struct reftable_stack **dest, const char *dir,
+ struct reftable_write_options config)
+{
@@ reftable/stack.c (new)
+ new_tables = NULL;
+ st->readers_len = new_readers_len;
+ if (st->merged != NULL) {
-+ merged_table_clear(st->merged);
++ merged_table_release(st->merged);
+ reftable_merged_table_free(st->merged);
+ }
+ if (st->readers != NULL) {
@@ reftable/stack.c (new)
+done:
+ reftable_iterator_destroy(&it);
+ if (mt != NULL) {
-+ merged_table_clear(mt);
++ merged_table_release(mt);
+ reftable_merged_table_free(mt);
+ }
-+ reftable_ref_record_clear(&ref);
-+ reftable_log_record_clear(&log);
++ reftable_ref_record_release(&ref);
++ reftable_log_record_release(&log);
+ st->stats.entries_written += entries;
+ return err;
+}
@@ reftable/stack.c (new)
+
+done:
+ if (err) {
-+ reftable_log_record_clear(log);
++ reftable_log_record_release(log);
+ }
+ reftable_iterator_destroy(&it);
+ return err;
@@ reftable/stack.c (new)
+
+done:
+ for (i = 0; i < len; i++) {
-+ reftable_ref_record_clear(&refs[i]);
++ reftable_ref_record_release(&refs[i]);
+ }
+
+ free(refs);
@@ reftable/stack.h (new)
+#ifndef STACK_H
+#define STACK_H
+
-+#include "reftable.h"
+#include "system.h"
++#include "reftable-writer.h"
++#include "reftable-stack.h"
+
+struct reftable_stack {
+ char *list_file;
@@ reftable/stack_test.c (new)
+#include "basics.h"
+#include "constants.h"
+#include "record.h"
-+#include "reftable.h"
+#include "test_framework.h"
+#include "reftable-tests.h"
+
+#include <sys/types.h>
+#include <dirent.h>
+
++static void clear_dir(const char *dirname)
++{
++ struct strbuf path = STRBUF_INIT;
++ strbuf_addstr(&path, dirname);
++ remove_dir_recursively(&path, 0);
++ strbuf_release(&path);
++}
++
+static void test_read_file(void)
+{
+ char fn[256] = "/tmp/stack.test_read_file.XXXXXX";
@@ reftable/stack_test.c (new)
+ char *want[] = { "line1", "line2", "line3" };
+ int i = 0;
+
-+ assert(fd > 0);
++ EXPECT(fd > 0);
+ n = write(fd, out, strlen(out));
-+ assert(n == strlen(out));
++ EXPECT(n == strlen(out));
+ err = close(fd);
-+ assert(err >= 0);
++ EXPECT(err >= 0);
+
+ err = read_lines(fn, &names);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ for (i = 0; names[i] != NULL; i++) {
-+ assert(0 == strcmp(want[i], names[i]));
++ EXPECT(0 == strcmp(want[i], names[i]));
+ }
+ free_names(names);
+ remove(fn);
@@ reftable/stack_test.c (new)
+ char **names = NULL;
+ parse_names(buf, strlen(buf), &names);
+
-+ assert(NULL != names[0]);
-+ assert(0 == strcmp(names[0], "line"));
-+ assert(NULL == names[1]);
++ EXPECT(NULL != names[0]);
++ EXPECT(0 == strcmp(names[0], "line"));
++ EXPECT(NULL == names[1]);
+ free_names(names);
+}
+
@@ reftable/stack_test.c (new)
+ char *b[] = { "a", "b", "d", NULL };
+ char *c[] = { "a", "b", NULL };
+
-+ assert(names_equal(a, a));
-+ assert(!names_equal(a, b));
-+ assert(!names_equal(a, c));
++ EXPECT(names_equal(a, a));
++ EXPECT(!names_equal(a, b));
++ EXPECT(!names_equal(a, c));
+}
+
+static int write_test_ref(struct reftable_writer *wr, void *arg)
@@ reftable/stack_test.c (new)
+ };
+ struct reftable_ref_record dest = { NULL };
+
-+ assert(mkdtemp(dir));
++ EXPECT(mkdtemp(dir));
+
+ err = reftable_new_stack(&st, dir, cfg);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_stack_add(st, &write_test_ref, &ref);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_stack_read_ref(st, ref.refname, &dest);
-+ assert_err(err);
-+ assert(0 == strcmp("master", dest.target));
++ EXPECT_ERR(err);
++ EXPECT(0 == strcmp("master", dest.target));
+
-+ reftable_ref_record_clear(&dest);
++ reftable_ref_record_release(&dest);
+ reftable_stack_destroy(st);
-+ reftable_clear_dir(dir);
++ clear_dir(dir);
+}
+
+static void test_reftable_stack_uptodate(void)
@@ reftable/stack_test.c (new)
+ .target = "master",
+ };
+
-+ assert(mkdtemp(dir));
++ EXPECT(mkdtemp(dir));
+
+ err = reftable_new_stack(&st1, dir, cfg);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_new_stack(&st2, dir, cfg);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_stack_add(st1, &write_test_ref, &ref1);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_stack_add(st2, &write_test_ref, &ref2);
-+ assert(err == REFTABLE_LOCK_ERROR);
++ EXPECT(err == REFTABLE_LOCK_ERROR);
+
+ err = reftable_stack_reload(st2);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_stack_add(st2, &write_test_ref, &ref2);
-+ assert_err(err);
++ EXPECT_ERR(err);
+ reftable_stack_destroy(st1);
+ reftable_stack_destroy(st2);
-+ reftable_clear_dir(dir);
++ clear_dir(dir);
+}
+
+static void test_reftable_stack_transaction_api(void)
@@ reftable/stack_test.c (new)
+ };
+ struct reftable_ref_record dest = { NULL };
+
-+ assert(mkdtemp(dir));
++ EXPECT(mkdtemp(dir));
+
+ err = reftable_new_stack(&st, dir, cfg);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ reftable_addition_destroy(add);
+
+ err = reftable_stack_new_addition(&add, st);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_addition_add(add, &write_test_ref, &ref);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_addition_commit(add);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ reftable_addition_destroy(add);
+
+ err = reftable_stack_read_ref(st, ref.refname, &dest);
-+ assert_err(err);
-+ assert(0 == strcmp("master", dest.target));
++ EXPECT_ERR(err);
++ EXPECT(0 == strcmp("master", dest.target));
+
-+ reftable_ref_record_clear(&dest);
++ reftable_ref_record_release(&dest);
+ reftable_stack_destroy(st);
-+ reftable_clear_dir(dir);
++ clear_dir(dir);
+}
+
+static void test_reftable_stack_validate_refname(void)
@@ reftable/stack_test.c (new)
+ };
+ char *additions[] = { "a", "a/b/c" };
+
-+ assert(mkdtemp(dir));
++ EXPECT(mkdtemp(dir));
+ err = reftable_new_stack(&st, dir, cfg);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_stack_add(st, &write_test_ref, &ref);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ for (i = 0; i < ARRAY_SIZE(additions); i++) {
+ struct reftable_ref_record ref = {
@@ reftable/stack_test.c (new)
+ };
+
+ err = reftable_stack_add(st, &write_test_ref, &ref);
-+ assert(err == REFTABLE_NAME_CONFLICT);
++ EXPECT(err == REFTABLE_NAME_CONFLICT);
+ }
+
+ reftable_stack_destroy(st);
-+ reftable_clear_dir(dir);
++ clear_dir(dir);
+}
+
+static int write_error(struct reftable_writer *wr, void *arg)
@@ reftable/stack_test.c (new)
+ .update_index = 1,
+ .target = "master",
+ };
-+ assert(mkdtemp(dir));
++ EXPECT(mkdtemp(dir));
+
+ err = reftable_new_stack(&st, dir, cfg);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_stack_add(st, &write_test_ref, &ref1);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_stack_add(st, &write_test_ref, &ref2);
-+ assert(err == REFTABLE_API_ERROR);
++ EXPECT(err == REFTABLE_API_ERROR);
+ reftable_stack_destroy(st);
-+ reftable_clear_dir(dir);
++ clear_dir(dir);
+}
+
+static void test_reftable_stack_lock_failure(void)
@@ reftable/stack_test.c (new)
+ struct reftable_write_options cfg = { 0 };
+ struct reftable_stack *st = NULL;
+ int err, i;
-+ assert(mkdtemp(dir));
++ EXPECT(mkdtemp(dir));
+
+ err = reftable_new_stack(&st, dir, cfg);
-+ assert_err(err);
++ EXPECT_ERR(err);
+ for (i = -1; i != REFTABLE_EMPTY_TABLE_ERROR; i--) {
+ err = reftable_stack_add(st, &write_error, &i);
-+ assert(err == i);
++ EXPECT(err == i);
+ }
+
+ reftable_stack_destroy(st);
-+ reftable_clear_dir(dir);
++ clear_dir(dir);
+}
+
+static void test_reftable_stack_add(void)
@@ reftable/stack_test.c (new)
+ struct reftable_log_record logs[2] = { { NULL } };
+ int N = ARRAY_SIZE(refs);
+
-+ assert(mkdtemp(dir));
++ EXPECT(mkdtemp(dir));
+
+ err = reftable_new_stack(&st, dir, cfg);
-+ assert_err(err);
++ EXPECT_ERR(err);
+ st->disable_auto_compact = 1;
+
+ for (i = 0; i < N; i++) {
@@ reftable/stack_test.c (new)
+
+ for (i = 0; i < N; i++) {
+ int err = reftable_stack_add(st, &write_test_ref, &refs[i]);
-+ assert_err(err);
++ EXPECT_ERR(err);
+ }
+
+ for (i = 0; i < N; i++) {
@@ reftable/stack_test.c (new)
+ .update_index = reftable_stack_next_update_index(st),
+ };
+ int err = reftable_stack_add(st, &write_test_log, &arg);
-+ assert_err(err);
++ EXPECT_ERR(err);
+ }
+
+ err = reftable_stack_compact_all(st, NULL);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ for (i = 0; i < N; i++) {
+ struct reftable_ref_record dest = { NULL };
+
+ int err = reftable_stack_read_ref(st, refs[i].refname, &dest);
-+ assert_err(err);
-+ assert(reftable_ref_record_equal(&dest, refs + i, SHA1_SIZE));
-+ reftable_ref_record_clear(&dest);
++ EXPECT_ERR(err);
++ EXPECT(reftable_ref_record_equal(&dest, refs + i, SHA1_SIZE));
++ reftable_ref_record_release(&dest);
+ }
+
+ for (i = 0; i < N; i++) {
+ struct reftable_log_record dest = { NULL };
+ int err = reftable_stack_read_log(st, refs[i].refname, &dest);
-+ assert_err(err);
-+ assert(reftable_log_record_equal(&dest, logs + i, SHA1_SIZE));
-+ reftable_log_record_clear(&dest);
++ EXPECT_ERR(err);
++ EXPECT(reftable_log_record_equal(&dest, logs + i, SHA1_SIZE));
++ reftable_log_record_release(&dest);
+ }
+
+ /* cleanup */
+ reftable_stack_destroy(st);
+ for (i = 0; i < N; i++) {
-+ reftable_ref_record_clear(&refs[i]);
-+ reftable_log_record_clear(&logs[i]);
++ reftable_ref_record_release(&refs[i]);
++ reftable_log_record_release(&logs[i]);
+ }
-+ reftable_clear_dir(dir);
++ clear_dir(dir);
+}
+
+static void test_reftable_stack_log_normalize(void)
@@ reftable/stack_test.c (new)
+ .update_index = 1,
+ };
+
-+ assert(mkdtemp(dir));
++ EXPECT(mkdtemp(dir));
+ err = reftable_new_stack(&st, dir, cfg);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ input.message = "one\ntwo";
+ err = reftable_stack_add(st, &write_test_log, &arg);
-+ assert(err == REFTABLE_API_ERROR);
++ EXPECT(err == REFTABLE_API_ERROR);
+
+ input.message = "one";
+ err = reftable_stack_add(st, &write_test_log, &arg);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_stack_read_log(st, input.refname, &dest);
-+ assert_err(err);
-+ assert(0 == strcmp(dest.message, "one\n"));
++ EXPECT_ERR(err);
++ EXPECT(0 == strcmp(dest.message, "one\n"));
+
+ input.message = "two\n";
+ arg.update_index = 2;
+ err = reftable_stack_add(st, &write_test_log, &arg);
-+ assert_err(err);
++ EXPECT_ERR(err);
+ err = reftable_stack_read_log(st, input.refname, &dest);
-+ assert_err(err);
-+ assert(0 == strcmp(dest.message, "two\n"));
++ EXPECT_ERR(err);
++ EXPECT(0 == strcmp(dest.message, "two\n"));
+
+ /* cleanup */
+ reftable_stack_destroy(st);
-+ reftable_log_record_clear(&dest);
-+ reftable_clear_dir(dir);
++ reftable_log_record_release(&dest);
++ clear_dir(dir);
+}
+
+static void test_reftable_stack_tombstone(void)
@@ reftable/stack_test.c (new)
+ struct reftable_ref_record dest = { NULL };
+ struct reftable_log_record log_dest = { NULL };
+
-+ assert(mkdtemp(dir));
++ EXPECT(mkdtemp(dir));
+
+ err = reftable_new_stack(&st, dir, cfg);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ for (i = 0; i < N; i++) {
+ const char *buf = "branch";
@@ reftable/stack_test.c (new)
+ }
+ for (i = 0; i < N; i++) {
+ int err = reftable_stack_add(st, &write_test_ref, &refs[i]);
-+ assert_err(err);
++ EXPECT_ERR(err);
+ }
+ for (i = 0; i < N; i++) {
+ struct write_log_arg arg = {
@@ reftable/stack_test.c (new)
+ .update_index = reftable_stack_next_update_index(st),
+ };
+ int err = reftable_stack_add(st, &write_test_log, &arg);
-+ assert_err(err);
++ EXPECT_ERR(err);
+ }
+
+ err = reftable_stack_read_ref(st, "branch", &dest);
-+ assert(err == 1);
-+ reftable_ref_record_clear(&dest);
++ EXPECT(err == 1);
++ reftable_ref_record_release(&dest);
+
+ err = reftable_stack_read_log(st, "branch", &log_dest);
-+ assert(err == 1);
-+ reftable_log_record_clear(&log_dest);
++ EXPECT(err == 1);
++ reftable_log_record_release(&log_dest);
+
+ err = reftable_stack_compact_all(st, NULL);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_stack_read_ref(st, "branch", &dest);
-+ assert(err == 1);
++ EXPECT(err == 1);
+
+ err = reftable_stack_read_log(st, "branch", &log_dest);
-+ assert(err == 1);
-+ reftable_ref_record_clear(&dest);
-+ reftable_log_record_clear(&log_dest);
++ EXPECT(err == 1);
++ reftable_ref_record_release(&dest);
++ reftable_log_record_release(&log_dest);
+
+ /* cleanup */
+ reftable_stack_destroy(st);
+ for (i = 0; i < N; i++) {
-+ reftable_ref_record_clear(&refs[i]);
-+ reftable_log_record_clear(&logs[i]);
++ reftable_ref_record_release(&refs[i]);
++ reftable_log_record_release(&logs[i]);
+ }
-+ reftable_clear_dir(dir);
++ clear_dir(dir);
+}
+
+static void test_reftable_stack_hash_id(void)
@@ reftable/stack_test.c (new)
+ struct reftable_stack *st_default = NULL;
+ struct reftable_ref_record dest = { NULL };
+
-+ assert(mkdtemp(dir));
++ EXPECT(mkdtemp(dir));
+ err = reftable_new_stack(&st, dir, cfg);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_stack_add(st, &write_test_ref, &ref);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ /* can't read it with the wrong hash ID. */
+ err = reftable_new_stack(&st32, dir, cfg32);
-+ assert(err == REFTABLE_FORMAT_ERROR);
++ EXPECT(err == REFTABLE_FORMAT_ERROR);
+
+ /* check that we can read it back with default config too. */
+ err = reftable_new_stack(&st_default, dir, cfg_default);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_stack_read_ref(st_default, "master", &dest);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
-+ assert(!strcmp(dest.target, ref.target));
-+ reftable_ref_record_clear(&dest);
++ EXPECT(!strcmp(dest.target, ref.target));
++ reftable_ref_record_release(&dest);
+ reftable_stack_destroy(st);
+ reftable_stack_destroy(st_default);
-+ reftable_clear_dir(dir);
++ clear_dir(dir);
+}
+
+static void test_log2(void)
+{
-+ assert(1 == fastlog2(3));
-+ assert(2 == fastlog2(4));
-+ assert(2 == fastlog2(5));
++ EXPECT(1 == fastlog2(3));
++ EXPECT(2 == fastlog2(4));
++ EXPECT(2 == fastlog2(5));
+}
+
+static void test_sizes_to_segments(void)
@@ reftable/stack_test.c (new)
+ int seglen = 0;
+ struct segment *segs =
+ sizes_to_segments(&seglen, sizes, ARRAY_SIZE(sizes));
-+ assert(segs[2].log == 3);
-+ assert(segs[2].start == 5);
-+ assert(segs[2].end == 6);
++ EXPECT(segs[2].log == 3);
++ EXPECT(segs[2].start == 5);
++ EXPECT(segs[2].end == 6);
+
-+ assert(segs[1].log == 2);
-+ assert(segs[1].start == 2);
-+ assert(segs[1].end == 5);
++ EXPECT(segs[1].log == 2);
++ EXPECT(segs[1].start == 2);
++ EXPECT(segs[1].end == 5);
+ reftable_free(segs);
+}
+
@@ reftable/stack_test.c (new)
+ int seglen = 0;
+ struct segment *segs =
+ sizes_to_segments(&seglen, sizes, ARRAY_SIZE(sizes));
-+ assert(seglen == 0);
++ EXPECT(seglen == 0);
+ reftable_free(segs);
+}
+
@@ reftable/stack_test.c (new)
+ int seglen = 0;
+ struct segment *segs =
+ sizes_to_segments(&seglen, sizes, ARRAY_SIZE(sizes));
-+ assert(seglen == 1);
-+ assert(segs[0].start == 0);
-+ assert(segs[0].end == 2);
++ EXPECT(seglen == 1);
++ EXPECT(segs[0].start == 0);
++ EXPECT(segs[0].end == 2);
+ reftable_free(segs);
+}
+
@@ reftable/stack_test.c (new)
+ /* .................0 1 2 3 4 5 6 */
+ struct segment min =
+ suggest_compaction_segment(sizes, ARRAY_SIZE(sizes));
-+ assert(min.start == 2);
-+ assert(min.end == 7);
++ EXPECT(min.start == 2);
++ EXPECT(min.end == 7);
+}
+
+static void test_suggest_compaction_segment_nothing(void)
@@ reftable/stack_test.c (new)
+ uint64_t sizes[] = { 64, 32, 16, 8, 4, 2 };
+ struct segment result =
+ suggest_compaction_segment(sizes, ARRAY_SIZE(sizes));
-+ assert(result.start == result.end);
++ EXPECT(result.start == result.end);
+}
+
+static void test_reflog_expire(void)
@@ reftable/stack_test.c (new)
+ };
+ struct reftable_log_record log = { NULL };
+
-+ assert(mkdtemp(dir));
++ EXPECT(mkdtemp(dir));
+
+ err = reftable_new_stack(&st, dir, cfg);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ for (i = 1; i <= N; i++) {
+ char buf[256];
@@ reftable/stack_test.c (new)
+ .update_index = reftable_stack_next_update_index(st),
+ };
+ int err = reftable_stack_add(st, &write_test_log, &arg);
-+ assert_err(err);
++ EXPECT_ERR(err);
+ }
+
+ err = reftable_stack_compact_all(st, NULL);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_stack_compact_all(st, &expiry);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_stack_read_log(st, logs[9].refname, &log);
-+ assert(err == 1);
++ EXPECT(err == 1);
+
+ err = reftable_stack_read_log(st, logs[11].refname, &log);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ expiry.min_update_index = 15;
+ err = reftable_stack_compact_all(st, &expiry);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_stack_read_log(st, logs[14].refname, &log);
-+ assert(err == 1);
++ EXPECT(err == 1);
+
+ err = reftable_stack_read_log(st, logs[16].refname, &log);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ /* cleanup */
+ reftable_stack_destroy(st);
+ for (i = 0; i <= N; i++) {
-+ reftable_log_record_clear(&logs[i]);
++ reftable_log_record_release(&logs[i]);
+ }
-+ reftable_clear_dir(dir);
-+ reftable_log_record_clear(&log);
++ clear_dir(dir);
++ reftable_log_record_release(&log);
+}
+
+static int write_nothing(struct reftable_writer *wr, void *arg)
@@ reftable/stack_test.c (new)
+ char dir[256] = "/tmp/stack_test.XXXXXX";
+ struct reftable_stack *st2 = NULL;
+
-+ assert(mkdtemp(dir));
++ EXPECT(mkdtemp(dir));
+
+ err = reftable_new_stack(&st, dir, cfg);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_stack_add(st, &write_nothing, NULL);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ err = reftable_new_stack(&st2, dir, cfg);
-+ assert_err(err);
-+ reftable_clear_dir(dir);
++ EXPECT_ERR(err);
++ clear_dir(dir);
+ reftable_stack_destroy(st);
+ reftable_stack_destroy(st2);
+}
@@ reftable/stack_test.c (new)
+ char dir[256] = "/tmp/stack_test.XXXXXX";
+ int err, i;
+ int N = 100;
-+ assert(mkdtemp(dir));
++ EXPECT(mkdtemp(dir));
+
+ err = reftable_new_stack(&st, dir, cfg);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
+ for (i = 0; i < N; i++) {
+ char name[100];
@@ reftable/stack_test.c (new)
+ snprintf(name, sizeof(name), "branch%04d", i);
+
+ err = reftable_stack_add(st, &write_test_ref, &ref);
-+ assert_err(err);
++ EXPECT_ERR(err);
+
-+ assert(i < 3 || st->merged->stack_len < 2 * fastlog2(i));
++ EXPECT(i < 3 || st->merged->stack_len < 2 * fastlog2(i));
+ }
+
-+ assert(reftable_stack_compaction_stats(st)->entries_written <
++ EXPECT(reftable_stack_compaction_stats(st)->entries_written <
+ (uint64_t)(N * fastlog2(N)));
+
+ reftable_stack_destroy(st);
-+ reftable_clear_dir(dir);
++ clear_dir(dir);
+}
+
+int stack_test_main(int argc, const char *argv[])
+{
-+ add_test_case("test_reftable_stack_uptodate",
-+ &test_reftable_stack_uptodate);
-+ add_test_case("test_reftable_stack_transaction_api",
-+ &test_reftable_stack_transaction_api);
-+ add_test_case("test_reftable_stack_hash_id",
-+ &test_reftable_stack_hash_id);
-+ add_test_case("test_sizes_to_segments_all_equal",
-+ &test_sizes_to_segments_all_equal);
-+ add_test_case("test_reftable_stack_auto_compaction",
-+ &test_reftable_stack_auto_compaction);
-+ add_test_case("test_reftable_stack_validate_refname",
-+ &test_reftable_stack_validate_refname);
-+ add_test_case("test_reftable_stack_update_index_check",
-+ &test_reftable_stack_update_index_check);
-+ add_test_case("test_reftable_stack_lock_failure",
-+ &test_reftable_stack_lock_failure);
-+ add_test_case("test_reftable_stack_log_normalize",
-+ &test_reftable_stack_log_normalize);
-+ add_test_case("test_reftable_stack_tombstone",
-+ &test_reftable_stack_tombstone);
-+ add_test_case("test_reftable_stack_add_one",
-+ &test_reftable_stack_add_one);
-+ add_test_case("test_empty_add", test_empty_add);
-+ add_test_case("test_reflog_expire", test_reflog_expire);
-+ add_test_case("test_suggest_compaction_segment",
-+ &test_suggest_compaction_segment);
-+ add_test_case("test_suggest_compaction_segment_nothing",
-+ &test_suggest_compaction_segment_nothing);
-+ add_test_case("test_sizes_to_segments", &test_sizes_to_segments);
-+ add_test_case("test_sizes_to_segments_empty",
-+ &test_sizes_to_segments_empty);
-+ add_test_case("test_log2", &test_log2);
-+ add_test_case("test_parse_names", &test_parse_names);
-+ add_test_case("test_read_file", &test_read_file);
-+ 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);
++ test_reftable_stack_uptodate();
++ test_reftable_stack_transaction_api();
++ test_reftable_stack_hash_id();
++ test_sizes_to_segments_all_equal();
++ test_reftable_stack_auto_compaction();
++ test_reftable_stack_validate_refname();
++ test_reftable_stack_update_index_check();
++ test_reftable_stack_lock_failure();
++ test_reftable_stack_log_normalize();
++ test_reftable_stack_tombstone();
++ test_reftable_stack_add_one();
++ test_empty_add();
++ test_reflog_expire();
++ test_suggest_compaction_segment();
++ test_suggest_compaction_segment_nothing();
++ test_sizes_to_segments();
++ test_sizes_to_segments_empty();
++ test_log2();
++ test_parse_names();
++ test_read_file();
++ test_names_equal();
++ test_reftable_stack_add();
++ return 0;
+}
- ## reftable/update.sh (new) ##
-@@
-+#!/bin/sh
-+
-+set -eu
-+
-+# Override this to import from somewhere else, say "../reftable".
-+SRC=${SRC:-origin}
-+BRANCH=${BRANCH:-master}
-+
-+((git --git-dir reftable-repo/.git fetch -f ${SRC} ${BRANCH}:import && cd reftable-repo && git checkout -f $(git rev-parse import) ) ||
-+ git clone https://github.com/google/reftable reftable-repo)
-+
-+cp reftable-repo/c/*.[ch] reftable/
-+cp reftable-repo/c/include/*.[ch] reftable/
-+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
-
## t/helper/test-reftable.c ##
-@@
- int cmd__reftable(int argc, const char **argv)
+@@ t/helper/test-reftable.c: int cmd__reftable(int argc, const char **argv)
{
+ basics_test_main(argc, argv);
block_test_main(argc, argv);
+ merged_test_main(argc, argv);
record_test_main(argc, argv);
+ refname_test_main(argc, argv);
reftable_test_main(argc, argv);
- strbuf_test_main(argc, argv);
+ stack_test_main(argc, argv);
tree_test_main(argc, argv);
return 0;
-: ---------- > 14: a590865a70 Reftable support for git-core
-: ---------- > 15: 57626bfe2d git-prompt: prepare for reftable refs backend
13: c535f838d6 ! 16: 6229da992e reftable: "test-tool dump-reftable" command. @@ Metadata
Author: Han-Wen Nienhuys <[email protected]>
## Commit message ##
- reftable: "test-tool dump-reftable" command.
+ Add "test-tool dump-reftable" command.
This command dumps individual tables or a stack of of tables.
## Makefile ##
-@@ Makefile: REFTABLE_OBJS += reftable/writer.o
- REFTABLE_OBJS += reftable/zlib-compat.o +@@ Makefile: REFTABLE_OBJS += reftable/zlib-compat.o
+ REFTABLE_TEST_OBJS += reftable/basics_test.o REFTABLE_TEST_OBJS += reftable/block_test.o +REFTABLE_TEST_OBJS += reftable/dump.o
-+REFTABLE_TEST_OBJS += reftable/merged_test.o
+ REFTABLE_TEST_OBJS += reftable/merged_test.o REFTABLE_TEST_OBJS += reftable/record_test.o REFTABLE_TEST_OBJS += reftable/refname_test.o
- REFTABLE_TEST_OBJS += reftable/reftable_test.o
- ## reftable/iter.c ##
-@@ reftable/iter.c: void reftable_iterator_destroy(struct reftable_iterator *it)
- int reftable_iterator_next_ref(struct reftable_iterator *it,
- struct reftable_ref_record *ref)
- {
-- struct reftable_record rec = { 0 };
-+ struct reftable_record rec = { NULL };
- reftable_record_from_ref(&rec, ref);
- return iterator_next(it, &rec);
- }
-@@ reftable/iter.c: int reftable_iterator_next_ref(struct reftable_iterator *it,
- int reftable_iterator_next_log(struct reftable_iterator *it,
- struct reftable_log_record *log)
- {
-- struct reftable_record rec = { 0 };
-+ struct reftable_record rec = { NULL };
- reftable_record_from_log(&rec, log);
- return iterator_next(it, &rec);
- }
-@@ reftable/iter.c: static int filtering_ref_iterator_next(void *iter_arg,
- }
+ ## reftable/dump.c ##
+@@ reftable/dump.c: license that can be found in the LICENSE file or at
+ #include <unistd.h>
+ #include <string.h>
- if (fri->double_check) {
-- struct reftable_iterator it = { 0 };
-+ struct reftable_iterator it = { NULL };
+-#include "reftable.h"
++#include "reftable-blocksource.h"
++#include "reftable-error.h"
++#include "reftable-merged.h"
++#include "reftable-record.h"
+ #include "reftable-tests.h"
++#include "reftable-writer.h"
++#include "reftable-iterator.h"
++#include "reftable-reader.h"
++#include "reftable-stack.h"
- err = reftable_table_seek_ref(&fri->tab, &it,
- ref->refname);
-
- ## reftable/reader.c ##
-@@ reftable/reader.c: static int parse_footer(struct reftable_reader *r, uint8_t *footer,
- int init_reader(struct reftable_reader *r, struct reftable_block_source *source,
- const char *name)
- {
-- struct reftable_block footer = { 0 };
-- struct reftable_block header = { 0 };
-+ struct reftable_block footer = { NULL };
-+ struct reftable_block header = { NULL };
- int err = 0;
+ static uint32_t hash_id;
- memset(r, 0, sizeof(struct reftable_reader));
-@@ reftable/reader.c: int reader_init_block_reader(struct reftable_reader *r, struct block_reader *br,
+ static int dump_table(const char *tablename)
{
- int32_t guess_block_size = r->block_size ? r->block_size :
- DEFAULT_BLOCK_SIZE;
-- struct reftable_block block = { 0 };
-+ struct reftable_block block = { NULL };
- uint8_t block_typ = 0;
- int err = 0;
- uint32_t header_off = next_off ? 0 : header_size(r->version);
-@@ reftable/reader.c: static int reader_seek_indexed(struct reftable_reader *r,
- struct reftable_record *rec)
+- struct reftable_block_source src = { 0 };
++ struct reftable_block_source src = { NULL };
+ int err = reftable_block_source_from_file(&src, tablename);
+- struct reftable_iterator it = { 0 };
+- struct reftable_ref_record ref = { 0 };
+- struct reftable_log_record log = { 0 };
++ struct reftable_iterator it = { NULL };
++ struct reftable_ref_record ref = { NULL };
++ struct reftable_log_record log = { NULL };
+ struct reftable_reader *r = NULL;
+
+ if (err < 0)
+@@ reftable/dump.c: static int dump_table(const char *tablename)
+ reftable_ref_record_print(&ref, hash_id);
+ }
+ reftable_iterator_destroy(&it);
+- reftable_ref_record_clear(&ref);
++ reftable_ref_record_release(&ref);
+
+ err = reftable_reader_seek_log(r, &it, "");
+ if (err < 0) {
+@@ reftable/dump.c: static int dump_table(const char *tablename)
+ reftable_log_record_print(&log, hash_id);
+ }
+ reftable_iterator_destroy(&it);
+- reftable_log_record_clear(&log);
++ reftable_log_record_release(&log);
+
+ reftable_reader_free(r);
+ return 0;
+@@ reftable/dump.c: static int dump_stack(const char *stackdir)
{
- struct reftable_index_record want_index = { .last_key = STRBUF_INIT };
-- struct reftable_record want_index_rec = { 0 };
-+ struct reftable_record want_index_rec = { NULL };
- struct reftable_index_record index_result = { .last_key = STRBUF_INIT };
-- struct reftable_record index_result_rec = { 0 };
-+ struct reftable_record index_result_rec = { NULL };
- struct table_iter index_iter = TABLE_ITER_INIT;
- struct table_iter next = TABLE_ITER_INIT;
- int err = 0;
-@@ reftable/reader.c: int reftable_reader_seek_ref(struct reftable_reader *r,
- struct reftable_ref_record ref = {
- .refname = (char *)name,
- };
-- struct reftable_record rec = { 0 };
-+ struct reftable_record rec = { NULL };
- reftable_record_from_ref(&rec, &ref);
- return reader_seek(r, it, &rec);
- }
-@@ reftable/reader.c: int reftable_reader_seek_log_at(struct reftable_reader *r,
- .refname = (char *)name,
- .update_index = update_index,
- };
-- struct reftable_record rec = { 0 };
-+ struct reftable_record rec = { NULL };
- reftable_record_from_log(&rec, &log);
- return reader_seek(r, it, &rec);
- }
-@@ reftable/reader.c: static int reftable_reader_refs_for_indexed(struct reftable_reader *r,
- .hash_prefix = oid,
- .hash_prefix_len = r->object_id_len,
- };
-- struct reftable_record want_rec = { 0 };
-- struct reftable_iterator oit = { 0 };
-- struct reftable_obj_record got = { 0 };
-- struct reftable_record got_rec = { 0 };
-+ struct reftable_record want_rec = { NULL };
-+ struct reftable_iterator oit = { NULL };
-+ struct reftable_obj_record got = { NULL };
-+ struct reftable_record got_rec = { NULL };
- int err = 0;
- struct indexed_table_ref_iter *itr = NULL;
+ struct reftable_stack *stack = NULL;
+ struct reftable_write_options cfg = {};
+- struct reftable_iterator it = { 0 };
+- struct reftable_ref_record ref = { 0 };
+- struct reftable_log_record log = { 0 };
++ struct reftable_iterator it = { NULL };
++ struct reftable_ref_record ref = { NULL };
++ struct reftable_log_record log = { NULL };
+ struct reftable_merged_table *merged = NULL;
+ int err = reftable_new_stack(&stack, stackdir, cfg);
+@@ reftable/dump.c: static int dump_stack(const char *stackdir)
+ reftable_ref_record_print(&ref, hash_id);
+ }
+ reftable_iterator_destroy(&it);
+- reftable_ref_record_clear(&ref);
++ reftable_ref_record_release(&ref);
+
+ err = reftable_merged_table_seek_log(merged, &it, "");
+ if (err < 0) {
+@@ reftable/dump.c: static int dump_stack(const char *stackdir)
+ reftable_log_record_print(&log, hash_id);
+ }
+ reftable_iterator_destroy(&it);
+- reftable_log_record_clear(&log);
++ reftable_log_record_release(&log);
+
+ reftable_stack_destroy(stack);
+ return 0;
## t/helper/test-reftable.c ##
@@ t/helper/test-reftable.c: int cmd__reftable(int argc, const char **argv)
@@ t/helper/test-tool.h: int cmd__dump_cache_tree(int argc, const char **argv);
int cmd__dump_untracked_cache(int argc, const char **argv);
+int cmd__dump_reftable(int argc, const char **argv);
int cmd__example_decorate(int argc, const char **argv);
+ int cmd__fast_rebase(int argc, const char **argv);
int cmd__genrandom(int argc, const char **argv);
- int cmd__genzeros(int argc, const char **argv);
Signature · 1 line
gitgitgadget
PATCH v3 1/16 move sleep_millisec to git-compat-util.h
From: Han-Wen Nienhuys <[email protected]>
The sleep function is defined in wrapper.c, so it makes more sense to be a in system compatibility header.
--- cache.h | 1 - git-compat-util.h | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-)
cache.h +0 -1
@@ -1960,7 +1960,6 @@ int stat_validity_check(struct stat_validity *sv, const char *path);void stat_validity_update (struct stat_validity *sv,int fd);int versioncmp (const char *s1,const char *s2); -void sleep_millisec (int millisec);/* * Create a directory and (if share is nonzero) adjust its permissions
git-compat-util.h +2 -0
@@ -1354,4 +1354,6 @@ static inline void *container_of_or_null_offset(void *ptr, size_t offset) ((uintptr_t)&(ptr)->member - (uintptr_t)(ptr))#endif /* !__GNUC__ */ +void sleep_millisec (int millisec); +#endif
Signature · 1 line
gitgitgadget
PATCH v3 2/16 init-db: set the_repository->hash_algo early on
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
The reftable backend needs to know the hash algorithm for writing the initialization hash table.
The initial reftable contains a symref HEAD => "main" (or "master"), which is agnostic to the size of hash value, but this is an exceptional circumstance, and the reftable library does not cater for this exceptional. It insists that all tables in the stack have a consistent format ID for the hash algorithm.
Call set_repo_hash_algo directly after reading out GIT_DEFAULT_HASH.
--- builtin/init-db.c | 1 + 1 file changed, 1 insertion(+)
builtin/init-db.c +1 -0
@@ -391,6 +391,7 @@ static void validate_hash_algorithm(struct repository_format *repo_fmt, int hashdie (_ ("unknown hash algorithm '%s'" ), env); repo_fmt->hash_algo = env_algo; } +repo_set_hash_algo (the_repository, repo_fmt->hash_algo); } int init_db(const char *git_dir, const char *real_git_dir,
Signature · 1 line
gitgitgadget
Re: [PATCH v3 02/16] init-db: set the_repository->hash_algo early on
Quoted 8 lines · Han-Wen Nienhuys via GitGitGadget
From: Han-Wen Nienhuys <[email protected]>
The reftable backend needs to know the hash algorithm for writing the initialization hash table.
The initial reftable contains a symref HEAD => "main" (or "master"), which is agnostic to the size of hash value, but this is an exceptional circumstance, and the reftable library does not cater for this exceptional. It insists that all
rephrase: "does not cater to this exception" maybe?
PATCH v3 3/16 reftable: add LICENSE
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
TODO: relicense?
--- reftable/LICENSE | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 reftable/LICENSE
reftable/LICENSE new file +31 -0
@@ -0,0 +1,31 @@ +BSD License + +Copyright (c) 2020, Google LLC +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +* Neither the name of Google LLC nor the names of its contributors may +be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Signature · 1 line
gitgitgadget
Re: [PATCH v3 03/16] reftable: add LICENSE
Quoted 3 lines · Han-Wen Nienhuys via GitGitGadget
From: Han-Wen Nienhuys <[email protected]>
TODO: relicense?
On the topics of TODOs, it would be nice to have an answer/some idea the "I am concerned..." question in about
https://lore.kernel.org/git/[email protected]/ relevant to the license etc.
Re: [PATCH v3 03/16] reftable: add LICENSE
Quoted 4 lines · Ævar Arnfjörð Bjarmason
On Thu, Nov 26 2020, Han-Wen Nienhuys via GitGitGadget wrote:
Quoted 8 lines
Quoted 3 lines
From: Han-Wen Nienhuys <[email protected]>
TODO: relicense?
On the topics of TODOs, it would be nice to have an answer/some idea the "I am concerned..." question in about
https://lore.kernel.org/git/[email protected]/ relevant to the license etc.
Long term objective is for this to live in git proper, but remains independently compilable, so it can be used by libgit2 as well.
This is achieved by having the code only depend on functions in strbuf.h and git-compat-util.h.
I would like to enforce the separation by having an actual standalone compile, but the discussion over the code to do so has caused more heat than light, so I have deferred this by reworking standalone code to to be on file level, and only importing the non-standalone code. In one of the other patches, you found a place where I had overlooked a REFTABLE_STANDALONE #ifdef; I'll remove that. I'll also remove the update.sh script.
Until the reftable code is actually merged into git, I work on the reftable library itself at github.com/google/reftable. I've asked our opensource team if we could switch off the CLA enforcement for this repo, to facilitate back & forth imports of fixes.
The current version of the patchseries mainly addresses overall feedback from the Git team at Google to split the public API along commits too, and updates the series so it applies to master again.
Signature · 11 lines
Han-Wen Nienhuys - Google Munich I work 80%. Don't expect answers from me on Fridays. -- Google Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Re: [PATCH v3 03/16] reftable: add LICENSE
Quoted 4 lines · Han-Wen Nienhuys
Until the reftable code is actually merged into git, I work on the reftable library itself at github.com/google/reftable. I've asked our opensource team if we could switch off the CLA enforcement for this repo, to facilitate back & forth imports of fixes.
I've asked our lawyers, but this is not currently possible, unfortunately.
Signature · 11 lines
Han-Wen Nienhuys - Google Munich I work 80%. Don't expect answers from me on Fridays. -- Google Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Re: [PATCH v3 03/16] reftable: add LICENSE
Quoted 8 lines · Han-Wen Nienhuys
Quoted 4 lines · Han-Wen Nienhuys
Until the reftable code is actually merged into git, I work on the reftable library itself at github.com/google/reftable. I've asked our opensource team if we could switch off the CLA enforcement for this repo, to facilitate back & forth imports of fixes.
I've asked our lawyers, but this is not currently possible, unfortunately.
Maybe I'm not understanding something correctly.
Git's license is GPLv2. When you add your Signed-off-by line you are saying you are authorized to provide this code to the Git project under its open source license.
Therefore, I should be able to take your patches, and rework them in any way I want as long as I keep Git's license.
I don't need your permission, you are supposedly giving it by adding your Signed-off-by line.
Read: https://developercertificate.org/
If you are not authorized to provide these patches under an open source license, then you should not add your Signed-off-by line.
And if these patches cannot be used by the Git project, nor can we work on them in a collaborative manner, then what's the point in providing them?
Sounds to me Google has not made its mind about actually contributing these changes.
Or am I missing something?
Cheers.
Signature · 1 line
Felipe Contreras
Re: [PATCH v3 03/16] reftable: add LICENSE
Quoted 9 lines · Felipe Contreras
Quoted 7 lines · Han-Wen Nienhuys
Quoted 4 lines · Han-Wen Nienhuys
Until the reftable code is actually merged into git, I work on the reftable library itself at github.com/google/reftable. I've asked our opensource team if we could switch off the CLA enforcement for this repo, to facilitate back & forth imports of fixes.
Quoted 8 lines
Quoted 1 line
I've asked our lawyers, but this is not currently possible, unfortunately.
Maybe I'm not understanding something correctly.
Sounds to me Google has not made its mind about actually contributing these changes.
Or am I missing something?
The restrictions are not about the patches themselves. They are about restricting what gets posted under github.com/google/ .
Signature · 11 lines
Han-Wen Nienhuys - Google Munich I work 80%. Don't expect answers from me on Fridays. -- Google Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Re: [PATCH v3 03/16] reftable: add LICENSE
Quoted 11 lines · Han-Wen Nienhuys
Quoted 6 lines · Felipe Contreras
On Mon, Nov 30, 2020 at 2:28 PM Han-Wen Nienhuys <[email protected]> wrote:
Sounds to me Google has not made its mind about actually contributing these changes.
Or am I missing something?
The restrictions are not about the patches themselves. They are about restricting what gets posted under github.com/google/ .
I see.
But it doesn't have to be posted on github.com/google/, it doesn't have to be posted on github.com at all. If the code is under an open source license, you (or anyone) can post it anywhere.
Signature · 1 line
Felipe Contreras
Re: [PATCH v3 03/16] reftable: add LICENSE
Quoted 10 lines · Felipe Contreras
Quoted 9 lines · Han-Wen Nienhuys
Quoted 6 lines · Felipe Contreras
On Mon, Nov 30, 2020 at 2:28 PM Han-Wen Nienhuys <[email protected]> wrote:
Sounds to me Google has not made its mind about actually contributing these changes.
Or am I missing something?
Quoted 8 lines
Quoted 2 lines
The restrictions are not about the patches themselves. They are about restricting what gets posted under github.com/google/ .
I see.
But it doesn't have to be posted on github.com/google/, it doesn't have to be posted on github.com at all. If the code is under an open source license, you (or anyone) can post it anywhere.
The libgit2.git and git.git codebases are under different licenses. Therefore if the goal is to have code that's used in both it's not viable to e.g. store it in git.git under our current contribution policies.
The first contributor to submit a fix to it under GPLv2 as opposed to "any GPL or LGPL version" or whatever will preclude its subsequent import into libgit2.
Assigning copyright to Google is a way around that. They own your work, and then they re-license it under whatever license those two projects use.
But as I pointed out in [1] it requires contributors to git.git's reftable/ directory & Junio to play along with that scheme, least the whole process come to a halt. Maybe that's worth it, maybe not. But seems like something the series should very explicitly highlight and document e.g. in Documentation/SubmittingPatches.
We do have some in-tree code in that state already, but it's mostly/entirely one-off imports of GNU/FSF code like compat/regex/ or sh-i18n--envsubst.c that we're not actively working on in any way, and isn't core to git like the reftable would be.
There's individual contributors who just don't like the idea of copyright assignment and wouldn't do it for that reason, but there's also contributors who do paid-for work for their employers on git.git sometimes.
E.g. I've done such work under terms that were basically "you can contribute to open source as part of your job, and under the license the relevant project uses". Going through the legal process of changing that to "we, your employer, who own copyright to your work, agree to license it to this third party project/company/whatever for the purposes of an open source contribution" would in my experience be *very much* an uphill battle.
So I think even if the individual contributor wouldn't mind the copyright assignment (I'd personally be fine with that), e.g. I've been in employment relationships where I'd just forget about sending a patch if it needed assignment, because there's no way I'd be getting it past legal, in the same way Han-Wen seems to be having an uphill battle with Google's lawyers.
Re: [PATCH v3 03/16] reftable: add LICENSE
Quoted 14 lines · Ævar Arnfjörð Bjarmason
Quoted 10 lines · Felipe Contreras
Quoted 9 lines · Han-Wen Nienhuys
Quoted 6 lines · Felipe Contreras
On Mon, Nov 30, 2020 at 2:28 PM Han-Wen Nienhuys <[email protected]> wrote:
Sounds to me Google has not made its mind about actually contributing these changes.
Or am I missing something?
Quoted 8 lines
Quoted 8 lines
Quoted 2 lines
The restrictions are not about the patches themselves. They are about restricting what gets posted under github.com/google/ .
I see.
But it doesn't have to be posted on github.com/google/, it doesn't have to be posted on github.com at all. If the code is under an open source license, you (or anyone) can post it anywhere.
I am also bound by my employment contract to follow company instructions. While you (Felipe) can post code where you want, the same is not true for me.
Quoted 5 lines
The libgit2.git and git.git codebases are under different licenses. Therefore if the goal is to have code that's used in both it's not viable to e.g. store it in git.git under our current contribution policies.
[..]
Thanks for sketching the dilemmas here. Me and Jun are talking to the open source folks here at Google, so I am optimistic that we will find some solution. Please give us a few days to sort this out.
Quoted 8 lines
The first contributor to submit a fix to it under GPLv2 as opposed to "any GPL or LGPL version" or whatever will preclude its subsequent import into libgit2.
Assigning copyright to Google is a way around that. They own your work, and then they re-license it under whatever license those two projects use.
Quoted 5 lines
But as I pointed out in [1] it requires contributors to git.git's reftable/ directory & Junio to play along with that scheme, least the whole process come to a halt. Maybe that's worth it, maybe not. But seems like something the series should very explicitly highlight and document e.g. in
Documentation/SubmittingPatches.
FWIW, the plan of record is still for this code to have its source of truth in the Git project. Once it's there, there is no need for Google to have CLAs for contributions to the reftable/ directory.
There is still the open question of how to set the license terms for this code, so it is OK for it to be copied into libgit2. I picked the current license (BSD) because it was the most liberal I could find. (Maybe I should document that in the commit message.)
Signature · 11 lines
Han-Wen Nienhuys - Google Munich I work 80%. Don't expect answers from me on Fridays. -- Google Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Re: [PATCH v3 03/16] reftable: add LICENSE
Quoted 9 lines · Han-Wen Nienhuys
Quoted 5 lines · Felipe Contreras
Quoted 4 lines
But it doesn't have to be posted on github.com/google/, it doesn't have to be posted on github.com at all. If the code is under an open source license, you (or anyone) can post it anywhere.
I am also bound by my employment contract to follow company instructions. While you (Felipe) can post code where you want, the same is not true for me.
I understand that, but what if a stubborn person decides to do that, and the code starts diverging from github.com/google/? So essentially a fork is created.
I don't think that's in the best interest of Google, and that would force their hand to either a) allow you to contribute to the open repo, or b) allow open source people to contribute to the google repo.
I find it silly that these thought experiments have to be explained to corporate lawyers, or worse; they actually have to be done so the lawyers get the point. But alas, that's what working in a big corporation entails.
Good luck with that. (No sarcasm intended)
Signature · 1 line
Felipe Contreras
Re: [PATCH v3 03/16] reftable: add LICENSE
In reply to Ævar Arnfjörð BjarmasonQuoted 3 lines · Ævar Arnfjörð Bjarmason
Assigning copyright to Google is a way around that. They own your work, and then they re-license it under whatever license those two projects use.
That's the default. Unless otherwise stated; you retain the copyright while contributing code to open source projects.
Generally when open source projects want to change license they have to get the agreement from the majority of copyright holders.
Some projects do ask you to give away your copyright, but I'm not comfortable with that. If I want to contribute my code with GPLv2, that's the license I want. If a project I contribute to wants to switch to GPLv3, then they have to remove my code (or do something illegal).
So I see nothing wrong with Google owning the copyright and controlling under which license(s) their code is distributed. In fact, I would see something wrong with the opposite; them giving away their copyright.
Cheers.
Signature · 1 line
Felipe Contreras
PATCH v3 4/16 reftable: add error related functionality
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
The reftable/ directory is structured as a library, so it cannot crash on misuse. Instead, it returns an error codes.
In addition, the error code can be used to signal conditions from lower levels of the library to be handled by higher levels of the library. For example, a transaction might legitimately write an empty reftable file, but in that case, we'd want to shortcut the transaction overhead.
--- reftable/error.c | 39 ++++++++++++++++++++++++ reftable/reftable-error.h | 62 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 reftable/error.c create mode 100644 reftable/reftable-error.h
reftable/error.c new file +39 -0
@@ -0,0 +1,39 @@ +/* +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 "reftable-error.h" + +#include <stdio.h> + +const char *reftable_error_str (int err) +{ +static char buf[250 ]; +switch (err) { +case REFTABLE_IO_ERROR : +return "I/O error" ; +case REFTABLE_FORMAT_ERROR : +return "corrupt reftable file" ; +case REFTABLE_NOT_EXIST_ERROR : +return "file does not exist" ; +case REFTABLE_LOCK_ERROR : +return "data is outdated" ; +case REFTABLE_API_ERROR : +return "misuse of the reftable API" ; +case REFTABLE_ZLIB_ERROR : +return "zlib failure" ; +case REFTABLE_NAME_CONFLICT : +return "file/directory conflict" ; +case REFTABLE_REFNAME_ERROR : +return "invalid refname" ; +case -1 : +return "general error" ; +default : +snprintf (buf,sizeof (buf),"unknown error code %d" , err); +return buf; + } +}
reftable/reftable-error.h new file +62 -0
62 lines not on this page · raw message
Signature · 1 line
gitgitgadget
Re: [PATCH v3 04/16] reftable: add error related functionality
reftable/reftable-error.h line 15
Quoted 4 lines · Han-Wen Nienhuys via GitGitGadget
reftable/reftable-error.h new file +4
-0+/* +Errors in reftable calls are signaled with negative integer return values. 0 +means success. +*/
This is the format of multi-line comments:
/*
* A very long
* multi-line comment.
*/
reftable/reftable-error.h line 21
Quoted 6 lines
reftable/reftable-error.h new file +6
-0+enum reftable_error { +/* Unexpected file system behavior */ +REFTABLE_IO_ERROR =-2 , + +/* Format inconsistency on reading data +*/
No need for a multi-line comment here.
reftable/reftable-error.h line 26
Quoted 5 lines
reftable/reftable-error.h new file +5
-0+ REFTABLE_FORMAT_ERROR = -3, + +/* File does not exist. Returned from block_source_from_file(), because +it needs special handling in stack. +*/
Once again, and for the rest of the file.
Signature · 1 line
Felipe Contreras
Re: [PATCH v3 04/16] reftable: add error related functionality
In reply to Han-Wen Nienhuys via GitGitGadgetQuoted 15 lines · Han-Wen Nienhuys via GitGitGadget
The reftable/ directory is structured as a library, so it cannot crash on misuse. Instead, it returns an error codes.
In addition, the error code can be used to signal conditions from lower levels of the library to be handled by higher levels of the library. For example, a transaction might legitimately write an empty reftable file, but in that case, we'd want to shortcut the transaction overhead.
[...]reftable/error.c new file +7
-0+static char buf[250 ]; +switch (err) { +case REFTABLE_IO_ERROR : +return "I/O error" ; +case REFTABLE_FORMAT_ERROR : +return "corrupt reftable file" ; +case REFTABLE_NOT_EXIST_ERROR :
reftable/error.c line 30
Quoted 10 lines
reftable/error.c new file +9
-0+return "file does not exist" ; +case REFTABLE_LOCK_ERROR : +return "data is outdated" ; +case REFTABLE_API_ERROR : +return "misuse of the reftable API" ; +case REFTABLE_ZLIB_ERROR : +return "zlib failure" ; +case REFTABLE_NAME_CONFLICT : +return "file/directory conflict" ;
Not for this series, but I wonder if the eventual integration into the i18n framework in git needs some thought, seems all the (including presumably stuff show to users) UI strings are hardcoded English at the moment.
Re: [PATCH v3 04/16] reftable: add error related functionality
Quoted 4 lines · Ævar Arnfjörð Bjarmason
Not for this series, but I wonder if the eventual integration into the i18n framework in git needs some thought, seems all the (including presumably stuff show to users) UI strings are hardcoded English at the moment.
AFAIK, these are the only strings that potentially show up in the UI.
If these strings need to be i18n'd. It is probably easier for git-core to provide its own version of reftable_error_str() ? Or we could remove reftable_error_str from the official API?
Signature · 11 lines
Han-Wen Nienhuys - Google Munich I work 80%. Don't expect answers from me on Fridays. -- Google Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
PATCH v3 5/16 reftable: utility functions
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
This commit provides basic utility classes for the reftable library.
--- Makefile | 24 +++++- reftable/basics.c | 144 +++++++++++++++++++++++++++++++++++ reftable/basics.h | 56 ++++++++++++++ reftable/basics_test.c | 56 ++++++++++++++ reftable/compat.h | 48 ++++++++++++ reftable/publicbasics.c | 58 ++++++++++++++ reftable/reftable-malloc.h | 20 +++++ reftable/reftable-tests.h | 22 ++++++ reftable/system.h | 32 ++++++++ reftable/test_framework.c | 23 ++++++ reftable/test_framework.h | 48 ++++++++++++ t/helper/test-reftable.c | 9 +++ t/helper/test-tool.c | 3 +- t/helper/test-tool.h | 1 + t/t0032-reftable-unittest.sh | 14 ++++ 15 files changed, 554 insertions(+), 4 deletions(-) create mode 100644 reftable/basics.c create mode 100644 reftable/basics.h create mode 100644 reftable/basics_test.c create mode 100644 reftable/compat.h create mode 100644 reftable/publicbasics.c create mode 100644 reftable/reftable-malloc.h create mode 100644 reftable/reftable-tests.h create mode 100644 reftable/system.h create mode 100644 reftable/test_framework.c create mode 100644 reftable/test_framework.h create mode 100644 t/helper/test-reftable.c create mode 100755 t/t0032-reftable-unittest.sh
Makefile +21 -3
@@ -732,6 +732,7 @@ TEST_BUILTINS_OBJS += test-read-cache.o TEST_BUILTINS_OBJS += test-read-graph.o TEST_BUILTINS_OBJS += test-read-midx.o TEST_BUILTINS_OBJS += test-ref-store.o +TEST_BUILTINS_OBJS += test-reftable.o TEST_BUILTINS_OBJS += test-regex.o TEST_BUILTINS_OBJS += test-repository.o TEST_BUILTINS_OBJS += test-revision-walking.o
@@ -821,6 +822,8 @@ TEST_SHELL_PATH = $(SHELL_PATH) LIB_FILE = libgit.a XDIFF_LIB = xdiff/lib.a +REFTABLE_LIB = reftable/libreftable.a +REFTABLE_TEST_LIB = reftable/libreftable_test.a GENERATED_H += command-list.h GENERATED_H += config-list.h
@@ -1185,7 +1188,7 @@ THIRD_PARTY_SOURCES += compat/regex/% THIRD_PARTY_SOURCES += sha1collisiondetection/% THIRD_PARTY_SOURCES += sha1dc/% -GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) +GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) EXTLIBS = GIT_USER_AGENT = git/$(GIT_VERSION)
@@ -2386,10 +2389,19 @@ XDIFF_OBJS += xdiff/xpatience.o XDIFF_OBJS += xdiff/xprepare.o XDIFF_OBJS += xdiff/xutils.o +REFTABLE_OBJS += reftable/basics.o +REFTABLE_OBJS += reftable/error.o +REFTABLE_OBJS += reftable/publicbasics.o + +REFTABLE_TEST_OBJS += reftable/test_framework.o +REFTABLE_TEST_OBJS += reftable/basics_test.o + TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \ $(XDIFF_OBJS) \ $(FUZZ_OBJS) \ + $(REFTABLE_OBJS) \ + $(REFTABLE_TEST_OBJS) \ common-main.o \ git.o ifndef NO_CURL
@@ -2541,6 +2553,12 @@ $(LIB_FILE): $(LIB_OBJS) $(XDIFF_LIB): $(XDIFF_OBJS) $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^ +$(REFTABLE_LIB): $(REFTABLE_OBJS) + $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^ + +$(REFTABLE_TEST_LIB): $(REFTABLE_TEST_OBJS) + $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^ + export DEFAULT_EDITOR DEFAULT_PAGER Documentation/GIT-EXCLUDED-PROGRAMS: FORCE
@@ -2822,7 +2840,7 @@ perf: all t/helper/test-tool$X: $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) -t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS) +t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS) $(REFTABLE_TEST_LIB) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS) check-sha1:: t/helper/test-tool$X
@@ -3151,7 +3169,7 @@ cocciclean: clean: profile-clean coverage-clean cocciclean $(RM) *.res $(RM) $(OBJECTS) - $(RM) $(LIB_FILE) $(XDIFF_LIB) + $(RM) $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(REFTABLE_TEST_LIB) $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X $(RM) $(TEST_PROGRAMS) $(RM) $(FUZZ_PROGRAMS)
reftable/basics.c new file +144 -0
144 lines not on this page · raw message
reftable/basics.h new file +56 -0
56 lines not on this page · raw message
reftable/basics_test.c new file +56 -0
56 lines not on this page · raw message
reftable/compat.h new file +48 -0
48 lines not on this page · raw message
reftable/publicbasics.c new file +58 -0
58 lines not on this page · raw message
reftable/reftable-malloc.h new file +20 -0
@@ -0,0 +1,20 @@ +/* +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 REFTABLE_H +#define REFTABLE_H + +#include <stddef.h> + +/* +Overrides the functions to use for memory management. +*/ +void reftable_set_alloc (void *(*malloc)(size_t ), +void *(*realloc)(void *,size_t ),void (*free)(void *)); + +#endif
reftable/reftable-tests.h new file +22 -0
@@ -0,0 +1,22 @@ +/* +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 REFTABLE_TESTS_H +#define REFTABLE_TESTS_H + +int basics_test_main (int argc,const char **argv); +int block_test_main (int argc,const char **argv); +int merged_test_main (int argc,const char **argv); +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 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); + +#endif
reftable/system.h new file +32 -0
@@ -0,0 +1,32 @@ +/* +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 SYSTEM_H +#define SYSTEM_H + +#include "git-compat-util.h" +#include "strbuf.h" + +#include <zlib.h> + +struct strbuf ; +/* In git, this is declared in dir.h */ +int remove_dir_recursively (struct strbuf *path,int flags); + +#define SHA1_ID 0x73686131 +#define SHA256_ID 0x73323536 +#define SHA1_SIZE 20 +#define SHA256_SIZE 32 + +/* This is uncompress2, which is only available in zlib as of 2017. +*/ +int uncompress_return_consumed (Bytef *dest,uLongf *destLen, +const Bytef *source,uLong *sourceLen); +int hash_size (uint32_t id); + +#endif
reftable/test_framework.c new file +23 -0
@@ -0,0 +1,23 @@ +/* +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 "system.h" +#include "test_framework.h" + +#include "basics.h" + +void set_test_hash (uint8_t *p,int i) +{ +memset (p, (uint8_t )i,hash_size (SHA1_ID )); +} + +int strbuf_add_void (void *b,const void *data,size_t sz) +{ +strbuf_add ((struct strbuf *)b, data, sz); +return sz; +}
reftable/test_framework.h new file +48 -0
48 lines not on this page · raw message
t/helper/test-reftable.c new file +9 -0
@@ -0,0 +1,9 @@ +#include "reftable/reftable-tests.h" +#include "test-tool.h" + +int cmd__reftable (int argc,const char **argv) +{ +basics_test_main (argc, argv); + +return 0 ; +}
t/helper/test-tool.c +2 -1
@@ -48,13 +48,14 @@ static struct test_cmd cmds[] = { { "path-utils", cmd__path_utils }, { "pkt-line", cmd__pkt_line }, { "prio-queue", cmd__prio_queue }, - { "proc-receive", cmd__proc_receive}, + { "proc-receive", cmd__proc_receive }, { "progress", cmd__progress }, { "reach", cmd__reach }, { "read-cache", cmd__read_cache }, { "read-graph", cmd__read_graph }, { "read-midx", cmd__read_midx }, { "ref-store", cmd__ref_store }, + { "reftable", cmd__reftable }, { "regex", cmd__regex }, { "repository", cmd__repository }, { "revision-walking", cmd__revision_walking },
t/helper/test-tool.h +1 -0
@@ -44,6 +44,7 @@ int cmd__read_cache(int argc, const char **argv);int cmd__read_graph (int argc,const char **argv);int cmd__read_midx (int argc,const char **argv);int cmd__ref_store (int argc,const char **argv); +int cmd__reftable (int argc,const char **argv);int cmd__regex (int argc,const char **argv);int cmd__repository (int argc,const char **argv);int cmd__revision_walking (int argc,const char **argv);
t/t0032-reftable-unittest.sh new file +14 -0
@@ -0,0 +1,14 @@ +#!/bin/sh +# +# Copyright (c) 2020 Google LLC +# + +test_description='reftable unittests' + +. ./test-lib.sh + +test_expect_success 'unittests' ' + test-tool reftable +' + +test_done
Signature · 1 line
gitgitgadget
Re: [PATCH v3 05/16] reftable: utility functions
reftable/basics.c line 33
Quoted 4 lines · Han-Wen Nienhuys via GitGitGadget
reftable/basics.c new file +4
-0+int binsearch (size_t sz,int (*f)(size_t k,void *args),void *args) +{ +size_t lo =0 ; +size_t hi = sz;
Is there anything wrong with:
size_t lo = 0, hi = sz;
reftable/basics.c line 37
Quoted 3 lines
reftable/basics.c new file +3
-0+/* invariant: (hi == sz) || f(hi) == true +(lo == 0 && f(0) == true) || fi(lo) == false +*/
This doesn't follow the multi-line format. And perhaps English would be better.
reftable/basics.c line 46
Quoted 9 lines
reftable/basics.c new file +9
-0+while (hi - lo >1 ) { +size_t mid = lo + (hi - lo) /2 ; + +int val =f (mid, args); +if (val) { + hi = mid; + }else { + lo = mid; + }
Unnecessary braces are frowned upon. Also, why store the value if you are not going to use it later?
if (f(mid, args))
hi = mid;
else
lo = mid;
reftable/basics.c line 49
Quoted 3 lines
reftable/basics.c new file +3
-0+ } + +if (lo ==0 ) {
if (!lo)
reftable/basics.c line 54
Quoted 5 lines
reftable/basics.c new file +5
-0+if (f (0 , args)) { +return 0 ; + }else { +return 1 ; + }
return f(0, args) ? 0 : 1;
Or just:
return !f(0, args);
reftable/basics.c line 57
Quoted 3 lines
reftable/basics.c new file +3
-0+ } + +return hi;
It would be much clearer to just do:
if (lo)
return hi;
else
return !f(0, args);
reftable/basics.c line 65
Quoted 8 lines
reftable/basics.c new file +8
-0+} + +void free_names (char **a) +{ +char **p = a; +if (p ==NULL ) { +return ; + }
Once again:
if (!p)
return;
reftable/basics.c line 69
Quoted 4 lines
reftable/basics.c new file +4
-0+while (*p) { +reftable_free (*p); + p++; + }
This is the same thing as:
if (!a)
return;
for (p = a; *p; p++)
reftable_free(*p);reftable/basics.c line 82
Quoted 10 lines
reftable/basics.c new file +10
-0+int names_length (char **names) +{ +int len =0 ; +char **p = names; +while (*p) { + p++; + len++; + } +return len; +}
Is there any need to keep track of len? It's always p - names.
This should do it:
char **p; for (p = names; *p; p++); return p - names;
reftable/basics.c line 92
Quoted 9 lines
reftable/basics.c new file +9
-0+void parse_names (char *buf,int size,char ***namesp) +{ +char **names =NULL ; +size_t names_cap =0 ; +size_t names_len =0 ; + +char *p = buf; +char *end = buf + size; +while (p < end) {
Yet another while that can be a for:
for (p = buf; p < end; p = next + 1)
reftable/basics.c line 98
Quoted 6 lines
reftable/basics.c new file +6
-0+char *next =strchr (p,'\n' ); +if (next !=NULL ) { + *next =0 ; + }else { + next = end; + }
if (next)
*next = 0;
else
next = end;
Quoted 1 line
+ if (p < next) {
Or just:
if (p >= next)
continue;
reftable/basics.c line 103
Quoted 4 lines
reftable/basics.c new file +4
-0+if (names_len == names_cap) { + names_cap =2 * names_cap +1 ; + names =reftable_realloc ( + names, names_cap *sizeof (char *));
sizeof(*names);
reftable/basics.c line 113
Quoted 10 lines
reftable/basics.c new file +10
-0+ } + names[names_len++] =xstrdup (p); + } + p = next +1 ; + } + +if (names_len == names_cap) { + names_cap =2 * names_cap +1 ; + names =reftable_realloc (names, names_cap *sizeof (char *)); + }
This is repeated, maybe a macro like ALLOC_GROW(), or literally ALLOC_GROW().
Quoted 4 lines
reftable/basics.c new file +4
-0+ names[names_len] =NULL ; + *namesp = names; +} +
reftable/basics.c line 128
Quoted 10 lines
reftable/basics.c new file +10
-0+int names_equal (char **a,char **b) +{ +while (*a && *b) { +if (strcmp (*a, *b)) { +return 0 ; + } + + a++; + b++; + }
Again:
for (; *a && *b, a++, b++) {
if (strcmp(*a, *b)
return 0;
}But instead of moving multiple pointers:
for (i = 0; a[i] && b[i], i++) {
if (strcmp(a[i], b[i])
return 0;
}reftable/basics.c line 136
Quoted 4 lines
reftable/basics.c new file +4
-0+int common_prefix_size (struct strbuf *a,struct strbuf *b) +{ +int p =0 ; +while (p < a->len && p < b->len) {
Once again, this can easily be a for.
reftable/basics.c line 144
Quoted 8 lines
reftable/basics.c new file +8
-0+if (a->buf[p] != b->buf[p]) { +break ; + } + p++; + } + +return p; +}
Cheers.
Signature · 1 line
Felipe Contreras
Re: [PATCH v3 05/16] reftable: utility functions
In reply to Han-Wen Nienhuys via GitGitGadgetQuoted 3 lines · Han-Wen Nienhuys via GitGitGadget
From: Han-Wen Nienhuys <[email protected]>
This commit provides basic utility classes for the reftable library.
Re Johannes's comments in v2
(https://lore.kernel.org/git/[email protected]/)
about strbuf.c etc. being duplicated I see that's gone now.
It seems you did some version of my suggestion in
https://lore.kernel.org/git/[email protected]/ of factoring out that code, except here:
reftable/compat.h line 20
Quoted 8 lines
[...]
reftable/compat.h new file +7
-0+#ifdef REFTABLE_STANDALONE + +/* functions that git-core provides, for standalone compilation */ +#include <stdint.h> + +uint64_t get_be64 (void *in); +void put_be64 (void *out,uint64_t i);
It's unclear whether that's an omission, or needed for some reason (is git.git ever going to have REFTABLE_STANDALONE=true?
The update shellscript is also gone from v2->v3, is that out of tree now somewhere? How does one test with the upstream code & presumably do some munging on it to make it diff-able with what's going to be in git.git?
It would be really useful to have that sort of info/summary in the cover letter between serieses. It's not that I mind e.g. having some of this REFTABLE_STANDALONE code here. Just having to do archaeology trying to figure out changes in this rather large series...
PATCH v3 6/16 reftable: add blocksource, an abstraction for random access reads
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
The reftable format is usually used with files for storage. However, we abstract away this using the blocksource data structure. This has two advantages:
* log blocks are zlib compressed, and handling them is simplified if we can discard byte segments from within the block layer.
* for unittests, it is useful to read and write in-memory. The blocksource allows us to abstract the data away from on-disk files.
--- Makefile | 1 + reftable/blocksource.c | 148 ++++++++++++++++++++++++++++++++ reftable/blocksource.h | 22 +++++ reftable/reftable-blocksource.h | 50 +++++++++++ 4 files changed, 221 insertions(+) create mode 100644 reftable/blocksource.c create mode 100644 reftable/blocksource.h create mode 100644 reftable/reftable-blocksource.h
Makefile +1 -0
@@ -2391,6 +2391,7 @@ XDIFF_OBJS += xdiff/xutils.o REFTABLE_OBJS += reftable/basics.o REFTABLE_OBJS += reftable/error.o +REFTABLE_OBJS += reftable/blocksource.o REFTABLE_OBJS += reftable/publicbasics.o REFTABLE_TEST_OBJS += reftable/test_framework.o
reftable/blocksource.c new file +148 -0
148 lines not on this page · raw message
reftable/blocksource.h new file +22 -0
@@ -0,0 +1,22 @@ +/* +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 BLOCKSOURCE_H +#define BLOCKSOURCE_H + +#include "system.h" + +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/reftable-blocksource.h new file +50 -0
50 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH v3 7/16 reftable: (de)serialization for the polymorphic record type.
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
The reftable format is structured as a sequence of blocks, and each block contains a sequence of prefix-compressed key-value records. There are 4 types of records, and they have similarities in how they must be handled. This is achieved by introducing a polymorphic 'record' type that encapsulates ref, log, index and object records.
--- Makefile | 2 + reftable/constants.h | 21 + reftable/record.c | 1116 ++++++++++++++++++++++++++++++++++++ reftable/record.h | 139 +++++ reftable/record_test.c | 404 +++++++++++++ reftable/reftable-record.h | 73 +++ t/helper/test-reftable.c | 2 +- 7 files changed, 1756 insertions(+), 1 deletion(-) create mode 100644 reftable/constants.h create mode 100644 reftable/record.c create mode 100644 reftable/record.h create mode 100644 reftable/record_test.c create mode 100644 reftable/reftable-record.h
Makefile +2 -0
@@ -2393,7 +2393,9 @@ REFTABLE_OBJS += reftable/basics.o REFTABLE_OBJS += reftable/error.o REFTABLE_OBJS += reftable/blocksource.o REFTABLE_OBJS += reftable/publicbasics.o +REFTABLE_OBJS += reftable/record.o +REFTABLE_TEST_OBJS += reftable/record_test.o REFTABLE_TEST_OBJS += reftable/test_framework.o REFTABLE_TEST_OBJS += reftable/basics_test.o
reftable/constants.h new file +21 -0
@@ -0,0 +1,21 @@ +/* +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 CONSTANTS_H +#define CONSTANTS_H + +#define BLOCK_TYPE_LOG 'g' +#define BLOCK_TYPE_INDEX 'i' +#define BLOCK_TYPE_REF 'r' +#define BLOCK_TYPE_OBJ 'o' +#define BLOCK_TYPE_ANY 0 + +#define MAX_RESTARTS ((1 << 16) - 1) +#define DEFAULT_BLOCK_SIZE 4096 + +#endif
reftable/record.c new file +1116 -0
1,116 lines not on this page · raw message
reftable/record.h new file +139 -0
139 lines not on this page · raw message
reftable/record_test.c new file +404 -0
404 lines not on this page · raw message
reftable/reftable-record.h new file +73 -0
73 lines not on this page · raw message
t/helper/test-reftable.c +1 -1
@@ -4,6 +4,6 @@int cmd__reftable (int argc,const char **argv) {basics_test_main (argc, argv); - +record_test_main (argc, argv);return 0 ; }
Signature · 1 line
gitgitgadget
PATCH v3 8/16 reftable: reading/writing blocks
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
The reftable format is structured as a sequence of block. Within a block, records are prefix compressed, with an index of offsets for fully expand keys to enable binary search within blocks.
This commit provides the logic to read and write these blocks.
Includes a code snippet copied from zlib
--- Makefile | 3 + reftable/.gitattributes | 1 + reftable/block.c | 440 +++++++++++++++++++++++++++++++++++++++ reftable/block.h | 129 ++++++++++++ reftable/block_test.c | 119 +++++++++++ reftable/zlib-compat.c | 92 ++++++++ t/helper/test-reftable.c | 1 + 7 files changed, 785 insertions(+) create mode 100644 reftable/.gitattributes create mode 100644 reftable/block.c create mode 100644 reftable/block.h create mode 100644 reftable/block_test.c create mode 100644 reftable/zlib-compat.c
Makefile +3 -0
@@ -2391,10 +2391,13 @@ XDIFF_OBJS += xdiff/xutils.o REFTABLE_OBJS += reftable/basics.o REFTABLE_OBJS += reftable/error.o +REFTABLE_OBJS += reftable/block.o REFTABLE_OBJS += reftable/blocksource.o REFTABLE_OBJS += reftable/publicbasics.o REFTABLE_OBJS += reftable/record.o +REFTABLE_OBJS += reftable/zlib-compat.o +REFTABLE_TEST_OBJS += reftable/block_test.o REFTABLE_TEST_OBJS += reftable/record_test.o REFTABLE_TEST_OBJS += reftable/test_framework.o REFTABLE_TEST_OBJS += reftable/basics_test.o
reftable/.gitattributes new file +1 -0
@@ -0,0 +1 @@ +/zlib-compat.c whitespace=-indent-with-non-tab,-trailing-space
reftable/block.c new file +440 -0
440 lines not on this page · raw message
reftable/block.h new file +129 -0
129 lines not on this page · raw message
reftable/block_test.c new file +119 -0
119 lines not on this page · raw message
reftable/zlib-compat.c new file +92 -0
92 lines not on this page · raw message
t/helper/test-reftable.c +1 -0
@@ -4,6 +4,7 @@int cmd__reftable (int argc,const char **argv) {basics_test_main (argc, argv); +block_test_main (argc, argv);record_test_main (argc, argv);return 0 ; }
Signature · 1 line
gitgitgadget
PATCH v3 9/16 reftable: a generic binary tree implementation
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
The reftable format includes support for an (OID => ref) map. This map can speed up visibility and reachability checks. In particular, various operations along the fetch/push path within Gerrit have ben sped up by using this structure.
The map is constructed with help of a binary tree. Object IDs are hashes, so they are uniformly distributed. Hence, the tree does not attempt forced rebalancing.
--- Makefile | 4 ++- reftable/tree.c | 63 ++++++++++++++++++++++++++++++++++++++++ reftable/tree.h | 34 ++++++++++++++++++++++ reftable/tree_test.c | 61 ++++++++++++++++++++++++++++++++++++++ t/helper/test-reftable.c | 1 + 5 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 reftable/tree.c create mode 100644 reftable/tree.h create mode 100644 reftable/tree_test.c
Makefile +3 -1
@@ -2395,12 +2395,14 @@ REFTABLE_OBJS += reftable/block.o REFTABLE_OBJS += reftable/blocksource.o REFTABLE_OBJS += reftable/publicbasics.o REFTABLE_OBJS += reftable/record.o +REFTABLE_OBJS += reftable/tree.o REFTABLE_OBJS += reftable/zlib-compat.o +REFTABLE_TEST_OBJS += reftable/basics_test.o REFTABLE_TEST_OBJS += reftable/block_test.o REFTABLE_TEST_OBJS += reftable/record_test.o REFTABLE_TEST_OBJS += reftable/test_framework.o -REFTABLE_TEST_OBJS += reftable/basics_test.o +REFTABLE_TEST_OBJS += reftable/tree_test.o TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
reftable/tree.c new file +63 -0
63 lines not on this page · raw message
reftable/tree.h new file +34 -0
@@ -0,0 +1,34 @@ +/* +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 TREE_H +#define TREE_H + +/* tree_node is a generic binary search tree. */ +struct tree_node { +void *key; +struct tree_node *left, *right; +}; + +/* looks for `key` in `rootp` using `compare` as comparison function. If insert +is set, insert the key if it's not found. Else, return NULL. +*/ +struct tree_node *tree_search (void *key,struct tree_node **rootp, +int (*compare)(const void *,const void *), +int insert); + +/* performs an infix walk of the tree. */ +void infix_walk (struct tree_node *t,void (*action)(void *arg,void *key), +void *arg); + +/* +deallocates the tree nodes recursively. Keys should be deallocated separately +by walking over the tree. */ +void tree_free (struct tree_node *t); + +#endif
reftable/tree_test.c new file +61 -0
61 lines not on this page · raw message
t/helper/test-reftable.c +1 -0
@@ -6,5 +6,6 @@ int cmd__reftable(int argc, const char **argv)basics_test_main (argc, argv);block_test_main (argc, argv);record_test_main (argc, argv); +tree_test_main (argc, argv);return 0 ; }
Signature · 1 line
gitgitgadget
PATCH v3 10/16 reftable: write reftable files
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- Makefile | 1 + reftable/reftable-writer.h | 149 ++++++++ reftable/writer.c | 676 +++++++++++++++++++++++++++++++++++++ reftable/writer.h | 50 +++ 4 files changed, 876 insertions(+) create mode 100644 reftable/reftable-writer.h create mode 100644 reftable/writer.c create mode 100644 reftable/writer.h
Makefile +1 -0
@@ -2396,6 +2396,7 @@ REFTABLE_OBJS += reftable/blocksource.o REFTABLE_OBJS += reftable/publicbasics.o REFTABLE_OBJS += reftable/record.o REFTABLE_OBJS += reftable/tree.o +REFTABLE_OBJS += reftable/writer.o REFTABLE_OBJS += reftable/zlib-compat.o REFTABLE_TEST_OBJS += reftable/basics_test.o
reftable/reftable-writer.h new file +149 -0
149 lines not on this page · raw message
reftable/writer.c new file +676 -0
676 lines not on this page · raw message
reftable/writer.h new file +50 -0
50 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH v3 11/16 reftable: read reftable files
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
This supports reading a single reftable file.
The commit introduces an abstract iterator type, which captures the usecases both of reading individual refs, and iterating over a segment of the ref namespace.
--- Makefile | 3 + reftable/iter.c | 242 ++++++++++++ reftable/iter.h | 75 ++++ reftable/reader.c | 733 +++++++++++++++++++++++++++++++++++ reftable/reader.h | 75 ++++ reftable/reftable-iterator.h | 37 ++ reftable/reftable-reader.h | 89 +++++ 7 files changed, 1254 insertions(+) create mode 100644 reftable/iter.c create mode 100644 reftable/iter.h create mode 100644 reftable/reader.c create mode 100644 reftable/reader.h create mode 100644 reftable/reftable-iterator.h create mode 100644 reftable/reftable-reader.h
Makefile +3 -0
@@ -2393,8 +2393,11 @@ REFTABLE_OBJS += reftable/basics.o REFTABLE_OBJS += reftable/error.o REFTABLE_OBJS += reftable/block.o REFTABLE_OBJS += reftable/blocksource.o +REFTABLE_OBJS += reftable/iter.o REFTABLE_OBJS += reftable/publicbasics.o +REFTABLE_OBJS += reftable/reader.o REFTABLE_OBJS += reftable/record.o +REFTABLE_OBJS += reftable/reftable.o REFTABLE_OBJS += reftable/tree.o REFTABLE_OBJS += reftable/writer.o REFTABLE_OBJS += reftable/zlib-compat.o
reftable/iter.c new file +242 -0
242 lines not on this page · raw message
reftable/iter.h new file +75 -0
75 lines not on this page · raw message
reftable/reader.c new file +733 -0
733 lines not on this page · raw message
reftable/reader.h new file +75 -0
75 lines not on this page · raw message
reftable/reftable-iterator.h new file +37 -0
@@ -0,0 +1,37 @@ +/* +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 REFTABLE_ITERATOR_H +#define REFTABLE_ITERATOR_H + +#include "reftable-record.h" + +/* iterator is the generic interface for walking over data stored in a +reftable. +*/ +struct reftable_iterator { +struct reftable_iterator_vtable *ops; +void *iter_arg; +}; + +/* reads the next reftable_ref_record. Returns < 0 for error, 0 for OK and > 0: +end of iteration. +*/ +int reftable_iterator_next_ref (struct reftable_iterator *it, +struct reftable_ref_record *ref); + +/* reads the next reftable_log_record. Returns < 0 for error, 0 for OK and > 0: +end of iteration. +*/ +int reftable_iterator_next_log (struct reftable_iterator *it, +struct reftable_log_record *log); + +/* releases resources associated with an iterator. */ +void reftable_iterator_destroy (struct reftable_iterator *it); + +#endif
reftable/reftable-reader.h new file +89 -0
89 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH v3 12/16 reftable: reftable file level tests
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
With support for reading and writing files in place, we can construct files (in memory) and attempt to read them back.
Because some sections of the format are optional (eg. indices, log entries), we have to exercise this code using multiple sizes of input data
--- Makefile | 1 + reftable/reftable_test.c | 577 +++++++++++++++++++++++++++++++++++++++ t/helper/test-reftable.c | 1 + 3 files changed, 579 insertions(+) create mode 100644 reftable/reftable_test.c
Makefile +1 -0
@@ -2405,6 +2405,7 @@ REFTABLE_OBJS += reftable/zlib-compat.o REFTABLE_TEST_OBJS += reftable/basics_test.o REFTABLE_TEST_OBJS += reftable/block_test.o REFTABLE_TEST_OBJS += reftable/record_test.o +REFTABLE_TEST_OBJS += reftable/reftable_test.o REFTABLE_TEST_OBJS += reftable/test_framework.o REFTABLE_TEST_OBJS += reftable/tree_test.o
reftable/reftable_test.c new file +577 -0
577 lines not on this page · raw message
t/helper/test-reftable.c +1 -0
@@ -6,6 +6,7 @@ int cmd__reftable(int argc, const char **argv)basics_test_main (argc, argv);block_test_main (argc, argv);record_test_main (argc, argv); +reftable_test_main (argc, argv);tree_test_main (argc, argv);return 0 ; }
Signature · 1 line
gitgitgadget
PATCH v3 13/16 reftable: rest of library
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- Makefile | 4 + reftable/VERSION | 1 + reftable/dump.c | 212 ++++++ reftable/merged.c | 366 ++++++++++ reftable/merged.h | 35 + reftable/merged_test.c | 333 ++++++++++ reftable/pq.c | 115 ++++ reftable/pq.h | 32 + reftable/refname.c | 209 ++++++ reftable/refname.h | 29 + reftable/refname_test.c | 100 +++ reftable/reftable-generic.h | 49 ++ reftable/reftable-merged.h | 68 ++ reftable/reftable-stack.h | 116 ++++ reftable/reftable.c | 98 +++ reftable/stack.c | 1247 +++++++++++++++++++++++++++++++++++ reftable/stack.h | 41 ++ reftable/stack_test.c | 781 ++++++++++++++++++++++ t/helper/test-reftable.c | 3 + 19 files changed, 3839 insertions(+) create mode 100644 reftable/VERSION create mode 100644 reftable/dump.c 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/refname.c create mode 100644 reftable/refname.h create mode 100644 reftable/refname_test.c create mode 100644 reftable/reftable-generic.h create mode 100644 reftable/reftable-merged.h create mode 100644 reftable/reftable-stack.h create mode 100644 reftable/reftable.c create mode 100644 reftable/stack.c create mode 100644 reftable/stack.h create mode 100644 reftable/stack_test.c
Makefile +4 -0
@@ -2394,10 +2394,14 @@ REFTABLE_OBJS += reftable/error.o REFTABLE_OBJS += reftable/block.o REFTABLE_OBJS += reftable/blocksource.o REFTABLE_OBJS += reftable/iter.o +REFTABLE_OBJS += reftable/merged.o +REFTABLE_OBJS += reftable/pq.o REFTABLE_OBJS += reftable/publicbasics.o REFTABLE_OBJS += reftable/reader.o REFTABLE_OBJS += reftable/record.o +REFTABLE_OBJS += reftable/refname.o REFTABLE_OBJS += reftable/reftable.o +REFTABLE_OBJS += reftable/stack.o REFTABLE_OBJS += reftable/tree.o REFTABLE_OBJS += reftable/writer.o REFTABLE_OBJS += reftable/zlib-compat.o
reftable/VERSION new file +1 -0
@@ -0,0 +1 @@ +b8ec0f74c74cb6752eb2033ad8e755a9c19aad15 C: add missing header
reftable/dump.c new file +212 -0
212 lines not on this page · raw message
reftable/merged.c new file +366 -0
366 lines not on this page · raw message
reftable/merged.h new file +35 -0
@@ -0,0 +1,35 @@ +/* +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 MERGED_H +#define MERGED_H + +#include "pq.h" + +struct reftable_merged_table { +struct reftable_table *stack; +size_t stack_len; +uint32_t hash_id; +int suppress_deletions; + +uint64_t min; +uint64_t max; +}; + +struct merged_iter { +struct reftable_iterator *stack; +uint32_t hash_id; +size_t stack_len; +uint8_t typ; +int suppress_deletions; +struct merged_iter_pqueue pq; +}; + +void merged_table_release (struct reftable_merged_table *mt); + +#endif
reftable/merged_test.c new file +333 -0
333 lines not on this page · raw message
reftable/pq.c new file +115 -0
115 lines not on this page · raw message
reftable/pq.h new file +32 -0
@@ -0,0 +1,32 @@ +/* +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 PQ_H +#define PQ_H + +#include "record.h" + +struct pq_entry { +int index; +struct reftable_record rec; +}; + +struct merged_iter_pqueue { +struct pq_entry *heap; +size_t len; +size_t cap; +}; + +struct pq_entry merged_iter_pqueue_top (struct merged_iter_pqueue pq); +int merged_iter_pqueue_is_empty (struct merged_iter_pqueue pq); +void merged_iter_pqueue_check (struct merged_iter_pqueue pq); +struct pq_entry merged_iter_pqueue_remove (struct merged_iter_pqueue *pq); +void merged_iter_pqueue_add (struct merged_iter_pqueue *pq,struct pq_entry e); +void merged_iter_pqueue_release (struct merged_iter_pqueue *pq); + +#endif
reftable/refname.c new file +209 -0
209 lines not on this page · raw message
reftable/refname.h new file +29 -0
@@ -0,0 +1,29 @@ +/* +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 REFNAME_H +#define REFNAME_H + +#include "reftable-record.h" +#include "reftable-generic.h" + +struct modification { +struct reftable_table tab; + +char **add; +size_t add_len; + +char **del; +size_t del_len; +}; + +int validate_ref_record_addition (struct reftable_table tab, +struct reftable_ref_record *recs,size_t sz); + +int modification_validate (struct modification *mod); + +#endif
reftable/refname_test.c new file +100 -0
100 lines not on this page · raw message
reftable/reftable-generic.h new file +49 -0
49 lines not on this page · raw message
reftable/reftable-merged.h new file +68 -0
68 lines not on this page · raw message
reftable/reftable-stack.h new file +116 -0
116 lines not on this page · raw message
reftable/reftable.c new file +98 -0
98 lines not on this page · raw message
reftable/stack.c new file +1247 -0
1,223 lines not on this page · raw message
reftable/stack.h new file +41 -0
41 lines not on this page · raw message
reftable/stack_test.c new file +781 -0
781 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Signature · 1 line
gitgitgadget
Part of the diff is not shown here. View the raw message.