2 Copyright 2020 Google LLC
4 Use of this source code is governed by a BSD-style
5 license that can be found in the LICENSE file or at
6 https://developers.google.com/open-source/licenses/bsd
14 #include "constants.h"
17 #include "reftable-error.h"
19 /* finishes a block, and writes it to storage */
20 static int writer_flush_block(struct reftable_writer
*w
);
22 /* deallocates memory related to the index */
23 static void writer_clear_index(struct reftable_writer
*w
);
25 /* finishes writing a 'r' (refs) or 'g' (reflogs) section */
26 static int writer_finish_public_section(struct reftable_writer
*w
);
28 static struct reftable_block_stats
*
29 writer_reftable_block_stats(struct reftable_writer
*w
, uint8_t typ
)
33 return &w
->stats
.ref_stats
;
35 return &w
->stats
.obj_stats
;
37 return &w
->stats
.idx_stats
;
39 return &w
->stats
.log_stats
;
45 /* write data, queuing the padding for the next write. Returns negative for
47 static int padded_write(struct reftable_writer
*w
, uint8_t *data
, size_t len
,
51 if (w
->pending_padding
> 0) {
52 uint8_t *zeroed
= reftable_calloc(w
->pending_padding
, sizeof(*zeroed
));
53 int n
= w
->write(w
->write_arg
, zeroed
, w
->pending_padding
);
57 w
->pending_padding
= 0;
58 reftable_free(zeroed
);
61 w
->pending_padding
= padding
;
62 n
= w
->write(w
->write_arg
, data
, len
);
69 static void options_set_defaults(struct reftable_write_options
*opts
)
71 if (opts
->restart_interval
== 0) {
72 opts
->restart_interval
= 16;
75 if (opts
->hash_id
== 0) {
76 opts
->hash_id
= GIT_SHA1_FORMAT_ID
;
78 if (opts
->block_size
== 0) {
79 opts
->block_size
= DEFAULT_BLOCK_SIZE
;
83 static int writer_version(struct reftable_writer
*w
)
85 return (w
->opts
.hash_id
== 0 || w
->opts
.hash_id
== GIT_SHA1_FORMAT_ID
) ?
90 static int writer_write_header(struct reftable_writer
*w
, uint8_t *dest
)
92 memcpy(dest
, "REFT", 4);
94 dest
[4] = writer_version(w
);
96 put_be24(dest
+ 5, w
->opts
.block_size
);
97 put_be64(dest
+ 8, w
->min_update_index
);
98 put_be64(dest
+ 16, w
->max_update_index
);
99 if (writer_version(w
) == 2) {
100 put_be32(dest
+ 24, w
->opts
.hash_id
);
102 return header_size(writer_version(w
));
105 static void writer_reinit_block_writer(struct reftable_writer
*w
, uint8_t typ
)
109 block_start
= header_size(writer_version(w
));
112 strbuf_release(&w
->last_key
);
113 block_writer_init(&w
->block_writer_data
, typ
, w
->block
,
114 w
->opts
.block_size
, block_start
,
115 hash_size(w
->opts
.hash_id
));
116 w
->block_writer
= &w
->block_writer_data
;
117 w
->block_writer
->restart_interval
= w
->opts
.restart_interval
;
120 static struct strbuf reftable_empty_strbuf
= STRBUF_INIT
;
122 struct reftable_writer
*
123 reftable_new_writer(ssize_t (*writer_func
)(void *, const void *, size_t),
124 int (*flush_func
)(void *),
125 void *writer_arg
, struct reftable_write_options
*opts
)
127 struct reftable_writer
*wp
= reftable_calloc(1, sizeof(*wp
));
128 strbuf_init(&wp
->block_writer_data
.last_key
, 0);
129 options_set_defaults(opts
);
130 if (opts
->block_size
>= (1 << 24)) {
131 /* TODO - error return? */
134 wp
->last_key
= reftable_empty_strbuf
;
135 REFTABLE_CALLOC_ARRAY(wp
->block
, opts
->block_size
);
136 wp
->write
= writer_func
;
137 wp
->write_arg
= writer_arg
;
139 wp
->flush
= flush_func
;
140 writer_reinit_block_writer(wp
, BLOCK_TYPE_REF
);
145 void reftable_writer_set_limits(struct reftable_writer
*w
, uint64_t min
,
148 w
->min_update_index
= min
;
149 w
->max_update_index
= max
;
152 void reftable_writer_free(struct reftable_writer
*w
)
156 reftable_free(w
->block
);
160 struct obj_index_tree_node
{
167 #define OBJ_INDEX_TREE_NODE_INIT \
169 .hash = STRBUF_INIT \
172 static int obj_index_tree_node_compare(const void *a
, const void *b
)
174 return strbuf_cmp(&((const struct obj_index_tree_node
*)a
)->hash
,
175 &((const struct obj_index_tree_node
*)b
)->hash
);
178 static void writer_index_hash(struct reftable_writer
*w
, struct strbuf
*hash
)
180 uint64_t off
= w
->next
;
182 struct obj_index_tree_node want
= { .hash
= *hash
};
184 struct tree_node
*node
= tree_search(&want
, &w
->obj_index_tree
,
185 &obj_index_tree_node_compare
, 0);
186 struct obj_index_tree_node
*key
= NULL
;
188 struct obj_index_tree_node empty
= OBJ_INDEX_TREE_NODE_INIT
;
189 key
= reftable_malloc(sizeof(struct obj_index_tree_node
));
192 strbuf_reset(&key
->hash
);
193 strbuf_addbuf(&key
->hash
, hash
);
194 tree_search((void *)key
, &w
->obj_index_tree
,
195 &obj_index_tree_node_compare
, 1);
200 if (key
->offset_len
> 0 && key
->offsets
[key
->offset_len
- 1] == off
) {
204 REFTABLE_ALLOC_GROW(key
->offsets
, key
->offset_len
+ 1, key
->offset_cap
);
205 key
->offsets
[key
->offset_len
++] = off
;
208 static int writer_add_record(struct reftable_writer
*w
,
209 struct reftable_record
*rec
)
211 struct strbuf key
= STRBUF_INIT
;
213 reftable_record_key(rec
, &key
);
214 if (strbuf_cmp(&w
->last_key
, &key
) >= 0) {
215 err
= REFTABLE_API_ERROR
;
219 strbuf_reset(&w
->last_key
);
220 strbuf_addbuf(&w
->last_key
, &key
);
221 if (!w
->block_writer
) {
222 writer_reinit_block_writer(w
, reftable_record_type(rec
));
225 assert(block_writer_type(w
->block_writer
) == reftable_record_type(rec
));
227 if (block_writer_add(w
->block_writer
, rec
) == 0) {
232 err
= writer_flush_block(w
);
237 writer_reinit_block_writer(w
, reftable_record_type(rec
));
238 err
= block_writer_add(w
->block_writer
, rec
);
240 /* we are writing into memory, so an error can only mean it
242 err
= REFTABLE_ENTRY_TOO_BIG_ERROR
;
247 strbuf_release(&key
);
251 int reftable_writer_add_ref(struct reftable_writer
*w
,
252 struct reftable_ref_record
*ref
)
254 struct reftable_record rec
= {
255 .type
= BLOCK_TYPE_REF
,
263 return REFTABLE_API_ERROR
;
264 if (ref
->update_index
< w
->min_update_index
||
265 ref
->update_index
> w
->max_update_index
)
266 return REFTABLE_API_ERROR
;
268 rec
.u
.ref
.update_index
-= w
->min_update_index
;
270 err
= writer_add_record(w
, &rec
);
274 if (!w
->opts
.skip_index_objects
&& reftable_ref_record_val1(ref
)) {
275 struct strbuf h
= STRBUF_INIT
;
276 strbuf_add(&h
, (char *)reftable_ref_record_val1(ref
),
277 hash_size(w
->opts
.hash_id
));
278 writer_index_hash(w
, &h
);
282 if (!w
->opts
.skip_index_objects
&& reftable_ref_record_val2(ref
)) {
283 struct strbuf h
= STRBUF_INIT
;
284 strbuf_add(&h
, reftable_ref_record_val2(ref
),
285 hash_size(w
->opts
.hash_id
));
286 writer_index_hash(w
, &h
);
292 int reftable_writer_add_refs(struct reftable_writer
*w
,
293 struct reftable_ref_record
*refs
, int n
)
297 QSORT(refs
, n
, reftable_ref_record_compare_name
);
298 for (i
= 0; err
== 0 && i
< n
; i
++) {
299 err
= reftable_writer_add_ref(w
, &refs
[i
]);
304 static int reftable_writer_add_log_verbatim(struct reftable_writer
*w
,
305 struct reftable_log_record
*log
)
307 struct reftable_record rec
= {
308 .type
= BLOCK_TYPE_LOG
,
313 if (w
->block_writer
&&
314 block_writer_type(w
->block_writer
) == BLOCK_TYPE_REF
) {
315 int err
= writer_finish_public_section(w
);
320 w
->next
-= w
->pending_padding
;
321 w
->pending_padding
= 0;
322 return writer_add_record(w
, &rec
);
325 int reftable_writer_add_log(struct reftable_writer
*w
,
326 struct reftable_log_record
*log
)
328 char *input_log_message
= NULL
;
329 struct strbuf cleaned_message
= STRBUF_INIT
;
332 if (log
->value_type
== REFTABLE_LOG_DELETION
)
333 return reftable_writer_add_log_verbatim(w
, log
);
336 return REFTABLE_API_ERROR
;
338 input_log_message
= log
->value
.update
.message
;
339 if (!w
->opts
.exact_log_message
&& log
->value
.update
.message
) {
340 strbuf_addstr(&cleaned_message
, log
->value
.update
.message
);
341 while (cleaned_message
.len
&&
342 cleaned_message
.buf
[cleaned_message
.len
- 1] == '\n')
343 strbuf_setlen(&cleaned_message
,
344 cleaned_message
.len
- 1);
345 if (strchr(cleaned_message
.buf
, '\n')) {
346 /* multiple lines not allowed. */
347 err
= REFTABLE_API_ERROR
;
350 strbuf_addstr(&cleaned_message
, "\n");
351 log
->value
.update
.message
= cleaned_message
.buf
;
354 err
= reftable_writer_add_log_verbatim(w
, log
);
355 log
->value
.update
.message
= input_log_message
;
357 strbuf_release(&cleaned_message
);
361 int reftable_writer_add_logs(struct reftable_writer
*w
,
362 struct reftable_log_record
*logs
, int n
)
366 QSORT(logs
, n
, reftable_log_record_compare_key
);
368 for (i
= 0; err
== 0 && i
< n
; i
++) {
369 err
= reftable_writer_add_log(w
, &logs
[i
]);
374 static int writer_finish_section(struct reftable_writer
*w
)
376 struct reftable_block_stats
*bstats
= NULL
;
377 uint8_t typ
= block_writer_type(w
->block_writer
);
378 uint64_t index_start
= 0;
380 size_t threshold
= w
->opts
.unpadded
? 1 : 3;
381 int before_blocks
= w
->stats
.idx_stats
.blocks
;
384 err
= writer_flush_block(w
);
389 * When the section we are about to index has a lot of blocks then the
390 * index itself may span across multiple blocks, as well. This would
391 * require a linear scan over index blocks only to find the desired
392 * indexed block, which is inefficient. Instead, we write a multi-level
393 * index where index records of level N+1 will refer to index blocks of
394 * level N. This isn't constant time, either, but at least logarithmic.
396 * This loop handles writing this multi-level index. Note that we write
397 * the lowest-level index pointing to the indexed blocks first. We then
398 * continue writing additional index levels until the current level has
399 * less blocks than the threshold so that the highest level will be at
400 * the end of the index section.
402 * Readers are thus required to start reading the index section from
403 * its end, which is why we set `index_start` to the beginning of the
404 * last index section.
406 while (w
->index_len
> threshold
) {
407 struct reftable_index_record
*idx
= NULL
;
411 index_start
= w
->next
;
412 writer_reinit_block_writer(w
, BLOCK_TYPE_INDEX
);
415 idx_len
= w
->index_len
;
420 for (i
= 0; i
< idx_len
; i
++) {
421 struct reftable_record rec
= {
422 .type
= BLOCK_TYPE_INDEX
,
428 err
= writer_add_record(w
, &rec
);
433 err
= writer_flush_block(w
);
437 for (i
= 0; i
< idx_len
; i
++)
438 strbuf_release(&idx
[i
].last_key
);
443 * The index may still contain a number of index blocks lower than the
444 * threshold. Clear it so that these entries don't leak into the next
447 writer_clear_index(w
);
449 bstats
= writer_reftable_block_stats(w
, typ
);
450 bstats
->index_blocks
= w
->stats
.idx_stats
.blocks
- before_blocks
;
451 bstats
->index_offset
= index_start
;
452 bstats
->max_index_level
= max_level
;
454 /* Reinit lastKey, as the next section can start with any key. */
460 struct common_prefix_arg
{
465 static void update_common(void *void_arg
, void *key
)
467 struct common_prefix_arg
*arg
= void_arg
;
468 struct obj_index_tree_node
*entry
= key
;
470 int n
= common_prefix_size(&entry
->hash
, arg
->last
);
475 arg
->last
= &entry
->hash
;
478 struct write_record_arg
{
479 struct reftable_writer
*w
;
483 static void write_object_record(void *void_arg
, void *key
)
485 struct write_record_arg
*arg
= void_arg
;
486 struct obj_index_tree_node
*entry
= key
;
487 struct reftable_record
488 rec
= { .type
= BLOCK_TYPE_OBJ
,
490 .hash_prefix
= (uint8_t *)entry
->hash
.buf
,
491 .hash_prefix_len
= arg
->w
->stats
.object_id_len
,
492 .offsets
= entry
->offsets
,
493 .offset_len
= entry
->offset_len
,
498 arg
->err
= block_writer_add(arg
->w
->block_writer
, &rec
);
502 arg
->err
= writer_flush_block(arg
->w
);
506 writer_reinit_block_writer(arg
->w
, BLOCK_TYPE_OBJ
);
507 arg
->err
= block_writer_add(arg
->w
->block_writer
, &rec
);
511 rec
.u
.obj
.offset_len
= 0;
512 arg
->err
= block_writer_add(arg
->w
->block_writer
, &rec
);
514 /* Should be able to write into a fresh block. */
515 assert(arg
->err
== 0);
520 static void object_record_free(void *void_arg
, void *key
)
522 struct obj_index_tree_node
*entry
= key
;
524 FREE_AND_NULL(entry
->offsets
);
525 strbuf_release(&entry
->hash
);
526 reftable_free(entry
);
529 static int writer_dump_object_index(struct reftable_writer
*w
)
531 struct write_record_arg closure
= { .w
= w
};
532 struct common_prefix_arg common
= {
533 .max
= 1, /* obj_id_len should be >= 2. */
535 if (w
->obj_index_tree
) {
536 infix_walk(w
->obj_index_tree
, &update_common
, &common
);
538 w
->stats
.object_id_len
= common
.max
+ 1;
540 writer_reinit_block_writer(w
, BLOCK_TYPE_OBJ
);
542 if (w
->obj_index_tree
) {
543 infix_walk(w
->obj_index_tree
, &write_object_record
, &closure
);
548 return writer_finish_section(w
);
551 static int writer_finish_public_section(struct reftable_writer
*w
)
556 if (!w
->block_writer
)
559 typ
= block_writer_type(w
->block_writer
);
560 err
= writer_finish_section(w
);
563 if (typ
== BLOCK_TYPE_REF
&& !w
->opts
.skip_index_objects
&&
564 w
->stats
.ref_stats
.index_blocks
> 0) {
565 err
= writer_dump_object_index(w
);
570 if (w
->obj_index_tree
) {
571 infix_walk(w
->obj_index_tree
, &object_record_free
, NULL
);
572 tree_free(w
->obj_index_tree
);
573 w
->obj_index_tree
= NULL
;
576 w
->block_writer
= NULL
;
580 int reftable_writer_close(struct reftable_writer
*w
)
584 int err
= writer_finish_public_section(w
);
585 int empty_table
= w
->next
== 0;
588 w
->pending_padding
= 0;
590 /* Empty tables need a header anyway. */
592 int n
= writer_write_header(w
, header
);
593 err
= padded_write(w
, header
, n
, 0);
598 p
+= writer_write_header(w
, footer
);
599 put_be64(p
, w
->stats
.ref_stats
.index_offset
);
601 put_be64(p
, (w
->stats
.obj_stats
.offset
) << 5 | w
->stats
.object_id_len
);
603 put_be64(p
, w
->stats
.obj_stats
.index_offset
);
606 put_be64(p
, w
->stats
.log_stats
.offset
);
608 put_be64(p
, w
->stats
.log_stats
.index_offset
);
611 put_be32(p
, crc32(0, footer
, p
- footer
));
614 err
= w
->flush(w
->write_arg
);
616 err
= REFTABLE_IO_ERROR
;
620 err
= padded_write(w
, footer
, footer_size(writer_version(w
)), 0);
625 err
= REFTABLE_EMPTY_TABLE_ERROR
;
630 /* free up memory. */
631 block_writer_release(&w
->block_writer_data
);
632 writer_clear_index(w
);
633 strbuf_release(&w
->last_key
);
637 static void writer_clear_index(struct reftable_writer
*w
)
639 for (size_t i
= 0; i
< w
->index_len
; i
++)
640 strbuf_release(&w
->index
[i
].last_key
);
641 FREE_AND_NULL(w
->index
);
646 static const int debug
= 0;
648 static int writer_flush_nonempty_block(struct reftable_writer
*w
)
650 uint8_t typ
= block_writer_type(w
->block_writer
);
651 struct reftable_block_stats
*bstats
=
652 writer_reftable_block_stats(w
, typ
);
653 uint64_t block_typ_off
= (bstats
->blocks
== 0) ? w
->next
: 0;
654 int raw_bytes
= block_writer_finish(w
->block_writer
);
657 struct reftable_index_record ir
= { .last_key
= STRBUF_INIT
};
661 if (!w
->opts
.unpadded
&& typ
!= BLOCK_TYPE_LOG
) {
662 padding
= w
->opts
.block_size
- raw_bytes
;
665 if (block_typ_off
> 0) {
666 bstats
->offset
= block_typ_off
;
669 bstats
->entries
+= w
->block_writer
->entries
;
670 bstats
->restarts
+= w
->block_writer
->restart_len
;
675 fprintf(stderr
, "block %c off %" PRIu64
" sz %d (%d)\n", typ
,
677 get_be24(w
->block
+ w
->block_writer
->header_off
+ 1));
681 writer_write_header(w
, w
->block
);
684 err
= padded_write(w
, w
->block
, raw_bytes
, padding
);
688 REFTABLE_ALLOC_GROW(w
->index
, w
->index_len
+ 1, w
->index_cap
);
691 strbuf_reset(&ir
.last_key
);
692 strbuf_addbuf(&ir
.last_key
, &w
->block_writer
->last_key
);
693 w
->index
[w
->index_len
] = ir
;
696 w
->next
+= padding
+ raw_bytes
;
697 w
->block_writer
= NULL
;
701 static int writer_flush_block(struct reftable_writer
*w
)
703 if (!w
->block_writer
)
705 if (w
->block_writer
->entries
== 0)
707 return writer_flush_nonempty_block(w
);
710 const struct reftable_stats
*reftable_writer_stats(struct reftable_writer
*w
)