aboutsummaryrefslogtreecommitdiffstats
path: root/reftable
diff options
context:
space:
mode:
Diffstat (limited to 'reftable')
-rw-r--r--reftable/basics.c174
-rw-r--r--reftable/basics.h97
-rw-r--r--reftable/block.c115
-rw-r--r--reftable/block.h26
-rw-r--r--reftable/block_test.c123
-rw-r--r--reftable/blocksource.c77
-rw-r--r--reftable/blocksource.h7
-rw-r--r--reftable/dump.c111
-rw-r--r--reftable/error.c2
-rw-r--r--reftable/generic.c229
-rw-r--r--reftable/generic.h37
-rw-r--r--reftable/iter.c158
-rw-r--r--reftable/iter.h40
-rw-r--r--reftable/merged.c136
-rw-r--r--reftable/merged.h13
-rw-r--r--reftable/merged_test.c461
-rw-r--r--reftable/pq.c38
-rw-r--r--reftable/pq.h3
-rw-r--r--reftable/pq_test.c74
-rw-r--r--reftable/publicbasics.c66
-rw-r--r--reftable/reader.c321
-rw-r--r--reftable/reader.h15
-rw-r--r--reftable/readwrite_test.c979
-rw-r--r--reftable/record.c430
-rw-r--r--reftable/record.h28
-rw-r--r--reftable/reftable-basics.h31
-rw-r--r--reftable/reftable-error.h3
-rw-r--r--reftable/reftable-generic.h47
-rw-r--r--reftable/reftable-malloc.h18
-rw-r--r--reftable/reftable-merged.h30
-rw-r--r--reftable/reftable-reader.h38
-rw-r--r--reftable/reftable-record.h20
-rw-r--r--reftable/reftable-stack.h25
-rw-r--r--reftable/reftable-tests.h22
-rw-r--r--reftable/reftable-writer.h40
-rw-r--r--reftable/stack.c923
-rw-r--r--reftable/stack_test.c1034
-rw-r--r--reftable/system.c126
-rw-r--r--reftable/system.h90
-rw-r--r--reftable/test_framework.c27
-rw-r--r--reftable/test_framework.h61
-rw-r--r--reftable/tree.c57
-rw-r--r--reftable/tree.h21
-rw-r--r--reftable/tree_test.c60
-rw-r--r--reftable/writer.c265
-rw-r--r--reftable/writer.h4
46 files changed, 2219 insertions, 4483 deletions
diff --git a/reftable/basics.c b/reftable/basics.c
index 0058619ca6..7d84a5d62d 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -6,7 +6,142 @@ license that can be found in the LICENSE file or at
https://developers.google.com/open-source/licenses/bsd
*/
+#define REFTABLE_ALLOW_BANNED_ALLOCATORS
#include "basics.h"
+#include "reftable-basics.h"
+#include "reftable-error.h"
+
+static void *(*reftable_malloc_ptr)(size_t sz);
+static void *(*reftable_realloc_ptr)(void *, size_t);
+static void (*reftable_free_ptr)(void *);
+
+void *reftable_malloc(size_t sz)
+{
+ if (reftable_malloc_ptr)
+ return (*reftable_malloc_ptr)(sz);
+ return malloc(sz);
+}
+
+void *reftable_realloc(void *p, size_t sz)
+{
+ if (reftable_realloc_ptr)
+ return (*reftable_realloc_ptr)(p, sz);
+ return realloc(p, sz);
+}
+
+void reftable_free(void *p)
+{
+ if (reftable_free_ptr)
+ reftable_free_ptr(p);
+ else
+ free(p);
+}
+
+void *reftable_calloc(size_t nelem, size_t elsize)
+{
+ void *p;
+
+ if (nelem && elsize > SIZE_MAX / nelem)
+ return NULL;
+
+ p = reftable_malloc(nelem * elsize);
+ if (!p)
+ return NULL;
+
+ memset(p, 0, nelem * elsize);
+ return p;
+}
+
+char *reftable_strdup(const char *str)
+{
+ size_t len = strlen(str);
+ char *result = reftable_malloc(len + 1);
+ if (!result)
+ return NULL;
+ memcpy(result, str, len + 1);
+ return result;
+}
+
+void reftable_set_alloc(void *(*malloc)(size_t),
+ void *(*realloc)(void *, size_t), void (*free)(void *))
+{
+ reftable_malloc_ptr = malloc;
+ reftable_realloc_ptr = realloc;
+ reftable_free_ptr = free;
+}
+
+void reftable_buf_init(struct reftable_buf *buf)
+{
+ struct reftable_buf empty = REFTABLE_BUF_INIT;
+ *buf = empty;
+}
+
+void reftable_buf_release(struct reftable_buf *buf)
+{
+ reftable_free(buf->buf);
+ reftable_buf_init(buf);
+}
+
+void reftable_buf_reset(struct reftable_buf *buf)
+{
+ if (buf->alloc) {
+ buf->len = 0;
+ buf->buf[0] = '\0';
+ }
+}
+
+int reftable_buf_setlen(struct reftable_buf *buf, size_t len)
+{
+ if (len > buf->len)
+ return -1;
+ if (len == buf->len)
+ return 0;
+ buf->buf[len] = '\0';
+ buf->len = len;
+ return 0;
+}
+
+int reftable_buf_cmp(const struct reftable_buf *a, const struct reftable_buf *b)
+{
+ size_t len = a->len < b->len ? a->len : b->len;
+ if (len) {
+ int cmp = memcmp(a->buf, b->buf, len);
+ if (cmp)
+ return cmp;
+ }
+ return a->len < b->len ? -1 : a->len != b->len;
+}
+
+int reftable_buf_add(struct reftable_buf *buf, const void *data, size_t len)
+{
+ size_t newlen = buf->len + len;
+
+ if (newlen + 1 > buf->alloc) {
+ char *reallocated = buf->buf;
+ REFTABLE_ALLOC_GROW(reallocated, newlen + 1, buf->alloc);
+ if (!reallocated)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
+ buf->buf = reallocated;
+ }
+
+ memcpy(buf->buf + buf->len, data, len);
+ buf->buf[newlen] = '\0';
+ buf->len = newlen;
+
+ return 0;
+}
+
+int reftable_buf_addstr(struct reftable_buf *buf, const char *s)
+{
+ return reftable_buf_add(buf, s, strlen(s));
+}
+
+char *reftable_buf_detach(struct reftable_buf *buf)
+{
+ char *result = buf->buf;
+ reftable_buf_init(buf);
+ return result;
+}
void put_be24(uint8_t *out, uint32_t i)
{
@@ -75,14 +210,14 @@ size_t names_length(const char **names)
return p - names;
}
-void parse_names(char *buf, int size, char ***namesp)
+char **parse_names(char *buf, int size)
{
char **names = NULL;
size_t names_cap = 0;
size_t names_len = 0;
-
char *p = buf;
char *end = buf + size;
+
while (p < end) {
char *next = strchr(p, '\n');
if (next && next < end) {
@@ -91,15 +226,29 @@ void parse_names(char *buf, int size, char ***namesp)
next = end;
}
if (p < next) {
- REFTABLE_ALLOC_GROW(names, names_len + 1, names_cap);
- names[names_len++] = xstrdup(p);
+ char **names_grown = names;
+ REFTABLE_ALLOC_GROW(names_grown, names_len + 1, names_cap);
+ if (!names_grown)
+ goto err;
+ names = names_grown;
+
+ names[names_len] = reftable_strdup(p);
+ if (!names[names_len++])
+ goto err;
}
p = next + 1;
}
REFTABLE_REALLOC_ARRAY(names, names_len + 1);
names[names_len] = NULL;
- *namesp = names;
+
+ return names;
+
+err:
+ for (size_t i = 0; i < names_len; i++)
+ reftable_free(names[i]);
+ reftable_free(names);
+ return NULL;
}
int names_equal(const char **a, const char **b)
@@ -111,7 +260,7 @@ int names_equal(const char **a, const char **b)
return a[i] == b[i];
}
-int common_prefix_size(struct strbuf *a, struct strbuf *b)
+int common_prefix_size(struct reftable_buf *a, struct reftable_buf *b)
{
int p = 0;
for (; p < a->len && p < b->len; p++) {
@@ -121,3 +270,16 @@ int common_prefix_size(struct strbuf *a, struct strbuf *b)
return p;
}
+
+int hash_size(enum reftable_hash id)
+{
+ if (!id)
+ return REFTABLE_HASH_SIZE_SHA1;
+ switch (id) {
+ case REFTABLE_HASH_SHA1:
+ return REFTABLE_HASH_SIZE_SHA1;
+ case REFTABLE_HASH_SHA256:
+ return REFTABLE_HASH_SIZE_SHA256;
+ }
+ abort();
+}
diff --git a/reftable/basics.h b/reftable/basics.h
index c8fec68d4e..36beda2c25 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -14,6 +14,65 @@ https://developers.google.com/open-source/licenses/bsd
*/
#include "system.h"
+#include "reftable-basics.h"
+
+struct reftable_buf {
+ size_t alloc;
+ size_t len;
+ char *buf;
+};
+#define REFTABLE_BUF_INIT { 0 }
+
+/*
+ * Initialize the buffer such that it is ready for use. This is equivalent to
+ * using REFTABLE_BUF_INIT for stack-allocated variables.
+ */
+void reftable_buf_init(struct reftable_buf *buf);
+
+/*
+ * Release memory associated with the buffer. The buffer is reinitialized such
+ * that it can be reused for subsequent operations.
+ */
+void reftable_buf_release(struct reftable_buf *buf);
+
+/*
+ * Reset the buffer such that it is effectively empty, without releasing the
+ * memory that this structure holds on to. This is equivalent to calling
+ * `reftable_buf_setlen(buf, 0)`.
+ */
+void reftable_buf_reset(struct reftable_buf *buf);
+
+/*
+ * Trim the buffer to a shorter length by updating the `len` member and writing
+ * a NUL byte to `buf[len]`. Returns 0 on success, -1 when `len` points outside
+ * of the array.
+ */
+int reftable_buf_setlen(struct reftable_buf *buf, size_t len);
+
+/*
+ * Lexicographically compare the two buffers. Returns 0 when both buffers have
+ * the same contents, -1 when `a` is lexicographically smaller than `b`, and 1
+ * otherwise.
+ */
+int reftable_buf_cmp(const struct reftable_buf *a, const struct reftable_buf *b);
+
+/*
+ * Append `len` bytes from `data` to the buffer. This function works with
+ * arbitrary byte sequences, including ones that contain embedded NUL
+ * characters. As such, we use `void *` as input type. Returns 0 on success,
+ * REFTABLE_OUT_OF_MEMORY_ERROR on allocation failure.
+ */
+int reftable_buf_add(struct reftable_buf *buf, const void *data, size_t len);
+
+/* Equivalent to `reftable_buf_add(buf, s, strlen(s))`. */
+int reftable_buf_addstr(struct reftable_buf *buf, const char *s);
+
+/*
+ * Detach the buffer from the structure such that the underlying memory is now
+ * owned by the caller. The buffer is reinitialized such that it can be reused
+ * for subsequent operations.
+ */
+char *reftable_buf_detach(struct reftable_buf *buf);
/* Bigendian en/decoding of integers */
@@ -37,9 +96,12 @@ size_t binsearch(size_t sz, int (*f)(size_t k, void *args), void *args);
*/
void free_names(char **a);
-/* parse a newline separated list of names. `size` is the length of the buffer,
- * without terminating '\0'. Empty names are discarded. */
-void parse_names(char *buf, int size, char ***namesp);
+/*
+ * Parse a newline separated list of names. `size` is the length of the buffer,
+ * without terminating '\0'. Empty names are discarded. Returns a `NULL`
+ * pointer when allocations fail.
+ */
+char **parse_names(char *buf, int size);
/* compares two NULL-terminated arrays of strings. */
int names_equal(const char **a, const char **b);
@@ -53,6 +115,7 @@ void *reftable_malloc(size_t sz);
void *reftable_realloc(void *p, size_t sz);
void reftable_free(void *p);
void *reftable_calloc(size_t nelem, size_t elsize);
+char *reftable_strdup(const char *str);
#define REFTABLE_ALLOC_ARRAY(x, alloc) (x) = reftable_malloc(st_mult(sizeof(*(x)), (alloc)))
#define REFTABLE_CALLOC_ARRAY(x, alloc) (x) = reftable_calloc((alloc), sizeof(*(x)))
@@ -66,9 +129,33 @@ void *reftable_calloc(size_t nelem, size_t elsize);
REFTABLE_REALLOC_ARRAY(x, alloc); \
} \
} while (0)
+#define REFTABLE_FREE_AND_NULL(p) do { reftable_free(p); (p) = NULL; } while (0)
+
+#ifndef REFTABLE_ALLOW_BANNED_ALLOCATORS
+# define REFTABLE_BANNED(func) use_reftable_##func##_instead
+# undef malloc
+# define malloc(sz) REFTABLE_BANNED(malloc)
+# undef realloc
+# define realloc(ptr, sz) REFTABLE_BANNED(realloc)
+# undef free
+# define free(ptr) REFTABLE_BANNED(free)
+# undef calloc
+# define calloc(nelem, elsize) REFTABLE_BANNED(calloc)
+# undef strdup
+# define strdup(str) REFTABLE_BANNED(strdup)
+#endif
/* Find the longest shared prefix size of `a` and `b` */
-struct strbuf;
-int common_prefix_size(struct strbuf *a, struct strbuf *b);
+int common_prefix_size(struct reftable_buf *a, struct reftable_buf *b);
+
+int hash_size(enum reftable_hash id);
+
+/*
+ * Format IDs that identify the hash function used by a reftable. Note that
+ * these constants end up on disk and thus mustn't change. The format IDs are
+ * "sha1" and "s256" in big endian, respectively.
+ */
+#define REFTABLE_FORMAT_ID_SHA1 ((uint32_t) 0x73686131)
+#define REFTABLE_FORMAT_ID_SHA256 ((uint32_t) 0x73323536)
#endif
diff --git a/reftable/block.c b/reftable/block.c
index 00030eee06..0198078485 100644
--- a/reftable/block.c
+++ b/reftable/block.c
@@ -38,9 +38,11 @@ int footer_size(int version)
}
static int block_writer_register_restart(struct block_writer *w, int n,
- int is_restart, struct strbuf *key)
+ int is_restart, struct reftable_buf *key)
{
- int rlen = w->restart_len;
+ int rlen, err;
+
+ rlen = w->restart_len;
if (rlen >= MAX_RESTARTS) {
is_restart = 0;
}
@@ -52,25 +54,30 @@ static int block_writer_register_restart(struct block_writer *w, int n,
return -1;
if (is_restart) {
REFTABLE_ALLOC_GROW(w->restarts, w->restart_len + 1, w->restart_cap);
+ if (!w->restarts)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
w->restarts[w->restart_len++] = w->next;
}
w->next += n;
- strbuf_reset(&w->last_key);
- strbuf_addbuf(&w->last_key, key);
+ reftable_buf_reset(&w->last_key);
+ err = reftable_buf_add(&w->last_key, key->buf, key->len);
+ if (err < 0)
+ return err;
+
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)
+int block_writer_init(struct block_writer *bw, uint8_t typ, uint8_t *block,
+ uint32_t block_size, uint32_t header_off, int hash_size)
{
- bw->buf = buf;
+ bw->block = block;
bw->hash_size = hash_size;
bw->block_size = block_size;
bw->header_off = header_off;
- bw->buf[header_off] = typ;
+ bw->block[header_off] = typ;
bw->next = header_off + 4;
bw->restart_interval = 16;
bw->entries = 0;
@@ -78,13 +85,17 @@ void block_writer_init(struct block_writer *bw, uint8_t typ, uint8_t *buf,
bw->last_key.len = 0;
if (!bw->zstream) {
REFTABLE_CALLOC_ARRAY(bw->zstream, 1);
+ if (!bw->zstream)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
deflateInit(bw->zstream, 9);
}
+
+ return 0;
}
uint8_t block_writer_type(struct block_writer *bw)
{
- return bw->buf[bw->header_off];
+ return bw->block[bw->header_off];
}
/* Adds the reftable_record to the block. Returns -1 if it does not fit, 0 on
@@ -92,42 +103,45 @@ uint8_t block_writer_type(struct block_writer *bw)
empty key. */
int block_writer_add(struct block_writer *w, struct reftable_record *rec)
{
- struct strbuf empty = STRBUF_INIT;
- struct strbuf last =
+ struct reftable_buf empty = REFTABLE_BUF_INIT;
+ struct reftable_buf last =
w->entries % w->restart_interval == 0 ? empty : w->last_key;
struct string_view out = {
- .buf = w->buf + w->next,
+ .buf = w->block + w->next,
.len = w->block_size - w->next,
};
-
struct string_view start = out;
-
int is_restart = 0;
- struct strbuf key = STRBUF_INIT;
int n = 0;
- int err = -1;
+ int err;
+
+ err = reftable_record_key(rec, &w->scratch);
+ if (err < 0)
+ goto done;
- reftable_record_key(rec, &key);
- if (!key.len) {
+ if (!w->scratch.len) {
err = REFTABLE_API_ERROR;
goto done;
}
- n = reftable_encode_key(&is_restart, out, last, key,
+ n = reftable_encode_key(&is_restart, out, last, w->scratch,
reftable_record_val_type(rec));
- if (n < 0)
+ if (n < 0) {
+ err = -1;
goto done;
+ }
string_view_consume(&out, n);
n = reftable_record_encode(rec, out, w->hash_size);
- if (n < 0)
+ if (n < 0) {
+ err = -1;
goto done;
+ }
string_view_consume(&out, n);
err = block_writer_register_restart(w, start.len - out.len, is_restart,
- &key);
+ &w->scratch);
done:
- strbuf_release(&key);
return err;
}
@@ -135,13 +149,13 @@ int block_writer_finish(struct block_writer *w)
{
int i;
for (i = 0; i < w->restart_len; i++) {
- put_be24(w->buf + w->next, w->restarts[i]);
+ put_be24(w->block + w->next, w->restarts[i]);
w->next += 3;
}
- put_be16(w->buf + w->next, w->restart_len);
+ put_be16(w->block + w->next, w->restart_len);
w->next += 2;
- put_be24(w->buf + 1 + w->header_off, w->next);
+ put_be24(w->block + 1 + w->header_off, w->next);
/*
* Log records are stored zlib-compressed. Note that the compression
@@ -163,10 +177,14 @@ int block_writer_finish(struct block_writer *w)
*/
compressed_len = deflateBound(w->zstream, src_len);
REFTABLE_ALLOC_GROW(w->compressed, compressed_len, w->compressed_cap);
+ if (!w->compressed) {
+ ret = REFTABLE_OUT_OF_MEMORY_ERROR;
+ return ret;
+ }
w->zstream->next_out = w->compressed;
w->zstream->avail_out = compressed_len;
- w->zstream->next_in = w->buf + block_header_skip;
+ w->zstream->next_in = w->block + block_header_skip;
w->zstream->avail_in = src_len;
/*
@@ -184,7 +202,7 @@ int block_writer_finish(struct block_writer *w)
* adjust the `next` pointer to point right after the
* compressed data.
*/
- memcpy(w->buf + block_header_skip, w->compressed,
+ memcpy(w->block + block_header_skip, w->compressed,
w->zstream->total_out);
w->next = w->zstream->total_out + block_header_skip;
}
@@ -219,12 +237,21 @@ int block_reader_init(struct block_reader *br, struct reftable_block *block,
/* Log blocks specify the *uncompressed* size in their header. */
REFTABLE_ALLOC_GROW(br->uncompressed_data, sz,
br->uncompressed_cap);
+ if (!br->uncompressed_data) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto done;
+ }
/* Copy over the block header verbatim. It's not compressed. */
memcpy(br->uncompressed_data, block->data, block_header_skip);
if (!br->zstream) {
REFTABLE_CALLOC_ARRAY(br->zstream, 1);
+ if (!br->zstream) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto done;
+ }
+
err = inflateInit(br->zstream);
} else {
err = inflateReset(br->zstream);
@@ -306,7 +333,7 @@ uint8_t block_reader_type(const struct block_reader *r)
return r->block.data[r->header_off];
}
-int block_reader_first_key(const struct block_reader *br, struct strbuf *key)
+int block_reader_first_key(const struct block_reader *br, struct reftable_buf *key)
{
int off = br->header_off + 4, n;
struct string_view in = {
@@ -315,7 +342,7 @@ int block_reader_first_key(const struct block_reader *br, struct strbuf *key)
};
uint8_t extra = 0;
- strbuf_reset(key);
+ reftable_buf_reset(key);
n = reftable_decode_key(key, &extra, in);
if (n < 0)
@@ -336,13 +363,13 @@ void block_iter_seek_start(struct block_iter *it, const struct block_reader *br)
it->block = br->block.data;
it->block_len = br->block_len;
it->hash_size = br->hash_size;
- strbuf_reset(&it->last_key);
+ reftable_buf_reset(&it->last_key);
it->next_off = br->header_off + 4;
}
struct restart_needle_less_args {
int error;
- struct strbuf needle;
+ struct reftable_buf needle;
const struct block_reader *reader;
};
@@ -414,7 +441,7 @@ int block_iter_next(struct block_iter *it, struct reftable_record *rec)
void block_iter_reset(struct block_iter *it)
{
- strbuf_reset(&it->last_key);
+ reftable_buf_reset(&it->last_key);
it->next_off = 0;
it->block = NULL;
it->block_len = 0;
@@ -423,12 +450,12 @@ void block_iter_reset(struct block_iter *it)
void block_iter_close(struct block_iter *it)
{
- strbuf_release(&it->last_key);
- strbuf_release(&it->scratch);
+ reftable_buf_release(&it->last_key);
+ reftable_buf_release(&it->scratch);
}
int block_iter_seek_key(struct block_iter *it, const struct block_reader *br,
- struct strbuf *want)
+ struct reftable_buf *want)
{
struct restart_needle_less_args args = {
.needle = *want,
@@ -503,6 +530,10 @@ int block_iter_seek_key(struct block_iter *it, const struct block_reader *br,
goto done;
}
+ err = reftable_record_key(&rec, &it->last_key);
+ if (err < 0)
+ goto done;
+
/*
* Check whether the current key is greater or equal to the
* sought-after key. In case it is greater we know that the
@@ -517,8 +548,7 @@ int block_iter_seek_key(struct block_iter *it, const struct block_reader *br,
* to `last_key` now, and naturally all keys share a prefix
* with themselves.
*/
- reftable_record_key(&rec, &it->last_key);
- if (strbuf_cmp(&it->last_key, want) >= 0) {
+ if (reftable_buf_cmp(&it->last_key, want) >= 0) {
it->next_off = prev_off;
goto done;
}
@@ -532,10 +562,11 @@ done:
void block_writer_release(struct block_writer *bw)
{
deflateEnd(bw->zstream);
- FREE_AND_NULL(bw->zstream);
- FREE_AND_NULL(bw->restarts);
- FREE_AND_NULL(bw->compressed);
- strbuf_release(&bw->last_key);
+ REFTABLE_FREE_AND_NULL(bw->zstream);
+ REFTABLE_FREE_AND_NULL(bw->restarts);
+ REFTABLE_FREE_AND_NULL(bw->compressed);
+ reftable_buf_release(&bw->scratch);
+ reftable_buf_release(&bw->last_key);
/* the block is not owned. */
}
diff --git a/reftable/block.h b/reftable/block.h
index 1c8f25ee6e..0431e8591f 100644
--- a/reftable/block.h
+++ b/reftable/block.h
@@ -22,7 +22,7 @@ struct block_writer {
unsigned char *compressed;
size_t compressed_cap;
- uint8_t *buf;
+ uint8_t *block;
uint32_t block_size;
/* Offset of the global header. Nonzero in the first block only. */
@@ -38,15 +38,17 @@ struct block_writer {
uint32_t restart_len;
uint32_t restart_cap;
- struct strbuf last_key;
+ struct reftable_buf last_key;
+ /* Scratch buffer used to avoid allocations. */
+ struct reftable_buf scratch;
int entries;
};
/*
- * initializes the blockwriter to write `typ` entries, using `buf` as temporary
- * storage. `buf` is not owned by the block_writer. */
-void block_writer_init(struct block_writer *bw, uint8_t typ, uint8_t *buf,
- uint32_t block_size, uint32_t header_off, int hash_size);
+ * initializes the blockwriter to write `typ` entries, using `block` as temporary
+ * storage. `block` is not owned by the block_writer. */
+int block_writer_init(struct block_writer *bw, uint8_t typ, uint8_t *block,
+ uint32_t block_size, uint32_t header_off, int hash_size);
/* returns the block type (eg. 'r' for ref records. */
uint8_t block_writer_type(struct block_writer *bw);
@@ -98,7 +100,7 @@ void block_reader_release(struct block_reader *br);
uint8_t block_reader_type(const struct block_reader *r);
/* Decodes the first key in the block */
-int block_reader_first_key(const struct block_reader *br, struct strbuf *key);
+int block_reader_first_key(const struct block_reader *br, struct reftable_buf *key);
/* Iterate over entries in a block */
struct block_iter {
@@ -109,13 +111,13 @@ struct block_iter {
int hash_size;
/* key for last entry we read. */
- struct strbuf last_key;
- struct strbuf scratch;
+ struct reftable_buf last_key;
+ struct reftable_buf scratch;
};
#define BLOCK_ITER_INIT { \
- .last_key = STRBUF_INIT, \
- .scratch = STRBUF_INIT, \
+ .last_key = REFTABLE_BUF_INIT, \
+ .scratch = REFTABLE_BUF_INIT, \
}
/* Position `it` at start of the block */
@@ -123,7 +125,7 @@ void block_iter_seek_start(struct block_iter *it, const struct block_reader *br)
/* Position `it` to the `want` key in the block */
int block_iter_seek_key(struct block_iter *it, const struct block_reader *br,
- struct strbuf *want);
+ struct reftable_buf *want);
/* return < 0 for error, 0 for OK, > 0 for EOF. */
int block_iter_next(struct block_iter *it, struct reftable_record *rec);
diff --git a/reftable/block_test.c b/reftable/block_test.c
deleted file mode 100644
index 90aecd5a7c..0000000000
--- a/reftable/block_test.c
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
-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 "block.h"
-
-#include "system.h"
-#include "blocksource.h"
-#include "basics.h"
-#include "constants.h"
-#include "record.h"
-#include "test_framework.h"
-#include "reftable-tests.h"
-
-static void test_block_read_write(void)
-{
- const int header_off = 21; /* random */
- char *names[30];
- const int N = ARRAY_SIZE(names);
- const int block_size = 1024;
- struct reftable_block block = { NULL };
- struct block_writer bw = {
- .last_key = STRBUF_INIT,
- };
- struct reftable_record rec = {
- .type = BLOCK_TYPE_REF,
- };
- int i = 0;
- int n;
- struct block_reader br = { 0 };
- struct block_iter it = BLOCK_ITER_INIT;
- int j = 0;
- struct strbuf want = STRBUF_INIT;
-
- REFTABLE_CALLOC_ARRAY(block.data, block_size);
- block.len = block_size;
- block.source = malloc_block_source();
- block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
- header_off, hash_size(GIT_SHA1_FORMAT_ID));
-
- rec.u.ref.refname = (char *) "";
- 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];
- snprintf(name, sizeof(name), "branch%02d", i);
-
- rec.u.ref.refname = name;
- rec.u.ref.value_type = REFTABLE_REF_VAL1;
- memset(rec.u.ref.value.val1, i, GIT_SHA1_RAWSZ);
-
- names[i] = xstrdup(name);
- n = block_writer_add(&bw, &rec);
- rec.u.ref.refname = NULL;
- rec.u.ref.value_type = REFTABLE_REF_DELETION;
- EXPECT(n == 0);
- }
-
- n = block_writer_finish(&bw);
- EXPECT(n > 0);
-
- block_writer_release(&bw);
-
- block_reader_init(&br, &block, header_off, block_size, GIT_SHA1_RAWSZ);
-
- block_iter_seek_start(&it, &br);
-
- while (1) {
- int r = block_iter_next(&it, &rec);
- EXPECT(r >= 0);
- if (r > 0) {
- break;
- }
- EXPECT_STREQ(names[j], rec.u.ref.refname);
- j++;
- }
-
- reftable_record_release(&rec);
- block_iter_close(&it);
-
- for (i = 0; i < N; i++) {
- struct block_iter it = BLOCK_ITER_INIT;
- strbuf_reset(&want);
- strbuf_addstr(&want, names[i]);
-
- n = block_iter_seek_key(&it, &br, &want);
- EXPECT(n == 0);
-
- n = block_iter_next(&it, &rec);
- EXPECT(n == 0);
-
- EXPECT_STREQ(names[i], rec.u.ref.refname);
-
- want.len--;
- n = block_iter_seek_key(&it, &br, &want);
- EXPECT(n == 0);
-
- n = block_iter_next(&it, &rec);
- EXPECT(n == 0);
- EXPECT_STREQ(names[10 * (i / 10)], rec.u.ref.refname);
-
- block_iter_close(&it);
- }
-
- reftable_record_release(&rec);
- reftable_block_done(&br.block);
- strbuf_release(&want);
- for (i = 0; i < N; i++) {
- reftable_free(names[i]);
- }
-}
-
-int block_test_main(int argc, const char *argv[])
-{
- RUN_TEST(test_block_read_write);
- return 0;
-}
diff --git a/reftable/blocksource.c b/reftable/blocksource.c
index eeed254ba9..52e0915a67 100644
--- a/reftable/blocksource.c
+++ b/reftable/blocksource.c
@@ -13,68 +13,50 @@ https://developers.google.com/open-source/licenses/bsd
#include "reftable-blocksource.h"
#include "reftable-error.h"
-static void strbuf_return_block(void *b, struct reftable_block *dest)
+static void reftable_buf_return_block(void *b UNUSED, struct reftable_block *dest)
{
if (dest->len)
memset(dest->data, 0xff, dest->len);
reftable_free(dest->data);
}
-static void strbuf_close(void *b)
+static void reftable_buf_close(void *b UNUSED)
{
}
-static int strbuf_read_block(void *v, struct reftable_block *dest, uint64_t off,
- uint32_t size)
+static int reftable_buf_read_block(void *v, struct reftable_block *dest,
+ uint64_t off, uint32_t size)
{
- struct strbuf *b = v;
+ struct reftable_buf *b = v;
assert(off + size <= b->len);
REFTABLE_CALLOC_ARRAY(dest->data, size);
+ if (!dest->data)
+ return -1;
memcpy(dest->data, b->buf + off, size);
dest->len = size;
return size;
}
-static uint64_t strbuf_size(void *b)
+static uint64_t reftable_buf_size(void *b)
{
- return ((struct strbuf *)b)->len;
+ return ((struct reftable_buf *)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,
+static struct reftable_block_source_vtable reftable_buf_vtable = {
+ .size = &reftable_buf_size,
+ .read_block = &reftable_buf_read_block,
+ .return_block = &reftable_buf_return_block,
+ .close = &reftable_buf_close,
};
-void block_source_from_strbuf(struct reftable_block_source *bs,
- struct strbuf *buf)
+void block_source_from_buf(struct reftable_block_source *bs,
+ struct reftable_buf *buf)
{
assert(!bs->ops);
- bs->ops = &strbuf_vtable;
+ bs->ops = &reftable_buf_vtable;
bs->arg = buf;
}
-static void malloc_return_block(void *b, struct reftable_block *dest)
-{
- if (dest->len)
- 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;
-}
-
struct file_block_source {
uint64_t size;
unsigned char *data;
@@ -85,7 +67,7 @@ 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)
+static void file_return_block(void *b UNUSED, struct reftable_block *dest UNUSED)
{
}
@@ -118,27 +100,40 @@ int reftable_block_source_from_file(struct reftable_block_source *bs,
{
struct file_block_source *p;
struct stat st;
- int fd;
+ int fd, err;
fd = open(name, O_RDONLY);
if (fd < 0) {
if (errno == ENOENT)
return REFTABLE_NOT_EXIST_ERROR;
- return -1;
+ err = -1;
+ goto out;
}
if (fstat(fd, &st) < 0) {
- close(fd);
- return REFTABLE_IO_ERROR;
+ err = REFTABLE_IO_ERROR;
+ goto out;
}
REFTABLE_CALLOC_ARRAY(p, 1);
+ if (!p) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
+
p->size = st.st_size;
p->data = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
- close(fd);
assert(!bs->ops);
bs->ops = &file_vtable;
bs->arg = p;
+
+ err = 0;
+
+out:
+ if (fd >= 0)
+ close(fd);
+ if (err < 0)
+ reftable_free(p);
return 0;
}
diff --git a/reftable/blocksource.h b/reftable/blocksource.h
index 072e2727ad..a84a3ccd89 100644
--- a/reftable/blocksource.h
+++ b/reftable/blocksource.h
@@ -12,11 +12,10 @@ https://developers.google.com/open-source/licenses/bsd
#include "system.h"
struct reftable_block_source;
+struct reftable_buf;
/* 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);
+void block_source_from_buf(struct reftable_block_source *bs,
+ struct reftable_buf *buf);
#endif
diff --git a/reftable/dump.c b/reftable/dump.c
deleted file mode 100644
index dd65d9e8bb..0000000000
--- a/reftable/dump.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
-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 "git-compat-util.h"
-#include "hash.h"
-
-#include "reftable-blocksource.h"
-#include "reftable-error.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"
-
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-
-static int compact_stack(const char *stackdir)
-{
- struct reftable_stack *stack = NULL;
- struct reftable_write_options opts = { 0 };
-
- int err = reftable_new_stack(&stack, stackdir, &opts);
- if (err < 0)
- goto done;
-
- err = reftable_stack_compact_all(stack, NULL);
- if (err < 0)
- goto done;
-done:
- if (stack) {
- reftable_stack_destroy(stack);
- }
- return err;
-}
-
-static void print_help(void)
-{
- printf("usage: dump [-cst] arg\n\n"
- "options: \n"
- " -c compact\n"
- " -b dump blocks\n"
- " -t dump table\n"
- " -s dump stack\n"
- " -6 sha256 hash format\n"
- " -h this help\n"
- "\n");
-}
-
-int reftable_dump_main(int argc, char *const *argv)
-{
- int err = 0;
- int opt_dump_blocks = 0;
- int opt_dump_table = 0;
- int opt_dump_stack = 0;
- int opt_compact = 0;
- uint32_t opt_hash_id = GIT_SHA1_FORMAT_ID;
- const char *arg = NULL, *argv0 = argv[0];
-
- for (; argc > 1; argv++, argc--)
- if (*argv[1] != '-')
- break;
- else if (!strcmp("-b", argv[1]))
- opt_dump_blocks = 1;
- else if (!strcmp("-t", argv[1]))
- opt_dump_table = 1;
- else if (!strcmp("-6", argv[1]))
- opt_hash_id = GIT_SHA256_FORMAT_ID;
- else if (!strcmp("-s", argv[1]))
- opt_dump_stack = 1;
- else if (!strcmp("-c", argv[1]))
- opt_compact = 1;
- else if (!strcmp("-?", argv[1]) || !strcmp("-h", argv[1])) {
- print_help();
- return 2;
- }
-
- if (argc != 2) {
- fprintf(stderr, "need argument\n");
- print_help();
- return 2;
- }
-
- arg = argv[1];
-
- if (opt_dump_blocks) {
- err = reftable_reader_print_blocks(arg);
- } else if (opt_dump_table) {
- err = reftable_reader_print_file(arg);
- } else if (opt_dump_stack) {
- err = reftable_stack_print_directory(arg, opt_hash_id);
- } else if (opt_compact) {
- err = compact_stack(arg);
- }
-
- if (err < 0) {
- fprintf(stderr, "%s: %s: %s\n", argv0, arg,
- reftable_error_str(err));
- return 1;
- }
- return 0;
-}
diff --git a/reftable/error.c b/reftable/error.c
index a25f28a43e..660d029617 100644
--- a/reftable/error.c
+++ b/reftable/error.c
@@ -35,6 +35,8 @@ const char *reftable_error_str(int err)
return "entry too large";
case REFTABLE_OUTDATED_ERROR:
return "data concurrently modified";
+ case REFTABLE_OUT_OF_MEMORY_ERROR:
+ return "out of memory";
case -1:
return "general error";
default:
diff --git a/reftable/generic.c b/reftable/generic.c
deleted file mode 100644
index 28ae26145e..0000000000
--- a/reftable/generic.c
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
-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 "constants.h"
-#include "record.h"
-#include "generic.h"
-#include "reftable-iterator.h"
-#include "reftable-generic.h"
-
-void table_init_iter(struct reftable_table *tab,
- struct reftable_iterator *it,
- uint8_t typ)
-{
-
- tab->ops->init_iter(tab->table_arg, it, typ);
-}
-
-void reftable_table_init_ref_iter(struct reftable_table *tab,
- struct reftable_iterator *it)
-{
- table_init_iter(tab, it, BLOCK_TYPE_REF);
-}
-
-void reftable_table_init_log_iter(struct reftable_table *tab,
- struct reftable_iterator *it)
-{
- table_init_iter(tab, it, BLOCK_TYPE_LOG);
-}
-
-int reftable_iterator_seek_ref(struct reftable_iterator *it,
- const char *name)
-{
- struct reftable_record want = {
- .type = BLOCK_TYPE_REF,
- .u.ref = {
- .refname = (char *)name,
- },
- };
- return it->ops->seek(it->iter_arg, &want);
-}
-
-int reftable_iterator_seek_log_at(struct reftable_iterator *it,
- const char *name, uint64_t update_index)
-{
- struct reftable_record want = {
- .type = BLOCK_TYPE_LOG,
- .u.log = {
- .refname = (char *)name,
- .update_index = update_index,
- },
- };
- return it->ops->seek(it->iter_arg, &want);
-}
-
-int reftable_iterator_seek_log(struct reftable_iterator *it,
- const char *name)
-{
- return reftable_iterator_seek_log_at(it, name, ~((uint64_t) 0));
-}
-
-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_init_ref_iter(tab, &it);
-
- err = reftable_iterator_seek_ref(&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;
-}
-
-int reftable_table_print(struct reftable_table *tab) {
- struct reftable_iterator it = { NULL };
- struct reftable_ref_record ref = { NULL };
- struct reftable_log_record log = { NULL };
- uint32_t hash_id = reftable_table_hash_id(tab);
- int err;
-
- reftable_table_init_ref_iter(tab, &it);
-
- err = reftable_iterator_seek_ref(&it, "");
- if (err < 0)
- return err;
-
- while (1) {
- err = reftable_iterator_next_ref(&it, &ref);
- if (err > 0) {
- break;
- }
- if (err < 0) {
- return err;
- }
- reftable_ref_record_print(&ref, hash_id);
- }
- reftable_iterator_destroy(&it);
- reftable_ref_record_release(&ref);
-
- reftable_table_init_log_iter(tab, &it);
-
- err = reftable_iterator_seek_log(&it, "");
- if (err < 0)
- return err;
-
- while (1) {
- err = reftable_iterator_next_log(&it, &log);
- if (err > 0) {
- break;
- }
- if (err < 0) {
- return err;
- }
- reftable_log_record_print(&log, hash_id);
- }
- reftable_iterator_destroy(&it);
- reftable_log_record_release(&log);
- return 0;
-}
-
-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);
-}
-
-void reftable_iterator_destroy(struct reftable_iterator *it)
-{
- if (!it->ops) {
- return;
- }
- it->ops->close(it->iter_arg);
- it->ops = NULL;
- FREE_AND_NULL(it->iter_arg);
-}
-
-int reftable_iterator_next_ref(struct reftable_iterator *it,
- struct reftable_ref_record *ref)
-{
- struct reftable_record rec = {
- .type = BLOCK_TYPE_REF,
- .u = {
- .ref = *ref
- },
- };
- int err = iterator_next(it, &rec);
- *ref = rec.u.ref;
- return err;
-}
-
-int reftable_iterator_next_log(struct reftable_iterator *it,
- struct reftable_log_record *log)
-{
- struct reftable_record rec = {
- .type = BLOCK_TYPE_LOG,
- .u = {
- .log = *log,
- },
- };
- int err = iterator_next(it, &rec);
- *log = rec.u.log;
- return err;
-}
-
-int iterator_seek(struct reftable_iterator *it, struct reftable_record *want)
-{
- return it->ops->seek(it->iter_arg, want);
-}
-
-int iterator_next(struct reftable_iterator *it, struct reftable_record *rec)
-{
- return it->ops->next(it->iter_arg, rec);
-}
-
-static int empty_iterator_seek(void *arg, struct reftable_record *want)
-{
- return 0;
-}
-
-static int empty_iterator_next(void *arg, struct reftable_record *rec)
-{
- return 1;
-}
-
-static void empty_iterator_close(void *arg)
-{
-}
-
-static struct reftable_iterator_vtable empty_vtable = {
- .seek = &empty_iterator_seek,
- .next = &empty_iterator_next,
- .close = &empty_iterator_close,
-};
-
-void iterator_set_empty(struct reftable_iterator *it)
-{
- assert(!it->ops);
- it->iter_arg = NULL;
- it->ops = &empty_vtable;
-}
diff --git a/reftable/generic.h b/reftable/generic.h
deleted file mode 100644
index 8341fa570e..0000000000
--- a/reftable/generic.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
-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 GENERIC_H
-#define GENERIC_H
-
-#include "record.h"
-#include "reftable-generic.h"
-
-/* generic interface to reftables */
-struct reftable_table_vtable {
- void (*init_iter)(void *tab, struct reftable_iterator *it, uint8_t typ);
- uint32_t (*hash_id)(void *tab);
- uint64_t (*min_update_index)(void *tab);
- uint64_t (*max_update_index)(void *tab);
-};
-
-void table_init_iter(struct reftable_table *tab,
- struct reftable_iterator *it,
- uint8_t typ);
-
-struct reftable_iterator_vtable {
- int (*seek)(void *iter_arg, struct reftable_record *want);
- int (*next)(void *iter_arg, struct reftable_record *rec);
- void (*close)(void *iter_arg);
-};
-
-void iterator_set_empty(struct reftable_iterator *it);
-int iterator_seek(struct reftable_iterator *it, struct reftable_record *want);
-int iterator_next(struct reftable_iterator *it, struct reftable_record *rec);
-
-#endif
diff --git a/reftable/iter.c b/reftable/iter.c
index fddea31e51..86e801ca9f 100644
--- a/reftable/iter.c
+++ b/reftable/iter.c
@@ -11,15 +11,51 @@ https://developers.google.com/open-source/licenses/bsd
#include "system.h"
#include "block.h"
-#include "generic.h"
#include "constants.h"
#include "reader.h"
#include "reftable-error.h"
+int iterator_seek(struct reftable_iterator *it, struct reftable_record *want)
+{
+ return it->ops->seek(it->iter_arg, want);
+}
+
+int iterator_next(struct reftable_iterator *it, struct reftable_record *rec)
+{
+ return it->ops->next(it->iter_arg, rec);
+}
+
+static int empty_iterator_seek(void *arg UNUSED, struct reftable_record *want UNUSED)
+{
+ return 0;
+}
+
+static int empty_iterator_next(void *arg UNUSED, struct reftable_record *rec UNUSED)
+{
+ return 1;
+}
+
+static void empty_iterator_close(void *arg UNUSED)
+{
+}
+
+static struct reftable_iterator_vtable empty_vtable = {
+ .seek = &empty_iterator_seek,
+ .next = &empty_iterator_next,
+ .close = &empty_iterator_close,
+};
+
+void iterator_set_empty(struct reftable_iterator *it)
+{
+ assert(!it->ops);
+ it->iter_arg = NULL;
+ it->ops = &empty_vtable;
+}
+
static void filtering_ref_iterator_close(void *iter_arg)
{
struct filtering_ref_iterator *fri = iter_arg;
- strbuf_release(&fri->oid);
+ reftable_buf_release(&fri->oid);
reftable_iterator_destroy(&fri->it);
}
@@ -42,26 +78,6 @@ static int filtering_ref_iterator_next(void *iter_arg,
break;
}
- if (fri->double_check) {
- struct reftable_iterator it = { NULL };
-
- reftable_table_init_ref_iter(&fri->tab, &it);
-
- err = reftable_iterator_seek_ref(&it, ref->refname);
- if (err == 0)
- err = reftable_iterator_next_ref(&it, ref);
-
- reftable_iterator_destroy(&it);
-
- if (err < 0) {
- break;
- }
-
- if (err > 0) {
- continue;
- }
- }
-
if (ref->value_type == REFTABLE_REF_VAL2 &&
(!memcmp(fri->oid.buf, ref->value.val2.target_value,
fri->oid.len) ||
@@ -99,7 +115,7 @@ static void indexed_table_ref_iter_close(void *p)
block_iter_close(&it->cur);
reftable_block_done(&it->block_reader.block);
reftable_free(it->offsets);
- strbuf_release(&it->oid);
+ reftable_buf_release(&it->oid);
}
static int indexed_table_ref_iter_next_block(struct indexed_table_ref_iter *it)
@@ -127,7 +143,8 @@ static int indexed_table_ref_iter_next_block(struct indexed_table_ref_iter *it)
return 0;
}
-static int indexed_table_ref_iter_seek(void *p, struct reftable_record *want)
+static int indexed_table_ref_iter_seek(void *p UNUSED,
+ struct reftable_record *want UNUSED)
{
BUG("seeking indexed table is not supported");
return -1;
@@ -164,26 +181,41 @@ static int indexed_table_ref_iter_next(void *p, struct reftable_record *rec)
}
}
-int new_indexed_table_ref_iter(struct indexed_table_ref_iter **dest,
+int indexed_table_ref_iter_new(struct indexed_table_ref_iter **dest,
struct reftable_reader *r, uint8_t *oid,
int oid_len, uint64_t *offsets, int offset_len)
{
struct indexed_table_ref_iter empty = INDEXED_TABLE_REF_ITER_INIT;
- struct indexed_table_ref_iter *itr = reftable_calloc(1, sizeof(*itr));
+ struct indexed_table_ref_iter *itr;
int err = 0;
+ itr = reftable_calloc(1, sizeof(*itr));
+ if (!itr) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
+
*itr = empty;
itr->r = r;
- strbuf_add(&itr->oid, oid, oid_len);
+
+ err = reftable_buf_add(&itr->oid, oid, oid_len);
+ if (err < 0)
+ goto out;
itr->offsets = offsets;
itr->offset_len = offset_len;
err = indexed_table_ref_iter_next_block(itr);
+ if (err < 0)
+ goto out;
+
+ *dest = itr;
+ err = 0;
+
+out:
if (err < 0) {
+ *dest = NULL;
reftable_free(itr);
- } else {
- *dest = itr;
}
return err;
}
@@ -201,3 +233,71 @@ void iterator_from_indexed_table_ref_iter(struct reftable_iterator *it,
it->iter_arg = itr;
it->ops = &indexed_table_ref_iter_vtable;
}
+
+void reftable_iterator_destroy(struct reftable_iterator *it)
+{
+ if (!it->ops)
+ return;
+ it->ops->close(it->iter_arg);
+ it->ops = NULL;
+ REFTABLE_FREE_AND_NULL(it->iter_arg);
+}
+
+int reftable_iterator_seek_ref(struct reftable_iterator *it,
+ const char *name)
+{
+ struct reftable_record want = {
+ .type = BLOCK_TYPE_REF,
+ .u.ref = {
+ .refname = (char *)name,
+ },
+ };
+ return it->ops->seek(it->iter_arg, &want);
+}
+
+int reftable_iterator_next_ref(struct reftable_iterator *it,
+ struct reftable_ref_record *ref)
+{
+ struct reftable_record rec = {
+ .type = BLOCK_TYPE_REF,
+ .u = {
+ .ref = *ref
+ },
+ };
+ int err = iterator_next(it, &rec);
+ *ref = rec.u.ref;
+ return err;
+}
+
+int reftable_iterator_seek_log_at(struct reftable_iterator *it,
+ const char *name, uint64_t update_index)
+{
+ struct reftable_record want = {
+ .type = BLOCK_TYPE_LOG,
+ .u.log = {
+ .refname = (char *)name,
+ .update_index = update_index,
+ },
+ };
+ return it->ops->seek(it->iter_arg, &want);
+}
+
+int reftable_iterator_seek_log(struct reftable_iterator *it,
+ const char *name)
+{
+ return reftable_iterator_seek_log_at(it, name, ~((uint64_t) 0));
+}
+
+int reftable_iterator_next_log(struct reftable_iterator *it,
+ struct reftable_log_record *log)
+{
+ struct reftable_record rec = {
+ .type = BLOCK_TYPE_LOG,
+ .u = {
+ .log = *log,
+ },
+ };
+ int err = iterator_next(it, &rec);
+ *log = rec.u.log;
+ return err;
+}
diff --git a/reftable/iter.h b/reftable/iter.h
index 537431baba..40f98893b8 100644
--- a/reftable/iter.h
+++ b/reftable/iter.h
@@ -14,18 +14,42 @@ https://developers.google.com/open-source/licenses/bsd
#include "record.h"
#include "reftable-iterator.h"
-#include "reftable-generic.h"
+
+/*
+ * The virtual function table for implementing generic reftable iterators.
+ */
+struct reftable_iterator_vtable {
+ int (*seek)(void *iter_arg, struct reftable_record *want);
+ int (*next)(void *iter_arg, struct reftable_record *rec);
+ void (*close)(void *iter_arg);
+};
+
+/*
+ * Position the iterator at the wanted record such that a call to
+ * `iterator_next()` would return that record, if it exists.
+ */
+int iterator_seek(struct reftable_iterator *it, struct reftable_record *want);
+
+/*
+ * Yield the next record and advance the iterator. Returns <0 on error, 0 when
+ * a record was yielded, and >0 when the iterator hit an error.
+ */
+int iterator_next(struct reftable_iterator *it, struct reftable_record *rec);
+
+/*
+ * Set up the iterator such that it behaves the same as an iterator with no
+ * entries.
+ */
+void iterator_set_empty(struct reftable_iterator *it);
/* iterator that produces only ref records that point to `oid` */
struct filtering_ref_iterator {
- int double_check;
- struct reftable_table tab;
- struct strbuf oid;
+ struct reftable_buf oid;
struct reftable_iterator it;
};
#define FILTERING_REF_ITERATOR_INIT \
{ \
- .oid = STRBUF_INIT \
+ .oid = REFTABLE_BUF_INIT \
}
void iterator_from_filtering_ref_iterator(struct reftable_iterator *,
@@ -36,7 +60,7 @@ void iterator_from_filtering_ref_iterator(struct reftable_iterator *,
*/
struct indexed_table_ref_iter {
struct reftable_reader *r;
- struct strbuf oid;
+ struct reftable_buf oid;
/* mutable */
uint64_t *offsets;
@@ -51,14 +75,14 @@ struct indexed_table_ref_iter {
#define INDEXED_TABLE_REF_ITER_INIT { \
.cur = BLOCK_ITER_INIT, \
- .oid = STRBUF_INIT, \
+ .oid = REFTABLE_BUF_INIT, \
}
void iterator_from_indexed_table_ref_iter(struct reftable_iterator *it,
struct indexed_table_ref_iter *itr);
/* Takes ownership of `offsets` */
-int new_indexed_table_ref_iter(struct indexed_table_ref_iter **dest,
+int indexed_table_ref_iter_new(struct indexed_table_ref_iter **dest,
struct reftable_reader *r, uint8_t *oid,
int oid_len, uint64_t *offsets, int offset_len);
diff --git a/reftable/merged.c b/reftable/merged.c
index 6adce44f4b..bb0836e344 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -11,8 +11,8 @@ https://developers.google.com/open-source/licenses/bsd
#include "constants.h"
#include "iter.h"
#include "pq.h"
+#include "reader.h"
#include "record.h"
-#include "generic.h"
#include "reftable-merged.h"
#include "reftable-error.h"
#include "system.h"
@@ -25,33 +25,17 @@ struct merged_subiter {
struct merged_iter {
struct merged_subiter *subiters;
struct merged_iter_pqueue pq;
- size_t stack_len;
+ size_t subiters_len;
int suppress_deletions;
ssize_t advance_index;
};
-static void merged_iter_init(struct merged_iter *mi,
- struct reftable_merged_table *mt,
- uint8_t typ)
-{
- memset(mi, 0, sizeof(*mi));
- mi->advance_index = -1;
- mi->suppress_deletions = mt->suppress_deletions;
-
- REFTABLE_CALLOC_ARRAY(mi->subiters, mt->stack_len);
- for (size_t i = 0; i < mt->stack_len; i++) {
- reftable_record_init(&mi->subiters[i].rec, typ);
- table_init_iter(&mt->stack[i], &mi->subiters[i].iter, typ);
- }
- mi->stack_len = mt->stack_len;
-}
-
static void merged_iter_close(void *p)
{
struct merged_iter *mi = p;
merged_iter_pqueue_release(&mi->pq);
- for (size_t i = 0; i < mi->stack_len; i++) {
+ for (size_t i = 0; i < mi->subiters_len; i++) {
reftable_iterator_destroy(&mi->subiters[i].iter);
reftable_record_release(&mi->subiters[i].rec);
}
@@ -70,7 +54,10 @@ static int merged_iter_advance_subiter(struct merged_iter *mi, size_t idx)
if (err)
return err;
- merged_iter_pqueue_add(&mi->pq, &e);
+ err = merged_iter_pqueue_add(&mi->pq, &e);
+ if (err)
+ return err;
+
return 0;
}
@@ -79,8 +66,10 @@ static int merged_iter_seek(struct merged_iter *mi, struct reftable_record *want
int err;
mi->advance_index = -1;
+ while (!merged_iter_pqueue_is_empty(mi->pq))
+ merged_iter_pqueue_remove(&mi->pq);
- for (size_t i = 0; i < mi->stack_len; i++) {
+ for (size_t i = 0; i < mi->subiters_len; i++) {
err = iterator_seek(&mi->subiters[i].iter, want);
if (err < 0)
return err;
@@ -192,19 +181,19 @@ static void iterator_from_merged_iter(struct reftable_iterator *it,
it->ops = &merged_iter_vtable;
}
-int reftable_new_merged_table(struct reftable_merged_table **dest,
- struct reftable_table *stack, size_t n,
- uint32_t hash_id)
+int reftable_merged_table_new(struct reftable_merged_table **dest,
+ struct reftable_reader **readers, size_t n,
+ enum reftable_hash hash_id)
{
struct reftable_merged_table *m = NULL;
uint64_t last_max = 0;
uint64_t first_min = 0;
for (size_t i = 0; i < n; i++) {
- uint64_t min = reftable_table_min_update_index(&stack[i]);
- uint64_t max = reftable_table_max_update_index(&stack[i]);
+ uint64_t min = reftable_reader_min_update_index(readers[i]);
+ uint64_t max = reftable_reader_max_update_index(readers[i]);
- if (reftable_table_hash_id(&stack[i]) != hash_id) {
+ if (reftable_reader_hash_id(readers[i]) != hash_id) {
return REFTABLE_FORMAT_ERROR;
}
if (i == 0 || min < first_min) {
@@ -216,8 +205,11 @@ int reftable_new_merged_table(struct reftable_merged_table **dest,
}
REFTABLE_CALLOC_ARRAY(m, 1);
- m->stack = stack;
- m->stack_len = n;
+ if (!m)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
+
+ m->readers = readers;
+ m->readers_len = n;
m->min = first_min;
m->max = last_max;
m->hash_id = hash_id;
@@ -229,7 +221,6 @@ void reftable_merged_table_free(struct reftable_merged_table *mt)
{
if (!mt)
return;
- FREE_AND_NULL(mt->stack);
reftable_free(mt);
}
@@ -245,53 +236,66 @@ reftable_merged_table_min_update_index(struct reftable_merged_table *mt)
return mt->min;
}
-void merged_table_init_iter(struct reftable_merged_table *mt,
- struct reftable_iterator *it,
- uint8_t typ)
+int merged_table_init_iter(struct reftable_merged_table *mt,
+ struct reftable_iterator *it,
+ uint8_t typ)
{
- struct merged_iter *mi = reftable_malloc(sizeof(*mi));
- merged_iter_init(mi, mt, typ);
- iterator_from_merged_iter(it, mi);
-}
+ struct merged_subiter *subiters;
+ struct merged_iter *mi = NULL;
+ int ret;
-uint32_t reftable_merged_table_hash_id(struct reftable_merged_table *mt)
-{
- return mt->hash_id;
-}
+ REFTABLE_CALLOC_ARRAY(subiters, mt->readers_len);
+ if (!subiters) {
+ ret = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
-static void reftable_merged_table_init_iter_void(void *tab,
- struct reftable_iterator *it,
- uint8_t typ)
-{
- merged_table_init_iter(tab, it, typ);
-}
+ for (size_t i = 0; i < mt->readers_len; i++) {
+ reftable_record_init(&subiters[i].rec, typ);
+ ret = reader_init_iter(mt->readers[i], &subiters[i].iter, typ);
+ if (ret < 0)
+ goto out;
+ }
-static uint32_t reftable_merged_table_hash_id_void(void *tab)
-{
- return reftable_merged_table_hash_id(tab);
+ REFTABLE_CALLOC_ARRAY(mi, 1);
+ if (!mi) {
+ ret = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
+ mi->advance_index = -1;
+ mi->suppress_deletions = mt->suppress_deletions;
+ mi->subiters = subiters;
+ mi->subiters_len = mt->readers_len;
+
+ iterator_from_merged_iter(it, mi);
+ ret = 0;
+
+out:
+ if (ret < 0) {
+ for (size_t i = 0; subiters && i < mt->readers_len; i++) {
+ reftable_iterator_destroy(&subiters[i].iter);
+ reftable_record_release(&subiters[i].rec);
+ }
+ reftable_free(subiters);
+ reftable_free(mi);
+ }
+
+ return ret;
}
-static uint64_t reftable_merged_table_min_update_index_void(void *tab)
+int reftable_merged_table_init_ref_iterator(struct reftable_merged_table *mt,
+ struct reftable_iterator *it)
{
- return reftable_merged_table_min_update_index(tab);
+ return merged_table_init_iter(mt, it, BLOCK_TYPE_REF);
}
-static uint64_t reftable_merged_table_max_update_index_void(void *tab)
+int reftable_merged_table_init_log_iterator(struct reftable_merged_table *mt,
+ struct reftable_iterator *it)
{
- return reftable_merged_table_max_update_index(tab);
+ return merged_table_init_iter(mt, it, BLOCK_TYPE_LOG);
}
-static struct reftable_table_vtable merged_table_vtable = {
- .init_iter = reftable_merged_table_init_iter_void,
- .hash_id = reftable_merged_table_hash_id_void,
- .min_update_index = reftable_merged_table_min_update_index_void,
- .max_update_index = reftable_merged_table_max_update_index_void,
-};
-
-void reftable_table_from_merged_table(struct reftable_table *tab,
- struct reftable_merged_table *merged)
+enum reftable_hash reftable_merged_table_hash_id(struct reftable_merged_table *mt)
{
- assert(!tab->ops);
- tab->ops = &merged_table_vtable;
- tab->table_arg = merged;
+ return mt->hash_id;
}
diff --git a/reftable/merged.h b/reftable/merged.h
index 2efe571da6..0b7d939e92 100644
--- a/reftable/merged.h
+++ b/reftable/merged.h
@@ -10,11 +10,12 @@ https://developers.google.com/open-source/licenses/bsd
#define MERGED_H
#include "system.h"
+#include "reftable-basics.h"
struct reftable_merged_table {
- struct reftable_table *stack;
- size_t stack_len;
- uint32_t hash_id;
+ struct reftable_reader **readers;
+ size_t readers_len;
+ enum reftable_hash hash_id;
/* If unset, produce deletions. This is useful for compaction. For the
* full stack, deletions should be produced. */
@@ -26,8 +27,8 @@ struct reftable_merged_table {
struct reftable_iterator;
-void merged_table_init_iter(struct reftable_merged_table *mt,
- struct reftable_iterator *it,
- uint8_t typ);
+int merged_table_init_iter(struct reftable_merged_table *mt,
+ struct reftable_iterator *it,
+ uint8_t typ);
#endif
diff --git a/reftable/merged_test.c b/reftable/merged_test.c
deleted file mode 100644
index a9d6661c13..0000000000
--- a/reftable/merged_test.c
+++ /dev/null
@@ -1,461 +0,0 @@
-/*
-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 "merged.h"
-
-#include "system.h"
-
-#include "basics.h"
-#include "blocksource.h"
-#include "constants.h"
-#include "reader.h"
-#include "record.h"
-#include "test_framework.h"
-#include "reftable-merged.h"
-#include "reftable-tests.h"
-#include "reftable-generic.h"
-#include "reftable-writer.h"
-
-static void write_test_table(struct strbuf *buf,
- struct reftable_ref_record refs[], int n)
-{
- uint64_t min = 0xffffffff;
- uint64_t max = 0;
- int i = 0;
- int err;
-
- struct reftable_write_options opts = {
- .block_size = 256,
- };
- struct reftable_writer *w = NULL;
- for (i = 0; i < n; i++) {
- uint64_t ui = refs[i].update_index;
- if (ui > max) {
- max = ui;
- }
- if (ui < min) {
- min = ui;
- }
- }
-
- w = reftable_new_writer(&strbuf_add_void, &noop_flush, buf, &opts);
- reftable_writer_set_limits(w, min, max);
-
- for (i = 0; i < n; i++) {
- uint64_t before = refs[i].update_index;
- int n = reftable_writer_add_ref(w, &refs[i]);
- EXPECT(n == 0);
- EXPECT(before == refs[i].update_index);
- }
-
- err = reftable_writer_close(w);
- EXPECT_ERR(err);
-
- reftable_writer_free(w);
-}
-
-static void write_test_log_table(struct strbuf *buf,
- struct reftable_log_record logs[], int n,
- uint64_t update_index)
-{
- int i = 0;
- int err;
-
- struct reftable_write_options opts = {
- .block_size = 256,
- .exact_log_message = 1,
- };
- struct reftable_writer *w = NULL;
- w = reftable_new_writer(&strbuf_add_void, &noop_flush, buf, &opts);
- reftable_writer_set_limits(w, update_index, update_index);
-
- for (i = 0; i < n; i++) {
- int err = reftable_writer_add_log(w, &logs[i]);
- EXPECT_ERR(err);
- }
-
- err = reftable_writer_close(w);
- EXPECT_ERR(err);
-
- reftable_writer_free(w);
-}
-
-static struct reftable_merged_table *
-merged_table_from_records(struct reftable_ref_record **refs,
- struct reftable_block_source **source,
- struct reftable_reader ***readers, int *sizes,
- struct strbuf *buf, size_t n)
-{
- struct reftable_merged_table *mt = NULL;
- struct reftable_table *tabs;
- int err;
-
- REFTABLE_CALLOC_ARRAY(tabs, n);
- REFTABLE_CALLOC_ARRAY(*readers, n);
- REFTABLE_CALLOC_ARRAY(*source, n);
-
- for (size_t i = 0; i < n; i++) {
- write_test_table(&buf[i], refs[i], sizes[i]);
- block_source_from_strbuf(&(*source)[i], &buf[i]);
-
- err = reftable_new_reader(&(*readers)[i], &(*source)[i],
- "name");
- EXPECT_ERR(err);
- reftable_table_from_reader(&tabs[i], (*readers)[i]);
- }
-
- err = reftable_new_merged_table(&mt, tabs, n, GIT_SHA1_FORMAT_ID);
- EXPECT_ERR(err);
- return mt;
-}
-
-static void readers_destroy(struct reftable_reader **readers, size_t n)
-{
- int i = 0;
- for (; i < n; i++)
- reftable_reader_free(readers[i]);
- reftable_free(readers);
-}
-
-static void test_merged_between(void)
-{
- struct reftable_ref_record r1[] = { {
- .refname = (char *) "b",
- .update_index = 1,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = { 1, 2, 3, 0 },
- } };
- struct reftable_ref_record r2[] = { {
- .refname = (char *) "a",
- .update_index = 2,
- .value_type = REFTABLE_REF_DELETION,
- } };
-
- struct reftable_ref_record *refs[] = { r1, r2 };
- int sizes[] = { 1, 1 };
- struct strbuf bufs[2] = { STRBUF_INIT, STRBUF_INIT };
- struct reftable_block_source *bs = NULL;
- struct reftable_reader **readers = NULL;
- struct reftable_merged_table *mt =
- merged_table_from_records(refs, &bs, &readers, sizes, bufs, 2);
- int i;
- struct reftable_ref_record ref = { NULL };
- struct reftable_iterator it = { NULL };
- int err;
-
- merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
- err = reftable_iterator_seek_ref(&it, "a");
- EXPECT_ERR(err);
-
- err = reftable_iterator_next_ref(&it, &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);
- for (i = 0; i < ARRAY_SIZE(bufs); i++) {
- strbuf_release(&bufs[i]);
- }
- reftable_free(bs);
-}
-
-static void test_merged(void)
-{
- struct reftable_ref_record r1[] = {
- {
- .refname = (char *) "a",
- .update_index = 1,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = { 1 },
- },
- {
- .refname = (char *) "b",
- .update_index = 1,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = { 1 },
- },
- {
- .refname = (char *) "c",
- .update_index = 1,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = { 1 },
- }
- };
- struct reftable_ref_record r2[] = { {
- .refname = (char *) "a",
- .update_index = 2,
- .value_type = REFTABLE_REF_DELETION,
- } };
- struct reftable_ref_record r3[] = {
- {
- .refname = (char *) "c",
- .update_index = 3,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = { 2 },
- },
- {
- .refname = (char *) "d",
- .update_index = 3,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = { 1 },
- },
- };
-
- struct reftable_ref_record *want[] = {
- &r2[0],
- &r1[1],
- &r3[0],
- &r3[1],
- };
-
- struct reftable_ref_record *refs[] = { r1, r2, r3 };
- int sizes[3] = { 3, 1, 2 };
- struct strbuf bufs[3] = { STRBUF_INIT, STRBUF_INIT, STRBUF_INIT };
- struct reftable_block_source *bs = NULL;
- struct reftable_reader **readers = NULL;
- struct reftable_merged_table *mt =
- merged_table_from_records(refs, &bs, &readers, sizes, bufs, 3);
- struct reftable_iterator it = { NULL };
- int err;
- struct reftable_ref_record *out = NULL;
- size_t len = 0;
- size_t cap = 0;
- int i = 0;
-
- merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
- err = reftable_iterator_seek_ref(&it, "a");
- EXPECT_ERR(err);
- EXPECT(reftable_merged_table_hash_id(mt) == GIT_SHA1_FORMAT_ID);
- EXPECT(reftable_merged_table_min_update_index(mt) == 1);
-
- while (len < 100) { /* cap loops/recursion. */
- struct reftable_ref_record ref = { NULL };
- int err = reftable_iterator_next_ref(&it, &ref);
- if (err > 0)
- break;
-
- REFTABLE_ALLOC_GROW(out, len + 1, cap);
- out[len++] = ref;
- }
- reftable_iterator_destroy(&it);
-
- EXPECT(ARRAY_SIZE(want) == len);
- for (i = 0; i < len; i++) {
- EXPECT(reftable_ref_record_equal(want[i], &out[i],
- GIT_SHA1_RAWSZ));
- }
- for (i = 0; i < len; i++) {
- reftable_ref_record_release(&out[i]);
- }
- reftable_free(out);
-
- for (i = 0; i < 3; i++) {
- strbuf_release(&bufs[i]);
- }
- readers_destroy(readers, 3);
- reftable_merged_table_free(mt);
- reftable_free(bs);
-}
-
-static struct reftable_merged_table *
-merged_table_from_log_records(struct reftable_log_record **logs,
- struct reftable_block_source **source,
- struct reftable_reader ***readers, int *sizes,
- struct strbuf *buf, size_t n)
-{
- struct reftable_merged_table *mt = NULL;
- struct reftable_table *tabs;
- int err;
-
- REFTABLE_CALLOC_ARRAY(tabs, n);
- REFTABLE_CALLOC_ARRAY(*readers, n);
- REFTABLE_CALLOC_ARRAY(*source, n);
-
- for (size_t i = 0; i < n; i++) {
- write_test_log_table(&buf[i], logs[i], sizes[i], i + 1);
- block_source_from_strbuf(&(*source)[i], &buf[i]);
-
- err = reftable_new_reader(&(*readers)[i], &(*source)[i],
- "name");
- EXPECT_ERR(err);
- reftable_table_from_reader(&tabs[i], (*readers)[i]);
- }
-
- err = reftable_new_merged_table(&mt, tabs, n, GIT_SHA1_FORMAT_ID);
- EXPECT_ERR(err);
- return mt;
-}
-
-static void test_merged_logs(void)
-{
- struct reftable_log_record r1[] = {
- {
- .refname = (char *) "a",
- .update_index = 2,
- .value_type = REFTABLE_LOG_UPDATE,
- .value.update = {
- .old_hash = { 2 },
- /* deletion */
- .name = (char *) "jane doe",
- .email = (char *) "jane@invalid",
- .message = (char *) "message2",
- }
- },
- {
- .refname = (char *) "a",
- .update_index = 1,
- .value_type = REFTABLE_LOG_UPDATE,
- .value.update = {
- .old_hash = { 1 },
- .new_hash = { 2 },
- .name = (char *) "jane doe",
- .email = (char *) "jane@invalid",
- .message = (char *) "message1",
- }
- },
- };
- struct reftable_log_record r2[] = {
- {
- .refname = (char *) "a",
- .update_index = 3,
- .value_type = REFTABLE_LOG_UPDATE,
- .value.update = {
- .new_hash = { 3 },
- .name = (char *) "jane doe",
- .email = (char *) "jane@invalid",
- .message = (char *) "message3",
- }
- },
- };
- struct reftable_log_record r3[] = {
- {
- .refname = (char *) "a",
- .update_index = 2,
- .value_type = REFTABLE_LOG_DELETION,
- },
- };
- struct reftable_log_record *want[] = {
- &r2[0],
- &r3[0],
- &r1[1],
- };
-
- struct reftable_log_record *logs[] = { r1, r2, r3 };
- int sizes[3] = { 2, 1, 1 };
- struct strbuf bufs[3] = { STRBUF_INIT, STRBUF_INIT, STRBUF_INIT };
- struct reftable_block_source *bs = NULL;
- struct reftable_reader **readers = NULL;
- struct reftable_merged_table *mt = merged_table_from_log_records(
- logs, &bs, &readers, sizes, bufs, 3);
- struct reftable_iterator it = { NULL };
- int err;
- struct reftable_log_record *out = NULL;
- size_t len = 0;
- size_t cap = 0;
- int i = 0;
-
- merged_table_init_iter(mt, &it, BLOCK_TYPE_LOG);
- err = reftable_iterator_seek_log(&it, "a");
- EXPECT_ERR(err);
- EXPECT(reftable_merged_table_hash_id(mt) == GIT_SHA1_FORMAT_ID);
- EXPECT(reftable_merged_table_min_update_index(mt) == 1);
-
- while (len < 100) { /* cap loops/recursion. */
- struct reftable_log_record log = { NULL };
- int err = reftable_iterator_next_log(&it, &log);
- if (err > 0)
- break;
-
- REFTABLE_ALLOC_GROW(out, len + 1, cap);
- out[len++] = log;
- }
- reftable_iterator_destroy(&it);
-
- EXPECT(ARRAY_SIZE(want) == len);
- for (i = 0; i < len; i++) {
- EXPECT(reftable_log_record_equal(want[i], &out[i],
- GIT_SHA1_RAWSZ));
- }
-
- merged_table_init_iter(mt, &it, BLOCK_TYPE_LOG);
- err = reftable_iterator_seek_log_at(&it, "a", 2);
- EXPECT_ERR(err);
- reftable_log_record_release(&out[0]);
- err = reftable_iterator_next_log(&it, &out[0]);
- EXPECT_ERR(err);
- EXPECT(reftable_log_record_equal(&out[0], &r3[0], GIT_SHA1_RAWSZ));
- reftable_iterator_destroy(&it);
-
- for (i = 0; i < len; i++) {
- reftable_log_record_release(&out[i]);
- }
- reftable_free(out);
-
- for (i = 0; i < 3; i++) {
- strbuf_release(&bufs[i]);
- }
- readers_destroy(readers, 3);
- reftable_merged_table_free(mt);
- reftable_free(bs);
-}
-
-static void test_default_write_opts(void)
-{
- struct reftable_write_options opts = { 0 };
- struct strbuf buf = STRBUF_INIT;
- struct reftable_writer *w =
- reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
-
- struct reftable_ref_record rec = {
- .refname = (char *) "master",
- .update_index = 1,
- };
- int err;
- struct reftable_block_source source = { NULL };
- struct reftable_table *tab = reftable_calloc(1, sizeof(*tab));
- uint32_t hash_id;
- struct reftable_reader *rd = NULL;
- struct reftable_merged_table *merged = NULL;
-
- reftable_writer_set_limits(w, 1, 1);
-
- err = reftable_writer_add_ref(w, &rec);
- EXPECT_ERR(err);
-
- err = reftable_writer_close(w);
- EXPECT_ERR(err);
- reftable_writer_free(w);
-
- block_source_from_strbuf(&source, &buf);
-
- err = reftable_new_reader(&rd, &source, "filename");
- EXPECT_ERR(err);
-
- hash_id = reftable_reader_hash_id(rd);
- EXPECT(hash_id == GIT_SHA1_FORMAT_ID);
-
- reftable_table_from_reader(&tab[0], rd);
- err = reftable_new_merged_table(&merged, tab, 1, GIT_SHA1_FORMAT_ID);
- EXPECT_ERR(err);
-
- reftable_reader_free(rd);
- reftable_merged_table_free(merged);
- strbuf_release(&buf);
-}
-
-/* XXX test refs_for(oid) */
-
-int merged_test_main(int argc, const char *argv[])
-{
- RUN_TEST(test_merged_logs);
- RUN_TEST(test_merged_between);
- RUN_TEST(test_merged);
- RUN_TEST(test_default_write_opts);
- return 0;
-}
diff --git a/reftable/pq.c b/reftable/pq.c
index 7fb45d8c60..6ee1164dd3 100644
--- a/reftable/pq.c
+++ b/reftable/pq.c
@@ -8,6 +8,7 @@ https://developers.google.com/open-source/licenses/bsd
#include "pq.h"
+#include "reftable-error.h"
#include "reftable-record.h"
#include "system.h"
#include "basics.h"
@@ -22,27 +23,21 @@ int pq_less(struct pq_entry *a, struct pq_entry *b)
struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq)
{
- int i = 0;
+ size_t i = 0;
struct pq_entry e = pq->heap[0];
pq->heap[0] = pq->heap[pq->len - 1];
pq->len--;
- i = 0;
while (i < pq->len) {
- int min = i;
- int j = 2 * i + 1;
- int k = 2 * i + 2;
- if (j < pq->len && pq_less(&pq->heap[j], &pq->heap[i])) {
+ size_t min = i;
+ size_t j = 2 * i + 1;
+ size_t k = 2 * i + 2;
+ if (j < pq->len && pq_less(&pq->heap[j], &pq->heap[i]))
min = j;
- }
- if (k < pq->len && pq_less(&pq->heap[k], &pq->heap[min])) {
+ if (k < pq->len && pq_less(&pq->heap[k], &pq->heap[min]))
min = k;
- }
-
- if (min == i) {
+ if (min == i)
break;
- }
-
SWAP(pq->heap[i], pq->heap[min]);
i = min;
}
@@ -50,28 +45,29 @@ struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq)
return e;
}
-void merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry *e)
+int merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry *e)
{
- int i = 0;
+ size_t i = 0;
REFTABLE_ALLOC_GROW(pq->heap, pq->len + 1, pq->cap);
+ if (!pq->heap)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
pq->heap[pq->len++] = *e;
i = pq->len - 1;
while (i > 0) {
- int j = (i - 1) / 2;
- if (pq_less(&pq->heap[j], &pq->heap[i])) {
+ size_t j = (i - 1) / 2;
+ if (pq_less(&pq->heap[j], &pq->heap[i]))
break;
- }
-
SWAP(pq->heap[j], pq->heap[i]);
-
i = j;
}
+
+ return 0;
}
void merged_iter_pqueue_release(struct merged_iter_pqueue *pq)
{
- FREE_AND_NULL(pq->heap);
+ REFTABLE_FREE_AND_NULL(pq->heap);
memset(pq, 0, sizeof(*pq));
}
diff --git a/reftable/pq.h b/reftable/pq.h
index f796c23179..83c062eeca 100644
--- a/reftable/pq.h
+++ b/reftable/pq.h
@@ -22,9 +22,8 @@ struct merged_iter_pqueue {
size_t cap;
};
-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, const struct pq_entry *e);
+int merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry *e);
void merged_iter_pqueue_release(struct merged_iter_pqueue *pq);
int pq_less(struct pq_entry *a, struct pq_entry *b);
diff --git a/reftable/pq_test.c b/reftable/pq_test.c
deleted file mode 100644
index b7d3c80cc7..0000000000
--- a/reftable/pq_test.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
-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 "basics.h"
-#include "constants.h"
-#include "pq.h"
-#include "record.h"
-#include "reftable-tests.h"
-#include "test_framework.h"
-
-void merged_iter_pqueue_check(struct merged_iter_pqueue pq)
-{
- int i;
- for (i = 1; i < pq.len; i++) {
- int parent = (i - 1) / 2;
-
- EXPECT(pq_less(&pq.heap[parent], &pq.heap[i]));
- }
-}
-
-static void test_pq(void)
-{
- struct merged_iter_pqueue pq = { NULL };
- struct reftable_record recs[54];
- int N = ARRAY_SIZE(recs) - 1, i;
- char *last = NULL;
-
- for (i = 0; i < N; i++) {
- struct strbuf refname = STRBUF_INIT;
- strbuf_addf(&refname, "%02d", i);
-
- reftable_record_init(&recs[i], BLOCK_TYPE_REF);
- recs[i].u.ref.refname = strbuf_detach(&refname, NULL);
- }
-
- i = 1;
- do {
- struct pq_entry e = {
- .rec = &recs[i],
- };
-
- merged_iter_pqueue_add(&pq, &e);
- merged_iter_pqueue_check(pq);
-
- i = (i * 7) % N;
- } while (i != 1);
-
- while (!merged_iter_pqueue_is_empty(pq)) {
- struct pq_entry e = merged_iter_pqueue_remove(&pq);
- merged_iter_pqueue_check(pq);
-
- EXPECT(reftable_record_type(e.rec) == BLOCK_TYPE_REF);
- if (last)
- EXPECT(strcmp(last, e.rec->u.ref.refname) < 0);
- last = e.rec->u.ref.refname;
- }
-
- for (i = 0; i < N; i++)
- reftable_record_release(&recs[i]);
- merged_iter_pqueue_release(&pq);
-}
-
-int pq_test_main(int argc, const char *argv[])
-{
- RUN_TEST(test_pq);
- return 0;
-}
diff --git a/reftable/publicbasics.c b/reftable/publicbasics.c
deleted file mode 100644
index 44b84a125e..0000000000
--- a/reftable/publicbasics.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
-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 "reftable-malloc.h"
-
-#include "basics.h"
-
-static void *(*reftable_malloc_ptr)(size_t sz);
-static void *(*reftable_realloc_ptr)(void *, size_t);
-static void (*reftable_free_ptr)(void *);
-
-void *reftable_malloc(size_t sz)
-{
- if (reftable_malloc_ptr)
- return (*reftable_malloc_ptr)(sz);
- return malloc(sz);
-}
-
-void *reftable_realloc(void *p, size_t sz)
-{
- if (reftable_realloc_ptr)
- return (*reftable_realloc_ptr)(p, sz);
- return realloc(p, sz);
-}
-
-void reftable_free(void *p)
-{
- if (reftable_free_ptr)
- reftable_free_ptr(p);
- else
- free(p);
-}
-
-void *reftable_calloc(size_t nelem, size_t elsize)
-{
- size_t sz = st_mult(nelem, elsize);
- void *p = reftable_malloc(sz);
- memset(p, 0, sz);
- return p;
-}
-
-void reftable_set_alloc(void *(*malloc)(size_t),
- void *(*realloc)(void *, size_t), void (*free)(void *))
-{
- reftable_malloc_ptr = malloc;
- reftable_realloc_ptr = realloc;
- reftable_free_ptr = free;
-}
-
-int hash_size(uint32_t id)
-{
- switch (id) {
- case 0:
- case GIT_SHA1_FORMAT_ID:
- return GIT_SHA1_RAWSZ;
- case GIT_SHA256_FORMAT_ID:
- return GIT_SHA256_RAWSZ;
- }
- abort();
-}
diff --git a/reftable/reader.c b/reftable/reader.c
index 29c99e2269..ea82955c9b 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -11,11 +11,9 @@ https://developers.google.com/open-source/licenses/bsd
#include "system.h"
#include "block.h"
#include "constants.h"
-#include "generic.h"
#include "iter.h"
#include "record.h"
#include "reftable-error.h"
-#include "reftable-generic.h"
uint64_t block_source_size(struct reftable_block_source *source)
{
@@ -69,7 +67,7 @@ static int reader_get_block(struct reftable_reader *r,
return block_source_read_block(&r->source, dest, off, sz);
}
-uint32_t reftable_reader_hash_id(struct reftable_reader *r)
+enum reftable_hash reftable_reader_hash_id(struct reftable_reader *r)
{
return r->hash_id;
}
@@ -109,18 +107,20 @@ static int parse_footer(struct reftable_reader *r, uint8_t *footer,
f += 8;
if (r->version == 1) {
- r->hash_id = GIT_SHA1_FORMAT_ID;
+ r->hash_id = REFTABLE_HASH_SHA1;
} else {
- r->hash_id = get_be32(f);
- switch (r->hash_id) {
- case GIT_SHA1_FORMAT_ID:
+ switch (get_be32(f)) {
+ case REFTABLE_FORMAT_ID_SHA1:
+ r->hash_id = REFTABLE_HASH_SHA1;
break;
- case GIT_SHA256_FORMAT_ID:
+ case REFTABLE_FORMAT_ID_SHA256:
+ r->hash_id = REFTABLE_HASH_SHA256;
break;
default:
err = REFTABLE_FORMAT_ERROR;
goto done;
}
+
f += 4;
}
@@ -164,58 +164,6 @@ done:
return err;
}
-int init_reader(struct reftable_reader *r, struct reftable_block_source *source,
- const char *name)
-{
- struct reftable_block footer = { NULL };
- struct reftable_block header = { NULL };
- int err = 0;
- uint64_t file_size = block_source_size(source);
-
- /* Need +1 to read type of first block. */
- uint32_t read_size = header_size(2) + 1; /* read v2 because it's larger. */
- memset(r, 0, sizeof(struct reftable_reader));
-
- if (read_size > file_size) {
- err = REFTABLE_FORMAT_ERROR;
- goto done;
- }
-
- err = block_source_read_block(source, &header, 0, read_size);
- if (err != read_size) {
- err = REFTABLE_IO_ERROR;
- goto done;
- }
-
- if (memcmp(header.data, "REFT", 4)) {
- err = REFTABLE_FORMAT_ERROR;
- goto done;
- }
- r->version = header.data[4];
- if (r->version != 1 && r->version != 2) {
- err = REFTABLE_FORMAT_ERROR;
- goto done;
- }
-
- r->size = file_size - footer_size(r->version);
- r->source = *source;
- r->name = xstrdup(name);
- r->hash_id = 0;
-
- err = block_source_read_block(source, &footer, r->size,
- footer_size(r->version));
- if (err != footer_size(r->version)) {
- err = REFTABLE_IO_ERROR;
- goto done;
- }
-
- err = parse_footer(r, footer.data, header.data);
-done:
- reftable_block_done(&footer);
- reftable_block_done(&header);
- return err;
-}
-
struct table_iter {
struct reftable_reader *r;
uint8_t typ;
@@ -229,6 +177,7 @@ static int table_iter_init(struct table_iter *ti, struct reftable_reader *r)
{
struct block_iter bi = BLOCK_ITER_INIT;
memset(ti, 0, sizeof(*ti));
+ reftable_reader_incref(r);
ti->r = r;
ti->bi = bi;
return 0;
@@ -316,6 +265,7 @@ static void table_iter_close(struct table_iter *ti)
{
table_iter_block_done(ti);
block_iter_close(&ti->bi);
+ reftable_reader_decref(ti->r);
}
static int table_iter_next_block(struct table_iter *ti)
@@ -380,6 +330,7 @@ static int table_iter_seek_to(struct table_iter *ti, uint64_t off, uint8_t typ)
ti->typ = block_reader_type(&ti->br);
ti->block_off = off;
block_iter_seek_start(&ti->bi, &ti->br);
+ ti->is_finished = 0;
return 0;
}
@@ -401,13 +352,15 @@ static int table_iter_seek_start(struct table_iter *ti, uint8_t typ, int index)
static int table_iter_seek_linear(struct table_iter *ti,
struct reftable_record *want)
{
- struct strbuf want_key = STRBUF_INIT;
- struct strbuf got_key = STRBUF_INIT;
+ struct reftable_buf want_key = REFTABLE_BUF_INIT;
+ struct reftable_buf got_key = REFTABLE_BUF_INIT;
struct reftable_record rec;
int err;
reftable_record_init(&rec, reftable_record_type(want));
- reftable_record_key(want, &want_key);
+ err = reftable_record_key(want, &want_key);
+ if (err < 0)
+ goto done;
/*
* First we need to locate the block that must contain our record. To
@@ -452,7 +405,7 @@ static int table_iter_seek_linear(struct table_iter *ti,
if (err < 0)
goto done;
- if (strbuf_cmp(&got_key, &want_key) > 0) {
+ if (reftable_buf_cmp(&got_key, &want_key) > 0) {
table_iter_block_done(&next);
break;
}
@@ -473,8 +426,8 @@ static int table_iter_seek_linear(struct table_iter *ti,
done:
reftable_record_release(&rec);
- strbuf_release(&want_key);
- strbuf_release(&got_key);
+ reftable_buf_release(&want_key);
+ reftable_buf_release(&got_key);
return err;
}
@@ -482,15 +435,17 @@ static int table_iter_seek_indexed(struct table_iter *ti,
struct reftable_record *rec)
{
struct reftable_record want_index = {
- .type = BLOCK_TYPE_INDEX, .u.idx = { .last_key = STRBUF_INIT }
+ .type = BLOCK_TYPE_INDEX, .u.idx = { .last_key = REFTABLE_BUF_INIT }
};
struct reftable_record index_result = {
.type = BLOCK_TYPE_INDEX,
- .u.idx = { .last_key = STRBUF_INIT },
+ .u.idx = { .last_key = REFTABLE_BUF_INIT },
};
int err;
- reftable_record_key(rec, &want_index.u.idx.last_key);
+ err = reftable_record_key(rec, &want_index.u.idx.last_key);
+ if (err < 0)
+ goto done;
/*
* The index may consist of multiple levels, where each level may have
@@ -605,59 +560,131 @@ static void iterator_from_table_iter(struct reftable_iterator *it,
it->ops = &table_iter_vtable;
}
-static void reader_init_iter(struct reftable_reader *r,
- struct reftable_iterator *it,
- uint8_t typ)
+int reader_init_iter(struct reftable_reader *r,
+ struct reftable_iterator *it,
+ uint8_t typ)
{
struct reftable_reader_offsets *offs = reader_offsets_for(r, typ);
if (offs->is_present) {
struct table_iter *ti;
REFTABLE_ALLOC_ARRAY(ti, 1);
+ if (!ti)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
+
table_iter_init(ti, r);
iterator_from_table_iter(it, ti);
} else {
iterator_set_empty(it);
}
-}
-void reftable_reader_init_ref_iterator(struct reftable_reader *r,
- struct reftable_iterator *it)
-{
- reader_init_iter(r, it, BLOCK_TYPE_REF);
+ return 0;
}
-void reftable_reader_init_log_iterator(struct reftable_reader *r,
- struct reftable_iterator *it)
+int reftable_reader_init_ref_iterator(struct reftable_reader *r,
+ struct reftable_iterator *it)
{
- reader_init_iter(r, it, BLOCK_TYPE_LOG);
+ return reader_init_iter(r, it, BLOCK_TYPE_REF);
}
-void reader_close(struct reftable_reader *r)
+int reftable_reader_init_log_iterator(struct reftable_reader *r,
+ struct reftable_iterator *it)
{
- block_source_close(&r->source);
- FREE_AND_NULL(r->name);
+ return reader_init_iter(r, it, BLOCK_TYPE_LOG);
}
-int reftable_new_reader(struct reftable_reader **p,
- struct reftable_block_source *src, char const *name)
+int reftable_reader_new(struct reftable_reader **out,
+ struct reftable_block_source *source, char const *name)
{
- struct reftable_reader *rd = reftable_calloc(1, sizeof(*rd));
- int err = init_reader(rd, src, name);
- if (err == 0) {
- *p = rd;
- } else {
- block_source_close(src);
- reftable_free(rd);
+ struct reftable_block footer = { 0 };
+ struct reftable_block header = { 0 };
+ struct reftable_reader *r;
+ uint64_t file_size = block_source_size(source);
+ uint32_t read_size;
+ int err;
+
+ REFTABLE_CALLOC_ARRAY(r, 1);
+ if (!r) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto done;
+ }
+
+ /*
+ * We need one extra byte to read the type of first block. We also
+ * pretend to always be reading v2 of the format because it is larger.
+ */
+ read_size = header_size(2) + 1;
+ if (read_size > file_size) {
+ err = REFTABLE_FORMAT_ERROR;
+ goto done;
+ }
+
+ err = block_source_read_block(source, &header, 0, read_size);
+ if (err != read_size) {
+ err = REFTABLE_IO_ERROR;
+ goto done;
+ }
+
+ if (memcmp(header.data, "REFT", 4)) {
+ err = REFTABLE_FORMAT_ERROR;
+ goto done;
+ }
+ r->version = header.data[4];
+ if (r->version != 1 && r->version != 2) {
+ err = REFTABLE_FORMAT_ERROR;
+ goto done;
+ }
+
+ r->size = file_size - footer_size(r->version);
+ r->source = *source;
+ r->name = reftable_strdup(name);
+ if (!r->name) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto done;
+ }
+ r->hash_id = 0;
+ r->refcount = 1;
+
+ err = block_source_read_block(source, &footer, r->size,
+ footer_size(r->version));
+ if (err != footer_size(r->version)) {
+ err = REFTABLE_IO_ERROR;
+ goto done;
+ }
+
+ err = parse_footer(r, footer.data, header.data);
+ if (err)
+ goto done;
+
+ *out = r;
+
+done:
+ reftable_block_done(&footer);
+ reftable_block_done(&header);
+ if (err) {
+ reftable_free(r);
+ block_source_close(source);
}
return err;
}
-void reftable_reader_free(struct reftable_reader *r)
+void reftable_reader_incref(struct reftable_reader *r)
+{
+ if (!r->refcount)
+ BUG("cannot increment ref counter of dead reader");
+ r->refcount++;
+}
+
+void reftable_reader_decref(struct reftable_reader *r)
{
if (!r)
return;
- reader_close(r);
+ if (!r->refcount)
+ BUG("cannot decrement ref counter of dead reader");
+ if (--r->refcount)
+ return;
+ block_source_close(&r->source);
+ REFTABLE_FREE_AND_NULL(r->name);
reftable_free(r);
}
@@ -681,7 +708,10 @@ static int reftable_reader_refs_for_indexed(struct reftable_reader *r,
struct indexed_table_ref_iter *itr = NULL;
/* Look through the reverse index. */
- reader_init_iter(r, &oit, BLOCK_TYPE_OBJ);
+ err = reader_init_iter(r, &oit, BLOCK_TYPE_OBJ);
+ if (err < 0)
+ goto done;
+
err = iterator_seek(&oit, &want);
if (err != 0)
goto done;
@@ -699,7 +729,7 @@ static int reftable_reader_refs_for_indexed(struct reftable_reader *r,
goto done;
}
- err = new_indexed_table_ref_iter(&itr, r, oid, hash_size(r->hash_id),
+ err = indexed_table_ref_iter_new(&itr, r, oid, hash_size(r->hash_id),
got.u.obj.offsets,
got.u.obj.offset_len);
if (err < 0)
@@ -724,23 +754,40 @@ static int reftable_reader_refs_for_unindexed(struct reftable_reader *r,
int err;
REFTABLE_ALLOC_ARRAY(ti, 1);
+ if (!ti) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
+
table_iter_init(ti, r);
err = table_iter_seek_start(ti, BLOCK_TYPE_REF, 0);
- if (err < 0) {
- reftable_free(ti);
- return err;
- }
+ if (err < 0)
+ goto out;
- filter = reftable_malloc(sizeof(struct filtering_ref_iterator));
+ filter = reftable_malloc(sizeof(*filter));
+ if (!filter) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
*filter = empty;
- strbuf_add(&filter->oid, oid, oid_len);
- reftable_table_from_reader(&filter->tab, r);
- filter->double_check = 0;
+ err = reftable_buf_add(&filter->oid, oid, oid_len);
+ if (err < 0)
+ goto out;
+
iterator_from_table_iter(&filter->it, ti);
iterator_from_filtering_ref_iterator(it, filter);
- return 0;
+
+ err = 0;
+
+out:
+ if (err < 0) {
+ if (ti)
+ table_iter_close(ti);
+ reftable_free(ti);
+ }
+ return err;
}
int reftable_reader_refs_for(struct reftable_reader *r,
@@ -761,66 +808,6 @@ uint64_t reftable_reader_min_update_index(struct reftable_reader *r)
return r->min_update_index;
}
-/* generic table interface. */
-
-static void reftable_reader_init_iter_void(void *tab,
- struct reftable_iterator *it,
- uint8_t typ)
-{
- reader_init_iter(tab, it, typ);
-}
-
-static uint32_t reftable_reader_hash_id_void(void *tab)
-{
- return reftable_reader_hash_id(tab);
-}
-
-static uint64_t reftable_reader_min_update_index_void(void *tab)
-{
- return reftable_reader_min_update_index(tab);
-}
-
-static uint64_t reftable_reader_max_update_index_void(void *tab)
-{
- return reftable_reader_max_update_index(tab);
-}
-
-static struct reftable_table_vtable reader_vtable = {
- .init_iter = reftable_reader_init_iter_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,
-};
-
-void reftable_table_from_reader(struct reftable_table *tab,
- struct reftable_reader *reader)
-{
- assert(!tab->ops);
- tab->ops = &reader_vtable;
- tab->table_arg = reader;
-}
-
-
-int reftable_reader_print_file(const char *tablename)
-{
- struct reftable_block_source src = { NULL };
- int err = reftable_block_source_from_file(&src, tablename);
- struct reftable_reader *r = NULL;
- struct reftable_table tab = { NULL };
- if (err < 0)
- goto done;
-
- err = reftable_new_reader(&r, &src, tablename);
- if (err < 0)
- goto done;
-
- reftable_table_from_reader(&tab, r);
- err = reftable_table_print(&tab);
-done:
- reftable_reader_free(r);
- return err;
-}
-
int reftable_reader_print_blocks(const char *tablename)
{
struct {
@@ -850,7 +837,7 @@ int reftable_reader_print_blocks(const char *tablename)
if (err < 0)
goto done;
- err = reftable_new_reader(&r, &src, tablename);
+ err = reftable_reader_new(&r, &src, tablename);
if (err < 0)
goto done;
@@ -881,7 +868,7 @@ int reftable_reader_print_blocks(const char *tablename)
}
done:
- reftable_reader_free(r);
+ reftable_reader_decref(r);
table_iter_close(&ti);
return err;
}
diff --git a/reftable/reader.h b/reftable/reader.h
index e869165f23..d2b48a4849 100644
--- a/reftable/reader.h
+++ b/reftable/reader.h
@@ -30,15 +30,15 @@ struct reftable_reader_offsets {
/* The state for reading a reftable file. */
struct reftable_reader {
- /* for convience, associate a name with the instance. */
+ /* for convenience, associate a name with the instance. */
char *name;
struct reftable_block_source source;
/* Size of the file, excluding the footer. */
uint64_t size;
- /* 'sha1' for SHA1, 's256' for SHA-256 */
- uint32_t hash_id;
+ /* The hash function used for ref records. */
+ enum reftable_hash hash_id;
uint32_t block_size;
uint64_t min_update_index;
@@ -50,13 +50,16 @@ struct reftable_reader {
struct reftable_reader_offsets ref_offsets;
struct reftable_reader_offsets obj_offsets;
struct reftable_reader_offsets log_offsets;
+
+ uint64_t refcount;
};
-int init_reader(struct reftable_reader *r, struct reftable_block_source *source,
- const char *name);
-void reader_close(struct reftable_reader *r);
const char *reader_name(struct reftable_reader *r);
+int reader_init_iter(struct reftable_reader *r,
+ struct reftable_iterator *it,
+ uint8_t typ);
+
/* initialize a block reader to read from `r` */
int reader_init_block_reader(struct reftable_reader *r, struct block_reader *br,
uint64_t next_off, uint8_t want_typ);
diff --git a/reftable/readwrite_test.c b/reftable/readwrite_test.c
deleted file mode 100644
index f411abfe9c..0000000000
--- a/reftable/readwrite_test.c
+++ /dev/null
@@ -1,979 +0,0 @@
-/*
-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 "basics.h"
-#include "block.h"
-#include "blocksource.h"
-#include "reader.h"
-#include "record.h"
-#include "test_framework.h"
-#include "reftable-tests.h"
-#include "reftable-writer.h"
-
-static const int update_index = 5;
-
-static void test_buffer(void)
-{
- struct strbuf buf = STRBUF_INIT;
- struct reftable_block_source source = { NULL };
- struct reftable_block out = { NULL };
- int n;
- uint8_t in[] = "hello";
- strbuf_add(&buf, in, sizeof(in));
- block_source_from_strbuf(&source, &buf);
- EXPECT(block_source_size(&source) == 6);
- n = block_source_read_block(&source, &out, 0, sizeof(in));
- EXPECT(n == sizeof(in));
- EXPECT(!memcmp(in, out.data, n));
- reftable_block_done(&out);
-
- n = block_source_read_block(&source, &out, 1, 2);
- EXPECT(n == 2);
- EXPECT(!memcmp(out.data, "el", 2));
-
- reftable_block_done(&out);
- block_source_close(&source);
- strbuf_release(&buf);
-}
-
-static void write_table(char ***names, struct strbuf *buf, int N,
- int block_size, uint32_t hash_id)
-{
- struct reftable_write_options opts = {
- .block_size = block_size,
- .hash_id = hash_id,
- };
- struct reftable_writer *w =
- reftable_new_writer(&strbuf_add_void, &noop_flush, buf, &opts);
- struct reftable_ref_record ref = { NULL };
- int i = 0, n;
- struct reftable_log_record log = { NULL };
- const struct reftable_stats *stats = NULL;
-
- REFTABLE_CALLOC_ARRAY(*names, N + 1);
-
- reftable_writer_set_limits(w, update_index, update_index);
- for (i = 0; i < N; i++) {
- char name[100];
- int n;
-
- snprintf(name, sizeof(name), "refs/heads/branch%02d", i);
-
- ref.refname = name;
- ref.update_index = update_index;
- ref.value_type = REFTABLE_REF_VAL1;
- set_test_hash(ref.value.val1, i);
- (*names)[i] = xstrdup(name);
-
- n = reftable_writer_add_ref(w, &ref);
- EXPECT(n == 0);
- }
-
- for (i = 0; i < N; i++) {
- char name[100];
- int n;
-
- snprintf(name, sizeof(name), "refs/heads/branch%02d", i);
-
- log.refname = name;
- log.update_index = update_index;
- log.value_type = REFTABLE_LOG_UPDATE;
- set_test_hash(log.value.update.new_hash, i);
- log.value.update.message = (char *) "message";
-
- n = reftable_writer_add_log(w, &log);
- EXPECT(n == 0);
- }
-
- n = reftable_writer_close(w);
- EXPECT(n == 0);
-
- stats = reftable_writer_stats(w);
- for (i = 0; i < stats->ref_stats.blocks; i++) {
- int off = i * opts.block_size;
- if (off == 0) {
- off = header_size(
- (hash_id == GIT_SHA256_FORMAT_ID) ? 2 : 1);
- }
- EXPECT(buf->buf[off] == 'r');
- }
-
- EXPECT(stats->log_stats.blocks > 0);
- reftable_writer_free(w);
-}
-
-static void test_log_buffer_size(void)
-{
- struct strbuf buf = STRBUF_INIT;
- struct reftable_write_options opts = {
- .block_size = 4096,
- };
- int err;
- int i;
- struct reftable_log_record
- log = { .refname = (char *) "refs/heads/master",
- .update_index = 0xa,
- .value_type = REFTABLE_LOG_UPDATE,
- .value = { .update = {
- .name = (char *) "Han-Wen Nienhuys",
- .email = (char *) "hanwen@google.com",
- .tz_offset = 100,
- .time = 0x5e430672,
- .message = (char *) "commit: 9\n",
- } } };
- struct reftable_writer *w =
- reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
-
- /* This tests buffer extension for log compression. Must use a random
- hash, to ensure that the compressed part is larger than the original.
- */
- for (i = 0; i < GIT_SHA1_RAWSZ; i++) {
- log.value.update.old_hash[i] = (uint8_t)(git_rand() % 256);
- log.value.update.new_hash[i] = (uint8_t)(git_rand() % 256);
- }
- reftable_writer_set_limits(w, update_index, update_index);
- err = reftable_writer_add_log(w, &log);
- EXPECT_ERR(err);
- err = reftable_writer_close(w);
- EXPECT_ERR(err);
- reftable_writer_free(w);
- strbuf_release(&buf);
-}
-
-static void test_log_overflow(void)
-{
- struct strbuf buf = STRBUF_INIT;
- char msg[256] = { 0 };
- struct reftable_write_options opts = {
- .block_size = ARRAY_SIZE(msg),
- };
- int err;
- struct reftable_log_record log = {
- .refname = (char *) "refs/heads/master",
- .update_index = 0xa,
- .value_type = REFTABLE_LOG_UPDATE,
- .value = {
- .update = {
- .old_hash = { 1 },
- .new_hash = { 2 },
- .name = (char *) "Han-Wen Nienhuys",
- .email = (char *) "hanwen@google.com",
- .tz_offset = 100,
- .time = 0x5e430672,
- .message = msg,
- },
- },
- };
- struct reftable_writer *w =
- reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
-
- memset(msg, 'x', sizeof(msg) - 1);
- reftable_writer_set_limits(w, update_index, update_index);
- err = reftable_writer_add_log(w, &log);
- EXPECT(err == REFTABLE_ENTRY_TOO_BIG_ERROR);
- reftable_writer_free(w);
- strbuf_release(&buf);
-}
-
-static void test_log_write_read(void)
-{
- int N = 2;
- char **names = reftable_calloc(N + 1, sizeof(*names));
- int err;
- struct reftable_write_options opts = {
- .block_size = 256,
- };
- struct reftable_ref_record ref = { NULL };
- int i = 0;
- struct reftable_log_record log = { NULL };
- int n;
- 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, &noop_flush, &buf, &opts);
- const struct reftable_stats *stats = NULL;
- reftable_writer_set_limits(w, 0, N);
- for (i = 0; i < N; i++) {
- char name[256];
- struct reftable_ref_record ref = { NULL };
- snprintf(name, sizeof(name), "b%02d%0*d", i, 130, 7);
- names[i] = xstrdup(name);
- ref.refname = name;
- ref.update_index = i;
-
- err = reftable_writer_add_ref(w, &ref);
- EXPECT_ERR(err);
- }
- for (i = 0; i < N; i++) {
- struct reftable_log_record log = { NULL };
-
- log.refname = names[i];
- log.update_index = i;
- log.value_type = REFTABLE_LOG_UPDATE;
- set_test_hash(log.value.update.old_hash, i);
- set_test_hash(log.value.update.new_hash, i + 1);
-
- err = reftable_writer_add_log(w, &log);
- EXPECT_ERR(err);
- }
-
- n = reftable_writer_close(w);
- EXPECT(n == 0);
-
- stats = reftable_writer_stats(w);
- 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");
- EXPECT_ERR(err);
-
- reftable_reader_init_ref_iterator(&rd, &it);
-
- err = reftable_iterator_seek_ref(&it, names[N - 1]);
- EXPECT_ERR(err);
-
- err = reftable_iterator_next_ref(&it, &ref);
- EXPECT_ERR(err);
-
- /* end of iteration. */
- err = reftable_iterator_next_ref(&it, &ref);
- EXPECT(0 < err);
-
- reftable_iterator_destroy(&it);
- reftable_ref_record_release(&ref);
-
- reftable_reader_init_log_iterator(&rd, &it);
-
- err = reftable_iterator_seek_log(&it, "");
- EXPECT_ERR(err);
-
- i = 0;
- while (1) {
- int err = reftable_iterator_next_log(&it, &log);
- if (err > 0) {
- break;
- }
-
- EXPECT_ERR(err);
- EXPECT_STREQ(names[i], log.refname);
- EXPECT(i == log.update_index);
- i++;
- reftable_log_record_release(&log);
- }
-
- EXPECT(i == N);
- reftable_iterator_destroy(&it);
-
- /* cleanup. */
- strbuf_release(&buf);
- free_names(names);
- reader_close(&rd);
-}
-
-static void test_log_zlib_corruption(void)
-{
- struct reftable_write_options opts = {
- .block_size = 256,
- };
- struct reftable_iterator it = { 0 };
- struct reftable_reader rd = { 0 };
- struct reftable_block_source source = { 0 };
- struct strbuf buf = STRBUF_INIT;
- struct reftable_writer *w =
- reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
- const struct reftable_stats *stats = NULL;
- char message[100] = { 0 };
- int err, i, n;
- struct reftable_log_record log = {
- .refname = (char *) "refname",
- .value_type = REFTABLE_LOG_UPDATE,
- .value = {
- .update = {
- .new_hash = { 1 },
- .old_hash = { 2 },
- .name = (char *) "My Name",
- .email = (char *) "myname@invalid",
- .message = message,
- },
- },
- };
-
- for (i = 0; i < sizeof(message) - 1; i++)
- message[i] = (uint8_t)(git_rand() % 64 + ' ');
-
- reftable_writer_set_limits(w, 1, 1);
-
- err = reftable_writer_add_log(w, &log);
- EXPECT_ERR(err);
-
- n = reftable_writer_close(w);
- EXPECT(n == 0);
-
- stats = reftable_writer_stats(w);
- EXPECT(stats->log_stats.blocks > 0);
- reftable_writer_free(w);
- w = NULL;
-
- /* corrupt the data. */
- buf.buf[50] ^= 0x99;
-
- block_source_from_strbuf(&source, &buf);
-
- err = init_reader(&rd, &source, "file.log");
- EXPECT_ERR(err);
-
- reftable_reader_init_log_iterator(&rd, &it);
- err = reftable_iterator_seek_log(&it, "refname");
- EXPECT(err == REFTABLE_ZLIB_ERROR);
-
- reftable_iterator_destroy(&it);
-
- /* cleanup. */
- strbuf_release(&buf);
- reader_close(&rd);
-}
-
-static void test_table_read_write_sequential(void)
-{
- char **names;
- struct strbuf buf = STRBUF_INIT;
- int N = 50;
- struct reftable_iterator it = { NULL };
- struct reftable_block_source source = { NULL };
- struct reftable_reader rd = { NULL };
- int err = 0;
- int j = 0;
-
- write_table(&names, &buf, N, 256, GIT_SHA1_FORMAT_ID);
-
- block_source_from_strbuf(&source, &buf);
-
- err = init_reader(&rd, &source, "file.ref");
- EXPECT_ERR(err);
-
- reftable_reader_init_ref_iterator(&rd, &it);
- err = reftable_iterator_seek_ref(&it, "");
- EXPECT_ERR(err);
-
- while (1) {
- struct reftable_ref_record ref = { NULL };
- int r = reftable_iterator_next_ref(&it, &ref);
- EXPECT(r >= 0);
- if (r > 0) {
- break;
- }
- EXPECT(0 == strcmp(names[j], ref.refname));
- EXPECT(update_index == ref.update_index);
-
- j++;
- reftable_ref_record_release(&ref);
- }
- EXPECT(j == N);
- reftable_iterator_destroy(&it);
- strbuf_release(&buf);
- free_names(names);
-
- reader_close(&rd);
-}
-
-static void test_table_write_small_table(void)
-{
- char **names;
- struct strbuf buf = STRBUF_INIT;
- int N = 1;
- write_table(&names, &buf, N, 4096, GIT_SHA1_FORMAT_ID);
- EXPECT(buf.len < 200);
- strbuf_release(&buf);
- free_names(names);
-}
-
-static void test_table_read_api(void)
-{
- char **names;
- struct strbuf buf = STRBUF_INIT;
- int N = 50;
- struct reftable_reader rd = { NULL };
- struct reftable_block_source source = { NULL };
- int err;
- int i;
- struct reftable_log_record log = { NULL };
- struct reftable_iterator it = { NULL };
-
- write_table(&names, &buf, N, 256, GIT_SHA1_FORMAT_ID);
-
- block_source_from_strbuf(&source, &buf);
-
- err = init_reader(&rd, &source, "file.ref");
- EXPECT_ERR(err);
-
- reftable_reader_init_ref_iterator(&rd, &it);
- err = reftable_iterator_seek_ref(&it, names[0]);
- EXPECT_ERR(err);
-
- err = reftable_iterator_next_log(&it, &log);
- EXPECT(err == REFTABLE_API_ERROR);
-
- strbuf_release(&buf);
- for (i = 0; i < N; i++) {
- reftable_free(names[i]);
- }
- reftable_iterator_destroy(&it);
- reftable_free(names);
- reader_close(&rd);
- strbuf_release(&buf);
-}
-
-static void test_table_read_write_seek(int index, int hash_id)
-{
- char **names;
- struct strbuf buf = STRBUF_INIT;
- int N = 50;
- struct reftable_reader rd = { NULL };
- struct reftable_block_source source = { NULL };
- int err;
- int i = 0;
-
- struct reftable_iterator it = { NULL };
- struct strbuf pastLast = STRBUF_INIT;
- struct reftable_ref_record ref = { NULL };
-
- write_table(&names, &buf, N, 256, hash_id);
-
- block_source_from_strbuf(&source, &buf);
-
- err = init_reader(&rd, &source, "file.ref");
- EXPECT_ERR(err);
- EXPECT(hash_id == reftable_reader_hash_id(&rd));
-
- if (!index) {
- rd.ref_offsets.index_offset = 0;
- } else {
- EXPECT(rd.ref_offsets.index_offset > 0);
- }
-
- for (i = 1; i < N; i++) {
- reftable_reader_init_ref_iterator(&rd, &it);
- err = reftable_iterator_seek_ref(&it, names[i]);
- EXPECT_ERR(err);
- err = reftable_iterator_next_ref(&it, &ref);
- EXPECT_ERR(err);
- EXPECT(0 == strcmp(names[i], ref.refname));
- EXPECT(REFTABLE_REF_VAL1 == ref.value_type);
- EXPECT(i == ref.value.val1[0]);
-
- reftable_ref_record_release(&ref);
- reftable_iterator_destroy(&it);
- }
-
- strbuf_addstr(&pastLast, names[N - 1]);
- strbuf_addstr(&pastLast, "/");
-
- reftable_reader_init_ref_iterator(&rd, &it);
- err = reftable_iterator_seek_ref(&it, pastLast.buf);
- if (err == 0) {
- struct reftable_ref_record ref = { NULL };
- int err = reftable_iterator_next_ref(&it, &ref);
- EXPECT(err > 0);
- } else {
- EXPECT(err > 0);
- }
-
- strbuf_release(&pastLast);
- reftable_iterator_destroy(&it);
-
- strbuf_release(&buf);
- for (i = 0; i < N; i++) {
- reftable_free(names[i]);
- }
- reftable_free(names);
- reader_close(&rd);
-}
-
-static void test_table_read_write_seek_linear(void)
-{
- test_table_read_write_seek(0, GIT_SHA1_FORMAT_ID);
-}
-
-static void test_table_read_write_seek_linear_sha256(void)
-{
- test_table_read_write_seek(0, GIT_SHA256_FORMAT_ID);
-}
-
-static void test_table_read_write_seek_index(void)
-{
- test_table_read_write_seek(1, GIT_SHA1_FORMAT_ID);
-}
-
-static void test_table_refs_for(int indexed)
-{
- int N = 50;
- char **want_names = reftable_calloc(N + 1, sizeof(*want_names));
- int want_names_len = 0;
- uint8_t want_hash[GIT_SHA1_RAWSZ];
-
- struct reftable_write_options opts = {
- .block_size = 256,
- };
- struct reftable_ref_record ref = { NULL };
- int i = 0;
- int n;
- int err;
- struct reftable_reader rd;
- struct reftable_block_source source = { NULL };
-
- struct strbuf buf = STRBUF_INIT;
- struct reftable_writer *w =
- reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
-
- struct reftable_iterator it = { NULL };
- int j;
-
- set_test_hash(want_hash, 4);
-
- for (i = 0; i < N; i++) {
- uint8_t hash[GIT_SHA1_RAWSZ];
- char fill[51] = { 0 };
- char name[100];
- struct reftable_ref_record ref = { NULL };
-
- memset(hash, i, sizeof(hash));
- memset(fill, 'x', 50);
- /* Put the variable part in the start */
- snprintf(name, sizeof(name), "br%02d%s", i, fill);
- name[40] = 0;
- ref.refname = name;
-
- ref.value_type = REFTABLE_REF_VAL2;
- set_test_hash(ref.value.val2.value, i / 4);
- set_test_hash(ref.value.val2.target_value, 3 + i / 4);
-
- /* 80 bytes / entry, so 3 entries per block. Yields 17
- */
- /* blocks. */
- n = reftable_writer_add_ref(w, &ref);
- EXPECT(n == 0);
-
- if (!memcmp(ref.value.val2.value, want_hash, GIT_SHA1_RAWSZ) ||
- !memcmp(ref.value.val2.target_value, want_hash, GIT_SHA1_RAWSZ)) {
- want_names[want_names_len++] = xstrdup(name);
- }
- }
-
- n = reftable_writer_close(w);
- EXPECT(n == 0);
-
- reftable_writer_free(w);
- w = NULL;
-
- block_source_from_strbuf(&source, &buf);
-
- err = init_reader(&rd, &source, "file.ref");
- EXPECT_ERR(err);
- if (!indexed) {
- rd.obj_offsets.is_present = 0;
- }
-
- reftable_reader_init_ref_iterator(&rd, &it);
- err = reftable_iterator_seek_ref(&it, "");
- EXPECT_ERR(err);
- reftable_iterator_destroy(&it);
-
- err = reftable_reader_refs_for(&rd, &it, want_hash);
- EXPECT_ERR(err);
-
- j = 0;
- while (1) {
- int err = reftable_iterator_next_ref(&it, &ref);
- EXPECT(err >= 0);
- if (err > 0) {
- break;
- }
-
- EXPECT(j < want_names_len);
- EXPECT(0 == strcmp(ref.refname, want_names[j]));
- j++;
- reftable_ref_record_release(&ref);
- }
- EXPECT(j == want_names_len);
-
- strbuf_release(&buf);
- free_names(want_names);
- reftable_iterator_destroy(&it);
- reader_close(&rd);
-}
-
-static void test_table_refs_for_no_index(void)
-{
- test_table_refs_for(0);
-}
-
-static void test_table_refs_for_obj_index(void)
-{
- test_table_refs_for(1);
-}
-
-static void test_write_empty_table(void)
-{
- struct reftable_write_options opts = { 0 };
- struct strbuf buf = STRBUF_INIT;
- struct reftable_writer *w =
- reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
- struct reftable_block_source source = { NULL };
- struct reftable_reader *rd = NULL;
- struct reftable_ref_record rec = { NULL };
- struct reftable_iterator it = { NULL };
- int err;
-
- reftable_writer_set_limits(w, 1, 1);
-
- err = reftable_writer_close(w);
- EXPECT(err == REFTABLE_EMPTY_TABLE_ERROR);
- reftable_writer_free(w);
-
- EXPECT(buf.len == header_size(1) + footer_size(1));
-
- block_source_from_strbuf(&source, &buf);
-
- err = reftable_new_reader(&rd, &source, "filename");
- EXPECT_ERR(err);
-
- reftable_reader_init_ref_iterator(rd, &it);
- err = reftable_iterator_seek_ref(&it, "");
- EXPECT_ERR(err);
-
- err = reftable_iterator_next_ref(&it, &rec);
- EXPECT(err > 0);
-
- reftable_iterator_destroy(&it);
- reftable_reader_free(rd);
- 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, &noop_flush, &buf, &opts);
- struct reftable_ref_record ref = {
- .update_index = 1,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = {42},
- };
- 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, &noop_flush, &buf, &opts);
- struct reftable_ref_record ref = {
- .update_index = 1,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = {42},
- };
- 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, &noop_flush, &buf, &opts);
- struct reftable_ref_record ref = {
- .refname = (char *) "",
- .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 };
- struct strbuf buf = STRBUF_INIT;
- struct reftable_writer *w =
- reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
- struct reftable_ref_record refs[2] = {
- {
- .refname = (char *) "b",
- .update_index = 1,
- .value_type = REFTABLE_REF_SYMREF,
- .value = {
- .symref = (char *) "target",
- },
- }, {
- .refname = (char *) "a",
- .update_index = 1,
- .value_type = REFTABLE_REF_SYMREF,
- .value = {
- .symref = (char *) "target",
- },
- }
- };
- int err;
-
- reftable_writer_set_limits(w, 1, 1);
- err = reftable_writer_add_ref(w, &refs[0]);
- EXPECT_ERR(err);
- err = reftable_writer_add_ref(w, &refs[1]);
- EXPECT(err == REFTABLE_API_ERROR);
- reftable_writer_close(w);
- reftable_writer_free(w);
- strbuf_release(&buf);
-}
-
-static void test_write_multiple_indices(void)
-{
- struct reftable_write_options opts = {
- .block_size = 100,
- };
- struct strbuf writer_buf = STRBUF_INIT, buf = STRBUF_INIT;
- struct reftable_block_source source = { 0 };
- struct reftable_iterator it = { 0 };
- const struct reftable_stats *stats;
- struct reftable_writer *writer;
- struct reftable_reader *reader;
- int err, i;
-
- writer = reftable_new_writer(&strbuf_add_void, &noop_flush, &writer_buf, &opts);
- reftable_writer_set_limits(writer, 1, 1);
- for (i = 0; i < 100; i++) {
- struct reftable_ref_record ref = {
- .update_index = 1,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = {i},
- };
-
- strbuf_reset(&buf);
- strbuf_addf(&buf, "refs/heads/%04d", i);
- ref.refname = buf.buf,
-
- err = reftable_writer_add_ref(writer, &ref);
- EXPECT_ERR(err);
- }
-
- for (i = 0; i < 100; i++) {
- struct reftable_log_record log = {
- .update_index = 1,
- .value_type = REFTABLE_LOG_UPDATE,
- .value.update = {
- .old_hash = { i },
- .new_hash = { i },
- },
- };
-
- strbuf_reset(&buf);
- strbuf_addf(&buf, "refs/heads/%04d", i);
- log.refname = buf.buf,
-
- err = reftable_writer_add_log(writer, &log);
- EXPECT_ERR(err);
- }
-
- reftable_writer_close(writer);
-
- /*
- * The written data should be sufficiently large to result in indices
- * for each of the block types.
- */
- stats = reftable_writer_stats(writer);
- EXPECT(stats->ref_stats.index_offset > 0);
- EXPECT(stats->obj_stats.index_offset > 0);
- EXPECT(stats->log_stats.index_offset > 0);
-
- block_source_from_strbuf(&source, &writer_buf);
- err = reftable_new_reader(&reader, &source, "filename");
- EXPECT_ERR(err);
-
- /*
- * Seeking the log uses the log index now. In case there is any
- * confusion regarding indices we would notice here.
- */
- reftable_reader_init_log_iterator(reader, &it);
- err = reftable_iterator_seek_log(&it, "");
- EXPECT_ERR(err);
-
- reftable_iterator_destroy(&it);
- reftable_writer_free(writer);
- reftable_reader_free(reader);
- strbuf_release(&writer_buf);
- strbuf_release(&buf);
-}
-
-static void test_write_multi_level_index(void)
-{
- struct reftable_write_options opts = {
- .block_size = 100,
- };
- struct strbuf writer_buf = STRBUF_INIT, buf = STRBUF_INIT;
- struct reftable_block_source source = { 0 };
- struct reftable_iterator it = { 0 };
- const struct reftable_stats *stats;
- struct reftable_writer *writer;
- struct reftable_reader *reader;
- int err;
-
- writer = reftable_new_writer(&strbuf_add_void, &noop_flush, &writer_buf, &opts);
- reftable_writer_set_limits(writer, 1, 1);
- for (size_t i = 0; i < 200; i++) {
- struct reftable_ref_record ref = {
- .update_index = 1,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = {i},
- };
-
- strbuf_reset(&buf);
- strbuf_addf(&buf, "refs/heads/%03" PRIuMAX, (uintmax_t)i);
- ref.refname = buf.buf,
-
- err = reftable_writer_add_ref(writer, &ref);
- EXPECT_ERR(err);
- }
- reftable_writer_close(writer);
-
- /*
- * The written refs should be sufficiently large to result in a
- * multi-level index.
- */
- stats = reftable_writer_stats(writer);
- EXPECT(stats->ref_stats.max_index_level == 2);
-
- block_source_from_strbuf(&source, &writer_buf);
- err = reftable_new_reader(&reader, &source, "filename");
- EXPECT_ERR(err);
-
- /*
- * Seeking the last ref should work as expected.
- */
- reftable_reader_init_ref_iterator(reader, &it);
- err = reftable_iterator_seek_ref(&it, "refs/heads/199");
- EXPECT_ERR(err);
-
- reftable_iterator_destroy(&it);
- reftable_writer_free(writer);
- reftable_reader_free(reader);
- strbuf_release(&writer_buf);
- strbuf_release(&buf);
-}
-
-static void test_corrupt_table_empty(void)
-{
- struct strbuf buf = STRBUF_INIT;
- struct reftable_block_source source = { NULL };
- struct reftable_reader rd = { NULL };
- int err;
-
- block_source_from_strbuf(&source, &buf);
- err = init_reader(&rd, &source, "file.log");
- EXPECT(err == REFTABLE_FORMAT_ERROR);
-}
-
-static void test_corrupt_table(void)
-{
- uint8_t zeros[1024] = { 0 };
- struct strbuf buf = STRBUF_INIT;
- struct reftable_block_source source = { NULL };
- struct reftable_reader rd = { NULL };
- int err;
- strbuf_add(&buf, zeros, sizeof(zeros));
-
- block_source_from_strbuf(&source, &buf);
- err = init_reader(&rd, &source, "file.log");
- EXPECT(err == REFTABLE_FORMAT_ERROR);
- strbuf_release(&buf);
-}
-
-int readwrite_test_main(int argc, const char *argv[])
-{
- RUN_TEST(test_log_zlib_corruption);
- RUN_TEST(test_corrupt_table);
- RUN_TEST(test_corrupt_table_empty);
- RUN_TEST(test_log_write_read);
- RUN_TEST(test_write_key_order);
- RUN_TEST(test_table_read_write_seek_linear_sha256);
- RUN_TEST(test_log_buffer_size);
- RUN_TEST(test_table_write_small_table);
- RUN_TEST(test_buffer);
- RUN_TEST(test_table_read_api);
- RUN_TEST(test_table_read_write_sequential);
- RUN_TEST(test_table_read_write_seek_linear);
- 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);
- RUN_TEST(test_write_multiple_indices);
- RUN_TEST(test_write_multi_level_index);
- return 0;
-}
diff --git a/reftable/record.c b/reftable/record.c
index a2cba5ef74..fb5652ed57 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -98,19 +98,24 @@ const unsigned char *reftable_ref_record_val2(const struct reftable_ref_record *
}
}
-static int decode_string(struct strbuf *dest, struct string_view in)
+static int decode_string(struct reftable_buf *dest, struct string_view in)
{
int start_len = in.len;
uint64_t tsize = 0;
- int n = get_var_int(&tsize, &in);
+ int n, err;
+
+ n = get_var_int(&tsize, &in);
if (n <= 0)
return -1;
string_view_consume(&in, n);
if (in.len < tsize)
return -1;
- strbuf_reset(dest);
- strbuf_add(dest, in.buf, tsize);
+ reftable_buf_reset(dest);
+ err = reftable_buf_add(dest, in.buf, tsize);
+ if (err < 0)
+ return err;
+
string_view_consume(&in, tsize);
return start_len - in.len;
@@ -133,7 +138,7 @@ static int encode_string(const char *str, struct string_view s)
}
int reftable_encode_key(int *restart, struct string_view dest,
- struct strbuf prev_key, struct strbuf key,
+ struct reftable_buf prev_key, struct reftable_buf key,
uint8_t extra)
{
struct string_view start = dest;
@@ -183,13 +188,13 @@ int reftable_decode_keylen(struct string_view in,
return start_len - in.len;
}
-int reftable_decode_key(struct strbuf *last_key, uint8_t *extra,
+int reftable_decode_key(struct reftable_buf *last_key, uint8_t *extra,
struct string_view in)
{
int start_len = in.len;
uint64_t prefix_len = 0;
uint64_t suffix_len = 0;
- int n;
+ int err, n;
n = reftable_decode_keylen(in, &prefix_len, &suffix_len, extra);
if (n < 0)
@@ -200,28 +205,35 @@ int reftable_decode_key(struct strbuf *last_key, uint8_t *extra,
prefix_len > last_key->len)
return -1;
- strbuf_setlen(last_key, prefix_len);
- strbuf_add(last_key, in.buf, suffix_len);
+ err = reftable_buf_setlen(last_key, prefix_len);
+ if (err < 0)
+ return err;
+
+ err = reftable_buf_add(last_key, in.buf, suffix_len);
+ if (err < 0)
+ return err;
+
string_view_consume(&in, suffix_len);
return start_len - in.len;
}
-static void reftable_ref_record_key(const void *r, struct strbuf *dest)
+static int reftable_ref_record_key(const void *r, struct reftable_buf *dest)
{
const struct reftable_ref_record *rec =
(const struct reftable_ref_record *)r;
- strbuf_reset(dest);
- strbuf_addstr(dest, rec->refname);
+ reftable_buf_reset(dest);
+ return reftable_buf_addstr(dest, rec->refname);
}
-static void reftable_ref_record_copy_from(void *rec, const void *src_rec,
- int hash_size)
+static int reftable_ref_record_copy_from(void *rec, const void *src_rec,
+ int hash_size)
{
struct reftable_ref_record *ref = rec;
const struct reftable_ref_record *src = src_rec;
char *refname = NULL;
size_t refname_cap = 0;
+ int err;
assert(hash_size > 0);
@@ -236,6 +248,11 @@ static void reftable_ref_record_copy_from(void *rec, const void *src_rec,
REFTABLE_ALLOC_GROW(ref->refname, refname_len + 1,
ref->refname_cap);
+ if (!ref->refname) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
+
memcpy(ref->refname, src->refname, refname_len);
ref->refname[refname_len] = 0;
}
@@ -254,61 +271,17 @@ static void reftable_ref_record_copy_from(void *rec, const void *src_rec,
src->value.val2.target_value, hash_size);
break;
case REFTABLE_REF_SYMREF:
- ref->value.symref = xstrdup(src->value.symref);
- break;
- }
-}
-
-static char hexdigit(int c)
-{
- if (c <= 9)
- return '0' + c;
- return 'a' + (c - 10);
-}
-
-static void hex_format(char *dest, const unsigned char *src, int hash_size)
-{
- assert(hash_size > 0);
- if (src) {
- int i = 0;
- for (i = 0; i < hash_size; i++) {
- dest[2 * i] = hexdigit(src[i] >> 4);
- dest[2 * i + 1] = hexdigit(src[i] & 0xf);
+ ref->value.symref = reftable_strdup(src->value.symref);
+ if (!ref->value.symref) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
}
- dest[2 * hash_size] = 0;
- }
-}
-
-static void reftable_ref_record_print_sz(const struct reftable_ref_record *ref,
- int hash_size)
-{
- char hex[GIT_MAX_HEXSZ + 1] = { 0 }; /* BUG */
- printf("ref{%s(%" PRIu64 ") ", ref->refname, ref->update_index);
- switch (ref->value_type) {
- case REFTABLE_REF_SYMREF:
- printf("=> %s", ref->value.symref);
- break;
- case REFTABLE_REF_VAL2:
- hex_format(hex, ref->value.val2.value, hash_size);
- printf("val 2 %s", hex);
- hex_format(hex, ref->value.val2.target_value,
- hash_size);
- printf("(T %s)", hex);
- break;
- case REFTABLE_REF_VAL1:
- hex_format(hex, ref->value.val1, hash_size);
- printf("val 1 %s", hex);
- break;
- case REFTABLE_REF_DELETION:
- printf("delete");
break;
}
- printf("}\n");
-}
-void reftable_ref_record_print(const struct reftable_ref_record *ref,
- uint32_t hash_id) {
- reftable_ref_record_print_sz(ref, hash_size(hash_id));
+ err = 0;
+out:
+ return err;
}
static void reftable_ref_record_release_void(void *rec)
@@ -388,16 +361,16 @@ static int reftable_ref_record_encode(const void *rec, struct string_view s,
return start.len - s.len;
}
-static int reftable_ref_record_decode(void *rec, struct strbuf key,
+static int reftable_ref_record_decode(void *rec, struct reftable_buf key,
uint8_t val_type, struct string_view in,
- int hash_size, struct strbuf *scratch)
+ int hash_size, struct reftable_buf *scratch)
{
struct reftable_ref_record *r = rec;
struct string_view start = in;
uint64_t update_index = 0;
const char *refname = NULL;
size_t refname_cap = 0;
- int n;
+ int n, err;
assert(hash_size > 0);
@@ -413,6 +386,10 @@ static int reftable_ref_record_decode(void *rec, struct strbuf key,
SWAP(r->refname_cap, refname_cap);
REFTABLE_ALLOC_GROW(r->refname, key.len + 1, r->refname_cap);
+ if (!r->refname) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto done;
+ }
memcpy(r->refname, key.buf, key.len);
r->refname[key.len] = 0;
@@ -421,7 +398,8 @@ static int reftable_ref_record_decode(void *rec, struct strbuf key,
switch (val_type) {
case REFTABLE_REF_VAL1:
if (in.len < hash_size) {
- return -1;
+ err = REFTABLE_FORMAT_ERROR;
+ goto done;
}
memcpy(r->value.val1, in.buf, hash_size);
@@ -430,7 +408,8 @@ static int reftable_ref_record_decode(void *rec, struct strbuf key,
case REFTABLE_REF_VAL2:
if (in.len < 2 * hash_size) {
- return -1;
+ err = REFTABLE_FORMAT_ERROR;
+ goto done;
}
memcpy(r->value.val2.value, in.buf, hash_size);
@@ -443,10 +422,11 @@ static int reftable_ref_record_decode(void *rec, struct strbuf key,
case REFTABLE_REF_SYMREF: {
int n = decode_string(scratch, in);
if (n < 0) {
- return -1;
+ err = REFTABLE_FORMAT_ERROR;
+ goto done;
}
string_view_consume(&in, n);
- r->value.symref = strbuf_detach(scratch, NULL);
+ r->value.symref = reftable_buf_detach(scratch);
} break;
case REFTABLE_REF_DELETION:
@@ -457,6 +437,9 @@ static int reftable_ref_record_decode(void *rec, struct strbuf key,
}
return start.len - in.len;
+
+done:
+ return err;
}
static int reftable_ref_record_is_deletion_void(const void *p)
@@ -480,12 +463,6 @@ static int reftable_ref_record_cmp_void(const void *_a, const void *_b)
return strcmp(a->refname, b->refname);
}
-static void reftable_ref_record_print_void(const void *rec,
- int hash_size)
-{
- reftable_ref_record_print_sz((struct reftable_ref_record *) rec, hash_size);
-}
-
static struct reftable_record_vtable reftable_ref_record_vtable = {
.key = &reftable_ref_record_key,
.type = BLOCK_TYPE_REF,
@@ -497,57 +474,46 @@ static struct reftable_record_vtable reftable_ref_record_vtable = {
.is_deletion = &reftable_ref_record_is_deletion_void,
.equal = &reftable_ref_record_equal_void,
.cmp = &reftable_ref_record_cmp_void,
- .print = &reftable_ref_record_print_void,
};
-static void reftable_obj_record_key(const void *r, struct strbuf *dest)
+static int reftable_obj_record_key(const void *r, struct reftable_buf *dest)
{
const struct reftable_obj_record *rec =
(const struct reftable_obj_record *)r;
- strbuf_reset(dest);
- strbuf_add(dest, rec->hash_prefix, rec->hash_prefix_len);
+ reftable_buf_reset(dest);
+ return reftable_buf_add(dest, rec->hash_prefix, rec->hash_prefix_len);
}
static void reftable_obj_record_release(void *rec)
{
struct reftable_obj_record *obj = rec;
- FREE_AND_NULL(obj->hash_prefix);
- FREE_AND_NULL(obj->offsets);
+ REFTABLE_FREE_AND_NULL(obj->hash_prefix);
+ REFTABLE_FREE_AND_NULL(obj->offsets);
memset(obj, 0, sizeof(struct reftable_obj_record));
}
-static void reftable_obj_record_print(const void *rec, int hash_size)
-{
- const struct reftable_obj_record *obj = rec;
- char hex[GIT_MAX_HEXSZ + 1] = { 0 };
- struct strbuf offset_str = STRBUF_INIT;
- int i;
-
- for (i = 0; i < obj->offset_len; i++)
- strbuf_addf(&offset_str, "%" PRIu64 " ", obj->offsets[i]);
- hex_format(hex, obj->hash_prefix, obj->hash_prefix_len);
- printf("prefix %s (len %d), offsets [%s]\n",
- hex, obj->hash_prefix_len, offset_str.buf);
- strbuf_release(&offset_str);
-}
-
-static void reftable_obj_record_copy_from(void *rec, const void *src_rec,
- int hash_size)
+static int reftable_obj_record_copy_from(void *rec, const void *src_rec,
+ int hash_size UNUSED)
{
struct reftable_obj_record *obj = rec;
- const struct reftable_obj_record *src =
- (const struct reftable_obj_record *)src_rec;
+ const struct reftable_obj_record *src = src_rec;
reftable_obj_record_release(obj);
REFTABLE_ALLOC_ARRAY(obj->hash_prefix, src->hash_prefix_len);
+ if (!obj->hash_prefix)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
obj->hash_prefix_len = src->hash_prefix_len;
if (src->hash_prefix_len)
memcpy(obj->hash_prefix, src->hash_prefix, obj->hash_prefix_len);
REFTABLE_ALLOC_ARRAY(obj->offsets, src->offset_len);
+ if (!obj->offsets)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
obj->offset_len = src->offset_len;
COPY_ARRAY(obj->offsets, src->offsets, src->offset_len);
+
+ return 0;
}
static uint8_t reftable_obj_record_val_type(const void *rec)
@@ -559,7 +525,7 @@ static uint8_t reftable_obj_record_val_type(const void *rec)
}
static int reftable_obj_record_encode(const void *rec, struct string_view s,
- int hash_size)
+ int hash_size UNUSED)
{
const struct reftable_obj_record *r = rec;
struct string_view start = s;
@@ -592,9 +558,10 @@ static int reftable_obj_record_encode(const void *rec, struct string_view s,
return start.len - s.len;
}
-static int reftable_obj_record_decode(void *rec, struct strbuf key,
+static int reftable_obj_record_decode(void *rec, struct reftable_buf key,
uint8_t val_type, struct string_view in,
- int hash_size, struct strbuf *scratch UNUSED)
+ int hash_size UNUSED,
+ struct reftable_buf *scratch UNUSED)
{
struct string_view start = in;
struct reftable_obj_record *r = rec;
@@ -606,6 +573,8 @@ static int reftable_obj_record_decode(void *rec, struct strbuf key,
reftable_obj_record_release(r);
REFTABLE_ALLOC_ARRAY(r->hash_prefix, key.len);
+ if (!r->hash_prefix)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
memcpy(r->hash_prefix, key.buf, key.len);
r->hash_prefix_len = key.len;
@@ -624,6 +593,8 @@ static int reftable_obj_record_decode(void *rec, struct strbuf key,
return start.len - in.len;
REFTABLE_ALLOC_ARRAY(r->offsets, count);
+ if (!r->offsets)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
r->offset_len = count;
n = get_var_int(&r->offsets[0], &in);
@@ -647,12 +618,13 @@ static int reftable_obj_record_decode(void *rec, struct strbuf key,
return start.len - in.len;
}
-static int not_a_deletion(const void *p)
+static int not_a_deletion(const void *p UNUSED)
{
return 0;
}
-static int reftable_obj_record_equal_void(const void *a, const void *b, int hash_size)
+static int reftable_obj_record_equal_void(const void *a, const void *b,
+ int hash_size UNUSED)
{
struct reftable_obj_record *ra = (struct reftable_obj_record *) a;
struct reftable_obj_record *rb = (struct reftable_obj_record *) b;
@@ -701,83 +673,69 @@ static struct reftable_record_vtable reftable_obj_record_vtable = {
.is_deletion = &not_a_deletion,
.equal = &reftable_obj_record_equal_void,
.cmp = &reftable_obj_record_cmp_void,
- .print = &reftable_obj_record_print,
};
-static void reftable_log_record_print_sz(struct reftable_log_record *log,
- int hash_size)
-{
- char hex[GIT_MAX_HEXSZ + 1] = { 0 };
-
- switch (log->value_type) {
- case REFTABLE_LOG_DELETION:
- printf("log{%s(%" PRIu64 ") delete\n", log->refname,
- log->update_index);
- break;
- case REFTABLE_LOG_UPDATE:
- printf("log{%s(%" PRIu64 ") %s <%s> %" PRIu64 " %04d\n",
- log->refname, log->update_index,
- log->value.update.name ? log->value.update.name : "",
- log->value.update.email ? log->value.update.email : "",
- log->value.update.time,
- log->value.update.tz_offset);
- hex_format(hex, log->value.update.old_hash, hash_size);
- printf("%s => ", hex);
- hex_format(hex, log->value.update.new_hash, hash_size);
- printf("%s\n\n%s\n}\n", hex,
- log->value.update.message ? log->value.update.message : "");
- break;
- }
-}
-
-void reftable_log_record_print(struct reftable_log_record *log,
- uint32_t hash_id)
-{
- reftable_log_record_print_sz(log, hash_size(hash_id));
-}
-
-static void reftable_log_record_key(const void *r, struct strbuf *dest)
+static int reftable_log_record_key(const void *r, struct reftable_buf *dest)
{
const struct reftable_log_record *rec =
(const struct reftable_log_record *)r;
- int len = strlen(rec->refname);
+ int len = strlen(rec->refname), err;
uint8_t i64[8];
uint64_t ts = 0;
- strbuf_reset(dest);
- strbuf_add(dest, (uint8_t *)rec->refname, len + 1);
+
+ reftable_buf_reset(dest);
+ err = reftable_buf_add(dest, (uint8_t *)rec->refname, len + 1);
+ if (err < 0)
+ return err;
ts = (~ts) - rec->update_index;
put_be64(&i64[0], ts);
- strbuf_add(dest, i64, sizeof(i64));
+
+ err = reftable_buf_add(dest, i64, sizeof(i64));
+ if (err < 0)
+ return err;
+
+ return 0;
}
-static void reftable_log_record_copy_from(void *rec, const void *src_rec,
- int hash_size)
+static int reftable_log_record_copy_from(void *rec, const void *src_rec,
+ int hash_size)
{
struct reftable_log_record *dst = rec;
const struct reftable_log_record *src =
(const struct reftable_log_record *)src_rec;
+ int ret;
reftable_log_record_release(dst);
*dst = *src;
+
if (dst->refname) {
- dst->refname = xstrdup(dst->refname);
+ dst->refname = reftable_strdup(dst->refname);
+ if (!dst->refname) {
+ ret = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
}
+
switch (dst->value_type) {
case REFTABLE_LOG_DELETION:
break;
case REFTABLE_LOG_UPDATE:
- if (dst->value.update.email) {
+ if (dst->value.update.email)
dst->value.update.email =
- xstrdup(dst->value.update.email);
- }
- if (dst->value.update.name) {
+ reftable_strdup(dst->value.update.email);
+ if (dst->value.update.name)
dst->value.update.name =
- xstrdup(dst->value.update.name);
- }
- if (dst->value.update.message) {
+ reftable_strdup(dst->value.update.name);
+ if (dst->value.update.message)
dst->value.update.message =
- xstrdup(dst->value.update.message);
+ reftable_strdup(dst->value.update.message);
+
+ if (!dst->value.update.email ||
+ !dst->value.update.name ||
+ !dst->value.update.message) {
+ ret = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
}
memcpy(dst->value.update.new_hash,
@@ -786,6 +744,10 @@ static void reftable_log_record_copy_from(void *rec, const void *src_rec,
src->value.update.old_hash, hash_size);
break;
}
+
+ ret = 0;
+out:
+ return ret;
}
static void reftable_log_record_release_void(void *rec)
@@ -864,20 +826,25 @@ static int reftable_log_record_encode(const void *rec, struct string_view s,
return start.len - s.len;
}
-static int reftable_log_record_decode(void *rec, struct strbuf key,
+static int reftable_log_record_decode(void *rec, struct reftable_buf key,
uint8_t val_type, struct string_view in,
- int hash_size, struct strbuf *scratch)
+ int hash_size, struct reftable_buf *scratch)
{
struct string_view start = in;
struct reftable_log_record *r = rec;
uint64_t max = 0;
uint64_t ts = 0;
- int n;
+ int err, n;
if (key.len <= 9 || key.buf[key.len - 9] != 0)
return REFTABLE_FORMAT_ERROR;
REFTABLE_ALLOC_GROW(r->refname, key.len - 8, r->refname_cap);
+ if (!r->refname) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto done;
+ }
+
memcpy(r->refname, key.buf, key.len - 8);
ts = get_be64(key.buf + key.len - 8);
@@ -886,10 +853,10 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,
if (val_type != r->value_type) {
switch (r->value_type) {
case REFTABLE_LOG_UPDATE:
- FREE_AND_NULL(r->value.update.message);
+ REFTABLE_FREE_AND_NULL(r->value.update.message);
r->value.update.message_cap = 0;
- FREE_AND_NULL(r->value.update.email);
- FREE_AND_NULL(r->value.update.name);
+ REFTABLE_FREE_AND_NULL(r->value.update.email);
+ REFTABLE_FREE_AND_NULL(r->value.update.name);
break;
case REFTABLE_LOG_DELETION:
break;
@@ -900,8 +867,10 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,
if (val_type == REFTABLE_LOG_DELETION)
return 0;
- if (in.len < 2 * hash_size)
- return REFTABLE_FORMAT_ERROR;
+ if (in.len < 2 * hash_size) {
+ err = REFTABLE_FORMAT_ERROR;
+ goto done;
+ }
memcpy(r->value.update.old_hash, in.buf, hash_size);
memcpy(r->value.update.new_hash, in.buf + hash_size, hash_size);
@@ -909,8 +878,10 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,
string_view_consume(&in, 2 * hash_size);
n = decode_string(scratch, in);
- if (n < 0)
+ if (n < 0) {
+ err = REFTABLE_FORMAT_ERROR;
goto done;
+ }
string_view_consume(&in, n);
/*
@@ -921,52 +892,75 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,
*/
if (!r->value.update.name ||
strcmp(r->value.update.name, scratch->buf)) {
- r->value.update.name =
- reftable_realloc(r->value.update.name, scratch->len + 1);
+ char *name = reftable_realloc(r->value.update.name, scratch->len + 1);
+ if (!name) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto done;
+ }
+
+ r->value.update.name = name;
memcpy(r->value.update.name, scratch->buf, scratch->len);
r->value.update.name[scratch->len] = 0;
}
n = decode_string(scratch, in);
- if (n < 0)
+ if (n < 0) {
+ err = REFTABLE_FORMAT_ERROR;
goto done;
+ }
string_view_consume(&in, n);
/* Same as above, but for the reflog email. */
if (!r->value.update.email ||
strcmp(r->value.update.email, scratch->buf)) {
- r->value.update.email =
- reftable_realloc(r->value.update.email, scratch->len + 1);
+ char *email = reftable_realloc(r->value.update.email, scratch->len + 1);
+ if (!email) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto done;
+ }
+
+ r->value.update.email = email;
memcpy(r->value.update.email, scratch->buf, scratch->len);
r->value.update.email[scratch->len] = 0;
}
ts = 0;
n = get_var_int(&ts, &in);
- if (n < 0)
+ if (n < 0) {
+ err = REFTABLE_FORMAT_ERROR;
goto done;
+ }
string_view_consume(&in, n);
r->value.update.time = ts;
- if (in.len < 2)
+ if (in.len < 2) {
+ err = REFTABLE_FORMAT_ERROR;
goto done;
+ }
r->value.update.tz_offset = get_be16(in.buf);
string_view_consume(&in, 2);
n = decode_string(scratch, in);
- if (n < 0)
+ if (n < 0) {
+ err = REFTABLE_FORMAT_ERROR;
goto done;
+ }
string_view_consume(&in, n);
REFTABLE_ALLOC_GROW(r->value.update.message, scratch->len + 1,
r->value.update.message_cap);
+ if (!r->value.update.message) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto done;
+ }
+
memcpy(r->value.update.message, scratch->buf, scratch->len);
r->value.update.message[scratch->len] = 0;
return start.len - in.len;
done:
- return REFTABLE_FORMAT_ERROR;
+ return err;
}
static int null_streq(const char *a, const char *b)
@@ -1039,11 +1033,6 @@ static int reftable_log_record_is_deletion_void(const void *p)
(const struct reftable_log_record *)p);
}
-static void reftable_log_record_print_void(const void *rec, int hash_size)
-{
- reftable_log_record_print_sz((struct reftable_log_record*)rec, hash_size);
-}
-
static struct reftable_record_vtable reftable_log_record_vtable = {
.key = &reftable_log_record_key,
.type = BLOCK_TYPE_LOG,
@@ -1055,40 +1044,44 @@ static struct reftable_record_vtable reftable_log_record_vtable = {
.is_deletion = &reftable_log_record_is_deletion_void,
.equal = &reftable_log_record_equal_void,
.cmp = &reftable_log_record_cmp_void,
- .print = &reftable_log_record_print_void,
};
-static void reftable_index_record_key(const void *r, struct strbuf *dest)
+static int reftable_index_record_key(const void *r, struct reftable_buf *dest)
{
const struct reftable_index_record *rec = r;
- strbuf_reset(dest);
- strbuf_addbuf(dest, &rec->last_key);
+ reftable_buf_reset(dest);
+ return reftable_buf_add(dest, rec->last_key.buf, rec->last_key.len);
}
-static void reftable_index_record_copy_from(void *rec, const void *src_rec,
- int hash_size)
+static int reftable_index_record_copy_from(void *rec, const void *src_rec,
+ int hash_size UNUSED)
{
struct reftable_index_record *dst = rec;
const struct reftable_index_record *src = src_rec;
+ int err;
- strbuf_reset(&dst->last_key);
- strbuf_addbuf(&dst->last_key, &src->last_key);
+ reftable_buf_reset(&dst->last_key);
+ err = reftable_buf_add(&dst->last_key, src->last_key.buf, src->last_key.len);
+ if (err < 0)
+ return err;
dst->offset = src->offset;
+
+ return 0;
}
static void reftable_index_record_release(void *rec)
{
struct reftable_index_record *idx = rec;
- strbuf_release(&idx->last_key);
+ reftable_buf_release(&idx->last_key);
}
-static uint8_t reftable_index_record_val_type(const void *rec)
+static uint8_t reftable_index_record_val_type(const void *rec UNUSED)
{
return 0;
}
static int reftable_index_record_encode(const void *rec, struct string_view out,
- int hash_size)
+ int hash_size UNUSED)
{
const struct reftable_index_record *r =
(const struct reftable_index_record *)rec;
@@ -1103,16 +1096,20 @@ static int reftable_index_record_encode(const void *rec, struct string_view out,
return start.len - out.len;
}
-static int reftable_index_record_decode(void *rec, struct strbuf key,
- uint8_t val_type, struct string_view in,
- int hash_size, struct strbuf *scratch UNUSED)
+static int reftable_index_record_decode(void *rec, struct reftable_buf key,
+ uint8_t val_type UNUSED,
+ struct string_view in,
+ int hash_size UNUSED,
+ struct reftable_buf *scratch UNUSED)
{
struct string_view start = in;
struct reftable_index_record *r = rec;
- int n = 0;
+ int err, n = 0;
- strbuf_reset(&r->last_key);
- strbuf_addbuf(&r->last_key, &key);
+ reftable_buf_reset(&r->last_key);
+ err = reftable_buf_add(&r->last_key, key.buf, key.len);
+ if (err < 0)
+ return err;
n = get_var_int(&r->offset, &in);
if (n < 0)
@@ -1122,26 +1119,20 @@ static int reftable_index_record_decode(void *rec, struct strbuf key,
return start.len - in.len;
}
-static int reftable_index_record_equal(const void *a, const void *b, int hash_size)
+static int reftable_index_record_equal(const void *a, const void *b,
+ int hash_size UNUSED)
{
struct reftable_index_record *ia = (struct reftable_index_record *) a;
struct reftable_index_record *ib = (struct reftable_index_record *) b;
- return ia->offset == ib->offset && !strbuf_cmp(&ia->last_key, &ib->last_key);
+ return ia->offset == ib->offset && !reftable_buf_cmp(&ia->last_key, &ib->last_key);
}
static int reftable_index_record_cmp(const void *_a, const void *_b)
{
const struct reftable_index_record *a = _a;
const struct reftable_index_record *b = _b;
- return strbuf_cmp(&a->last_key, &b->last_key);
-}
-
-static void reftable_index_record_print(const void *rec, int hash_size)
-{
- const struct reftable_index_record *idx = rec;
- /* TODO: escape null chars? */
- printf("\"%s\" %" PRIu64 "\n", idx->last_key.buf, idx->offset);
+ return reftable_buf_cmp(&a->last_key, &b->last_key);
}
static struct reftable_record_vtable reftable_index_record_vtable = {
@@ -1155,12 +1146,11 @@ static struct reftable_record_vtable reftable_index_record_vtable = {
.is_deletion = &not_a_deletion,
.equal = &reftable_index_record_equal,
.cmp = &reftable_index_record_cmp,
- .print = &reftable_index_record_print,
};
-void reftable_record_key(struct reftable_record *rec, struct strbuf *dest)
+int reftable_record_key(struct reftable_record *rec, struct reftable_buf *dest)
{
- reftable_record_vtable(rec)->key(reftable_record_data(rec), dest);
+ return reftable_record_vtable(rec)->key(reftable_record_data(rec), dest);
}
int reftable_record_encode(struct reftable_record *rec, struct string_view dest,
@@ -1170,14 +1160,14 @@ int reftable_record_encode(struct reftable_record *rec, struct string_view dest,
dest, hash_size);
}
-void reftable_record_copy_from(struct reftable_record *rec,
+int reftable_record_copy_from(struct reftable_record *rec,
struct reftable_record *src, int hash_size)
{
assert(src->type == rec->type);
- reftable_record_vtable(rec)->copy_from(reftable_record_data(rec),
- reftable_record_data(src),
- hash_size);
+ return reftable_record_vtable(rec)->copy_from(reftable_record_data(rec),
+ reftable_record_data(src),
+ hash_size);
}
uint8_t reftable_record_val_type(struct reftable_record *rec)
@@ -1185,9 +1175,9 @@ uint8_t reftable_record_val_type(struct reftable_record *rec)
return reftable_record_vtable(rec)->val_type(reftable_record_data(rec));
}
-int reftable_record_decode(struct reftable_record *rec, struct strbuf key,
+int reftable_record_decode(struct reftable_record *rec, struct reftable_buf key,
uint8_t extra, struct string_view src, int hash_size,
- struct strbuf *scratch)
+ struct reftable_buf *scratch)
{
return reftable_record_vtable(rec)->decode(reftable_record_data(rec),
key, extra, src, hash_size,
@@ -1328,15 +1318,9 @@ void reftable_record_init(struct reftable_record *rec, uint8_t typ)
case BLOCK_TYPE_OBJ:
return;
case BLOCK_TYPE_INDEX:
- strbuf_init(&rec->u.idx.last_key, 0);
+ reftable_buf_init(&rec->u.idx.last_key);
return;
default:
BUG("unhandled record type");
}
}
-
-void reftable_record_print(struct reftable_record *rec, int hash_size)
-{
- printf("'%c': ", rec->type);
- reftable_record_vtable(rec)->print(reftable_record_data(rec), hash_size);
-}
diff --git a/reftable/record.h b/reftable/record.h
index d778133e6e..25aa908c85 100644
--- a/reftable/record.h
+++ b/reftable/record.h
@@ -9,6 +9,7 @@ https://developers.google.com/open-source/licenses/bsd
#ifndef RECORD_H
#define RECORD_H
+#include "basics.h"
#include "system.h"
#include <stdint.h>
@@ -38,13 +39,13 @@ int put_var_int(struct string_view *dest, uint64_t val);
/* Methods for records. */
struct reftable_record_vtable {
- /* encode the key of to a uint8_t strbuf. */
- void (*key)(const void *rec, struct strbuf *dest);
+ /* encode the key of to a uint8_t reftable_buf. */
+ int (*key)(const void *rec, struct reftable_buf *dest);
/* The record type of ('r' for ref). */
uint8_t type;
- void (*copy_from)(void *dest, const void *src, int hash_size);
+ int (*copy_from)(void *dest, const void *src, int hash_size);
/* a value of [0..7], indicating record subvariants (eg. ref vs. symref
* vs ref deletion) */
@@ -54,9 +55,9 @@ struct reftable_record_vtable {
int (*encode)(const void *rec, struct string_view dest, int hash_size);
/* decode data from `src` into the record. */
- int (*decode)(void *rec, struct strbuf key, uint8_t extra,
+ int (*decode)(void *rec, struct reftable_buf key, uint8_t extra,
struct string_view src, int hash_size,
- struct strbuf *scratch);
+ struct reftable_buf *scratch);
/* deallocate and null the record. */
void (*release)(void *rec);
@@ -83,7 +84,7 @@ int reftable_is_block_type(uint8_t typ);
/* 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,
- struct strbuf prev_key, struct strbuf key,
+ struct reftable_buf prev_key, struct reftable_buf key,
uint8_t extra);
/* Decode a record's key lengths. */
@@ -96,13 +97,13 @@ int reftable_decode_keylen(struct string_view in,
* Decode into `last_key` and `extra` from `in`. `last_key` is expected to
* contain the decoded key of the preceding record, if any.
*/
-int reftable_decode_key(struct strbuf *last_key, uint8_t *extra,
+int reftable_decode_key(struct reftable_buf *last_key, uint8_t *extra,
struct string_view in);
/* reftable_index_record are used internally to speed up lookups. */
struct reftable_index_record {
uint64_t offset; /* Offset of block */
- struct strbuf last_key; /* Last key of the block. */
+ struct reftable_buf last_key; /* Last key of the block. */
};
/* reftable_obj_record stores an object ID => ref mapping. */
@@ -136,16 +137,15 @@ void reftable_record_init(struct reftable_record *rec, uint8_t typ);
/* see struct record_vtable */
int reftable_record_cmp(struct reftable_record *a, struct reftable_record *b);
int reftable_record_equal(struct reftable_record *a, struct reftable_record *b, int hash_size);
-void reftable_record_print(struct reftable_record *rec, int hash_size);
-void reftable_record_key(struct reftable_record *rec, struct strbuf *dest);
-void reftable_record_copy_from(struct reftable_record *rec,
- struct reftable_record *src, int hash_size);
+int reftable_record_key(struct reftable_record *rec, struct reftable_buf *dest);
+int reftable_record_copy_from(struct reftable_record *rec,
+ struct reftable_record *src, int hash_size);
uint8_t reftable_record_val_type(struct reftable_record *rec);
int reftable_record_encode(struct reftable_record *rec, struct string_view dest,
int hash_size);
-int reftable_record_decode(struct reftable_record *rec, struct strbuf key,
+int reftable_record_decode(struct reftable_record *rec, struct reftable_buf key,
uint8_t extra, struct string_view src,
- int hash_size, struct strbuf *scratch);
+ int hash_size, struct reftable_buf *scratch);
int reftable_record_is_deletion(struct reftable_record *rec);
static inline uint8_t reftable_record_type(struct reftable_record *rec)
diff --git a/reftable/reftable-basics.h b/reftable/reftable-basics.h
new file mode 100644
index 0000000000..e0397ed583
--- /dev/null
+++ b/reftable/reftable-basics.h
@@ -0,0 +1,31 @@
+/*
+ * 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_BASICS_H
+#define REFTABLE_BASICS_H
+
+#include <stddef.h>
+
+/*
+ * Hash functions understood by the reftable library. Note that the values are
+ * arbitrary and somewhat random such that we can easily detect cases where the
+ * hash hasn't been properly set up.
+ */
+enum reftable_hash {
+ REFTABLE_HASH_SHA1 = 89,
+ REFTABLE_HASH_SHA256 = 247,
+};
+#define REFTABLE_HASH_SIZE_SHA1 20
+#define REFTABLE_HASH_SIZE_SHA256 32
+#define REFTABLE_HASH_SIZE_MAX REFTABLE_HASH_SIZE_SHA256
+
+/* 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
diff --git a/reftable/reftable-error.h b/reftable/reftable-error.h
index 6368cd9ed9..f404826562 100644
--- a/reftable/reftable-error.h
+++ b/reftable/reftable-error.h
@@ -57,6 +57,9 @@ enum reftable_error {
/* Trying to write out-of-date data. */
REFTABLE_OUTDATED_ERROR = -12,
+
+ /* An allocation has failed due to an out-of-memory situation. */
+ REFTABLE_OUT_OF_MEMORY_ERROR = -13,
};
/* convert the numeric error code to a string. The string should not be
diff --git a/reftable/reftable-generic.h b/reftable/reftable-generic.h
deleted file mode 100644
index 65670ea093..0000000000
--- a/reftable/reftable-generic.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
-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"
-
-struct reftable_table_vtable;
-
-/*
- * Provides a unified API for reading tables, either merged tables, or single
- * readers. */
-struct reftable_table {
- struct reftable_table_vtable *ops;
- void *table_arg;
-};
-
-void reftable_table_init_ref_iter(struct reftable_table *tab,
- struct reftable_iterator *it);
-
-void reftable_table_init_log_iter(struct reftable_table *tab,
- struct reftable_iterator *it);
-
-/* returns the hash ID from a generic reftable_table */
-uint32_t reftable_table_hash_id(struct reftable_table *tab);
-
-/* 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);
-
-/* dump table contents onto stdout for debugging */
-int reftable_table_print(struct reftable_table *tab);
-
-#endif
diff --git a/reftable/reftable-malloc.h b/reftable/reftable-malloc.h
deleted file mode 100644
index 5f2185f1f3..0000000000
--- a/reftable/reftable-malloc.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
-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
diff --git a/reftable/reftable-merged.h b/reftable/reftable-merged.h
index 14d5fc9f05..f2d01c3ef8 100644
--- a/reftable/reftable-merged.h
+++ b/reftable/reftable-merged.h
@@ -26,15 +26,23 @@ https://developers.google.com/open-source/licenses/bsd
/* A merged table is implements seeking/iterating over a stack of tables. */
struct reftable_merged_table;
-/* A generic reftable; see below. */
-struct reftable_table;
+struct reftable_reader;
-/* 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, size_t n,
- uint32_t hash_id);
+/*
+ * reftable_merged_table_new creates a new merged table. The readers must be
+ * kept alive as long as the merged table is still in use.
+ */
+int reftable_merged_table_new(struct reftable_merged_table **dest,
+ struct reftable_reader **readers, size_t n,
+ enum reftable_hash hash_id);
+
+/* Initialize a merged table iterator for reading refs. */
+int reftable_merged_table_init_ref_iterator(struct reftable_merged_table *mt,
+ struct reftable_iterator *it);
+
+/* Initialize a merged table iterator for reading logs. */
+int reftable_merged_table_init_log_iterator(struct reftable_merged_table *mt,
+ struct reftable_iterator *it);
/* returns the max update_index covered by this merged table. */
uint64_t
@@ -48,10 +56,6 @@ reftable_merged_table_min_update_index(struct reftable_merged_table *mt);
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);
-
-/* create a generic table from reftable_merged_table */
-void reftable_table_from_merged_table(struct reftable_table *tab,
- struct reftable_merged_table *table);
+enum reftable_hash reftable_merged_table_hash_id(struct reftable_merged_table *m);
#endif
diff --git a/reftable/reftable-reader.h b/reftable/reftable-reader.h
index a32f31d648..0085fbb903 100644
--- a/reftable/reftable-reader.h
+++ b/reftable/reftable-reader.h
@@ -23,32 +23,38 @@
/* The reader struct is a handle to an open reftable file. */
struct reftable_reader;
-/* Generic table. */
-struct reftable_table;
-
-/* reftable_new_reader opens a reftable for reading. If successful,
+/* reftable_reader_new 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(). On error, the block source `src` is
* closed as well.
*/
-int reftable_new_reader(struct reftable_reader **pp,
+int reftable_reader_new(struct reftable_reader **pp,
struct reftable_block_source *src, const char *name);
+/*
+ * Manage the reference count of the reftable reader. A newly initialized
+ * reader starts with a refcount of 1 and will be deleted once the refcount has
+ * reached 0.
+ *
+ * This is required because readers may have longer lifetimes than the stack
+ * they belong to. The stack may for example be reloaded while the old tables
+ * are still being accessed by an iterator.
+ */
+void reftable_reader_incref(struct reftable_reader *reader);
+void reftable_reader_decref(struct reftable_reader *reader);
+
/* Initialize a reftable iterator for reading refs. */
-void reftable_reader_init_ref_iterator(struct reftable_reader *r,
- struct reftable_iterator *it);
+int reftable_reader_init_ref_iterator(struct reftable_reader *r,
+ struct reftable_iterator *it);
/* Initialize a reftable iterator for reading logs. */
-void reftable_reader_init_log_iterator(struct reftable_reader *r,
- struct reftable_iterator *it);
+int reftable_reader_init_log_iterator(struct reftable_reader *r,
+ struct reftable_iterator *it);
/* returns the hash ID used in this table. */
-uint32_t reftable_reader_hash_id(struct reftable_reader *r);
-
-/* closes and deallocates a reader. */
-void reftable_reader_free(struct reftable_reader *);
+enum reftable_hash reftable_reader_hash_id(struct reftable_reader *r);
/* return an iterator for the refs pointing to `oid`. */
int reftable_reader_refs_for(struct reftable_reader *r,
@@ -60,12 +66,6 @@ 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);
-/* creates a generic table from a file reader. */
-void reftable_table_from_reader(struct reftable_table *tab,
- struct reftable_reader *reader);
-
-/* print table onto stdout for debugging. */
-int reftable_reader_print_file(const char *tablename);
/* print blocks onto stdout for debugging. */
int reftable_reader_print_blocks(const char *tablename);
diff --git a/reftable/reftable-record.h b/reftable/reftable-record.h
index ff486eb1f7..ddd48eb579 100644
--- a/reftable/reftable-record.h
+++ b/reftable/reftable-record.h
@@ -9,7 +9,7 @@ https://developers.google.com/open-source/licenses/bsd
#ifndef REFTABLE_RECORD_H
#define REFTABLE_RECORD_H
-#include "hash.h"
+#include "reftable-basics.h"
#include <stdint.h>
/*
@@ -40,10 +40,10 @@ struct reftable_ref_record {
#define REFTABLE_NR_REF_VALUETYPES 4
} value_type;
union {
- unsigned char val1[GIT_MAX_RAWSZ];
+ unsigned char val1[REFTABLE_HASH_SIZE_MAX];
struct {
- unsigned char value[GIT_MAX_RAWSZ]; /* first hash */
- unsigned char target_value[GIT_MAX_RAWSZ]; /* second hash */
+ unsigned char value[REFTABLE_HASH_SIZE_MAX]; /* first hash */
+ unsigned char target_value[REFTABLE_HASH_SIZE_MAX]; /* second hash */
} val2;
char *symref; /* referent, malloced 0-terminated string */
} value;
@@ -60,10 +60,6 @@ const unsigned char *reftable_ref_record_val2(const struct reftable_ref_record *
/* 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. Useful for debugging. */
-void reftable_ref_record_print(const struct reftable_ref_record *ref,
- uint32_t hash_id);
-
/* frees and nulls all pointer values inside `ref`. */
void reftable_ref_record_release(struct reftable_ref_record *ref);
@@ -89,8 +85,8 @@ struct reftable_log_record {
union {
struct {
- unsigned char new_hash[GIT_MAX_RAWSZ];
- unsigned char old_hash[GIT_MAX_RAWSZ];
+ unsigned char new_hash[REFTABLE_HASH_SIZE_MAX];
+ unsigned char old_hash[REFTABLE_HASH_SIZE_MAX];
char *name;
char *email;
uint64_t time;
@@ -111,8 +107,4 @@ void reftable_log_record_release(struct reftable_log_record *log);
int reftable_log_record_equal(const struct reftable_log_record *a,
const 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
diff --git a/reftable/reftable-stack.h b/reftable/reftable-stack.h
index 09e97c9991..ae14270ea7 100644
--- a/reftable/reftable-stack.h
+++ b/reftable/reftable-stack.h
@@ -37,12 +37,21 @@ 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;
+enum {
+ /*
+ * Reload the stack when the stack is out-of-date after locking it.
+ */
+ REFTABLE_STACK_NEW_ADDITION_RELOAD = (1 << 0),
+};
+
/*
* returns a new transaction to add reftables to the given stack. As a side
- * effect, the ref database is locked.
+ * effect, the ref database is locked. Accepts REFTABLE_STACK_NEW_ADDITION_*
+ * flags.
*/
int reftable_stack_new_addition(struct reftable_addition **dest,
- struct reftable_stack *st);
+ struct reftable_stack *st,
+ unsigned int flags);
/* Adds a reftable to transaction. */
int reftable_addition_add(struct reftable_addition *add,
@@ -73,16 +82,16 @@ struct reftable_iterator;
* be used to iterate through refs. The iterator is valid until the next reload
* or write.
*/
-void reftable_stack_init_ref_iterator(struct reftable_stack *st,
- struct reftable_iterator *it);
+int reftable_stack_init_ref_iterator(struct reftable_stack *st,
+ struct reftable_iterator *it);
/*
* Initialize an iterator for the merged tables contained in the stack that can
* be used to iterate through logs. The iterator is valid until the next reload
* or write.
*/
-void reftable_stack_init_log_iterator(struct reftable_stack *st,
- struct reftable_iterator *it);
+int reftable_stack_init_log_iterator(struct reftable_stack *st,
+ struct reftable_iterator *it);
/* returns the merged_table for seeking. This table is valid until the
* next write or reload, and should not be closed or deleted.
@@ -140,7 +149,7 @@ struct reftable_compaction_stats {
struct reftable_compaction_stats *
reftable_stack_compaction_stats(struct reftable_stack *st);
-/* print the entire stack represented by the directory */
-int reftable_stack_print_directory(const char *stackdir, uint32_t hash_id);
+/* Return the hash of the stack. */
+enum reftable_hash reftable_stack_hash_id(struct reftable_stack *st);
#endif
diff --git a/reftable/reftable-tests.h b/reftable/reftable-tests.h
deleted file mode 100644
index 114cc3d053..0000000000
--- a/reftable/reftable-tests.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
-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 pq_test_main(int argc, const char **argv);
-int record_test_main(int argc, const char **argv);
-int readwrite_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
diff --git a/reftable/reftable-writer.h b/reftable/reftable-writer.h
index 189b1f4144..5f9afa620b 100644
--- a/reftable/reftable-writer.h
+++ b/reftable/reftable-writer.h
@@ -33,7 +33,7 @@ struct reftable_write_options {
/* 4-byte identifier ("sha1", "s256") of the hash.
* Defaults to SHA1 if unset
*/
- uint32_t hash_id;
+ enum reftable_hash hash_id;
/* Default mode for creating files. If unset, use 0666 (+umask) */
unsigned int default_permissions;
@@ -51,6 +51,32 @@ struct reftable_write_options {
* tables to compact. Defaults to 2 if unset.
*/
uint8_t auto_compaction_factor;
+
+ /*
+ * The number of milliseconds to wait when trying to lock "tables.list".
+ * Note that this does not apply to locking individual tables, as these
+ * should only ever be locked when already holding the "tables.list"
+ * lock.
+ *
+ * Passing 0 will fail immediately when the file is locked, passing a
+ * negative value will cause us to block indefinitely.
+ */
+ long lock_timeout_ms;
+
+ /*
+ * Optional callback used to fsync files to disk. Falls back to using
+ * fsync(3P) when unset.
+ */
+ int (*fsync)(int fd);
+
+ /*
+ * Callback function to execute whenever the stack is being reloaded.
+ * This can be used e.g. to discard cached information that relies on
+ * the old stack's data. The payload data will be passed as argument to
+ * the callback.
+ */
+ void (*on_reload)(void *payload);
+ void *on_reload_payload;
};
/* reftable_block_stats holds statistics for a single block type */
@@ -90,11 +116,13 @@ struct reftable_stats {
int object_id_len;
};
-/* reftable_new_writer creates a new writer */
-struct reftable_writer *
-reftable_new_writer(ssize_t (*writer_func)(void *, const void *, size_t),
- int (*flush_func)(void *),
- void *writer_arg, const struct reftable_write_options *opts);
+struct reftable_writer;
+
+/* Create a new writer. */
+int reftable_writer_new(struct reftable_writer **out,
+ ssize_t (*writer_func)(void *, const void *, size_t),
+ int (*flush_func)(void *),
+ void *writer_arg, const 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
diff --git a/reftable/stack.c b/reftable/stack.c
index 737591125e..59fd695a12 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -8,17 +8,14 @@ https://developers.google.com/open-source/licenses/bsd
#include "stack.h"
-#include "../write-or-die.h"
#include "system.h"
#include "constants.h"
#include "merged.h"
#include "reader.h"
#include "reftable-error.h"
-#include "reftable-generic.h"
#include "reftable-record.h"
#include "reftable-merged.h"
#include "writer.h"
-#include "tempfile.h"
static int stack_try_add(struct reftable_stack *st,
int (*write_table)(struct reftable_writer *wr,
@@ -32,58 +29,87 @@ static void reftable_addition_close(struct reftable_addition *add);
static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
int reuse_open);
-static void stack_filename(struct strbuf *dest, struct reftable_stack *st,
- const char *name)
+static int stack_filename(struct reftable_buf *dest, struct reftable_stack *st,
+ const char *name)
{
- strbuf_reset(dest);
- strbuf_addstr(dest, st->reftable_dir);
- strbuf_addstr(dest, "/");
- strbuf_addstr(dest, name);
+ int err;
+ reftable_buf_reset(dest);
+ if ((err = reftable_buf_addstr(dest, st->reftable_dir)) < 0 ||
+ (err = reftable_buf_addstr(dest, "/")) < 0 ||
+ (err = reftable_buf_addstr(dest, name)) < 0)
+ return err;
+ return 0;
}
-static ssize_t reftable_fd_write(void *arg, const void *data, size_t sz)
+static int stack_fsync(const struct reftable_write_options *opts, int fd)
{
- int *fdp = (int *)arg;
- return write_in_full(*fdp, data, sz);
+ if (opts->fsync)
+ return opts->fsync(fd);
+ return fsync(fd);
}
-static int reftable_fd_flush(void *arg)
+struct fd_writer {
+ const struct reftable_write_options *opts;
+ int fd;
+};
+
+static ssize_t fd_writer_write(void *arg, const void *data, size_t sz)
{
- int *fdp = (int *)arg;
+ struct fd_writer *writer = arg;
+ return write_in_full(writer->fd, data, sz);
+}
- return fsync_component(FSYNC_COMPONENT_REFERENCE, *fdp);
+static int fd_writer_flush(void *arg)
+{
+ struct fd_writer *writer = arg;
+ return stack_fsync(writer->opts, writer->fd);
}
int reftable_new_stack(struct reftable_stack **dest, const char *dir,
const struct reftable_write_options *_opts)
{
- struct reftable_stack *p = reftable_calloc(1, sizeof(*p));
- struct strbuf list_file_name = STRBUF_INIT;
- struct reftable_write_options opts = {0};
- int err = 0;
+ struct reftable_buf list_file_name = REFTABLE_BUF_INIT;
+ struct reftable_write_options opts = { 0 };
+ struct reftable_stack *p;
+ int err;
+
+ p = reftable_calloc(1, sizeof(*p));
+ if (!p) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
if (_opts)
opts = *_opts;
if (opts.hash_id == 0)
- opts.hash_id = GIT_SHA1_FORMAT_ID;
+ opts.hash_id = REFTABLE_HASH_SHA1;
*dest = NULL;
- strbuf_reset(&list_file_name);
- strbuf_addstr(&list_file_name, dir);
- strbuf_addstr(&list_file_name, "/tables.list");
+ reftable_buf_reset(&list_file_name);
+ if ((err = reftable_buf_addstr(&list_file_name, dir)) < 0 ||
+ (err = reftable_buf_addstr(&list_file_name, "/tables.list")) < 0)
+ goto out;
- p->list_file = strbuf_detach(&list_file_name, NULL);
+ p->list_file = reftable_buf_detach(&list_file_name);
p->list_fd = -1;
- p->reftable_dir = xstrdup(dir);
p->opts = opts;
+ p->reftable_dir = reftable_strdup(dir);
+ if (!p->reftable_dir) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
err = reftable_stack_reload_maybe_reuse(p, 1);
- if (err < 0) {
+ if (err < 0)
+ goto out;
+
+ *dest = p;
+ err = 0;
+
+out:
+ if (err < 0)
reftable_stack_destroy(p);
- } else {
- *dest = p;
- }
return err;
}
@@ -103,13 +129,22 @@ static int fd_read_lines(int fd, char ***namesp)
}
REFTABLE_ALLOC_ARRAY(buf, size + 1);
+ if (!buf) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto done;
+ }
+
if (read_in_full(fd, buf, size) != size) {
err = REFTABLE_IO_ERROR;
goto done;
}
buf[size] = 0;
- parse_names(buf, size, namesp);
+ *namesp = parse_names(buf, size);
+ if (!*namesp) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto done;
+ }
done:
reftable_free(buf);
@@ -123,6 +158,8 @@ int read_lines(const char *filename, char ***namesp)
if (fd < 0) {
if (errno == ENOENT) {
REFTABLE_CALLOC_ARRAY(*namesp, 1);
+ if (!*namesp)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
return 0;
}
@@ -133,18 +170,18 @@ int read_lines(const char *filename, char ***namesp)
return err;
}
-void reftable_stack_init_ref_iterator(struct reftable_stack *st,
+int reftable_stack_init_ref_iterator(struct reftable_stack *st,
struct reftable_iterator *it)
{
- merged_table_init_iter(reftable_stack_merged_table(st),
- it, BLOCK_TYPE_REF);
+ return merged_table_init_iter(reftable_stack_merged_table(st),
+ it, BLOCK_TYPE_REF);
}
-void reftable_stack_init_log_iterator(struct reftable_stack *st,
- struct reftable_iterator *it)
+int reftable_stack_init_log_iterator(struct reftable_stack *st,
+ struct reftable_iterator *it)
{
- merged_table_init_iter(reftable_stack_merged_table(st),
- it, BLOCK_TYPE_LOG);
+ return merged_table_init_iter(reftable_stack_merged_table(st),
+ it, BLOCK_TYPE_LOG);
}
struct reftable_merged_table *
@@ -168,6 +205,10 @@ void reftable_stack_destroy(struct reftable_stack *st)
{
char **names = NULL;
int err = 0;
+
+ if (!st)
+ return;
+
if (st->merged) {
reftable_merged_table_free(st->merged);
st->merged = NULL;
@@ -175,28 +216,31 @@ void reftable_stack_destroy(struct reftable_stack *st)
err = read_lines(st->list_file, &names);
if (err < 0) {
- FREE_AND_NULL(names);
+ REFTABLE_FREE_AND_NULL(names);
}
if (st->readers) {
int i = 0;
- struct strbuf filename = STRBUF_INIT;
+ struct reftable_buf filename = REFTABLE_BUF_INIT;
for (i = 0; i < st->readers_len; i++) {
const char *name = reader_name(st->readers[i]);
- strbuf_reset(&filename);
+ int try_unlinking = 1;
+
+ reftable_buf_reset(&filename);
if (names && !has_name(names, name)) {
- stack_filename(&filename, st, name);
+ if (stack_filename(&filename, st, name) < 0)
+ try_unlinking = 0;
}
- reftable_reader_free(st->readers[i]);
+ reftable_reader_decref(st->readers[i]);
- if (filename.len) {
+ if (try_unlinking && filename.len) {
/* On Windows, can only unlink after closing. */
unlink(filename.buf);
}
}
- strbuf_release(&filename);
+ reftable_buf_release(&filename);
st->readers_len = 0;
- FREE_AND_NULL(st->readers);
+ REFTABLE_FREE_AND_NULL(st->readers);
}
if (st->list_fd >= 0) {
@@ -204,20 +248,20 @@ void reftable_stack_destroy(struct reftable_stack *st)
st->list_fd = -1;
}
- FREE_AND_NULL(st->list_file);
- FREE_AND_NULL(st->reftable_dir);
+ REFTABLE_FREE_AND_NULL(st->list_file);
+ REFTABLE_FREE_AND_NULL(st->reftable_dir);
reftable_free(st);
free_names(names);
}
static struct reftable_reader **stack_copy_readers(struct reftable_stack *st,
- int cur_len)
+ size_t cur_len)
{
struct reftable_reader **cur = reftable_calloc(cur_len, sizeof(*cur));
- int i = 0;
- for (i = 0; i < cur_len; i++) {
+ if (!cur)
+ return NULL;
+ for (size_t i = 0; i < cur_len; i++)
cur[i] = st->readers[i];
- }
return cur;
}
@@ -225,19 +269,31 @@ static int reftable_stack_reload_once(struct reftable_stack *st,
const char **names,
int reuse_open)
{
- size_t cur_len = !st->merged ? 0 : st->merged->stack_len;
- struct reftable_reader **cur = stack_copy_readers(st, cur_len);
- size_t names_len = names_length(names);
- struct reftable_reader **new_readers =
- reftable_calloc(names_len, sizeof(*new_readers));
- struct reftable_table *new_tables =
- reftable_calloc(names_len, sizeof(*new_tables));
+ size_t cur_len = !st->merged ? 0 : st->merged->readers_len;
+ struct reftable_reader **cur;
+ struct reftable_reader **reused = NULL;
+ struct reftable_reader **new_readers;
+ size_t reused_len = 0, reused_alloc = 0, names_len;
size_t new_readers_len = 0;
struct reftable_merged_table *new_merged = NULL;
- struct strbuf table_path = STRBUF_INIT;
+ struct reftable_buf table_path = REFTABLE_BUF_INIT;
int err = 0;
size_t i;
+ cur = stack_copy_readers(st, cur_len);
+ if (!cur) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto done;
+ }
+
+ names_len = names_length(names);
+
+ new_readers = reftable_calloc(names_len, sizeof(*new_readers));
+ if (!new_readers) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto done;
+ }
+
while (*names) {
struct reftable_reader *rd = NULL;
const char *name = *names++;
@@ -248,70 +304,100 @@ static int reftable_stack_reload_once(struct reftable_stack *st,
if (cur[i] && 0 == strcmp(cur[i]->name, name)) {
rd = cur[i];
cur[i] = NULL;
+
+ /*
+ * When reloading the stack fails, we end up
+ * releasing all new readers. This also
+ * includes the reused readers, even though
+ * they are still in used by the old stack. We
+ * thus need to keep them alive here, which we
+ * do by bumping their refcount.
+ */
+ REFTABLE_ALLOC_GROW(reused, reused_len + 1, reused_alloc);
+ if (!reused) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto done;
+ }
+ reused[reused_len++] = rd;
+ reftable_reader_incref(rd);
break;
}
}
if (!rd) {
struct reftable_block_source src = { NULL };
- stack_filename(&table_path, st, name);
+
+ err = stack_filename(&table_path, st, name);
+ if (err < 0)
+ goto done;
err = reftable_block_source_from_file(&src,
table_path.buf);
if (err < 0)
goto done;
- err = reftable_new_reader(&rd, &src, name);
+ err = reftable_reader_new(&rd, &src, name);
if (err < 0)
goto done;
}
new_readers[new_readers_len] = rd;
- reftable_table_from_reader(&new_tables[new_readers_len], rd);
new_readers_len++;
}
/* success! */
- err = reftable_new_merged_table(&new_merged, new_tables,
+ err = reftable_merged_table_new(&new_merged, new_readers,
new_readers_len, st->opts.hash_id);
if (err < 0)
goto done;
- new_tables = NULL;
- st->readers_len = new_readers_len;
- if (st->merged)
- reftable_merged_table_free(st->merged);
- if (st->readers) {
- reftable_free(st->readers);
- }
- st->readers = new_readers;
- new_readers = NULL;
- new_readers_len = 0;
-
- new_merged->suppress_deletions = 1;
- st->merged = new_merged;
+ /*
+ * Close the old, non-reused readers and proactively try to unlink
+ * them. This is done for systems like Windows, where the underlying
+ * file of such an open reader wouldn't have been possible to be
+ * unlinked by the compacting process.
+ */
for (i = 0; i < cur_len; i++) {
if (cur[i]) {
const char *name = reader_name(cur[i]);
- stack_filename(&table_path, st, name);
- reader_close(cur[i]);
- reftable_reader_free(cur[i]);
+ err = stack_filename(&table_path, st, name);
+ if (err < 0)
+ goto done;
- /* On Windows, can only unlink after closing. */
+ reftable_reader_decref(cur[i]);
unlink(table_path.buf);
}
}
+ /* Update the stack to point to the new tables. */
+ if (st->merged)
+ reftable_merged_table_free(st->merged);
+ new_merged->suppress_deletions = 1;
+ st->merged = new_merged;
+
+ if (st->readers)
+ reftable_free(st->readers);
+ st->readers = new_readers;
+ st->readers_len = new_readers_len;
+ new_readers = NULL;
+ new_readers_len = 0;
+
+ /*
+ * Decrement the refcount of reused readers again. This only needs to
+ * happen on the successful case, because on the unsuccessful one we
+ * decrement their refcount via `new_readers`.
+ */
+ for (i = 0; i < reused_len; i++)
+ reftable_reader_decref(reused[i]);
+
done:
- for (i = 0; i < new_readers_len; i++) {
- reader_close(new_readers[i]);
- reftable_reader_free(new_readers[i]);
- }
+ for (i = 0; i < new_readers_len; i++)
+ reftable_reader_decref(new_readers[i]);
reftable_free(new_readers);
- reftable_free(new_tables);
+ reftable_free(reused);
reftable_free(cur);
- strbuf_release(&table_path);
+ reftable_buf_release(&table_path);
return err;
}
@@ -364,6 +450,10 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
}
REFTABLE_CALLOC_ARRAY(names, 1);
+ if (!names) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
} else {
err = fd_read_lines(fd, &names);
if (err < 0)
@@ -458,6 +548,10 @@ out:
close(fd);
free_names(names);
free_names(names_after);
+
+ if (st->opts.on_reload)
+ st->opts.on_reload(st->opts.on_reload_payload);
+
return err;
}
@@ -520,7 +614,7 @@ static int stack_uptodate(struct reftable_stack *st)
}
}
- if (names[st->merged->stack_len]) {
+ if (names[st->merged->readers_len]) {
err = 1;
goto done;
}
@@ -556,18 +650,18 @@ int reftable_stack_add(struct reftable_stack *st,
return 0;
}
-static void format_name(struct strbuf *dest, uint64_t min, uint64_t max)
+static int format_name(struct reftable_buf *dest, uint64_t min, uint64_t max)
{
char buf[100];
uint32_t rnd = (uint32_t)git_rand();
snprintf(buf, sizeof(buf), "0x%012" PRIx64 "-0x%012" PRIx64 "-%08x",
min, max, rnd);
- strbuf_reset(dest);
- strbuf_addstr(dest, buf);
+ reftable_buf_reset(dest);
+ return reftable_buf_addstr(dest, buf);
}
struct reftable_addition {
- struct tempfile *lock_file;
+ struct reftable_flock tables_list_lock;
struct reftable_stack *stack;
char **new_tables;
@@ -578,16 +672,17 @@ struct reftable_addition {
#define REFTABLE_ADDITION_INIT {0}
static int reftable_stack_init_addition(struct reftable_addition *add,
- struct reftable_stack *st)
+ struct reftable_stack *st,
+ unsigned int flags)
{
- struct strbuf lock_file_name = STRBUF_INIT;
- int err = 0;
- add->stack = st;
+ struct reftable_buf lock_file_name = REFTABLE_BUF_INIT;
+ int err;
- strbuf_addf(&lock_file_name, "%s.lock", st->list_file);
+ add->stack = st;
- add->lock_file = create_tempfile(lock_file_name.buf);
- if (!add->lock_file) {
+ err = flock_acquire(&add->tables_list_lock, st->list_file,
+ st->opts.lock_timeout_ms);
+ if (err < 0) {
if (errno == EEXIST) {
err = REFTABLE_LOCK_ERROR;
} else {
@@ -596,7 +691,8 @@ static int reftable_stack_init_addition(struct reftable_addition *add,
goto done;
}
if (st->opts.default_permissions) {
- if (chmod(add->lock_file->filename.buf, st->opts.default_permissions) < 0) {
+ if (chmod(add->tables_list_lock.path,
+ st->opts.default_permissions) < 0) {
err = REFTABLE_IO_ERROR;
goto done;
}
@@ -605,6 +701,11 @@ static int reftable_stack_init_addition(struct reftable_addition *add,
err = stack_uptodate(st);
if (err < 0)
goto done;
+ if (err > 0 && flags & REFTABLE_STACK_NEW_ADDITION_RELOAD) {
+ err = reftable_stack_reload_maybe_reuse(add->stack, 1);
+ if (err)
+ goto done;
+ }
if (err > 0) {
err = REFTABLE_OUTDATED_ERROR;
goto done;
@@ -612,21 +713,20 @@ static int reftable_stack_init_addition(struct reftable_addition *add,
add->next_update_index = reftable_stack_next_update_index(st);
done:
- if (err) {
+ if (err)
reftable_addition_close(add);
- }
- strbuf_release(&lock_file_name);
+ reftable_buf_release(&lock_file_name);
return err;
}
static void reftable_addition_close(struct reftable_addition *add)
{
- struct strbuf nm = STRBUF_INIT;
+ struct reftable_buf nm = REFTABLE_BUF_INIT;
size_t i;
for (i = 0; i < add->new_tables_len; i++) {
- stack_filename(&nm, add->stack, add->new_tables[i]);
- unlink(nm.buf);
+ if (!stack_filename(&nm, add->stack, add->new_tables[i]))
+ unlink(nm.buf);
reftable_free(add->new_tables[i]);
add->new_tables[i] = NULL;
}
@@ -635,8 +735,8 @@ static void reftable_addition_close(struct reftable_addition *add)
add->new_tables_len = 0;
add->new_tables_cap = 0;
- delete_tempfile(&add->lock_file);
- strbuf_release(&nm);
+ flock_release(&add->tables_list_lock);
+ reftable_buf_release(&nm);
}
void reftable_addition_destroy(struct reftable_addition *add)
@@ -650,34 +750,38 @@ void reftable_addition_destroy(struct reftable_addition *add)
int reftable_addition_commit(struct reftable_addition *add)
{
- struct strbuf table_list = STRBUF_INIT;
- int lock_file_fd = get_tempfile_fd(add->lock_file);
+ struct reftable_buf table_list = REFTABLE_BUF_INIT;
int err = 0;
size_t i;
if (add->new_tables_len == 0)
goto done;
- for (i = 0; i < add->stack->merged->stack_len; i++) {
- strbuf_addstr(&table_list, add->stack->readers[i]->name);
- strbuf_addstr(&table_list, "\n");
+ for (i = 0; i < add->stack->merged->readers_len; i++) {
+ if ((err = reftable_buf_addstr(&table_list, add->stack->readers[i]->name)) < 0 ||
+ (err = reftable_buf_addstr(&table_list, "\n")) < 0)
+ goto done;
}
for (i = 0; i < add->new_tables_len; i++) {
- strbuf_addstr(&table_list, add->new_tables[i]);
- strbuf_addstr(&table_list, "\n");
+ if ((err = reftable_buf_addstr(&table_list, add->new_tables[i])) < 0 ||
+ (err = reftable_buf_addstr(&table_list, "\n")) < 0)
+ goto done;
}
- err = write_in_full(lock_file_fd, table_list.buf, table_list.len);
- strbuf_release(&table_list);
+ err = write_in_full(add->tables_list_lock.fd, table_list.buf, table_list.len);
+ reftable_buf_release(&table_list);
if (err < 0) {
err = REFTABLE_IO_ERROR;
goto done;
}
- fsync_component_or_die(FSYNC_COMPONENT_REFERENCE, lock_file_fd,
- get_tempfile_path(add->lock_file));
+ err = stack_fsync(&add->stack->opts, add->tables_list_lock.fd);
+ if (err < 0) {
+ err = REFTABLE_IO_ERROR;
+ goto done;
+ }
- err = rename_tempfile(&add->lock_file, add->stack->list_file);
+ err = flock_commit(&add->tables_list_lock);
if (err < 0) {
err = REFTABLE_IO_ERROR;
goto done;
@@ -715,13 +819,18 @@ done:
}
int reftable_stack_new_addition(struct reftable_addition **dest,
- struct reftable_stack *st)
+ struct reftable_stack *st,
+ unsigned int flags)
{
int err = 0;
struct reftable_addition empty = REFTABLE_ADDITION_INIT;
+
REFTABLE_CALLOC_ARRAY(*dest, 1);
+ if (!*dest)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
+
**dest = empty;
- err = reftable_stack_init_addition(*dest, st);
+ err = reftable_stack_init_addition(*dest, st, flags);
if (err) {
reftable_free(*dest);
*dest = NULL;
@@ -735,7 +844,7 @@ static int stack_try_add(struct reftable_stack *st,
void *arg)
{
struct reftable_addition add = REFTABLE_ADDITION_INIT;
- int err = reftable_stack_init_addition(&add, st);
+ int err = reftable_stack_init_addition(&add, st, 0);
if (err < 0)
goto done;
@@ -754,36 +863,47 @@ int reftable_addition_add(struct reftable_addition *add,
void *arg),
void *arg)
{
- struct strbuf temp_tab_file_name = STRBUF_INIT;
- struct strbuf tab_file_name = STRBUF_INIT;
- struct strbuf next_name = STRBUF_INIT;
+ struct reftable_buf temp_tab_file_name = REFTABLE_BUF_INIT;
+ struct reftable_buf tab_file_name = REFTABLE_BUF_INIT;
+ struct reftable_buf next_name = REFTABLE_BUF_INIT;
struct reftable_writer *wr = NULL;
- struct tempfile *tab_file = NULL;
+ struct reftable_tmpfile tab_file = REFTABLE_TMPFILE_INIT;
+ struct fd_writer writer = {
+ .opts = &add->stack->opts,
+ };
int err = 0;
- int tab_fd;
- strbuf_reset(&next_name);
- format_name(&next_name, add->next_update_index, add->next_update_index);
+ reftable_buf_reset(&next_name);
- stack_filename(&temp_tab_file_name, add->stack, next_name.buf);
- strbuf_addstr(&temp_tab_file_name, ".temp.XXXXXX");
+ err = format_name(&next_name, add->next_update_index, add->next_update_index);
+ if (err < 0)
+ goto done;
- tab_file = mks_tempfile(temp_tab_file_name.buf);
- if (!tab_file) {
- err = REFTABLE_IO_ERROR;
+ err = stack_filename(&temp_tab_file_name, add->stack, next_name.buf);
+ if (err < 0)
+ goto done;
+
+ err = reftable_buf_addstr(&temp_tab_file_name, ".temp.XXXXXX");
+ if (err < 0)
+ goto done;
+
+ err = tmpfile_from_pattern(&tab_file, temp_tab_file_name.buf);
+ if (err < 0)
goto done;
- }
if (add->stack->opts.default_permissions) {
- if (chmod(get_tempfile_path(tab_file),
+ if (chmod(tab_file.path,
add->stack->opts.default_permissions)) {
err = REFTABLE_IO_ERROR;
goto done;
}
}
- tab_fd = get_tempfile_fd(tab_file);
- wr = reftable_new_writer(reftable_fd_write, reftable_fd_flush, &tab_fd,
- &add->stack->opts);
+ writer.fd = tab_file.fd;
+ err = reftable_writer_new(&wr, fd_writer_write, fd_writer_flush,
+ &writer, &add->stack->opts);
+ if (err < 0)
+ goto done;
+
err = write_table(wr, arg);
if (err < 0)
goto done;
@@ -796,46 +916,55 @@ int reftable_addition_add(struct reftable_addition *add,
if (err < 0)
goto done;
- err = close_tempfile_gently(tab_file);
- if (err < 0) {
- err = REFTABLE_IO_ERROR;
+ err = tmpfile_close(&tab_file);
+ if (err < 0)
goto done;
- }
if (wr->min_update_index < add->next_update_index) {
err = REFTABLE_API_ERROR;
goto done;
}
- format_name(&next_name, wr->min_update_index, wr->max_update_index);
- strbuf_addstr(&next_name, ".ref");
- stack_filename(&tab_file_name, add->stack, next_name.buf);
+ err = format_name(&next_name, wr->min_update_index, wr->max_update_index);
+ if (err < 0)
+ goto done;
+
+ err = reftable_buf_addstr(&next_name, ".ref");
+ if (err < 0)
+ goto done;
+
+ err = stack_filename(&tab_file_name, add->stack, next_name.buf);
+ if (err < 0)
+ goto done;
/*
On windows, this relies on rand() picking a unique destination name.
Maybe we should do retry loop as well?
*/
- err = rename_tempfile(&tab_file, tab_file_name.buf);
- if (err < 0) {
- err = REFTABLE_IO_ERROR;
+ err = tmpfile_rename(&tab_file, tab_file_name.buf);
+ if (err < 0)
goto done;
- }
REFTABLE_ALLOC_GROW(add->new_tables, add->new_tables_len + 1,
add->new_tables_cap);
- add->new_tables[add->new_tables_len++] = strbuf_detach(&next_name, NULL);
+ if (!add->new_tables) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto done;
+ }
+ add->new_tables[add->new_tables_len++] = reftable_buf_detach(&next_name);
+
done:
- delete_tempfile(&tab_file);
- strbuf_release(&temp_tab_file_name);
- strbuf_release(&tab_file_name);
- strbuf_release(&next_name);
+ tmpfile_delete(&tab_file);
+ reftable_buf_release(&temp_tab_file_name);
+ reftable_buf_release(&tab_file_name);
+ reftable_buf_release(&next_name);
reftable_writer_free(wr);
return err;
}
uint64_t reftable_stack_next_update_index(struct reftable_stack *st)
{
- int sz = st->merged->stack_len;
+ int sz = st->merged->readers_len;
if (sz > 0)
return reftable_reader_max_update_index(st->readers[sz - 1]) +
1;
@@ -845,35 +974,46 @@ uint64_t reftable_stack_next_update_index(struct reftable_stack *st)
static int stack_compact_locked(struct reftable_stack *st,
size_t first, size_t last,
struct reftable_log_expiry_config *config,
- struct tempfile **tab_file_out)
+ struct reftable_tmpfile *tab_file_out)
{
- struct strbuf next_name = STRBUF_INIT;
- struct strbuf tab_file_path = STRBUF_INIT;
+ struct reftable_buf next_name = REFTABLE_BUF_INIT;
+ struct reftable_buf tab_file_path = REFTABLE_BUF_INIT;
struct reftable_writer *wr = NULL;
- struct tempfile *tab_file;
- int tab_fd, err = 0;
+ struct fd_writer writer= {
+ .opts = &st->opts,
+ };
+ struct reftable_tmpfile tab_file = REFTABLE_TMPFILE_INIT;
+ int err = 0;
+
+ err = format_name(&next_name, reftable_reader_min_update_index(st->readers[first]),
+ reftable_reader_max_update_index(st->readers[last]));
+ if (err < 0)
+ goto done;
- format_name(&next_name,
- reftable_reader_min_update_index(st->readers[first]),
- reftable_reader_max_update_index(st->readers[last]));
- stack_filename(&tab_file_path, st, next_name.buf);
- strbuf_addstr(&tab_file_path, ".temp.XXXXXX");
+ err = stack_filename(&tab_file_path, st, next_name.buf);
+ if (err < 0)
+ goto done;
- tab_file = mks_tempfile(tab_file_path.buf);
- if (!tab_file) {
- err = REFTABLE_IO_ERROR;
+ err = reftable_buf_addstr(&tab_file_path, ".temp.XXXXXX");
+ if (err < 0)
+ goto done;
+
+ err = tmpfile_from_pattern(&tab_file, tab_file_path.buf);
+ if (err < 0)
goto done;
- }
- tab_fd = get_tempfile_fd(tab_file);
if (st->opts.default_permissions &&
- chmod(get_tempfile_path(tab_file), st->opts.default_permissions) < 0) {
+ chmod(tab_file.path, st->opts.default_permissions) < 0) {
err = REFTABLE_IO_ERROR;
goto done;
}
- wr = reftable_new_writer(reftable_fd_write, reftable_fd_flush,
- &tab_fd, &st->opts);
+ writer.fd = tab_file.fd;
+ err = reftable_writer_new(&wr, fd_writer_write, fd_writer_flush,
+ &writer, &st->opts);
+ if (err < 0)
+ goto done;
+
err = stack_write_compact(st, wr, first, last, config);
if (err < 0)
goto done;
@@ -882,18 +1022,18 @@ static int stack_compact_locked(struct reftable_stack *st,
if (err < 0)
goto done;
- err = close_tempfile_gently(tab_file);
+ err = tmpfile_close(&tab_file);
if (err < 0)
goto done;
*tab_file_out = tab_file;
- tab_file = NULL;
+ tab_file = REFTABLE_TMPFILE_INIT;
done:
- delete_tempfile(&tab_file);
+ tmpfile_delete(&tab_file);
reftable_writer_free(wr);
- strbuf_release(&next_name);
- strbuf_release(&tab_file_path);
+ reftable_buf_release(&next_name);
+ reftable_buf_release(&tab_file_path);
return err;
}
@@ -902,32 +1042,28 @@ static int stack_write_compact(struct reftable_stack *st,
size_t first, size_t last,
struct reftable_log_expiry_config *config)
{
- size_t subtabs_len = last - first + 1;
- struct reftable_table *subtabs = reftable_calloc(
- last - first + 1, sizeof(*subtabs));
struct reftable_merged_table *mt = NULL;
struct reftable_iterator it = { NULL };
struct reftable_ref_record ref = { NULL };
struct reftable_log_record log = { NULL };
+ size_t subtabs_len = last - first + 1;
uint64_t entries = 0;
int err = 0;
- for (size_t i = first, j = 0; i <= last; i++) {
- struct reftable_reader *t = st->readers[i];
- reftable_table_from_reader(&subtabs[j++], t);
- st->stats.bytes += t->size;
- }
+ for (size_t i = first; i <= last; i++)
+ st->stats.bytes += st->readers[i]->size;
reftable_writer_set_limits(wr, st->readers[first]->min_update_index,
st->readers[last]->max_update_index);
- err = reftable_new_merged_table(&mt, subtabs, subtabs_len,
+ err = reftable_merged_table_new(&mt, st->readers + first, subtabs_len,
st->opts.hash_id);
- if (err < 0) {
- reftable_free(subtabs);
+ if (err < 0)
+ goto done;
+
+ err = merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
+ if (err < 0)
goto done;
- }
- merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
err = reftable_iterator_seek_ref(&it, "");
if (err < 0)
goto done;
@@ -952,7 +1088,10 @@ static int stack_write_compact(struct reftable_stack *st,
}
reftable_iterator_destroy(&it);
- merged_table_init_iter(mt, &it, BLOCK_TYPE_LOG);
+ err = merged_table_init_iter(mt, &it, BLOCK_TYPE_LOG);
+ if (err < 0)
+ goto done;
+
err = reftable_iterator_seek_log(&it, "");
if (err < 0)
goto done;
@@ -995,6 +1134,15 @@ done:
return err;
}
+enum stack_compact_range_flags {
+ /*
+ * Perform a best-effort compaction. That is, even if we cannot lock
+ * all tables in the specified range, we will try to compact the
+ * remaining slice.
+ */
+ STACK_COMPACT_RANGE_BEST_EFFORT = (1 << 0),
+};
+
/*
* Compact all tables in the range `[first, last)` into a single new table.
*
@@ -1006,17 +1154,20 @@ done:
*/
static int stack_compact_range(struct reftable_stack *st,
size_t first, size_t last,
- struct reftable_log_expiry_config *expiry)
+ struct reftable_log_expiry_config *expiry,
+ unsigned int flags)
{
- struct strbuf tables_list_buf = STRBUF_INIT;
- struct strbuf new_table_name = STRBUF_INIT;
- struct strbuf new_table_path = STRBUF_INIT;
- struct strbuf table_name = STRBUF_INIT;
- struct lock_file tables_list_lock = LOCK_INIT;
- struct lock_file *table_locks = NULL;
- struct tempfile *new_table = NULL;
+ struct reftable_buf tables_list_buf = REFTABLE_BUF_INIT;
+ struct reftable_buf new_table_name = REFTABLE_BUF_INIT;
+ struct reftable_buf new_table_path = REFTABLE_BUF_INIT;
+ struct reftable_buf table_name = REFTABLE_BUF_INIT;
+ struct reftable_flock tables_list_lock = REFTABLE_FLOCK_INIT;
+ struct reftable_flock *table_locks = NULL;
+ struct reftable_tmpfile new_table = REFTABLE_TMPFILE_INIT;
int is_empty_table = 0, err = 0;
- size_t i;
+ size_t first_to_replace, last_to_replace;
+ size_t i, nlocks = 0;
+ char **names = NULL;
if (first > last || (!expiry && first == last)) {
err = 0;
@@ -1029,8 +1180,7 @@ static int stack_compact_range(struct reftable_stack *st,
* Hold the lock so that we can read "tables.list" and lock all tables
* which are part of the user-specified range.
*/
- err = hold_lock_file_for_update(&tables_list_lock, st->list_file,
- LOCK_NO_DEREF);
+ err = flock_acquire(&tables_list_lock, st->list_file, st->opts.lock_timeout_ms);
if (err < 0) {
if (errno == EEXIST)
err = REFTABLE_LOCK_ERROR;
@@ -1046,19 +1196,55 @@ static int stack_compact_range(struct reftable_stack *st,
/*
* Lock all tables in the user-provided range. This is the slice of our
* stack which we'll compact.
+ *
+ * Note that we lock tables in reverse order from last to first. The
+ * intent behind this is to allow a newer process to perform best
+ * effort compaction of tables that it has added in the case where an
+ * older process is still busy compacting tables which are preexisting
+ * from the point of view of the newer process.
*/
- REFTABLE_CALLOC_ARRAY(table_locks, last - first + 1);
- for (i = first; i <= last; i++) {
- stack_filename(&table_name, st, reader_name(st->readers[i]));
+ REFTABLE_ALLOC_ARRAY(table_locks, last - first + 1);
+ if (!table_locks) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto done;
+ }
+ for (i = 0; i < last - first + 1; i++)
+ table_locks[i] = REFTABLE_FLOCK_INIT;
+
+ for (i = last + 1; i > first; i--) {
+ err = stack_filename(&table_name, st, reader_name(st->readers[i - 1]));
+ if (err < 0)
+ goto done;
- err = hold_lock_file_for_update(&table_locks[i - first],
- table_name.buf, LOCK_NO_DEREF);
+ err = flock_acquire(&table_locks[nlocks], table_name.buf, 0);
if (err < 0) {
- if (errno == EEXIST)
+ /*
+ * When the table is locked already we may do a
+ * best-effort compaction and compact only the tables
+ * that we have managed to lock so far. This of course
+ * requires that we have been able to lock at least two
+ * tables, otherwise there would be nothing to compact.
+ * In that case, we return a lock error to our caller.
+ */
+ if (errno == EEXIST && last - (i - 1) >= 2 &&
+ flags & STACK_COMPACT_RANGE_BEST_EFFORT) {
+ err = 0;
+ /*
+ * The subtraction is to offset the index, the
+ * addition is to only compact up to the table
+ * of the preceding iteration. They obviously
+ * cancel each other out, but that may be
+ * non-obvious when it was omitted.
+ */
+ first = (i - 1) + 1;
+ break;
+ } else if (errno == EEXIST) {
err = REFTABLE_LOCK_ERROR;
- else
+ goto done;
+ } else {
err = REFTABLE_IO_ERROR;
- goto done;
+ goto done;
+ }
}
/*
@@ -1066,7 +1252,7 @@ static int stack_compact_range(struct reftable_stack *st,
* run into file descriptor exhaustion when we compress a lot
* of tables.
*/
- err = close_lock_file_gently(&table_locks[i - first]);
+ err = flock_close(&table_locks[nlocks++]);
if (err < 0) {
err = REFTABLE_IO_ERROR;
goto done;
@@ -1078,7 +1264,7 @@ static int stack_compact_range(struct reftable_stack *st,
* "tables.list" lock while compacting the locked tables. This allows
* concurrent updates to the stack to proceed.
*/
- err = rollback_lock_file(&tables_list_lock);
+ err = flock_release(&tables_list_lock);
if (err < 0) {
err = REFTABLE_IO_ERROR;
goto done;
@@ -1101,8 +1287,7 @@ static int stack_compact_range(struct reftable_stack *st,
* "tables.list". We'll then replace the compacted range of tables with
* the new table.
*/
- err = hold_lock_file_for_update(&tables_list_lock, st->list_file,
- LOCK_NO_DEREF);
+ err = flock_acquire(&tables_list_lock, st->list_file, st->opts.lock_timeout_ms);
if (err < 0) {
if (errno == EEXIST)
err = REFTABLE_LOCK_ERROR;
@@ -1112,7 +1297,7 @@ static int stack_compact_range(struct reftable_stack *st,
}
if (st->opts.default_permissions) {
- if (chmod(get_lock_file_path(&tables_list_lock),
+ if (chmod(tables_list_lock.path,
st->opts.default_permissions) < 0) {
err = REFTABLE_IO_ERROR;
goto done;
@@ -1120,20 +1305,130 @@ static int stack_compact_range(struct reftable_stack *st,
}
/*
+ * As we have unlocked the stack while compacting our slice of tables
+ * it may have happened that a concurrently running process has updated
+ * the stack while we were compacting. In that case, we need to check
+ * whether the tables that we have just compacted still exist in the
+ * stack in the exact same order as we have compacted them.
+ *
+ * If they do exist, then it is fine to continue and replace those
+ * tables with our compacted version. If they don't, then we need to
+ * abort.
+ */
+ err = stack_uptodate(st);
+ if (err < 0)
+ goto done;
+ if (err > 0) {
+ ssize_t new_offset = -1;
+ int fd;
+
+ fd = open(st->list_file, O_RDONLY);
+ if (fd < 0) {
+ err = REFTABLE_IO_ERROR;
+ goto done;
+ }
+
+ err = fd_read_lines(fd, &names);
+ close(fd);
+ if (err < 0)
+ goto done;
+
+ /*
+ * Search for the offset of the first table that we have
+ * compacted in the updated "tables.list" file.
+ */
+ for (size_t i = 0; names[i]; i++) {
+ if (strcmp(names[i], st->readers[first]->name))
+ continue;
+
+ /*
+ * We have found the first entry. Verify that all the
+ * subsequent tables we have compacted still exist in
+ * the modified stack in the exact same order as we
+ * have compacted them.
+ */
+ for (size_t j = 1; j < last - first + 1; j++) {
+ const char *old = first + j < st->merged->readers_len ?
+ st->readers[first + j]->name : NULL;
+ const char *new = names[i + j];
+
+ /*
+ * If some entries are missing or in case the tables
+ * have changed then we need to bail out. Again, this
+ * shouldn't ever happen because we have locked the
+ * tables we are compacting.
+ */
+ if (!old || !new || strcmp(old, new)) {
+ err = REFTABLE_OUTDATED_ERROR;
+ goto done;
+ }
+ }
+
+ new_offset = i;
+ break;
+ }
+
+ /*
+ * In case we didn't find our compacted tables in the stack we
+ * need to bail out. In theory, this should have never happened
+ * because we locked the tables we are compacting.
+ */
+ if (new_offset < 0) {
+ err = REFTABLE_OUTDATED_ERROR;
+ goto done;
+ }
+
+ /*
+ * We have found the new range that we want to replace, so
+ * let's update the range of tables that we want to replace.
+ */
+ first_to_replace = new_offset;
+ last_to_replace = last + (new_offset - first);
+ } else {
+ /*
+ * `fd_read_lines()` uses a `NULL` sentinel to indicate that
+ * the array is at its end. As we use `free_names()` to free
+ * the array, we need to include this sentinel value here and
+ * thus have to allocate `readers_len + 1` many entries.
+ */
+ REFTABLE_CALLOC_ARRAY(names, st->merged->readers_len + 1);
+ if (!names) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto done;
+ }
+
+ for (size_t i = 0; i < st->merged->readers_len; i++) {
+ names[i] = reftable_strdup(st->readers[i]->name);
+ if (!names[i]) {
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto done;
+ }
+ }
+ first_to_replace = first;
+ last_to_replace = last;
+ }
+
+ /*
* If the resulting compacted table is not empty, then we need to move
* it into place now.
*/
if (!is_empty_table) {
- format_name(&new_table_name, st->readers[first]->min_update_index,
- st->readers[last]->max_update_index);
- strbuf_addstr(&new_table_name, ".ref");
- stack_filename(&new_table_path, st, new_table_name.buf);
+ err = format_name(&new_table_name, st->readers[first]->min_update_index,
+ st->readers[last]->max_update_index);
+ if (err < 0)
+ goto done;
- err = rename_tempfile(&new_table, new_table_path.buf);
- if (err < 0) {
- err = REFTABLE_IO_ERROR;
+ err = reftable_buf_addstr(&new_table_name, ".ref");
+ if (err < 0)
+ goto done;
+
+ err = stack_filename(&new_table_path, st, new_table_name.buf);
+ if (err < 0)
+ goto done;
+
+ err = tmpfile_rename(&new_table, new_table_path.buf);
+ if (err < 0)
goto done;
- }
}
/*
@@ -1141,14 +1436,23 @@ static int stack_compact_range(struct reftable_stack *st,
* have just written. In case the compacted table became empty we
* simply skip writing it.
*/
- for (i = 0; i < first; i++)
- strbuf_addf(&tables_list_buf, "%s\n", st->readers[i]->name);
- if (!is_empty_table)
- strbuf_addf(&tables_list_buf, "%s\n", new_table_name.buf);
- for (i = last + 1; i < st->merged->stack_len; i++)
- strbuf_addf(&tables_list_buf, "%s\n", st->readers[i]->name);
-
- err = write_in_full(get_lock_file_fd(&tables_list_lock),
+ for (i = 0; i < first_to_replace; i++) {
+ if ((err = reftable_buf_addstr(&tables_list_buf, names[i])) < 0 ||
+ (err = reftable_buf_addstr(&tables_list_buf, "\n")) < 0)
+ goto done;
+ }
+ if (!is_empty_table) {
+ if ((err = reftable_buf_addstr(&tables_list_buf, new_table_name.buf)) < 0 ||
+ (err = reftable_buf_addstr(&tables_list_buf, "\n")) < 0)
+ goto done;
+ }
+ for (i = last_to_replace + 1; names[i]; i++) {
+ if ((err = reftable_buf_addstr(&tables_list_buf, names[i])) < 0 ||
+ (err = reftable_buf_addstr(&tables_list_buf, "\n")) < 0)
+ goto done;
+ }
+
+ err = write_in_full(tables_list_lock.fd,
tables_list_buf.buf, tables_list_buf.len);
if (err < 0) {
err = REFTABLE_IO_ERROR;
@@ -1156,14 +1460,14 @@ static int stack_compact_range(struct reftable_stack *st,
goto done;
}
- err = fsync_component(FSYNC_COMPONENT_REFERENCE, get_lock_file_fd(&tables_list_lock));
+ err = stack_fsync(&st->opts, tables_list_lock.fd);
if (err < 0) {
err = REFTABLE_IO_ERROR;
unlink(new_table_path.buf);
goto done;
}
- err = commit_lock_file(&tables_list_lock);
+ err = flock_commit(&tables_list_lock);
if (err < 0) {
err = REFTABLE_IO_ERROR;
unlink(new_table_path.buf);
@@ -1183,43 +1487,42 @@ static int stack_compact_range(struct reftable_stack *st,
* Delete the old tables. They may still be in use by concurrent
* readers, so it is expected that unlinking tables may fail.
*/
- for (i = first; i <= last; i++) {
- struct lock_file *table_lock = &table_locks[i - first];
- char *table_path = get_locked_file_path(table_lock);
- unlink(table_path);
- free(table_path);
+ for (i = 0; i < nlocks; i++) {
+ struct reftable_flock *table_lock = &table_locks[i];
+
+ reftable_buf_reset(&table_name);
+ err = reftable_buf_add(&table_name, table_lock->path,
+ strlen(table_lock->path) - strlen(".lock"));
+ if (err)
+ continue;
+
+ unlink(table_name.buf);
}
done:
- rollback_lock_file(&tables_list_lock);
- for (i = first; table_locks && i <= last; i++)
- rollback_lock_file(&table_locks[i - first]);
+ flock_release(&tables_list_lock);
+ for (i = 0; table_locks && i < nlocks; i++)
+ flock_release(&table_locks[i]);
reftable_free(table_locks);
- delete_tempfile(&new_table);
- strbuf_release(&new_table_name);
- strbuf_release(&new_table_path);
+ tmpfile_delete(&new_table);
+ reftable_buf_release(&new_table_name);
+ reftable_buf_release(&new_table_path);
+ reftable_buf_release(&tables_list_buf);
+ reftable_buf_release(&table_name);
+ free_names(names);
+
+ if (err == REFTABLE_LOCK_ERROR)
+ st->stats.failures++;
- strbuf_release(&tables_list_buf);
- strbuf_release(&table_name);
return err;
}
int reftable_stack_compact_all(struct reftable_stack *st,
struct reftable_log_expiry_config *config)
{
- return stack_compact_range(st, 0, st->merged->stack_len ?
- st->merged->stack_len - 1 : 0, config);
-}
-
-static int stack_compact_range_stats(struct reftable_stack *st,
- size_t first, size_t last,
- struct reftable_log_expiry_config *config)
-{
- int err = stack_compact_range(st, first, last, config);
- if (err == REFTABLE_LOCK_ERROR)
- st->stats.failures++;
- return err;
+ size_t last = st->merged->readers_len ? st->merged->readers_len - 1 : 0;
+ return stack_compact_range(st, 0, last, config, 0);
}
static int segment_size(struct segment *s)
@@ -1305,27 +1608,36 @@ struct segment suggest_compaction_segment(uint64_t *sizes, size_t n,
static uint64_t *stack_table_sizes_for_compaction(struct reftable_stack *st)
{
- uint64_t *sizes =
- reftable_calloc(st->merged->stack_len, sizeof(*sizes));
- int version = (st->opts.hash_id == GIT_SHA1_FORMAT_ID) ? 1 : 2;
+ int version = (st->opts.hash_id == REFTABLE_HASH_SHA1) ? 1 : 2;
int overhead = header_size(version) - 1;
- int i = 0;
- for (i = 0; i < st->merged->stack_len; i++) {
+ uint64_t *sizes;
+
+ REFTABLE_CALLOC_ARRAY(sizes, st->merged->readers_len);
+ if (!sizes)
+ return NULL;
+
+ for (size_t i = 0; i < st->merged->readers_len; i++)
sizes[i] = st->readers[i]->size - overhead;
- }
+
return sizes;
}
int reftable_stack_auto_compact(struct reftable_stack *st)
{
- uint64_t *sizes = stack_table_sizes_for_compaction(st);
- struct segment seg =
- suggest_compaction_segment(sizes, st->merged->stack_len,
- st->opts.auto_compaction_factor);
+ struct segment seg;
+ uint64_t *sizes;
+
+ sizes = stack_table_sizes_for_compaction(st);
+ if (!sizes)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
+
+ seg = suggest_compaction_segment(sizes, st->merged->readers_len,
+ st->opts.auto_compaction_factor);
reftable_free(sizes);
+
if (segment_size(&seg) > 0)
- return stack_compact_range_stats(st, seg.start, seg.end - 1,
- NULL);
+ return stack_compact_range(st, seg.start, seg.end - 1,
+ NULL, STACK_COMPACT_RANGE_BEST_EFFORT);
return 0;
}
@@ -1339,9 +1651,31 @@ reftable_stack_compaction_stats(struct reftable_stack *st)
int reftable_stack_read_ref(struct reftable_stack *st, const char *refname,
struct reftable_ref_record *ref)
{
- struct reftable_table tab = { NULL };
- reftable_table_from_merged_table(&tab, reftable_stack_merged_table(st));
- return reftable_table_read_ref(&tab, refname, ref);
+ struct reftable_iterator it = { 0 };
+ int ret;
+
+ ret = reftable_merged_table_init_ref_iterator(st->merged, &it);
+ if (ret)
+ goto out;
+
+ ret = reftable_iterator_seek_ref(&it, refname);
+ if (ret)
+ goto out;
+
+ ret = reftable_iterator_next_ref(&it, ref);
+ if (ret)
+ goto out;
+
+ if (strcmp(ref->refname, refname) ||
+ reftable_ref_record_is_deletion(ref)) {
+ reftable_ref_record_release(ref);
+ ret = 1;
+ goto out;
+ }
+
+out:
+ reftable_iterator_destroy(&it);
+ return ret;
}
int reftable_stack_read_log(struct reftable_stack *st, const char *refname,
@@ -1350,7 +1684,10 @@ int reftable_stack_read_log(struct reftable_stack *st, const char *refname,
struct reftable_iterator it = {0};
int err;
- reftable_stack_init_log_iterator(st, &it);
+ err = reftable_stack_init_log_iterator(st, &it);
+ if (err)
+ goto done;
+
err = reftable_iterator_seek_log(&it, refname);
if (err)
goto done;
@@ -1386,25 +1723,28 @@ static void remove_maybe_stale_table(struct reftable_stack *st, uint64_t max,
uint64_t update_idx = 0;
struct reftable_block_source src = { NULL };
struct reftable_reader *rd = NULL;
- struct strbuf table_path = STRBUF_INIT;
- stack_filename(&table_path, st, name);
+ struct reftable_buf table_path = REFTABLE_BUF_INIT;
+
+ err = stack_filename(&table_path, st, name);
+ if (err < 0)
+ goto done;
err = reftable_block_source_from_file(&src, table_path.buf);
if (err < 0)
goto done;
- err = reftable_new_reader(&rd, &src, name);
+ err = reftable_reader_new(&rd, &src, name);
if (err < 0)
goto done;
update_idx = reftable_reader_max_update_index(rd);
- reftable_reader_free(rd);
+ reftable_reader_decref(rd);
if (update_idx <= max) {
unlink(table_path.buf);
}
done:
- strbuf_release(&table_path);
+ reftable_buf_release(&table_path);
}
static int reftable_stack_clean_locked(struct reftable_stack *st)
@@ -1439,7 +1779,7 @@ static int reftable_stack_clean_locked(struct reftable_stack *st)
int reftable_stack_clean(struct reftable_stack *st)
{
struct reftable_addition *add = NULL;
- int err = reftable_stack_new_addition(&add, st);
+ int err = reftable_stack_new_addition(&add, st, 0);
if (err < 0) {
goto done;
}
@@ -1456,22 +1796,7 @@ done:
return err;
}
-int reftable_stack_print_directory(const char *stackdir, uint32_t hash_id)
+enum reftable_hash reftable_stack_hash_id(struct reftable_stack *st)
{
- struct reftable_stack *stack = NULL;
- struct reftable_write_options opts = { .hash_id = hash_id };
- struct reftable_merged_table *merged = NULL;
- struct reftable_table table = { NULL };
-
- int err = reftable_new_stack(&stack, stackdir, &opts);
- if (err < 0)
- goto done;
-
- merged = reftable_stack_merged_table(stack);
- reftable_table_from_merged_table(&table, merged);
- err = reftable_table_print(&table);
-done:
- if (stack)
- reftable_stack_destroy(stack);
- return err;
+ return reftable_merged_table_hash_id(st->merged);
}
diff --git a/reftable/stack_test.c b/reftable/stack_test.c
deleted file mode 100644
index e3c11e6a6e..0000000000
--- a/reftable/stack_test.c
+++ /dev/null
@@ -1,1034 +0,0 @@
-/*
-Copyright 2020 Google LLC
-
-Use of this source code is governed by a BSD-style
-license that can be found in the LICENSE file or at
-https://developers.google.com/open-source/licenses/bsd
-*/
-
-#include "stack.h"
-
-#include "system.h"
-
-#include "reftable-reader.h"
-#include "merged.h"
-#include "basics.h"
-#include "record.h"
-#include "test_framework.h"
-#include "reftable-tests.h"
-#include "reader.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 int count_dir_entries(const char *dirname)
-{
- DIR *dir = opendir(dirname);
- int len = 0;
- struct dirent *d;
- if (!dir)
- return 0;
-
- while ((d = readdir(dir))) {
- /*
- * Besides skipping over "." and "..", we also need to
- * skip over other files that have a leading ".". This
- * is due to behaviour of NFS, which will rename files
- * to ".nfs*" to emulate delete-on-last-close.
- *
- * In any case this should be fine as the reftable
- * library will never write files with leading dots
- * anyway.
- */
- if (starts_with(d->d_name, "."))
- continue;
- len++;
- }
- closedir(dir);
- return len;
-}
-
-/*
- * Work linenumber into the tempdir, so we can see which tests forget to
- * cleanup.
- */
-static char *get_tmp_template(int linenumber)
-{
- const char *tmp = getenv("TMPDIR");
- static char template[1024];
- snprintf(template, sizeof(template) - 1, "%s/stack_test-%d.XXXXXX",
- tmp ? tmp : "/tmp", linenumber);
- return template;
-}
-
-static char *get_tmp_dir(int linenumber)
-{
- char *dir = get_tmp_template(linenumber);
- EXPECT(mkdtemp(dir));
- return dir;
-}
-
-static void test_read_file(void)
-{
- char *fn = get_tmp_template(__LINE__);
- int fd = mkstemp(fn);
- char out[1024] = "line1\n\nline2\nline3";
- int n, err;
- char **names = NULL;
- const char *want[] = { "line1", "line2", "line3" };
- int i = 0;
-
- EXPECT(fd > 0);
- n = write_in_full(fd, out, strlen(out));
- EXPECT(n == strlen(out));
- err = close(fd);
- EXPECT(err >= 0);
-
- err = read_lines(fn, &names);
- EXPECT_ERR(err);
-
- for (i = 0; names[i]; i++) {
- EXPECT(0 == strcmp(want[i], names[i]));
- }
- free_names(names);
- (void) remove(fn);
-}
-
-static int write_test_ref(struct reftable_writer *wr, void *arg)
-{
- struct reftable_ref_record *ref = arg;
- reftable_writer_set_limits(wr, ref->update_index, ref->update_index);
- return reftable_writer_add_ref(wr, ref);
-}
-
-struct write_log_arg {
- struct reftable_log_record *log;
- uint64_t update_index;
-};
-
-static int write_test_log(struct reftable_writer *wr, void *arg)
-{
- struct write_log_arg *wla = arg;
-
- reftable_writer_set_limits(wr, wla->update_index, wla->update_index);
- return reftable_writer_add_log(wr, wla->log);
-}
-
-static void test_reftable_stack_add_one(void)
-{
- char *dir = get_tmp_dir(__LINE__);
- struct strbuf scratch = STRBUF_INIT;
- int mask = umask(002);
- struct reftable_write_options opts = {
- .default_permissions = 0660,
- };
- struct reftable_stack *st = NULL;
- int err;
- struct reftable_ref_record ref = {
- .refname = (char *) "HEAD",
- .update_index = 1,
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "master",
- };
- struct reftable_ref_record dest = { NULL };
- struct stat stat_result = { 0 };
- err = reftable_new_stack(&st, dir, &opts);
- EXPECT_ERR(err);
-
- err = reftable_stack_add(st, &write_test_ref, &ref);
- EXPECT_ERR(err);
-
- err = reftable_stack_read_ref(st, ref.refname, &dest);
- EXPECT_ERR(err);
- EXPECT(0 == strcmp("master", dest.value.symref));
- EXPECT(st->readers_len > 0);
-
- printf("testing print functionality:\n");
- err = reftable_stack_print_directory(dir, GIT_SHA1_FORMAT_ID);
- EXPECT_ERR(err);
-
- err = reftable_stack_print_directory(dir, GIT_SHA256_FORMAT_ID);
- EXPECT(err == REFTABLE_FORMAT_ERROR);
-
-#ifndef GIT_WINDOWS_NATIVE
- strbuf_addstr(&scratch, dir);
- strbuf_addstr(&scratch, "/tables.list");
- err = stat(scratch.buf, &stat_result);
- EXPECT(!err);
- EXPECT((stat_result.st_mode & 0777) == opts.default_permissions);
-
- strbuf_reset(&scratch);
- strbuf_addstr(&scratch, dir);
- strbuf_addstr(&scratch, "/");
- /* do not try at home; not an external API for reftable. */
- strbuf_addstr(&scratch, st->readers[0]->name);
- err = stat(scratch.buf, &stat_result);
- EXPECT(!err);
- EXPECT((stat_result.st_mode & 0777) == opts.default_permissions);
-#else
- (void) stat_result;
-#endif
-
- reftable_ref_record_release(&dest);
- reftable_stack_destroy(st);
- strbuf_release(&scratch);
- clear_dir(dir);
- umask(mask);
-}
-
-static void test_reftable_stack_uptodate(void)
-{
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st1 = NULL;
- struct reftable_stack *st2 = NULL;
- char *dir = get_tmp_dir(__LINE__);
-
- int err;
- struct reftable_ref_record ref1 = {
- .refname = (char *) "HEAD",
- .update_index = 1,
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "master",
- };
- struct reftable_ref_record ref2 = {
- .refname = (char *) "branch2",
- .update_index = 2,
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "master",
- };
-
-
- /* simulate multi-process access to the same stack
- by creating two stacks for the same directory.
- */
- err = reftable_new_stack(&st1, dir, &opts);
- EXPECT_ERR(err);
-
- err = reftable_new_stack(&st2, dir, &opts);
- EXPECT_ERR(err);
-
- err = reftable_stack_add(st1, &write_test_ref, &ref1);
- EXPECT_ERR(err);
-
- err = reftable_stack_add(st2, &write_test_ref, &ref2);
- EXPECT(err == REFTABLE_OUTDATED_ERROR);
-
- err = reftable_stack_reload(st2);
- EXPECT_ERR(err);
-
- err = reftable_stack_add(st2, &write_test_ref, &ref2);
- EXPECT_ERR(err);
- reftable_stack_destroy(st1);
- reftable_stack_destroy(st2);
- clear_dir(dir);
-}
-
-static void test_reftable_stack_transaction_api(void)
-{
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st = NULL;
- int err;
- struct reftable_addition *add = NULL;
-
- struct reftable_ref_record ref = {
- .refname = (char *) "HEAD",
- .update_index = 1,
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "master",
- };
- struct reftable_ref_record dest = { NULL };
-
- err = reftable_new_stack(&st, dir, &opts);
- EXPECT_ERR(err);
-
- reftable_addition_destroy(add);
-
- err = reftable_stack_new_addition(&add, st);
- EXPECT_ERR(err);
-
- err = reftable_addition_add(add, &write_test_ref, &ref);
- EXPECT_ERR(err);
-
- err = reftable_addition_commit(add);
- EXPECT_ERR(err);
-
- reftable_addition_destroy(add);
-
- err = reftable_stack_read_ref(st, ref.refname, &dest);
- EXPECT_ERR(err);
- EXPECT(REFTABLE_REF_SYMREF == dest.value_type);
- EXPECT(0 == strcmp("master", dest.value.symref));
-
- reftable_ref_record_release(&dest);
- reftable_stack_destroy(st);
- clear_dir(dir);
-}
-
-static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
-{
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_write_options opts = {0};
- struct reftable_addition *add = NULL;
- struct reftable_stack *st = NULL;
- int i, n = 20, err;
-
- err = reftable_new_stack(&st, dir, &opts);
- EXPECT_ERR(err);
-
- for (i = 0; i <= n; i++) {
- struct reftable_ref_record ref = {
- .update_index = reftable_stack_next_update_index(st),
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "master",
- };
- char name[100];
-
- snprintf(name, sizeof(name), "branch%04d", i);
- ref.refname = name;
-
- /*
- * Disable auto-compaction for all but the last runs. Like this
- * we can ensure that we indeed honor this setting and have
- * better control over when exactly auto compaction runs.
- */
- st->opts.disable_auto_compact = i != n;
-
- err = reftable_stack_new_addition(&add, st);
- EXPECT_ERR(err);
-
- err = reftable_addition_add(add, &write_test_ref, &ref);
- EXPECT_ERR(err);
-
- err = reftable_addition_commit(add);
- EXPECT_ERR(err);
-
- reftable_addition_destroy(add);
-
- /*
- * The stack length should grow continuously for all runs where
- * auto compaction is disabled. When enabled, we should merge
- * all tables in the stack.
- */
- if (i != n)
- EXPECT(st->merged->stack_len == i + 1);
- else
- EXPECT(st->merged->stack_len == 1);
- }
-
- reftable_stack_destroy(st);
- clear_dir(dir);
-}
-
-static void test_reftable_stack_auto_compaction_fails_gracefully(void)
-{
- struct reftable_ref_record ref = {
- .refname = (char *) "refs/heads/master",
- .update_index = 1,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = {0x01},
- };
- struct reftable_write_options opts = {0};
- struct reftable_stack *st;
- struct strbuf table_path = STRBUF_INIT;
- char *dir = get_tmp_dir(__LINE__);
- int err;
-
- err = reftable_new_stack(&st, dir, &opts);
- EXPECT_ERR(err);
-
- err = reftable_stack_add(st, write_test_ref, &ref);
- EXPECT_ERR(err);
- EXPECT(st->merged->stack_len == 1);
- EXPECT(st->stats.attempts == 0);
- EXPECT(st->stats.failures == 0);
-
- /*
- * Lock the newly written table such that it cannot be compacted.
- * Adding a new table to the stack should not be impacted by this, even
- * though auto-compaction will now fail.
- */
- strbuf_addf(&table_path, "%s/%s.lock", dir, st->readers[0]->name);
- write_file_buf(table_path.buf, "", 0);
-
- ref.update_index = 2;
- err = reftable_stack_add(st, write_test_ref, &ref);
- EXPECT_ERR(err);
- EXPECT(st->merged->stack_len == 2);
- EXPECT(st->stats.attempts == 1);
- EXPECT(st->stats.failures == 1);
-
- reftable_stack_destroy(st);
- strbuf_release(&table_path);
- clear_dir(dir);
-}
-
-static int write_error(struct reftable_writer *wr, void *arg)
-{
- return *((int *)arg);
-}
-
-static void test_reftable_stack_update_index_check(void)
-{
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st = NULL;
- int err;
- struct reftable_ref_record ref1 = {
- .refname = (char *) "name1",
- .update_index = 1,
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "master",
- };
- struct reftable_ref_record ref2 = {
- .refname = (char *) "name2",
- .update_index = 1,
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "master",
- };
-
- err = reftable_new_stack(&st, dir, &opts);
- EXPECT_ERR(err);
-
- err = reftable_stack_add(st, &write_test_ref, &ref1);
- EXPECT_ERR(err);
-
- err = reftable_stack_add(st, &write_test_ref, &ref2);
- EXPECT(err == REFTABLE_API_ERROR);
- reftable_stack_destroy(st);
- clear_dir(dir);
-}
-
-static void test_reftable_stack_lock_failure(void)
-{
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st = NULL;
- int err, i;
-
- err = reftable_new_stack(&st, dir, &opts);
- EXPECT_ERR(err);
- for (i = -1; i != REFTABLE_EMPTY_TABLE_ERROR; i--) {
- err = reftable_stack_add(st, &write_error, &i);
- EXPECT(err == i);
- }
-
- reftable_stack_destroy(st);
- clear_dir(dir);
-}
-
-static void test_reftable_stack_add(void)
-{
- int i = 0;
- int err = 0;
- struct reftable_write_options opts = {
- .exact_log_message = 1,
- .default_permissions = 0660,
- .disable_auto_compact = 1,
- };
- struct reftable_stack *st = NULL;
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_ref_record refs[2] = { { NULL } };
- struct reftable_log_record logs[2] = { { NULL } };
- struct strbuf path = STRBUF_INIT;
- struct stat stat_result;
- int N = ARRAY_SIZE(refs);
-
- err = reftable_new_stack(&st, dir, &opts);
- EXPECT_ERR(err);
-
- for (i = 0; i < N; i++) {
- char buf[256];
- snprintf(buf, sizeof(buf), "branch%02d", i);
- refs[i].refname = xstrdup(buf);
- refs[i].update_index = i + 1;
- refs[i].value_type = REFTABLE_REF_VAL1;
- set_test_hash(refs[i].value.val1, i);
-
- logs[i].refname = xstrdup(buf);
- logs[i].update_index = N + i + 1;
- logs[i].value_type = REFTABLE_LOG_UPDATE;
- logs[i].value.update.email = xstrdup("identity@invalid");
- set_test_hash(logs[i].value.update.new_hash, i);
- }
-
- for (i = 0; i < N; i++) {
- int err = reftable_stack_add(st, &write_test_ref, &refs[i]);
- EXPECT_ERR(err);
- }
-
- for (i = 0; i < N; i++) {
- struct write_log_arg arg = {
- .log = &logs[i],
- .update_index = reftable_stack_next_update_index(st),
- };
- int err = reftable_stack_add(st, &write_test_log, &arg);
- EXPECT_ERR(err);
- }
-
- err = reftable_stack_compact_all(st, NULL);
- 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);
- EXPECT_ERR(err);
- EXPECT(reftable_ref_record_equal(&dest, refs + i,
- GIT_SHA1_RAWSZ));
- 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);
- EXPECT_ERR(err);
- EXPECT(reftable_log_record_equal(&dest, logs + i,
- GIT_SHA1_RAWSZ));
- reftable_log_record_release(&dest);
- }
-
-#ifndef GIT_WINDOWS_NATIVE
- strbuf_addstr(&path, dir);
- strbuf_addstr(&path, "/tables.list");
- err = stat(path.buf, &stat_result);
- EXPECT(!err);
- EXPECT((stat_result.st_mode & 0777) == opts.default_permissions);
-
- strbuf_reset(&path);
- strbuf_addstr(&path, dir);
- strbuf_addstr(&path, "/");
- /* do not try at home; not an external API for reftable. */
- strbuf_addstr(&path, st->readers[0]->name);
- err = stat(path.buf, &stat_result);
- EXPECT(!err);
- EXPECT((stat_result.st_mode & 0777) == opts.default_permissions);
-#else
- (void) stat_result;
-#endif
-
- /* cleanup */
- reftable_stack_destroy(st);
- for (i = 0; i < N; i++) {
- reftable_ref_record_release(&refs[i]);
- reftable_log_record_release(&logs[i]);
- }
- strbuf_release(&path);
- clear_dir(dir);
-}
-
-static void test_reftable_stack_log_normalize(void)
-{
- int err = 0;
- struct reftable_write_options opts = {
- 0,
- };
- struct reftable_stack *st = NULL;
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_log_record input = {
- .refname = (char *) "branch",
- .update_index = 1,
- .value_type = REFTABLE_LOG_UPDATE,
- .value = {
- .update = {
- .new_hash = { 1 },
- .old_hash = { 2 },
- },
- },
- };
- struct reftable_log_record dest = {
- .update_index = 0,
- };
- struct write_log_arg arg = {
- .log = &input,
- .update_index = 1,
- };
-
- err = reftable_new_stack(&st, dir, &opts);
- EXPECT_ERR(err);
-
- input.value.update.message = (char *) "one\ntwo";
- err = reftable_stack_add(st, &write_test_log, &arg);
- EXPECT(err == REFTABLE_API_ERROR);
-
- input.value.update.message = (char *) "one";
- err = reftable_stack_add(st, &write_test_log, &arg);
- EXPECT_ERR(err);
-
- err = reftable_stack_read_log(st, input.refname, &dest);
- EXPECT_ERR(err);
- EXPECT(0 == strcmp(dest.value.update.message, "one\n"));
-
- input.value.update.message = (char *) "two\n";
- arg.update_index = 2;
- err = reftable_stack_add(st, &write_test_log, &arg);
- EXPECT_ERR(err);
- err = reftable_stack_read_log(st, input.refname, &dest);
- EXPECT_ERR(err);
- EXPECT(0 == strcmp(dest.value.update.message, "two\n"));
-
- /* cleanup */
- reftable_stack_destroy(st);
- reftable_log_record_release(&dest);
- clear_dir(dir);
-}
-
-static void test_reftable_stack_tombstone(void)
-{
- int i = 0;
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st = NULL;
- int err;
- struct reftable_ref_record refs[2] = { { NULL } };
- struct reftable_log_record logs[2] = { { NULL } };
- int N = ARRAY_SIZE(refs);
- struct reftable_ref_record dest = { NULL };
- struct reftable_log_record log_dest = { NULL };
-
- err = reftable_new_stack(&st, dir, &opts);
- EXPECT_ERR(err);
-
- /* even entries add the refs, odd entries delete them. */
- for (i = 0; i < N; i++) {
- const char *buf = "branch";
- refs[i].refname = xstrdup(buf);
- refs[i].update_index = i + 1;
- if (i % 2 == 0) {
- refs[i].value_type = REFTABLE_REF_VAL1;
- set_test_hash(refs[i].value.val1, i);
- }
-
- logs[i].refname = xstrdup(buf);
- /* update_index is part of the key. */
- logs[i].update_index = 42;
- if (i % 2 == 0) {
- logs[i].value_type = REFTABLE_LOG_UPDATE;
- set_test_hash(logs[i].value.update.new_hash, i);
- logs[i].value.update.email =
- xstrdup("identity@invalid");
- }
- }
- for (i = 0; i < N; i++) {
- int err = reftable_stack_add(st, &write_test_ref, &refs[i]);
- EXPECT_ERR(err);
- }
-
- for (i = 0; i < N; i++) {
- struct write_log_arg arg = {
- .log = &logs[i],
- .update_index = reftable_stack_next_update_index(st),
- };
- int err = reftable_stack_add(st, &write_test_log, &arg);
- EXPECT_ERR(err);
- }
-
- err = reftable_stack_read_ref(st, "branch", &dest);
- EXPECT(err == 1);
- reftable_ref_record_release(&dest);
-
- err = reftable_stack_read_log(st, "branch", &log_dest);
- EXPECT(err == 1);
- reftable_log_record_release(&log_dest);
-
- err = reftable_stack_compact_all(st, NULL);
- EXPECT_ERR(err);
-
- err = reftable_stack_read_ref(st, "branch", &dest);
- EXPECT(err == 1);
-
- err = reftable_stack_read_log(st, "branch", &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_release(&refs[i]);
- reftable_log_record_release(&logs[i]);
- }
- clear_dir(dir);
-}
-
-static void test_reftable_stack_hash_id(void)
-{
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st = NULL;
- int err;
-
- struct reftable_ref_record ref = {
- .refname = (char *) "master",
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "target",
- .update_index = 1,
- };
- struct reftable_write_options opts32 = { .hash_id = GIT_SHA256_FORMAT_ID };
- struct reftable_stack *st32 = NULL;
- struct reftable_write_options opts_default = { 0 };
- struct reftable_stack *st_default = NULL;
- struct reftable_ref_record dest = { NULL };
-
- err = reftable_new_stack(&st, dir, &opts);
- EXPECT_ERR(err);
-
- err = reftable_stack_add(st, &write_test_ref, &ref);
- EXPECT_ERR(err);
-
- /* can't read it with the wrong hash ID. */
- err = reftable_new_stack(&st32, dir, &opts32);
- EXPECT(err == REFTABLE_FORMAT_ERROR);
-
- /* check that we can read it back with default opts too. */
- err = reftable_new_stack(&st_default, dir, &opts_default);
- EXPECT_ERR(err);
-
- err = reftable_stack_read_ref(st_default, "master", &dest);
- EXPECT_ERR(err);
-
- EXPECT(reftable_ref_record_equal(&ref, &dest, GIT_SHA1_RAWSZ));
- reftable_ref_record_release(&dest);
- reftable_stack_destroy(st);
- reftable_stack_destroy(st_default);
- clear_dir(dir);
-}
-
-static void test_suggest_compaction_segment(void)
-{
- uint64_t sizes[] = { 512, 64, 17, 16, 9, 9, 9, 16, 2, 16 };
- struct segment min =
- suggest_compaction_segment(sizes, ARRAY_SIZE(sizes), 2);
- EXPECT(min.start == 1);
- EXPECT(min.end == 10);
-}
-
-static void test_suggest_compaction_segment_nothing(void)
-{
- uint64_t sizes[] = { 64, 32, 16, 8, 4, 2 };
- struct segment result =
- suggest_compaction_segment(sizes, ARRAY_SIZE(sizes), 2);
- EXPECT(result.start == result.end);
-}
-
-static void test_reflog_expire(void)
-{
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st = NULL;
- 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 = { NULL };
-
- err = reftable_new_stack(&st, dir, &opts);
- EXPECT_ERR(err);
-
- for (i = 1; i <= N; i++) {
- char buf[256];
- snprintf(buf, sizeof(buf), "branch%02d", i);
-
- logs[i].refname = xstrdup(buf);
- logs[i].update_index = i;
- logs[i].value_type = REFTABLE_LOG_UPDATE;
- logs[i].value.update.time = i;
- logs[i].value.update.email = xstrdup("identity@invalid");
- set_test_hash(logs[i].value.update.new_hash, i);
- }
-
- for (i = 1; i <= N; i++) {
- struct write_log_arg arg = {
- .log = &logs[i],
- .update_index = reftable_stack_next_update_index(st),
- };
- int err = reftable_stack_add(st, &write_test_log, &arg);
- EXPECT_ERR(err);
- }
-
- err = reftable_stack_compact_all(st, NULL);
- EXPECT_ERR(err);
-
- err = reftable_stack_compact_all(st, &expiry);
- EXPECT_ERR(err);
-
- err = reftable_stack_read_log(st, logs[9].refname, &log);
- EXPECT(err == 1);
-
- err = reftable_stack_read_log(st, logs[11].refname, &log);
- EXPECT_ERR(err);
-
- expiry.min_update_index = 15;
- err = reftable_stack_compact_all(st, &expiry);
- EXPECT_ERR(err);
-
- err = reftable_stack_read_log(st, logs[14].refname, &log);
- EXPECT(err == 1);
-
- err = reftable_stack_read_log(st, logs[16].refname, &log);
- EXPECT_ERR(err);
-
- /* cleanup */
- reftable_stack_destroy(st);
- for (i = 0; i <= N; i++) {
- reftable_log_record_release(&logs[i]);
- }
- clear_dir(dir);
- reftable_log_record_release(&log);
-}
-
-static int write_nothing(struct reftable_writer *wr, void *arg)
-{
- reftable_writer_set_limits(wr, 1, 1);
- return 0;
-}
-
-static void test_empty_add(void)
-{
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st = NULL;
- int err;
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_stack *st2 = NULL;
-
- err = reftable_new_stack(&st, dir, &opts);
- EXPECT_ERR(err);
-
- err = reftable_stack_add(st, &write_nothing, NULL);
- EXPECT_ERR(err);
-
- err = reftable_new_stack(&st2, dir, &opts);
- EXPECT_ERR(err);
- clear_dir(dir);
- reftable_stack_destroy(st);
- reftable_stack_destroy(st2);
-}
-
-static int fastlog2(uint64_t sz)
-{
- int l = 0;
- if (sz == 0)
- return 0;
- for (; sz; sz /= 2)
- l++;
- return l - 1;
-}
-
-static void test_reftable_stack_auto_compaction(void)
-{
- struct reftable_write_options opts = {
- .disable_auto_compact = 1,
- };
- struct reftable_stack *st = NULL;
- char *dir = get_tmp_dir(__LINE__);
- int err, i;
- int N = 100;
-
- err = reftable_new_stack(&st, dir, &opts);
- EXPECT_ERR(err);
-
- for (i = 0; i < N; i++) {
- char name[100];
- struct reftable_ref_record ref = {
- .refname = name,
- .update_index = reftable_stack_next_update_index(st),
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "master",
- };
- snprintf(name, sizeof(name), "branch%04d", i);
-
- err = reftable_stack_add(st, &write_test_ref, &ref);
- EXPECT_ERR(err);
-
- err = reftable_stack_auto_compact(st);
- EXPECT_ERR(err);
- EXPECT(i < 3 || st->merged->stack_len < 2 * fastlog2(i));
- }
-
- EXPECT(reftable_stack_compaction_stats(st)->entries_written <
- (uint64_t)(N * fastlog2(N)));
-
- reftable_stack_destroy(st);
- clear_dir(dir);
-}
-
-static void test_reftable_stack_add_performs_auto_compaction(void)
-{
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st = NULL;
- struct strbuf refname = STRBUF_INIT;
- char *dir = get_tmp_dir(__LINE__);
- int err, i, n = 20;
-
- err = reftable_new_stack(&st, dir, &opts);
- EXPECT_ERR(err);
-
- for (i = 0; i <= n; i++) {
- struct reftable_ref_record ref = {
- .update_index = reftable_stack_next_update_index(st),
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "master",
- };
-
- /*
- * Disable auto-compaction for all but the last runs. Like this
- * we can ensure that we indeed honor this setting and have
- * better control over when exactly auto compaction runs.
- */
- st->opts.disable_auto_compact = i != n;
-
- strbuf_reset(&refname);
- strbuf_addf(&refname, "branch-%04d", i);
- ref.refname = refname.buf;
-
- err = reftable_stack_add(st, &write_test_ref, &ref);
- EXPECT_ERR(err);
-
- /*
- * The stack length should grow continuously for all runs where
- * auto compaction is disabled. When enabled, we should merge
- * all tables in the stack.
- */
- if (i != n)
- EXPECT(st->merged->stack_len == i + 1);
- else
- EXPECT(st->merged->stack_len == 1);
- }
-
- reftable_stack_destroy(st);
- strbuf_release(&refname);
- clear_dir(dir);
-}
-
-static void test_reftable_stack_compaction_concurrent(void)
-{
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st1 = NULL, *st2 = NULL;
- char *dir = get_tmp_dir(__LINE__);
- int err, i;
- int N = 3;
-
- err = reftable_new_stack(&st1, dir, &opts);
- EXPECT_ERR(err);
-
- for (i = 0; i < N; i++) {
- char name[100];
- struct reftable_ref_record ref = {
- .refname = name,
- .update_index = reftable_stack_next_update_index(st1),
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "master",
- };
- snprintf(name, sizeof(name), "branch%04d", i);
-
- err = reftable_stack_add(st1, &write_test_ref, &ref);
- EXPECT_ERR(err);
- }
-
- err = reftable_new_stack(&st2, dir, &opts);
- EXPECT_ERR(err);
-
- err = reftable_stack_compact_all(st1, NULL);
- EXPECT_ERR(err);
-
- reftable_stack_destroy(st1);
- reftable_stack_destroy(st2);
-
- EXPECT(count_dir_entries(dir) == 2);
- clear_dir(dir);
-}
-
-static void unclean_stack_close(struct reftable_stack *st)
-{
- /* break abstraction boundary to simulate unclean shutdown. */
- int i = 0;
- for (; i < st->readers_len; i++) {
- reftable_reader_free(st->readers[i]);
- }
- st->readers_len = 0;
- FREE_AND_NULL(st->readers);
-}
-
-static void test_reftable_stack_compaction_concurrent_clean(void)
-{
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st1 = NULL, *st2 = NULL, *st3 = NULL;
- char *dir = get_tmp_dir(__LINE__);
- int err, i;
- int N = 3;
-
- err = reftable_new_stack(&st1, dir, &opts);
- EXPECT_ERR(err);
-
- for (i = 0; i < N; i++) {
- char name[100];
- struct reftable_ref_record ref = {
- .refname = name,
- .update_index = reftable_stack_next_update_index(st1),
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "master",
- };
- snprintf(name, sizeof(name), "branch%04d", i);
-
- err = reftable_stack_add(st1, &write_test_ref, &ref);
- EXPECT_ERR(err);
- }
-
- err = reftable_new_stack(&st2, dir, &opts);
- EXPECT_ERR(err);
-
- err = reftable_stack_compact_all(st1, NULL);
- EXPECT_ERR(err);
-
- unclean_stack_close(st1);
- unclean_stack_close(st2);
-
- err = reftable_new_stack(&st3, dir, &opts);
- EXPECT_ERR(err);
-
- err = reftable_stack_clean(st3);
- EXPECT_ERR(err);
- EXPECT(count_dir_entries(dir) == 2);
-
- reftable_stack_destroy(st1);
- reftable_stack_destroy(st2);
- reftable_stack_destroy(st3);
-
- clear_dir(dir);
-}
-
-int stack_test_main(int argc, const char *argv[])
-{
- RUN_TEST(test_empty_add);
- RUN_TEST(test_read_file);
- RUN_TEST(test_reflog_expire);
- RUN_TEST(test_reftable_stack_add);
- RUN_TEST(test_reftable_stack_add_one);
- RUN_TEST(test_reftable_stack_auto_compaction);
- RUN_TEST(test_reftable_stack_add_performs_auto_compaction);
- RUN_TEST(test_reftable_stack_compaction_concurrent);
- RUN_TEST(test_reftable_stack_compaction_concurrent_clean);
- RUN_TEST(test_reftable_stack_hash_id);
- RUN_TEST(test_reftable_stack_lock_failure);
- RUN_TEST(test_reftable_stack_log_normalize);
- RUN_TEST(test_reftable_stack_tombstone);
- RUN_TEST(test_reftable_stack_transaction_api);
- RUN_TEST(test_reftable_stack_transaction_api_performs_auto_compaction);
- RUN_TEST(test_reftable_stack_auto_compaction_fails_gracefully);
- RUN_TEST(test_reftable_stack_update_index_check);
- RUN_TEST(test_reftable_stack_uptodate);
- RUN_TEST(test_suggest_compaction_segment);
- RUN_TEST(test_suggest_compaction_segment_nothing);
- return 0;
-}
diff --git a/reftable/system.c b/reftable/system.c
new file mode 100644
index 0000000000..adf8e4d30b
--- /dev/null
+++ b/reftable/system.c
@@ -0,0 +1,126 @@
+#include "system.h"
+#include "basics.h"
+#include "reftable-error.h"
+#include "../lockfile.h"
+#include "../tempfile.h"
+
+int tmpfile_from_pattern(struct reftable_tmpfile *out, const char *pattern)
+{
+ struct tempfile *tempfile;
+
+ tempfile = mks_tempfile(pattern);
+ if (!tempfile)
+ return REFTABLE_IO_ERROR;
+
+ out->path = tempfile->filename.buf;
+ out->fd = tempfile->fd;
+ out->priv = tempfile;
+
+ return 0;
+}
+
+int tmpfile_close(struct reftable_tmpfile *t)
+{
+ struct tempfile *tempfile = t->priv;
+ int ret = close_tempfile_gently(tempfile);
+ t->fd = -1;
+ if (ret < 0)
+ return REFTABLE_IO_ERROR;
+ return 0;
+}
+
+int tmpfile_delete(struct reftable_tmpfile *t)
+{
+ struct tempfile *tempfile = t->priv;
+ int ret = delete_tempfile(&tempfile);
+ *t = REFTABLE_TMPFILE_INIT;
+ if (ret < 0)
+ return REFTABLE_IO_ERROR;
+ return 0;
+}
+
+int tmpfile_rename(struct reftable_tmpfile *t, const char *path)
+{
+ struct tempfile *tempfile = t->priv;
+ int ret = rename_tempfile(&tempfile, path);
+ *t = REFTABLE_TMPFILE_INIT;
+ if (ret < 0)
+ return REFTABLE_IO_ERROR;
+ return 0;
+}
+
+int flock_acquire(struct reftable_flock *l, const char *target_path,
+ long timeout_ms)
+{
+ struct lock_file *lockfile;
+ int err;
+
+ lockfile = reftable_malloc(sizeof(*lockfile));
+ if (!lockfile)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
+
+ err = hold_lock_file_for_update_timeout(lockfile, target_path, LOCK_NO_DEREF,
+ timeout_ms);
+ if (err < 0) {
+ reftable_free(lockfile);
+ if (errno == EEXIST)
+ return REFTABLE_LOCK_ERROR;
+ return -1;
+ }
+
+ l->fd = get_lock_file_fd(lockfile);
+ l->path = get_lock_file_path(lockfile);
+ l->priv = lockfile;
+
+ return 0;
+}
+
+int flock_close(struct reftable_flock *l)
+{
+ struct lock_file *lockfile = l->priv;
+ int ret;
+
+ if (!lockfile)
+ return REFTABLE_API_ERROR;
+
+ ret = close_lock_file_gently(lockfile);
+ l->fd = -1;
+ if (ret < 0)
+ return REFTABLE_IO_ERROR;
+
+ return 0;
+}
+
+int flock_release(struct reftable_flock *l)
+{
+ struct lock_file *lockfile = l->priv;
+ int ret;
+
+ if (!lockfile)
+ return 0;
+
+ ret = rollback_lock_file(lockfile);
+ reftable_free(lockfile);
+ *l = REFTABLE_FLOCK_INIT;
+ if (ret < 0)
+ return REFTABLE_IO_ERROR;
+
+ return 0;
+}
+
+int flock_commit(struct reftable_flock *l)
+{
+ struct lock_file *lockfile = l->priv;
+ int ret;
+
+ if (!lockfile)
+ return REFTABLE_API_ERROR;
+
+ ret = commit_lock_file(lockfile);
+ reftable_free(lockfile);
+ *l = REFTABLE_FLOCK_INIT;
+ if (ret < 0)
+ return REFTABLE_IO_ERROR;
+
+ return 0;
+}
diff --git a/reftable/system.h b/reftable/system.h
index d0cabd5d17..7d5f803eeb 100644
--- a/reftable/system.h
+++ b/reftable/system.h
@@ -12,12 +12,90 @@ https://developers.google.com/open-source/licenses/bsd
/* This header glues the reftable library to the rest of Git */
#include "git-compat-util.h"
-#include "lockfile.h"
-#include "strbuf.h"
-#include "tempfile.h"
-#include "hash.h" /* hash ID, sizes.*/
-#include "dir.h" /* remove_dir_recursively, for tests.*/
-int hash_size(uint32_t id);
+/*
+ * An implementation-specific temporary file. By making this specific to the
+ * implementation it becomes possible to tie temporary files into any kind of
+ * signal or atexit handlers for cleanup on abnormal situations.
+ */
+struct reftable_tmpfile {
+ const char *path;
+ int fd;
+ void *priv;
+};
+#define REFTABLE_TMPFILE_INIT ((struct reftable_tmpfile) { .fd = -1, })
+
+/*
+ * Create a temporary file from a pattern similar to how mkstemp(3p) would.
+ * The `pattern` shall not be modified. On success, the structure at `out` has
+ * been initialized such that it is ready for use. Returns 0 on success, a
+ * reftable error code on error.
+ */
+int tmpfile_from_pattern(struct reftable_tmpfile *out, const char *pattern);
+
+/*
+ * Close the temporary file's file descriptor without removing the file itself.
+ * This is a no-op in case the file has already been closed beforehand. Returns
+ * 0 on success, a reftable error code on error.
+ */
+int tmpfile_close(struct reftable_tmpfile *t);
+
+/*
+ * Close the temporary file and delete it. This is a no-op in case the file has
+ * already been deleted or renamed beforehand. Returns 0 on success, a reftable
+ * error code on error.
+ */
+int tmpfile_delete(struct reftable_tmpfile *t);
+
+/*
+ * Rename the temporary file to the provided path. The temporary file must be
+ * active. Return 0 on success, a reftable error code on error. Deactivates the
+ * temporary file.
+ */
+int tmpfile_rename(struct reftable_tmpfile *t, const char *path);
+
+/*
+ * An implementation-specific file lock. Same as with `reftable_tmpfile`,
+ * making this specific to the implementation makes it possible to tie this
+ * into signal or atexit handlers such that we know to clean up stale locks on
+ * abnormal exits.
+ */
+struct reftable_flock {
+ const char *path;
+ int fd;
+ void *priv;
+};
+#define REFTABLE_FLOCK_INIT ((struct reftable_flock){ .fd = -1, })
+
+/*
+ * Acquire the lock for the given target path by exclusively creating a file
+ * with ".lock" appended to it. If that lock exists, we wait up to `timeout_ms`
+ * to acquire the lock. If `timeout_ms` is 0 we don't wait, if it is negative
+ * we block indefinitely.
+ *
+ * Retrun 0 on success, a reftable error code on error.
+ */
+int flock_acquire(struct reftable_flock *l, const char *target_path,
+ long timeout_ms);
+
+/*
+ * Close the lockfile's file descriptor without removing the lock itself. This
+ * is a no-op in case the lockfile has already been closed beforehand. Returns
+ * 0 on success, a reftable error code on error.
+ */
+int flock_close(struct reftable_flock *l);
+
+/*
+ * Release the lock by unlinking the lockfile. This is a no-op in case the
+ * lockfile has already been released or committed beforehand. Returns 0 on
+ * success, a reftable error code on error.
+ */
+int flock_release(struct reftable_flock *l);
+
+/*
+ * Commit the lock by renaming the lockfile into place. Returns 0 on success, a
+ * reftable error code on error.
+ */
+int flock_commit(struct reftable_flock *l);
#endif
diff --git a/reftable/test_framework.c b/reftable/test_framework.c
deleted file mode 100644
index 4066924eee..0000000000
--- a/reftable/test_framework.c
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
-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"
-
-
-void set_test_hash(uint8_t *p, int i)
-{
- memset(p, (uint8_t)i, hash_size(GIT_SHA1_FORMAT_ID));
-}
-
-ssize_t strbuf_add_void(void *b, const void *data, size_t sz)
-{
- strbuf_add(b, data, sz);
- return sz;
-}
-
-int noop_flush(void *arg)
-{
- return 0;
-}
diff --git a/reftable/test_framework.h b/reftable/test_framework.h
deleted file mode 100644
index 687390f9c2..0000000000
--- a/reftable/test_framework.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
-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 TEST_FRAMEWORK_H
-#define TEST_FRAMEWORK_H
-
-#include "system.h"
-#include "reftable-error.h"
-
-#define EXPECT_ERR(c) \
- do { \
- 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(); \
- } \
- } while (0)
-
-#define EXPECT_STREQ(a, b) \
- do { \
- if (strcmp(a, b)) { \
- fflush(stderr); \
- fflush(stdout); \
- fprintf(stderr, "%s:%d: %s (%s) != %s (%s)\n", __FILE__, \
- __LINE__, #a, a, #b, b); \
- abort(); \
- } \
- } while (0)
-
-#define EXPECT(c) \
- do { \
- if (!(c)) { \
- fflush(stderr); \
- fflush(stdout); \
- fprintf(stderr, "%s: %d: failed assertion %s\n", __FILE__, \
- __LINE__, #c); \
- abort(); \
- } \
- } while (0)
-
-#define RUN_TEST(f) \
- fprintf(stderr, "running %s\n", #f); \
- fflush(stderr); \
- f();
-
-void set_test_hash(uint8_t *p, int i);
-
-/* Like strbuf_add, but suitable for passing to reftable_new_writer
- */
-ssize_t strbuf_add_void(void *b, const void *data, size_t sz);
-
-int noop_flush(void *);
-
-#endif
diff --git a/reftable/tree.c b/reftable/tree.c
index 528f33ae38..f4dbe72090 100644
--- a/reftable/tree.c
+++ b/reftable/tree.c
@@ -11,53 +11,64 @@ https://developers.google.com/open-source/licenses/bsd
#include "basics.h"
-struct tree_node *tree_search(void *key, struct tree_node **rootp,
- int (*compare)(const void *, const void *),
- int insert)
+struct tree_node *tree_search(struct tree_node *tree,
+ void *key,
+ int (*compare)(const void *, const void *))
{
int res;
+ if (!tree)
+ return NULL;
+ res = compare(key, tree->key);
+ if (res < 0)
+ return tree_search(tree->left, key, compare);
+ else if (res > 0)
+ return tree_search(tree->right, key, compare);
+ return tree;
+}
+
+struct tree_node *tree_insert(struct tree_node **rootp,
+ void *key,
+ int (*compare)(const void *, const void *))
+{
+ int res;
+
if (!*rootp) {
- if (!insert) {
+ struct tree_node *n;
+
+ REFTABLE_CALLOC_ARRAY(n, 1);
+ if (!n)
return NULL;
- } else {
- struct tree_node *n;
- REFTABLE_CALLOC_ARRAY(n, 1);
- n->key = key;
- *rootp = n;
- return *rootp;
- }
+
+ n->key = key;
+ *rootp = n;
+ return *rootp;
}
res = compare(key, (*rootp)->key);
if (res < 0)
- return tree_search(key, &(*rootp)->left, compare, insert);
+ return tree_insert(&(*rootp)->left, key, compare);
else if (res > 0)
- return tree_search(key, &(*rootp)->right, compare, insert);
+ return tree_insert(&(*rootp)->right, key, compare);
return *rootp;
}
void infix_walk(struct tree_node *t, void (*action)(void *arg, void *key),
void *arg)
{
- if (t->left) {
+ if (t->left)
infix_walk(t->left, action, arg);
- }
action(arg, t->key);
- if (t->right) {
+ if (t->right)
infix_walk(t->right, action, arg);
- }
}
void tree_free(struct tree_node *t)
{
- if (!t) {
+ if (!t)
return;
- }
- if (t->left) {
+ if (t->left)
tree_free(t->left);
- }
- if (t->right) {
+ if (t->right)
tree_free(t->right);
- }
reftable_free(t);
}
diff --git a/reftable/tree.h b/reftable/tree.h
index fbdd002e23..9604453b6d 100644
--- a/reftable/tree.h
+++ b/reftable/tree.h
@@ -15,12 +15,23 @@ struct tree_node {
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.
+/*
+ * Search the tree for the node matching the given key using `compare` as
+ * comparison function. Returns the node whose key matches or `NULL` in case
+ * the key does not exist in the tree.
+ */
+struct tree_node *tree_search(struct tree_node *tree,
+ void *key,
+ int (*compare)(const void *, const void *));
+
+/*
+ * Insert a node into the tree. Returns the newly inserted node if the key does
+ * not yet exist. Otherwise it returns the preexisting node. Returns `NULL`
+ * when allocating the new node fails.
*/
-struct tree_node *tree_search(void *key, struct tree_node **rootp,
- int (*compare)(const void *, const void *),
- int insert);
+struct tree_node *tree_insert(struct tree_node **rootp,
+ void *key,
+ int (*compare)(const void *, const void *));
/* performs an infix walk of the tree. */
void infix_walk(struct tree_node *t, void (*action)(void *arg, void *key),
diff --git a/reftable/tree_test.c b/reftable/tree_test.c
deleted file mode 100644
index 6961a657ad..0000000000
--- a/reftable/tree_test.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
-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 "tree.h"
-
-#include "test_framework.h"
-#include "reftable-tests.h"
-
-static int test_compare(const void *a, const void *b)
-{
- return (char *)a - (char *)b;
-}
-
-struct curry {
- void *last;
-};
-
-static void check_increasing(void *arg, void *key)
-{
- struct curry *c = arg;
- if (c->last) {
- EXPECT(test_compare(c->last, key) < 0);
- }
- c->last = key;
-}
-
-static void test_tree(void)
-{
- struct tree_node *root = NULL;
-
- void *values[11] = { NULL };
- struct tree_node *nodes[11] = { NULL };
- int i = 1;
- struct curry c = { NULL };
- do {
- nodes[i] = tree_search(values + i, &root, &test_compare, 1);
- i = (i * 7) % 11;
- } while (i != 1);
-
- for (i = 1; i < ARRAY_SIZE(nodes); i++) {
- EXPECT(values + i == nodes[i]->key);
- EXPECT(nodes[i] ==
- tree_search(values + i, &root, &test_compare, 0));
- }
-
- infix_walk(root, check_increasing, &c);
- tree_free(root);
-}
-
-int tree_test_main(int argc, const char *argv[])
-{
- RUN_TEST(test_tree);
- return 0;
-}
diff --git a/reftable/writer.c b/reftable/writer.c
index 45b3e9ce1f..9efeab13e1 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -49,8 +49,14 @@ static int padded_write(struct reftable_writer *w, uint8_t *data, size_t len,
{
int n = 0;
if (w->pending_padding > 0) {
- uint8_t *zeroed = reftable_calloc(w->pending_padding, sizeof(*zeroed));
- int n = w->write(w->write_arg, zeroed, w->pending_padding);
+ uint8_t *zeroed;
+ int n;
+
+ zeroed = reftable_calloc(w->pending_padding, sizeof(*zeroed));
+ if (!zeroed)
+ return -1;
+
+ n = w->write(w->write_arg, zeroed, w->pending_padding);
if (n < 0)
return n;
@@ -73,7 +79,7 @@ static void options_set_defaults(struct reftable_write_options *opts)
}
if (opts->hash_id == 0) {
- opts->hash_id = GIT_SHA1_FORMAT_ID;
+ opts->hash_id = REFTABLE_HASH_SHA1;
}
if (opts->block_size == 0) {
opts->block_size = DEFAULT_BLOCK_SIZE;
@@ -82,7 +88,7 @@ static void options_set_defaults(struct reftable_write_options *opts)
static int writer_version(struct reftable_writer *w)
{
- return (w->opts.hash_id == 0 || w->opts.hash_id == GIT_SHA1_FORMAT_ID) ?
+ return (w->opts.hash_id == 0 || w->opts.hash_id == REFTABLE_HASH_SHA1) ?
1 :
2;
}
@@ -97,33 +103,56 @@ static int writer_write_header(struct reftable_writer *w, uint8_t *dest)
put_be64(dest + 8, w->min_update_index);
put_be64(dest + 16, w->max_update_index);
if (writer_version(w) == 2) {
- put_be32(dest + 24, w->opts.hash_id);
+ uint32_t hash_id;
+
+ switch (w->opts.hash_id) {
+ case REFTABLE_HASH_SHA1:
+ hash_id = REFTABLE_FORMAT_ID_SHA1;
+ break;
+ case REFTABLE_HASH_SHA256:
+ hash_id = REFTABLE_FORMAT_ID_SHA256;
+ break;
+ default:
+ return -1;
+ }
+
+ put_be32(dest + 24, hash_id);
}
+
return header_size(writer_version(w));
}
-static void writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ)
+static int writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ)
{
- int block_start = 0;
- if (w->next == 0) {
+ int block_start = 0, ret;
+
+ if (w->next == 0)
block_start = header_size(writer_version(w));
- }
- strbuf_reset(&w->last_key);
- block_writer_init(&w->block_writer_data, typ, w->block,
- w->opts.block_size, block_start,
- hash_size(w->opts.hash_id));
+ reftable_buf_reset(&w->last_key);
+ ret = block_writer_init(&w->block_writer_data, typ, w->block,
+ w->opts.block_size, block_start,
+ hash_size(w->opts.hash_id));
+ if (ret < 0)
+ return ret;
+
w->block_writer = &w->block_writer_data;
w->block_writer->restart_interval = w->opts.restart_interval;
+
+ return 0;
}
-struct reftable_writer *
-reftable_new_writer(ssize_t (*writer_func)(void *, const void *, size_t),
- int (*flush_func)(void *),
- void *writer_arg, const struct reftable_write_options *_opts)
+int reftable_writer_new(struct reftable_writer **out,
+ ssize_t (*writer_func)(void *, const void *, size_t),
+ int (*flush_func)(void *),
+ void *writer_arg, const struct reftable_write_options *_opts)
{
- struct reftable_writer *wp = reftable_calloc(1, sizeof(*wp));
struct reftable_write_options opts = {0};
+ struct reftable_writer *wp;
+
+ wp = reftable_calloc(1, sizeof(*wp));
+ if (!wp)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
if (_opts)
opts = *_opts;
@@ -131,16 +160,23 @@ reftable_new_writer(ssize_t (*writer_func)(void *, const void *, size_t),
if (opts.block_size >= (1 << 24))
BUG("configured block size exceeds 16MB");
- strbuf_init(&wp->block_writer_data.last_key, 0);
- strbuf_init(&wp->last_key, 0);
+ reftable_buf_init(&wp->block_writer_data.last_key);
+ reftable_buf_init(&wp->last_key);
+ reftable_buf_init(&wp->scratch);
REFTABLE_CALLOC_ARRAY(wp->block, opts.block_size);
+ if (!wp->block) {
+ reftable_free(wp);
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
+ }
wp->write = writer_func;
wp->write_arg = writer_arg;
wp->opts = opts;
wp->flush = flush_func;
writer_reinit_block_writer(wp, BLOCK_TYPE_REF);
- return wp;
+ *out = wp;
+
+ return 0;
}
void reftable_writer_set_limits(struct reftable_writer *w, uint64_t min,
@@ -158,7 +194,8 @@ static void writer_release(struct reftable_writer *w)
block_writer_release(&w->block_writer_data);
w->block_writer = NULL;
writer_clear_index(w);
- strbuf_release(&w->last_key);
+ reftable_buf_release(&w->last_key);
+ reftable_buf_release(&w->scratch);
}
}
@@ -169,7 +206,7 @@ void reftable_writer_free(struct reftable_writer *w)
}
struct obj_index_tree_node {
- struct strbuf hash;
+ struct reftable_buf hash;
uint64_t *offsets;
size_t offset_len;
size_t offset_cap;
@@ -177,61 +214,78 @@ struct obj_index_tree_node {
#define OBJ_INDEX_TREE_NODE_INIT \
{ \
- .hash = STRBUF_INIT \
+ .hash = REFTABLE_BUF_INIT \
}
static int obj_index_tree_node_compare(const void *a, const void *b)
{
- return strbuf_cmp(&((const struct obj_index_tree_node *)a)->hash,
+ return reftable_buf_cmp(&((const struct obj_index_tree_node *)a)->hash,
&((const struct obj_index_tree_node *)b)->hash);
}
-static void writer_index_hash(struct reftable_writer *w, struct strbuf *hash)
+static int writer_index_hash(struct reftable_writer *w, struct reftable_buf *hash)
{
uint64_t off = w->next;
-
struct obj_index_tree_node want = { .hash = *hash };
+ struct obj_index_tree_node *key;
+ struct tree_node *node;
- struct tree_node *node = tree_search(&want, &w->obj_index_tree,
- &obj_index_tree_node_compare, 0);
- struct obj_index_tree_node *key = NULL;
+ node = tree_search(w->obj_index_tree, &want, &obj_index_tree_node_compare);
if (!node) {
struct obj_index_tree_node empty = OBJ_INDEX_TREE_NODE_INIT;
- key = reftable_malloc(sizeof(struct obj_index_tree_node));
+ int err;
+
+ key = reftable_malloc(sizeof(*key));
+ if (!key)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
+
*key = empty;
- strbuf_reset(&key->hash);
- strbuf_addbuf(&key->hash, hash);
- tree_search((void *)key, &w->obj_index_tree,
- &obj_index_tree_node_compare, 1);
+ reftable_buf_reset(&key->hash);
+ err = reftable_buf_add(&key->hash, hash->buf, hash->len);
+ if (err < 0)
+ return err;
+ tree_insert(&w->obj_index_tree, key,
+ &obj_index_tree_node_compare);
} else {
key = node->key;
}
- if (key->offset_len > 0 && key->offsets[key->offset_len - 1] == off) {
- return;
- }
+ if (key->offset_len > 0 && key->offsets[key->offset_len - 1] == off)
+ return 0;
REFTABLE_ALLOC_GROW(key->offsets, key->offset_len + 1, key->offset_cap);
+ if (!key->offsets)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
key->offsets[key->offset_len++] = off;
+
+ return 0;
}
static int writer_add_record(struct reftable_writer *w,
struct reftable_record *rec)
{
- struct strbuf key = STRBUF_INIT;
int err;
- reftable_record_key(rec, &key);
- if (strbuf_cmp(&w->last_key, &key) >= 0) {
+ err = reftable_record_key(rec, &w->scratch);
+ if (err < 0)
+ goto done;
+
+ if (reftable_buf_cmp(&w->last_key, &w->scratch) >= 0) {
err = REFTABLE_API_ERROR;
goto done;
}
- strbuf_reset(&w->last_key);
- strbuf_addbuf(&w->last_key, &key);
- if (!w->block_writer)
- writer_reinit_block_writer(w, reftable_record_type(rec));
+ reftable_buf_reset(&w->last_key);
+ err = reftable_buf_add(&w->last_key, w->scratch.buf, w->scratch.len);
+ if (err < 0)
+ goto done;
+
+ if (!w->block_writer) {
+ err = writer_reinit_block_writer(w, reftable_record_type(rec));
+ if (err < 0)
+ goto done;
+ }
if (block_writer_type(w->block_writer) != reftable_record_type(rec))
BUG("record of type %d added to writer of type %d",
@@ -254,7 +308,9 @@ static int writer_add_record(struct reftable_writer *w,
err = writer_flush_block(w);
if (err < 0)
goto done;
- writer_reinit_block_writer(w, reftable_record_type(rec));
+ err = writer_reinit_block_writer(w, reftable_record_type(rec));
+ if (err < 0)
+ goto done;
/*
* Try to add the record to the writer again. If this still fails then
@@ -271,7 +327,6 @@ static int writer_add_record(struct reftable_writer *w,
}
done:
- strbuf_release(&key);
return err;
}
@@ -284,11 +339,10 @@ int reftable_writer_add_ref(struct reftable_writer *w,
.ref = *ref
},
};
- int err = 0;
+ int err;
- if (!ref->refname)
- return REFTABLE_API_ERROR;
- if (ref->update_index < w->min_update_index ||
+ if (!ref->refname ||
+ ref->update_index < w->min_update_index ||
ref->update_index > w->max_update_index)
return REFTABLE_API_ERROR;
@@ -296,24 +350,36 @@ int reftable_writer_add_ref(struct reftable_writer *w,
err = writer_add_record(w, &rec);
if (err < 0)
- return err;
+ goto out;
if (!w->opts.skip_index_objects && reftable_ref_record_val1(ref)) {
- struct strbuf h = STRBUF_INIT;
- strbuf_add(&h, (char *)reftable_ref_record_val1(ref),
- hash_size(w->opts.hash_id));
- writer_index_hash(w, &h);
- strbuf_release(&h);
+ reftable_buf_reset(&w->scratch);
+ err = reftable_buf_add(&w->scratch, (char *)reftable_ref_record_val1(ref),
+ hash_size(w->opts.hash_id));
+ if (err < 0)
+ goto out;
+
+ err = writer_index_hash(w, &w->scratch);
+ if (err < 0)
+ goto out;
}
if (!w->opts.skip_index_objects && reftable_ref_record_val2(ref)) {
- struct strbuf h = STRBUF_INIT;
- strbuf_add(&h, reftable_ref_record_val2(ref),
- hash_size(w->opts.hash_id));
- writer_index_hash(w, &h);
- strbuf_release(&h);
+ reftable_buf_reset(&w->scratch);
+ err = reftable_buf_add(&w->scratch, reftable_ref_record_val2(ref),
+ hash_size(w->opts.hash_id));
+ if (err < 0)
+ goto out;
+
+ err = writer_index_hash(w, &w->scratch);
+ if (err < 0)
+ goto out;
}
- return 0;
+
+ err = 0;
+
+out:
+ return err;
}
int reftable_writer_add_refs(struct reftable_writer *w,
@@ -353,7 +419,7 @@ int reftable_writer_add_log(struct reftable_writer *w,
struct reftable_log_record *log)
{
char *input_log_message = NULL;
- struct strbuf cleaned_message = STRBUF_INIT;
+ struct reftable_buf cleaned_message = REFTABLE_BUF_INIT;
int err = 0;
if (log->value_type == REFTABLE_LOG_DELETION)
@@ -364,24 +430,34 @@ int reftable_writer_add_log(struct reftable_writer *w,
input_log_message = log->value.update.message;
if (!w->opts.exact_log_message && log->value.update.message) {
- strbuf_addstr(&cleaned_message, log->value.update.message);
+ err = reftable_buf_addstr(&cleaned_message, log->value.update.message);
+ if (err < 0)
+ goto done;
+
while (cleaned_message.len &&
- cleaned_message.buf[cleaned_message.len - 1] == '\n')
- strbuf_setlen(&cleaned_message,
- cleaned_message.len - 1);
+ cleaned_message.buf[cleaned_message.len - 1] == '\n') {
+ err = reftable_buf_setlen(&cleaned_message,
+ cleaned_message.len - 1);
+ if (err < 0)
+ goto done;
+ }
if (strchr(cleaned_message.buf, '\n')) {
/* multiple lines not allowed. */
err = REFTABLE_API_ERROR;
goto done;
}
- strbuf_addstr(&cleaned_message, "\n");
+
+ err = reftable_buf_addstr(&cleaned_message, "\n");
+ if (err < 0)
+ goto done;
+
log->value.update.message = cleaned_message.buf;
}
err = reftable_writer_add_log_verbatim(w, log);
log->value.update.message = input_log_message;
done:
- strbuf_release(&cleaned_message);
+ reftable_buf_release(&cleaned_message);
return err;
}
@@ -436,7 +512,9 @@ static int writer_finish_section(struct reftable_writer *w)
max_level++;
index_start = w->next;
- writer_reinit_block_writer(w, BLOCK_TYPE_INDEX);
+ err = writer_reinit_block_writer(w, BLOCK_TYPE_INDEX);
+ if (err < 0)
+ return err;
idx = w->index;
idx_len = w->index_len;
@@ -462,7 +540,7 @@ static int writer_finish_section(struct reftable_writer *w)
return err;
for (i = 0; i < idx_len; i++)
- strbuf_release(&idx[i].last_key);
+ reftable_buf_release(&idx[i].last_key);
reftable_free(idx);
}
@@ -479,13 +557,13 @@ static int writer_finish_section(struct reftable_writer *w)
bstats->max_index_level = max_level;
/* Reinit lastKey, as the next section can start with any key. */
- strbuf_reset(&w->last_key);
+ reftable_buf_reset(&w->last_key);
return 0;
}
struct common_prefix_arg {
- struct strbuf *last;
+ struct reftable_buf *last;
int max;
};
@@ -530,7 +608,10 @@ static void write_object_record(void *void_arg, void *key)
if (arg->err < 0)
goto done;
- writer_reinit_block_writer(arg->w, BLOCK_TYPE_OBJ);
+ arg->err = writer_reinit_block_writer(arg->w, BLOCK_TYPE_OBJ);
+ if (arg->err < 0)
+ goto done;
+
arg->err = block_writer_add(arg->w->block_writer, &rec);
if (arg->err == 0)
goto done;
@@ -544,12 +625,12 @@ static void write_object_record(void *void_arg, void *key)
done:;
}
-static void object_record_free(void *void_arg, void *key)
+static void object_record_free(void *void_arg UNUSED, void *key)
{
struct obj_index_tree_node *entry = key;
- FREE_AND_NULL(entry->offsets);
- strbuf_release(&entry->hash);
+ REFTABLE_FREE_AND_NULL(entry->offsets);
+ reftable_buf_release(&entry->hash);
reftable_free(entry);
}
@@ -559,16 +640,18 @@ static int writer_dump_object_index(struct reftable_writer *w)
struct common_prefix_arg common = {
.max = 1, /* obj_id_len should be >= 2. */
};
- if (w->obj_index_tree) {
+ int err;
+
+ if (w->obj_index_tree)
infix_walk(w->obj_index_tree, &update_common, &common);
- }
w->stats.object_id_len = common.max + 1;
- writer_reinit_block_writer(w, BLOCK_TYPE_OBJ);
+ err = writer_reinit_block_writer(w, BLOCK_TYPE_OBJ);
+ if (err < 0)
+ return err;
- if (w->obj_index_tree) {
+ if (w->obj_index_tree)
infix_walk(w->obj_index_tree, &write_object_record, &closure);
- }
if (closure.err < 0)
return closure.err;
@@ -661,8 +744,8 @@ done:
static void writer_clear_index(struct reftable_writer *w)
{
for (size_t i = 0; w->index && i < w->index_len; i++)
- strbuf_release(&w->index[i].last_key);
- FREE_AND_NULL(w->index);
+ reftable_buf_release(&w->index[i].last_key);
+ REFTABLE_FREE_AND_NULL(w->index);
w->index_len = 0;
w->index_cap = 0;
}
@@ -670,7 +753,7 @@ static void writer_clear_index(struct reftable_writer *w)
static int writer_flush_nonempty_block(struct reftable_writer *w)
{
struct reftable_index_record index_record = {
- .last_key = STRBUF_INIT,
+ .last_key = REFTABLE_BUF_INIT,
};
uint8_t typ = block_writer_type(w->block_writer);
struct reftable_block_stats *bstats;
@@ -726,9 +809,15 @@ static int writer_flush_nonempty_block(struct reftable_writer *w)
* case we will end up with a multi-level index.
*/
REFTABLE_ALLOC_GROW(w->index, w->index_len + 1, w->index_cap);
+ if (!w->index)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
+
index_record.offset = w->next;
- strbuf_reset(&index_record.last_key);
- strbuf_addbuf(&index_record.last_key, &w->block_writer->last_key);
+ reftable_buf_reset(&index_record.last_key);
+ err = reftable_buf_add(&index_record.last_key, w->block_writer->last_key.buf,
+ w->block_writer->last_key.len);
+ if (err < 0)
+ return err;
w->index[w->index_len] = index_record;
w->index_len++;
diff --git a/reftable/writer.h b/reftable/writer.h
index 8d0df9cc52..1f4788a430 100644
--- a/reftable/writer.h
+++ b/reftable/writer.h
@@ -19,7 +19,9 @@ struct reftable_writer {
int (*flush)(void *);
void *write_arg;
int pending_padding;
- struct strbuf last_key;
+ struct reftable_buf last_key;
+ /* Scratch buffer used to avoid allocations. */
+ struct reftable_buf scratch;
/* offset of next block to write. */
uint64_t next;