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
11 #include "blocksource.h"
12 #include "constants.h"
14 #include "reftable-error.h"
18 int header_size(int version
)
29 int footer_size(int version
)
40 static int block_writer_register_restart(struct block_writer
*w
, int n
,
41 int is_restart
, struct strbuf
*key
)
43 int rlen
= w
->restart_len
;
44 if (rlen
>= MAX_RESTARTS
) {
51 if (2 + 3 * rlen
+ n
> w
->block_size
- w
->next
)
54 REFTABLE_ALLOC_GROW(w
->restarts
, w
->restart_len
+ 1, w
->restart_cap
);
55 w
->restarts
[w
->restart_len
++] = w
->next
;
60 strbuf_reset(&w
->last_key
);
61 strbuf_addbuf(&w
->last_key
, key
);
66 void block_writer_init(struct block_writer
*bw
, uint8_t typ
, uint8_t *buf
,
67 uint32_t block_size
, uint32_t header_off
, int hash_size
)
70 bw
->hash_size
= hash_size
;
71 bw
->block_size
= block_size
;
72 bw
->header_off
= header_off
;
73 bw
->buf
[header_off
] = typ
;
74 bw
->next
= header_off
+ 4;
75 bw
->restart_interval
= 16;
81 uint8_t block_writer_type(struct block_writer
*bw
)
83 return bw
->buf
[bw
->header_off
];
86 /* Adds the reftable_record to the block. Returns -1 if it does not fit, 0 on
87 success. Returns REFTABLE_API_ERROR if attempting to write a record with
89 int block_writer_add(struct block_writer
*w
, struct reftable_record
*rec
)
91 struct strbuf empty
= STRBUF_INIT
;
93 w
->entries
% w
->restart_interval
== 0 ? empty
: w
->last_key
;
94 struct string_view out
= {
95 .buf
= w
->buf
+ w
->next
,
96 .len
= w
->block_size
- w
->next
,
99 struct string_view start
= out
;
102 struct strbuf key
= STRBUF_INIT
;
106 reftable_record_key(rec
, &key
);
108 err
= REFTABLE_API_ERROR
;
112 n
= reftable_encode_key(&is_restart
, out
, last
, key
,
113 reftable_record_val_type(rec
));
116 string_view_consume(&out
, n
);
118 n
= reftable_record_encode(rec
, out
, w
->hash_size
);
121 string_view_consume(&out
, n
);
123 err
= block_writer_register_restart(w
, start
.len
- out
.len
, is_restart
,
126 strbuf_release(&key
);
130 int block_writer_finish(struct block_writer
*w
)
133 for (i
= 0; i
< w
->restart_len
; i
++) {
134 put_be24(w
->buf
+ w
->next
, w
->restarts
[i
]);
138 put_be16(w
->buf
+ w
->next
, w
->restart_len
);
140 put_be24(w
->buf
+ 1 + w
->header_off
, w
->next
);
142 if (block_writer_type(w
) == BLOCK_TYPE_LOG
) {
143 int block_header_skip
= 4 + w
->header_off
;
144 uLongf src_len
= w
->next
- block_header_skip
;
145 uLongf dest_cap
= src_len
* 1.001 + 12;
148 REFTABLE_ALLOC_ARRAY(compressed
, dest_cap
);
151 uLongf out_dest_len
= dest_cap
;
152 int zresult
= compress2(compressed
, &out_dest_len
,
153 w
->buf
+ block_header_skip
,
155 if (zresult
== Z_BUF_ERROR
&& dest_cap
< LONG_MAX
) {
158 reftable_realloc(compressed
, dest_cap
);
163 if (Z_OK
!= zresult
) {
164 reftable_free(compressed
);
165 return REFTABLE_ZLIB_ERROR
;
168 memcpy(w
->buf
+ block_header_skip
, compressed
,
170 w
->next
= out_dest_len
+ block_header_skip
;
171 reftable_free(compressed
);
178 int block_reader_init(struct block_reader
*br
, struct reftable_block
*block
,
179 uint32_t header_off
, uint32_t table_block_size
,
182 uint32_t full_block_size
= table_block_size
;
183 uint8_t typ
= block
->data
[header_off
];
184 uint32_t sz
= get_be24(block
->data
+ header_off
+ 1);
186 uint16_t restart_count
= 0;
187 uint32_t restart_start
= 0;
188 uint8_t *restart_bytes
= NULL
;
189 uint8_t *uncompressed
= NULL
;
191 if (!reftable_is_block_type(typ
)) {
192 err
= REFTABLE_FORMAT_ERROR
;
196 if (typ
== BLOCK_TYPE_LOG
) {
197 int block_header_skip
= 4 + header_off
;
198 uLongf dst_len
= sz
- block_header_skip
; /* total size of dest
200 uLongf src_len
= block
->len
- block_header_skip
;
202 /* Log blocks specify the *uncompressed* size in their header. */
203 REFTABLE_ALLOC_ARRAY(uncompressed
, sz
);
205 /* Copy over the block header verbatim. It's not compressed. */
206 memcpy(uncompressed
, block
->data
, block_header_skip
);
210 uncompress2(uncompressed
+ block_header_skip
, &dst_len
,
211 block
->data
+ block_header_skip
, &src_len
)) {
212 err
= REFTABLE_ZLIB_ERROR
;
216 if (dst_len
+ block_header_skip
!= sz
) {
217 err
= REFTABLE_FORMAT_ERROR
;
221 /* We're done with the input data. */
222 reftable_block_done(block
);
223 block
->data
= uncompressed
;
226 block
->source
= malloc_block_source();
227 full_block_size
= src_len
+ block_header_skip
;
228 } else if (full_block_size
== 0) {
229 full_block_size
= sz
;
230 } else if (sz
< full_block_size
&& sz
< block
->len
&&
231 block
->data
[sz
] != 0) {
232 /* If the block is smaller than the full block size, it is
233 padded (data followed by '\0') or the next block is
235 full_block_size
= sz
;
238 restart_count
= get_be16(block
->data
+ sz
- 2);
239 restart_start
= sz
- 2 - 3 * restart_count
;
240 restart_bytes
= block
->data
+ restart_start
;
242 /* transfer ownership. */
247 br
->hash_size
= hash_size
;
248 br
->block_len
= restart_start
;
249 br
->full_block_size
= full_block_size
;
250 br
->header_off
= header_off
;
251 br
->restart_count
= restart_count
;
252 br
->restart_bytes
= restart_bytes
;
255 reftable_free(uncompressed
);
259 uint8_t block_reader_type(struct block_reader
*r
)
261 return r
->block
.data
[r
->header_off
];
264 int block_reader_first_key(struct block_reader
*br
, struct strbuf
*key
)
266 int off
= br
->header_off
+ 4, n
;
267 struct string_view in
= {
268 .buf
= br
->block
.data
+ off
,
269 .len
= br
->block_len
- off
,
275 n
= reftable_decode_key(key
, &extra
, in
);
279 return REFTABLE_FORMAT_ERROR
;
284 static uint32_t block_reader_restart_offset(struct block_reader
*br
, int i
)
286 return get_be24(br
->restart_bytes
+ 3 * i
);
289 void block_iter_seek_start(struct block_iter
*it
, struct block_reader
*br
)
292 strbuf_reset(&it
->last_key
);
293 it
->next_off
= br
->header_off
+ 4;
296 struct restart_needle_less_args
{
298 struct strbuf needle
;
299 struct block_reader
*reader
;
302 static int restart_needle_less(size_t idx
, void *_args
)
304 struct restart_needle_less_args
*args
= _args
;
305 uint32_t off
= block_reader_restart_offset(args
->reader
, idx
);
306 struct string_view in
= {
307 .buf
= args
->reader
->block
.data
+ off
,
308 .len
= args
->reader
->block_len
- off
,
310 uint64_t prefix_len
, suffix_len
;
315 * Records at restart points are stored without prefix compression, so
316 * there is no need to fully decode the record key here. This removes
317 * the need for allocating memory.
319 n
= reftable_decode_keylen(in
, &prefix_len
, &suffix_len
, &extra
);
320 if (n
< 0 || prefix_len
) {
325 string_view_consume(&in
, n
);
326 if (suffix_len
> in
.len
) {
331 n
= memcmp(args
->needle
.buf
, in
.buf
,
332 args
->needle
.len
< suffix_len
? args
->needle
.len
: suffix_len
);
335 return args
->needle
.len
< suffix_len
;
338 void block_iter_copy_from(struct block_iter
*dest
, struct block_iter
*src
)
341 dest
->next_off
= src
->next_off
;
342 strbuf_reset(&dest
->last_key
);
343 strbuf_addbuf(&dest
->last_key
, &src
->last_key
);
346 int block_iter_next(struct block_iter
*it
, struct reftable_record
*rec
)
348 struct string_view in
= {
349 .buf
= it
->br
->block
.data
+ it
->next_off
,
350 .len
= it
->br
->block_len
- it
->next_off
,
352 struct string_view start
= in
;
356 if (it
->next_off
>= it
->br
->block_len
)
359 n
= reftable_decode_key(&it
->last_key
, &extra
, in
);
362 if (!it
->last_key
.len
)
363 return REFTABLE_FORMAT_ERROR
;
365 string_view_consume(&in
, n
);
366 n
= reftable_record_decode(rec
, it
->last_key
, extra
, in
, it
->br
->hash_size
,
370 string_view_consume(&in
, n
);
372 it
->next_off
+= start
.len
- in
.len
;
376 void block_iter_close(struct block_iter
*it
)
378 strbuf_release(&it
->last_key
);
379 strbuf_release(&it
->scratch
);
382 int block_iter_seek_key(struct block_iter
*it
, struct block_reader
*br
,
385 struct restart_needle_less_args args
= {
389 struct block_iter next
= BLOCK_ITER_INIT
;
390 struct reftable_record rec
;
395 * Perform a binary search over the block's restart points, which
396 * avoids doing a linear scan over the whole block. Like this, we
397 * identify the section of the block that should contain our key.
399 * Note that we explicitly search for the first restart point _greater_
400 * than the sought-after record, not _greater or equal_ to it. In case
401 * the sought-after record is located directly at the restart point we
402 * would otherwise start doing the linear search at the preceding
403 * restart point. While that works alright, we would end up scanning
406 i
= binsearch(br
->restart_count
, &restart_needle_less
, &args
);
408 err
= REFTABLE_FORMAT_ERROR
;
413 * Now there are multiple cases:
415 * - `i == 0`: The wanted record is smaller than the record found at
416 * the first restart point. As the first restart point is the first
417 * record in the block, our wanted record cannot be located in this
418 * block at all. We still need to position the iterator so that the
419 * next call to `block_iter_next()` will yield an end-of-iterator
422 * - `i == restart_count`: The wanted record was not found at any of
423 * the restart points. As there is no restart point at the end of
424 * the section the record may thus be contained in the last block.
426 * - `i > 0`: The wanted record must be contained in the section
427 * before the found restart point. We thus do a linear search
428 * starting from the preceding restart point.
431 it
->next_off
= block_reader_restart_offset(br
, i
- 1);
433 it
->next_off
= br
->header_off
+ 4;
436 reftable_record_init(&rec
, block_reader_type(br
));
439 * We're looking for the last entry less than the wanted key so that
440 * the next call to `block_reader_next()` would yield the wanted
441 * record. We thus don't want to position our reader at the sought
442 * after record, but one before. To do so, we have to go one entry too
443 * far and then back up.
446 block_iter_copy_from(&next
, it
);
447 err
= block_iter_next(&next
, &rec
);
456 * Check whether the current key is greater or equal to the
457 * sought-after key. In case it is greater we know that the
458 * record does not exist in the block and can thus abort early.
459 * In case it is equal to the sought-after key we have found
460 * the desired record.
462 reftable_record_key(&rec
, &it
->last_key
);
463 if (strbuf_cmp(&it
->last_key
, want
) >= 0)
466 block_iter_copy_from(it
, &next
);
470 block_iter_close(&next
);
471 reftable_record_release(&rec
);
476 void block_writer_release(struct block_writer
*bw
)
478 FREE_AND_NULL(bw
->restarts
);
479 strbuf_release(&bw
->last_key
);
480 /* the block is not owned. */
483 void reftable_block_done(struct reftable_block
*blockp
)
485 struct reftable_block_source source
= blockp
->source
;
486 if (blockp
&& source
.ops
)
487 source
.ops
->return_block(source
.arg
, blockp
);
490 blockp
->source
.ops
= NULL
;
491 blockp
->source
.arg
= NULL
;