Merge branch 'hn/reftable-no-empty-keys'
General clean-up in reftable implementation, including clarification of the API documentation, tightening the code to honor documented length limit, etc. * hn/reftable-no-empty-keys: reftable: rename writer_stats to reftable_writer_stats reftable: add test for length of disambiguating prefix reftable: ensure that obj_id_len is >= 2 on writing reftable: avoid writing empty keys at the block layer reftable: add a test that verifies that writing empty keys fails reftable: reject 0 object_id_len Documentation: object_id_len goes up to 31
This commit is contained in:
commit
4763ccd7f4
@ -443,7 +443,7 @@ Obj block format
|
||||
Object blocks are optional. Writers may choose to omit object blocks,
|
||||
especially if readers will not use the object name to ref mapping.
|
||||
|
||||
Object blocks use unique, abbreviated 2-32 object name keys, mapping to
|
||||
Object blocks use unique, abbreviated 2-31 byte object name keys, mapping to
|
||||
ref blocks containing references pointing to that object directly, or as
|
||||
the peeled value of an annotated tag. Like ref blocks, object blocks use
|
||||
the file's standard block size. The abbreviation length is available in
|
||||
|
@ -88,8 +88,9 @@ uint8_t block_writer_type(struct block_writer *bw)
|
||||
return bw->buf[bw->header_off];
|
||||
}
|
||||
|
||||
/* adds the reftable_record to the block. Returns -1 if it does not fit, 0 on
|
||||
success */
|
||||
/* Adds the reftable_record to the block. Returns -1 if it does not fit, 0 on
|
||||
success. Returns REFTABLE_API_ERROR if attempting to write a record with
|
||||
empty key. */
|
||||
int block_writer_add(struct block_writer *w, struct reftable_record *rec)
|
||||
{
|
||||
struct strbuf empty = STRBUF_INIT;
|
||||
@ -105,8 +106,14 @@ int block_writer_add(struct block_writer *w, struct reftable_record *rec)
|
||||
int is_restart = 0;
|
||||
struct strbuf key = STRBUF_INIT;
|
||||
int n = 0;
|
||||
int err = -1;
|
||||
|
||||
reftable_record_key(rec, &key);
|
||||
if (!key.len) {
|
||||
err = REFTABLE_API_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
n = reftable_encode_key(&is_restart, out, last, key,
|
||||
reftable_record_val_type(rec));
|
||||
if (n < 0)
|
||||
@ -118,16 +125,11 @@ int block_writer_add(struct block_writer *w, struct reftable_record *rec)
|
||||
goto done;
|
||||
string_view_consume(&out, n);
|
||||
|
||||
if (block_writer_register_restart(w, start.len - out.len, is_restart,
|
||||
&key) < 0)
|
||||
goto done;
|
||||
|
||||
strbuf_release(&key);
|
||||
return 0;
|
||||
|
||||
err = block_writer_register_restart(w, start.len - out.len, is_restart,
|
||||
&key);
|
||||
done:
|
||||
strbuf_release(&key);
|
||||
return -1;
|
||||
return err;
|
||||
}
|
||||
|
||||
int block_writer_finish(struct block_writer *w)
|
||||
@ -332,6 +334,9 @@ int block_iter_next(struct block_iter *it, struct reftable_record *rec)
|
||||
if (n < 0)
|
||||
return -1;
|
||||
|
||||
if (!key.len)
|
||||
return REFTABLE_FORMAT_ERROR;
|
||||
|
||||
string_view_consume(&in, n);
|
||||
n = reftable_record_decode(rec, key, extra, in, it->br->hash_size);
|
||||
if (n < 0)
|
||||
@ -358,6 +363,8 @@ int block_reader_first_key(struct block_reader *br, struct strbuf *key)
|
||||
int n = reftable_decode_key(key, &extra, empty, in);
|
||||
if (n < 0)
|
||||
return n;
|
||||
if (!key->len)
|
||||
return REFTABLE_FORMAT_ERROR;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -42,6 +42,11 @@ static void test_block_read_write(void)
|
||||
block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
|
||||
header_off, hash_size(GIT_SHA1_FORMAT_ID));
|
||||
|
||||
rec.u.ref.refname = "";
|
||||
rec.u.ref.value_type = REFTABLE_REF_DELETION;
|
||||
n = block_writer_add(&bw, &rec);
|
||||
EXPECT(n == REFTABLE_API_ERROR);
|
||||
|
||||
for (i = 0; i < N; i++) {
|
||||
char name[100];
|
||||
uint8_t hash[GIT_SHA1_RAWSZ];
|
||||
|
@ -155,6 +155,11 @@ static int parse_footer(struct reftable_reader *r, uint8_t *footer,
|
||||
r->log_offsets.is_present = (first_block_typ == BLOCK_TYPE_LOG ||
|
||||
r->log_offsets.offset > 0);
|
||||
r->obj_offsets.is_present = r->obj_offsets.offset > 0;
|
||||
if (r->obj_offsets.is_present && !r->object_id_len) {
|
||||
err = REFTABLE_FORMAT_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
done:
|
||||
return err;
|
||||
|
@ -100,7 +100,7 @@ static void write_table(char ***names, struct strbuf *buf, int N,
|
||||
n = reftable_writer_close(w);
|
||||
EXPECT(n == 0);
|
||||
|
||||
stats = writer_stats(w);
|
||||
stats = reftable_writer_stats(w);
|
||||
for (i = 0; i < stats->ref_stats.blocks; i++) {
|
||||
int off = i * opts.block_size;
|
||||
if (off == 0) {
|
||||
@ -239,7 +239,7 @@ static void test_log_write_read(void)
|
||||
n = reftable_writer_close(w);
|
||||
EXPECT(n == 0);
|
||||
|
||||
stats = writer_stats(w);
|
||||
stats = reftable_writer_stats(w);
|
||||
EXPECT(stats->log_stats.blocks > 0);
|
||||
reftable_writer_free(w);
|
||||
w = NULL;
|
||||
@ -330,7 +330,7 @@ static void test_log_zlib_corruption(void)
|
||||
n = reftable_writer_close(w);
|
||||
EXPECT(n == 0);
|
||||
|
||||
stats = writer_stats(w);
|
||||
stats = reftable_writer_stats(w);
|
||||
EXPECT(stats->log_stats.blocks > 0);
|
||||
reftable_writer_free(w);
|
||||
w = NULL;
|
||||
@ -667,6 +667,102 @@ static void test_write_empty_table(void)
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
static void test_write_object_id_min_length(void)
|
||||
{
|
||||
struct reftable_write_options opts = {
|
||||
.block_size = 75,
|
||||
};
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct reftable_writer *w =
|
||||
reftable_new_writer(&strbuf_add_void, &buf, &opts);
|
||||
uint8_t hash[GIT_SHA1_RAWSZ] = {42};
|
||||
struct reftable_ref_record ref = {
|
||||
.update_index = 1,
|
||||
.value_type = REFTABLE_REF_VAL1,
|
||||
.value.val1 = hash,
|
||||
};
|
||||
int err;
|
||||
int i;
|
||||
|
||||
reftable_writer_set_limits(w, 1, 1);
|
||||
|
||||
/* Write the same hash in many refs. If there is only 1 hash, the
|
||||
* disambiguating prefix is length 0 */
|
||||
for (i = 0; i < 256; i++) {
|
||||
char name[256];
|
||||
snprintf(name, sizeof(name), "ref%05d", i);
|
||||
ref.refname = name;
|
||||
err = reftable_writer_add_ref(w, &ref);
|
||||
EXPECT_ERR(err);
|
||||
}
|
||||
|
||||
err = reftable_writer_close(w);
|
||||
EXPECT_ERR(err);
|
||||
EXPECT(reftable_writer_stats(w)->object_id_len == 2);
|
||||
reftable_writer_free(w);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
static void test_write_object_id_length(void)
|
||||
{
|
||||
struct reftable_write_options opts = {
|
||||
.block_size = 75,
|
||||
};
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct reftable_writer *w =
|
||||
reftable_new_writer(&strbuf_add_void, &buf, &opts);
|
||||
uint8_t hash[GIT_SHA1_RAWSZ] = {42};
|
||||
struct reftable_ref_record ref = {
|
||||
.update_index = 1,
|
||||
.value_type = REFTABLE_REF_VAL1,
|
||||
.value.val1 = hash,
|
||||
};
|
||||
int err;
|
||||
int i;
|
||||
|
||||
reftable_writer_set_limits(w, 1, 1);
|
||||
|
||||
/* Write the same hash in many refs. If there is only 1 hash, the
|
||||
* disambiguating prefix is length 0 */
|
||||
for (i = 0; i < 256; i++) {
|
||||
char name[256];
|
||||
snprintf(name, sizeof(name), "ref%05d", i);
|
||||
ref.refname = name;
|
||||
ref.value.val1[15] = i;
|
||||
err = reftable_writer_add_ref(w, &ref);
|
||||
EXPECT_ERR(err);
|
||||
}
|
||||
|
||||
err = reftable_writer_close(w);
|
||||
EXPECT_ERR(err);
|
||||
EXPECT(reftable_writer_stats(w)->object_id_len == 16);
|
||||
reftable_writer_free(w);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
static void test_write_empty_key(void)
|
||||
{
|
||||
struct reftable_write_options opts = { 0 };
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct reftable_writer *w =
|
||||
reftable_new_writer(&strbuf_add_void, &buf, &opts);
|
||||
struct reftable_ref_record ref = {
|
||||
.refname = "",
|
||||
.update_index = 1,
|
||||
.value_type = REFTABLE_REF_DELETION,
|
||||
};
|
||||
int err;
|
||||
|
||||
reftable_writer_set_limits(w, 1, 1);
|
||||
err = reftable_writer_add_ref(w, &ref);
|
||||
EXPECT(err == REFTABLE_API_ERROR);
|
||||
|
||||
err = reftable_writer_close(w);
|
||||
EXPECT(err == REFTABLE_EMPTY_TABLE_ERROR);
|
||||
reftable_writer_free(w);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
static void test_write_key_order(void)
|
||||
{
|
||||
struct reftable_write_options opts = { 0 };
|
||||
@ -746,7 +842,10 @@ int readwrite_test_main(int argc, const char *argv[])
|
||||
RUN_TEST(test_table_read_write_seek_index);
|
||||
RUN_TEST(test_table_refs_for_no_index);
|
||||
RUN_TEST(test_table_refs_for_obj_index);
|
||||
RUN_TEST(test_write_empty_key);
|
||||
RUN_TEST(test_write_empty_table);
|
||||
RUN_TEST(test_log_overflow);
|
||||
RUN_TEST(test_write_object_id_length);
|
||||
RUN_TEST(test_write_object_id_min_length);
|
||||
return 0;
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ int reftable_writer_close(struct reftable_writer *w);
|
||||
|
||||
This struct becomes invalid when the writer is freed.
|
||||
*/
|
||||
const struct reftable_stats *writer_stats(struct reftable_writer *w);
|
||||
const struct reftable_stats *reftable_writer_stats(struct reftable_writer *w);
|
||||
|
||||
/* reftable_writer_free deallocates memory for the writer */
|
||||
void reftable_writer_free(struct reftable_writer *w);
|
||||
|
@ -240,14 +240,13 @@ static int writer_add_record(struct reftable_writer *w,
|
||||
|
||||
writer_reinit_block_writer(w, reftable_record_type(rec));
|
||||
err = block_writer_add(w->block_writer, rec);
|
||||
if (err < 0) {
|
||||
if (err == -1) {
|
||||
/* we are writing into memory, so an error can only mean it
|
||||
* doesn't fit. */
|
||||
err = REFTABLE_ENTRY_TOO_BIG_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
done:
|
||||
strbuf_release(&key);
|
||||
return err;
|
||||
@ -516,7 +515,9 @@ static void object_record_free(void *void_arg, void *key)
|
||||
static int writer_dump_object_index(struct reftable_writer *w)
|
||||
{
|
||||
struct write_record_arg closure = { .w = w };
|
||||
struct common_prefix_arg common = { NULL };
|
||||
struct common_prefix_arg common = {
|
||||
.max = 1, /* obj_id_len should be >= 2. */
|
||||
};
|
||||
if (w->obj_index_tree) {
|
||||
infix_walk(w->obj_index_tree, &update_common, &common);
|
||||
}
|
||||
@ -694,7 +695,7 @@ static int writer_flush_block(struct reftable_writer *w)
|
||||
return writer_flush_nonempty_block(w);
|
||||
}
|
||||
|
||||
const struct reftable_stats *writer_stats(struct reftable_writer *w)
|
||||
const struct reftable_stats *reftable_writer_stats(struct reftable_writer *w)
|
||||
{
|
||||
return &w->stats;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user