Git fork
1/*
2 * Copyright 2020 Google LLC
3 *
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
7 */
8
9/* record.c - methods for different types of records. */
10
11#include "record.h"
12
13#include "system.h"
14#include "constants.h"
15#include "reftable-error.h"
16#include "basics.h"
17
18static struct reftable_record_vtable *
19reftable_record_vtable(struct reftable_record *rec);
20static void *reftable_record_data(struct reftable_record *rec);
21
22int get_var_int(uint64_t *dest, struct string_view *in)
23{
24 const unsigned char *buf = in->buf;
25 unsigned char c;
26 uint64_t val;
27
28 if (!in->len)
29 return -1;
30 c = *buf++;
31 val = c & 0x7f;
32
33 while (c & 0x80) {
34 /*
35 * We use a micro-optimization here: whenever we see that the
36 * 0x80 bit is set, we know that the remainder of the value
37 * cannot be 0. The zero-values thus doesn't need to be encoded
38 * at all, which is why we subtract 1 when encoding and add 1
39 * when decoding.
40 *
41 * This allows us to save a byte in some edge cases.
42 */
43 val += 1;
44 if (!val || (val & (uint64_t)(~0ULL << (64 - 7))))
45 return -1; /* overflow */
46 if (buf >= in->buf + in->len)
47 return -1;
48 c = *buf++;
49 val = (val << 7) + (c & 0x7f);
50 }
51
52 *dest = val;
53 return buf - in->buf;
54}
55
56int put_var_int(struct string_view *dest, uint64_t value)
57{
58 unsigned char varint[10];
59 unsigned pos = sizeof(varint) - 1;
60 varint[pos] = value & 0x7f;
61 while (value >>= 7)
62 varint[--pos] = 0x80 | (--value & 0x7f);
63 if (dest->len < sizeof(varint) - pos)
64 return REFTABLE_ENTRY_TOO_BIG_ERROR;
65 memcpy(dest->buf, varint + pos, sizeof(varint) - pos);
66 return sizeof(varint) - pos;
67}
68
69int reftable_is_block_type(uint8_t typ)
70{
71 switch (typ) {
72 case REFTABLE_BLOCK_TYPE_REF:
73 case REFTABLE_BLOCK_TYPE_LOG:
74 case REFTABLE_BLOCK_TYPE_OBJ:
75 case REFTABLE_BLOCK_TYPE_INDEX:
76 return 1;
77 }
78 return 0;
79}
80
81const unsigned char *reftable_ref_record_val1(const struct reftable_ref_record *rec)
82{
83 // Implemented in reftable_record.rs
84 switch (rec->value_type) {
85 case REFTABLE_REF_VAL1:
86 return rec->value.val1;
87 case REFTABLE_REF_VAL2:
88 return rec->value.val2.value;
89 default:
90 return NULL;
91 }
92}
93
94const unsigned char *reftable_ref_record_val2(const struct reftable_ref_record *rec)
95{
96 // Implemented in reftable_record.rs
97 switch (rec->value_type) {
98 case REFTABLE_REF_VAL2:
99 return rec->value.val2.target_value;
100 default:
101 return NULL;
102 }
103}
104
105static int decode_string(struct reftable_buf *dest, struct string_view in)
106{
107 int start_len = in.len;
108 uint64_t tsize = 0;
109 int n, err;
110
111 n = get_var_int(&tsize, &in);
112 if (n <= 0)
113 return -1;
114 string_view_consume(&in, n);
115 if (in.len < tsize)
116 return -1;
117
118 reftable_buf_reset(dest);
119 err = reftable_buf_add(dest, in.buf, tsize);
120 if (err < 0)
121 return err;
122
123 string_view_consume(&in, tsize);
124
125 return start_len - in.len;
126}
127
128static int encode_string(const char *str, struct string_view s)
129{
130 struct string_view start = s;
131 size_t l = strlen(str);
132 int n = put_var_int(&s, l);
133 if (n < 0)
134 return n;
135 string_view_consume(&s, n);
136 if (s.len < l)
137 return REFTABLE_ENTRY_TOO_BIG_ERROR;
138 memcpy(s.buf, str, l);
139 string_view_consume(&s, l);
140
141 return start.len - s.len;
142}
143
144int reftable_encode_key(int *restart, struct string_view dest,
145 struct reftable_buf prev_key, struct reftable_buf key,
146 uint8_t extra)
147{
148 struct string_view start = dest;
149 size_t prefix_len = common_prefix_size(&prev_key, &key);
150 uint64_t suffix_len = key.len - prefix_len;
151 int n = put_var_int(&dest, prefix_len);
152 if (n < 0)
153 return n;
154 string_view_consume(&dest, n);
155
156 *restart = (prefix_len == 0);
157
158 n = put_var_int(&dest, suffix_len << 3 | (uint64_t)extra);
159 if (n < 0)
160 return n;
161 string_view_consume(&dest, n);
162
163 if (dest.len < suffix_len)
164 return REFTABLE_ENTRY_TOO_BIG_ERROR;
165 memcpy(dest.buf, key.buf + prefix_len, suffix_len);
166 string_view_consume(&dest, suffix_len);
167
168 return start.len - dest.len;
169}
170
171int reftable_decode_keylen(struct string_view in,
172 uint64_t *prefix_len,
173 uint64_t *suffix_len,
174 uint8_t *extra)
175{
176 size_t start_len = in.len;
177 int n;
178
179 n = get_var_int(prefix_len, &in);
180 if (n < 0)
181 return -1;
182 string_view_consume(&in, n);
183
184 n = get_var_int(suffix_len, &in);
185 if (n <= 0)
186 return -1;
187 string_view_consume(&in, n);
188
189 *extra = (uint8_t)(*suffix_len & 0x7);
190 *suffix_len >>= 3;
191
192 return start_len - in.len;
193}
194
195int reftable_decode_key(struct reftable_buf *last_key, uint8_t *extra,
196 struct string_view in)
197{
198 int start_len = in.len;
199 uint64_t prefix_len = 0;
200 uint64_t suffix_len = 0;
201 int err, n;
202
203 n = reftable_decode_keylen(in, &prefix_len, &suffix_len, extra);
204 if (n < 0)
205 return -1;
206 string_view_consume(&in, n);
207
208 if (in.len < suffix_len ||
209 prefix_len > last_key->len)
210 return -1;
211
212 err = reftable_buf_setlen(last_key, prefix_len);
213 if (err < 0)
214 return err;
215
216 err = reftable_buf_add(last_key, in.buf, suffix_len);
217 if (err < 0)
218 return err;
219
220 string_view_consume(&in, suffix_len);
221
222 return start_len - in.len;
223}
224
225static int reftable_ref_record_key(const void *r, struct reftable_buf *dest)
226{
227 const struct reftable_ref_record *rec =
228 (const struct reftable_ref_record *)r;
229 reftable_buf_reset(dest);
230 return reftable_buf_addstr(dest, rec->refname);
231}
232
233static int reftable_ref_record_copy_from(void *rec, const void *src_rec,
234 uint32_t hash_size)
235{
236 struct reftable_ref_record *ref = rec;
237 const struct reftable_ref_record *src = src_rec;
238 char *refname = NULL;
239 size_t refname_cap = 0;
240 int err;
241
242 REFTABLE_SWAP(refname, ref->refname);
243 REFTABLE_SWAP(refname_cap, ref->refname_cap);
244 reftable_ref_record_release(ref);
245 REFTABLE_SWAP(ref->refname, refname);
246 REFTABLE_SWAP(ref->refname_cap, refname_cap);
247
248 if (src->refname) {
249 size_t refname_len = strlen(src->refname);
250
251 REFTABLE_ALLOC_GROW_OR_NULL(ref->refname, refname_len + 1,
252 ref->refname_cap);
253 if (!ref->refname) {
254 err = REFTABLE_OUT_OF_MEMORY_ERROR;
255 goto out;
256 }
257
258 memcpy(ref->refname, src->refname, refname_len);
259 ref->refname[refname_len] = 0;
260 }
261
262 ref->update_index = src->update_index;
263 ref->value_type = src->value_type;
264 switch (src->value_type) {
265 case REFTABLE_REF_DELETION:
266 break;
267 case REFTABLE_REF_VAL1:
268 memcpy(ref->value.val1, src->value.val1, hash_size);
269 break;
270 case REFTABLE_REF_VAL2:
271 memcpy(ref->value.val2.value, src->value.val2.value, hash_size);
272 memcpy(ref->value.val2.target_value,
273 src->value.val2.target_value, hash_size);
274 break;
275 case REFTABLE_REF_SYMREF:
276 ref->value.symref = reftable_strdup(src->value.symref);
277 if (!ref->value.symref) {
278 err = REFTABLE_OUT_OF_MEMORY_ERROR;
279 goto out;
280 }
281 break;
282 }
283
284 err = 0;
285out:
286 return err;
287}
288
289static void reftable_ref_record_release_void(void *rec)
290{
291 reftable_ref_record_release(rec);
292}
293
294void reftable_ref_record_release(struct reftable_ref_record *ref)
295{
296 switch (ref->value_type) {
297 case REFTABLE_REF_SYMREF:
298 reftable_free(ref->value.symref);
299 break;
300 case REFTABLE_REF_VAL2:
301 break;
302 case REFTABLE_REF_VAL1:
303 break;
304 case REFTABLE_REF_DELETION:
305 break;
306 default:
307 abort();
308 }
309
310 reftable_free(ref->refname);
311 memset(ref, 0, sizeof(struct reftable_ref_record));
312}
313
314static uint8_t reftable_ref_record_val_type(const void *rec)
315{
316 const struct reftable_ref_record *r =
317 (const struct reftable_ref_record *)rec;
318 return r->value_type;
319}
320
321static int reftable_ref_record_encode(const void *rec, struct string_view s,
322 uint32_t hash_size)
323{
324 const struct reftable_ref_record *r =
325 (const struct reftable_ref_record *)rec;
326 struct string_view start = s;
327 int n = put_var_int(&s, r->update_index);
328 if (n < 0)
329 return n;
330 string_view_consume(&s, n);
331
332 switch (r->value_type) {
333 case REFTABLE_REF_SYMREF:
334 n = encode_string(r->value.symref, s);
335 if (n < 0)
336 return n;
337 string_view_consume(&s, n);
338 break;
339 case REFTABLE_REF_VAL2:
340 if (s.len < 2 * hash_size)
341 return REFTABLE_ENTRY_TOO_BIG_ERROR;
342 memcpy(s.buf, r->value.val2.value, hash_size);
343 string_view_consume(&s, hash_size);
344 memcpy(s.buf, r->value.val2.target_value, hash_size);
345 string_view_consume(&s, hash_size);
346 break;
347 case REFTABLE_REF_VAL1:
348 if (s.len < hash_size)
349 return REFTABLE_ENTRY_TOO_BIG_ERROR;
350 memcpy(s.buf, r->value.val1, hash_size);
351 string_view_consume(&s, hash_size);
352 break;
353 case REFTABLE_REF_DELETION:
354 break;
355 default:
356 abort();
357 }
358
359 return start.len - s.len;
360}
361
362static int reftable_ref_record_decode(void *rec, struct reftable_buf key,
363 uint8_t val_type, struct string_view in,
364 uint32_t hash_size, struct reftable_buf *scratch)
365{
366 struct reftable_ref_record *r = rec;
367 struct string_view start = in;
368 uint64_t update_index = 0;
369 const char *refname = NULL;
370 size_t refname_cap = 0;
371 int n, err;
372
373 n = get_var_int(&update_index, &in);
374 if (n < 0)
375 return n;
376 string_view_consume(&in, n);
377
378 REFTABLE_SWAP(refname, r->refname);
379 REFTABLE_SWAP(refname_cap, r->refname_cap);
380 reftable_ref_record_release(r);
381 REFTABLE_SWAP(r->refname, refname);
382 REFTABLE_SWAP(r->refname_cap, refname_cap);
383
384 REFTABLE_ALLOC_GROW_OR_NULL(r->refname, key.len + 1, r->refname_cap);
385 if (!r->refname) {
386 err = REFTABLE_OUT_OF_MEMORY_ERROR;
387 goto done;
388 }
389 memcpy(r->refname, key.buf, key.len);
390 r->refname[key.len] = 0;
391
392 r->update_index = update_index;
393 r->value_type = val_type;
394 switch (val_type) {
395 case REFTABLE_REF_VAL1:
396 if (in.len < hash_size) {
397 err = REFTABLE_FORMAT_ERROR;
398 goto done;
399 }
400
401 memcpy(r->value.val1, in.buf, hash_size);
402 string_view_consume(&in, hash_size);
403 break;
404
405 case REFTABLE_REF_VAL2:
406 if (in.len < 2 * hash_size) {
407 err = REFTABLE_FORMAT_ERROR;
408 goto done;
409 }
410
411 memcpy(r->value.val2.value, in.buf, hash_size);
412 string_view_consume(&in, hash_size);
413
414 memcpy(r->value.val2.target_value, in.buf, hash_size);
415 string_view_consume(&in, hash_size);
416 break;
417
418 case REFTABLE_REF_SYMREF: {
419 int n = decode_string(scratch, in);
420 if (n < 0) {
421 err = REFTABLE_FORMAT_ERROR;
422 goto done;
423 }
424 string_view_consume(&in, n);
425 r->value.symref = reftable_buf_detach(scratch);
426 } break;
427
428 case REFTABLE_REF_DELETION:
429 break;
430 default:
431 abort();
432 break;
433 }
434
435 return start.len - in.len;
436
437done:
438 return err;
439}
440
441static int reftable_ref_record_is_deletion_void(const void *p)
442{
443 return reftable_ref_record_is_deletion(
444 (const struct reftable_ref_record *)p);
445}
446
447static int reftable_ref_record_equal_void(const void *a,
448 const void *b, uint32_t hash_size)
449{
450 struct reftable_ref_record *ra = (struct reftable_ref_record *) a;
451 struct reftable_ref_record *rb = (struct reftable_ref_record *) b;
452 return reftable_ref_record_equal(ra, rb, hash_size);
453}
454
455static int reftable_ref_record_cmp_void(const void *_a, const void *_b)
456{
457 const struct reftable_ref_record *a = _a;
458 const struct reftable_ref_record *b = _b;
459 return strcmp(a->refname, b->refname);
460}
461
462static struct reftable_record_vtable reftable_ref_record_vtable = {
463 .key = &reftable_ref_record_key,
464 .type = REFTABLE_BLOCK_TYPE_REF,
465 .copy_from = &reftable_ref_record_copy_from,
466 .val_type = &reftable_ref_record_val_type,
467 .encode = &reftable_ref_record_encode,
468 .decode = &reftable_ref_record_decode,
469 .release = &reftable_ref_record_release_void,
470 .is_deletion = &reftable_ref_record_is_deletion_void,
471 .equal = &reftable_ref_record_equal_void,
472 .cmp = &reftable_ref_record_cmp_void,
473};
474
475static int reftable_obj_record_key(const void *r, struct reftable_buf *dest)
476{
477 const struct reftable_obj_record *rec =
478 (const struct reftable_obj_record *)r;
479 reftable_buf_reset(dest);
480 return reftable_buf_add(dest, rec->hash_prefix, rec->hash_prefix_len);
481}
482
483static void reftable_obj_record_release(void *rec)
484{
485 struct reftable_obj_record *obj = rec;
486 REFTABLE_FREE_AND_NULL(obj->hash_prefix);
487 REFTABLE_FREE_AND_NULL(obj->offsets);
488 memset(obj, 0, sizeof(struct reftable_obj_record));
489}
490
491static int reftable_obj_record_copy_from(void *rec, const void *src_rec,
492 uint32_t hash_size REFTABLE_UNUSED)
493{
494 struct reftable_obj_record *obj = rec;
495 const struct reftable_obj_record *src = src_rec;
496
497 reftable_obj_record_release(obj);
498
499 REFTABLE_ALLOC_ARRAY(obj->hash_prefix, src->hash_prefix_len);
500 if (!obj->hash_prefix)
501 return REFTABLE_OUT_OF_MEMORY_ERROR;
502 obj->hash_prefix_len = src->hash_prefix_len;
503 if (src->hash_prefix_len)
504 memcpy(obj->hash_prefix, src->hash_prefix, obj->hash_prefix_len);
505
506 if (src->offset_len) {
507 if (sizeof(*src->offsets) > SIZE_MAX / src->offset_len)
508 return REFTABLE_OUT_OF_MEMORY_ERROR;
509
510 REFTABLE_ALLOC_ARRAY(obj->offsets, src->offset_len);
511 if (!obj->offsets)
512 return REFTABLE_OUT_OF_MEMORY_ERROR;
513
514 memcpy(obj->offsets, src->offsets, sizeof(*src->offsets) * src->offset_len);
515 obj->offset_len = src->offset_len;
516 }
517
518 return 0;
519}
520
521static uint8_t reftable_obj_record_val_type(const void *rec)
522{
523 const struct reftable_obj_record *r = rec;
524 if (r->offset_len > 0 && r->offset_len < 8)
525 return r->offset_len;
526 return 0;
527}
528
529static int reftable_obj_record_encode(const void *rec, struct string_view s,
530 uint32_t hash_size REFTABLE_UNUSED)
531{
532 const struct reftable_obj_record *r = rec;
533 struct string_view start = s;
534 int i = 0;
535 int n = 0;
536 uint64_t last = 0;
537 if (r->offset_len == 0 || r->offset_len >= 8) {
538 n = put_var_int(&s, r->offset_len);
539 if (n < 0)
540 return n;
541 string_view_consume(&s, n);
542 }
543 if (r->offset_len == 0)
544 return start.len - s.len;
545 n = put_var_int(&s, r->offsets[0]);
546 if (n < 0)
547 return n;
548 string_view_consume(&s, n);
549
550 last = r->offsets[0];
551 for (i = 1; i < r->offset_len; i++) {
552 int n = put_var_int(&s, r->offsets[i] - last);
553 if (n < 0)
554 return n;
555 string_view_consume(&s, n);
556 last = r->offsets[i];
557 }
558 return start.len - s.len;
559}
560
561static int reftable_obj_record_decode(void *rec, struct reftable_buf key,
562 uint8_t val_type, struct string_view in,
563 uint32_t hash_size REFTABLE_UNUSED,
564 struct reftable_buf *scratch REFTABLE_UNUSED)
565{
566 struct string_view start = in;
567 struct reftable_obj_record *r = rec;
568 uint64_t count = val_type;
569 int n = 0;
570 uint64_t last;
571
572 reftable_obj_record_release(r);
573
574 REFTABLE_ALLOC_ARRAY(r->hash_prefix, key.len);
575 if (!r->hash_prefix)
576 return REFTABLE_OUT_OF_MEMORY_ERROR;
577 memcpy(r->hash_prefix, key.buf, key.len);
578 r->hash_prefix_len = key.len;
579
580 if (val_type == 0) {
581 n = get_var_int(&count, &in);
582 if (n < 0) {
583 return n;
584 }
585
586 string_view_consume(&in, n);
587 }
588
589 r->offsets = NULL;
590 r->offset_len = 0;
591 if (count == 0)
592 return start.len - in.len;
593
594 REFTABLE_ALLOC_ARRAY(r->offsets, count);
595 if (!r->offsets)
596 return REFTABLE_OUT_OF_MEMORY_ERROR;
597 r->offset_len = count;
598
599 n = get_var_int(&r->offsets[0], &in);
600 if (n < 0)
601 return n;
602 string_view_consume(&in, n);
603
604 last = r->offsets[0];
605 for (uint64_t j = 1; j < count; j++) {
606 uint64_t delta = 0;
607 int n = get_var_int(&delta, &in);
608 if (n < 0) {
609 return n;
610 }
611 string_view_consume(&in, n);
612
613 last = r->offsets[j] = (delta + last);
614 }
615 return start.len - in.len;
616}
617
618static int not_a_deletion(const void *p REFTABLE_UNUSED)
619{
620 return 0;
621}
622
623static int reftable_obj_record_equal_void(const void *a, const void *b,
624 uint32_t hash_size REFTABLE_UNUSED)
625{
626 struct reftable_obj_record *ra = (struct reftable_obj_record *) a;
627 struct reftable_obj_record *rb = (struct reftable_obj_record *) b;
628
629 if (ra->hash_prefix_len != rb->hash_prefix_len
630 || ra->offset_len != rb->offset_len)
631 return 0;
632
633 if (ra->hash_prefix_len &&
634 memcmp(ra->hash_prefix, rb->hash_prefix, ra->hash_prefix_len))
635 return 0;
636 if (ra->offset_len &&
637 memcmp(ra->offsets, rb->offsets, ra->offset_len * sizeof(uint64_t)))
638 return 0;
639
640 return 1;
641}
642
643static int reftable_obj_record_cmp_void(const void *_a, const void *_b)
644{
645 const struct reftable_obj_record *a = _a;
646 const struct reftable_obj_record *b = _b;
647 int cmp;
648
649 cmp = memcmp(a->hash_prefix, b->hash_prefix,
650 a->hash_prefix_len > b->hash_prefix_len ?
651 a->hash_prefix_len : b->hash_prefix_len);
652 if (cmp)
653 return cmp;
654
655 /*
656 * When the prefix is the same then the object record that is longer is
657 * considered to be bigger.
658 */
659 return a->hash_prefix_len - b->hash_prefix_len;
660}
661
662static struct reftable_record_vtable reftable_obj_record_vtable = {
663 .key = &reftable_obj_record_key,
664 .type = REFTABLE_BLOCK_TYPE_OBJ,
665 .copy_from = &reftable_obj_record_copy_from,
666 .val_type = &reftable_obj_record_val_type,
667 .encode = &reftable_obj_record_encode,
668 .decode = &reftable_obj_record_decode,
669 .release = &reftable_obj_record_release,
670 .is_deletion = ¬_a_deletion,
671 .equal = &reftable_obj_record_equal_void,
672 .cmp = &reftable_obj_record_cmp_void,
673};
674
675static int reftable_log_record_key(const void *r, struct reftable_buf *dest)
676{
677 const struct reftable_log_record *rec =
678 (const struct reftable_log_record *)r;
679 int len = strlen(rec->refname), err;
680 uint8_t i64[8];
681 uint64_t ts = 0;
682
683 reftable_buf_reset(dest);
684 err = reftable_buf_add(dest, (uint8_t *)rec->refname, len + 1);
685 if (err < 0)
686 return err;
687
688 ts = (~ts) - rec->update_index;
689 reftable_put_be64(&i64[0], ts);
690
691 err = reftable_buf_add(dest, i64, sizeof(i64));
692 if (err < 0)
693 return err;
694
695 return 0;
696}
697
698static int reftable_log_record_copy_from(void *rec, const void *src_rec,
699 uint32_t hash_size)
700{
701 struct reftable_log_record *dst = rec;
702 const struct reftable_log_record *src =
703 (const struct reftable_log_record *)src_rec;
704 int ret;
705
706 reftable_log_record_release(dst);
707 *dst = *src;
708
709 if (dst->refname) {
710 dst->refname = reftable_strdup(dst->refname);
711 if (!dst->refname) {
712 ret = REFTABLE_OUT_OF_MEMORY_ERROR;
713 goto out;
714 }
715 }
716
717 switch (dst->value_type) {
718 case REFTABLE_LOG_DELETION:
719 break;
720 case REFTABLE_LOG_UPDATE:
721 if (dst->value.update.email)
722 dst->value.update.email =
723 reftable_strdup(dst->value.update.email);
724 if (dst->value.update.name)
725 dst->value.update.name =
726 reftable_strdup(dst->value.update.name);
727 if (dst->value.update.message)
728 dst->value.update.message =
729 reftable_strdup(dst->value.update.message);
730
731 if (!dst->value.update.email ||
732 !dst->value.update.name ||
733 !dst->value.update.message) {
734 ret = REFTABLE_OUT_OF_MEMORY_ERROR;
735 goto out;
736 }
737
738 memcpy(dst->value.update.new_hash,
739 src->value.update.new_hash, hash_size);
740 memcpy(dst->value.update.old_hash,
741 src->value.update.old_hash, hash_size);
742 break;
743 }
744
745 ret = 0;
746out:
747 return ret;
748}
749
750static void reftable_log_record_release_void(void *rec)
751{
752 struct reftable_log_record *r = rec;
753 reftable_log_record_release(r);
754}
755
756void reftable_log_record_release(struct reftable_log_record *r)
757{
758 reftable_free(r->refname);
759 switch (r->value_type) {
760 case REFTABLE_LOG_DELETION:
761 break;
762 case REFTABLE_LOG_UPDATE:
763 reftable_free(r->value.update.name);
764 reftable_free(r->value.update.email);
765 reftable_free(r->value.update.message);
766 break;
767 }
768 memset(r, 0, sizeof(struct reftable_log_record));
769}
770
771static uint8_t reftable_log_record_val_type(const void *rec)
772{
773 const struct reftable_log_record *log =
774 (const struct reftable_log_record *)rec;
775
776 return reftable_log_record_is_deletion(log) ? 0 : 1;
777}
778
779static int reftable_log_record_encode(const void *rec, struct string_view s,
780 uint32_t hash_size)
781{
782 const struct reftable_log_record *r = rec;
783 struct string_view start = s;
784 int n = 0;
785 if (reftable_log_record_is_deletion(r))
786 return 0;
787
788 if (s.len < 2 * hash_size)
789 return REFTABLE_ENTRY_TOO_BIG_ERROR;
790
791 memcpy(s.buf, r->value.update.old_hash, hash_size);
792 memcpy(s.buf + hash_size, r->value.update.new_hash, hash_size);
793 string_view_consume(&s, 2 * hash_size);
794
795 n = encode_string(r->value.update.name ? r->value.update.name : "", s);
796 if (n < 0)
797 return n;
798 string_view_consume(&s, n);
799
800 n = encode_string(r->value.update.email ? r->value.update.email : "",
801 s);
802 if (n < 0)
803 return n;
804 string_view_consume(&s, n);
805
806 n = put_var_int(&s, r->value.update.time);
807 if (n < 0)
808 return n;
809 string_view_consume(&s, n);
810
811 if (s.len < 2)
812 return REFTABLE_ENTRY_TOO_BIG_ERROR;
813
814 reftable_put_be16(s.buf, r->value.update.tz_offset);
815 string_view_consume(&s, 2);
816
817 n = encode_string(
818 r->value.update.message ? r->value.update.message : "", s);
819 if (n < 0)
820 return n;
821 string_view_consume(&s, n);
822
823 return start.len - s.len;
824}
825
826static int reftable_log_record_decode(void *rec, struct reftable_buf key,
827 uint8_t val_type, struct string_view in,
828 uint32_t hash_size, struct reftable_buf *scratch)
829{
830 struct string_view start = in;
831 struct reftable_log_record *r = rec;
832 uint64_t max = 0;
833 uint64_t ts = 0;
834 int err, n;
835
836 if (key.len <= 9 || key.buf[key.len - 9] != 0)
837 return REFTABLE_FORMAT_ERROR;
838
839 REFTABLE_ALLOC_GROW_OR_NULL(r->refname, key.len - 8, r->refname_cap);
840 if (!r->refname) {
841 err = REFTABLE_OUT_OF_MEMORY_ERROR;
842 goto done;
843 }
844
845 memcpy(r->refname, key.buf, key.len - 8);
846 ts = reftable_get_be64((unsigned char *)key.buf + key.len - 8);
847
848 r->update_index = (~max) - ts;
849
850 if (val_type != r->value_type) {
851 switch (r->value_type) {
852 case REFTABLE_LOG_UPDATE:
853 REFTABLE_FREE_AND_NULL(r->value.update.message);
854 r->value.update.message_cap = 0;
855 REFTABLE_FREE_AND_NULL(r->value.update.email);
856 REFTABLE_FREE_AND_NULL(r->value.update.name);
857 break;
858 case REFTABLE_LOG_DELETION:
859 break;
860 }
861 }
862
863 r->value_type = val_type;
864 if (val_type == REFTABLE_LOG_DELETION)
865 return 0;
866
867 if (in.len < 2 * hash_size) {
868 err = REFTABLE_FORMAT_ERROR;
869 goto done;
870 }
871
872 memcpy(r->value.update.old_hash, in.buf, hash_size);
873 memcpy(r->value.update.new_hash, in.buf + hash_size, hash_size);
874
875 string_view_consume(&in, 2 * hash_size);
876
877 n = decode_string(scratch, in);
878 if (n < 0) {
879 err = REFTABLE_FORMAT_ERROR;
880 goto done;
881 }
882 string_view_consume(&in, n);
883
884 /*
885 * In almost all cases we can expect the reflog name to not change for
886 * reflog entries as they are tied to the local identity, not to the
887 * target commits. As an optimization for this common case we can thus
888 * skip copying over the name in case it's accurate already.
889 */
890 if (!r->value.update.name ||
891 strcmp(r->value.update.name, scratch->buf)) {
892 char *name = reftable_realloc(r->value.update.name, scratch->len + 1);
893 if (!name) {
894 err = REFTABLE_OUT_OF_MEMORY_ERROR;
895 goto done;
896 }
897
898 r->value.update.name = name;
899 memcpy(r->value.update.name, scratch->buf, scratch->len);
900 r->value.update.name[scratch->len] = 0;
901 }
902
903 n = decode_string(scratch, in);
904 if (n < 0) {
905 err = REFTABLE_FORMAT_ERROR;
906 goto done;
907 }
908 string_view_consume(&in, n);
909
910 /* Same as above, but for the reflog email. */
911 if (!r->value.update.email ||
912 strcmp(r->value.update.email, scratch->buf)) {
913 char *email = reftable_realloc(r->value.update.email, scratch->len + 1);
914 if (!email) {
915 err = REFTABLE_OUT_OF_MEMORY_ERROR;
916 goto done;
917 }
918
919 r->value.update.email = email;
920 memcpy(r->value.update.email, scratch->buf, scratch->len);
921 r->value.update.email[scratch->len] = 0;
922 }
923
924 ts = 0;
925 n = get_var_int(&ts, &in);
926 if (n < 0) {
927 err = REFTABLE_FORMAT_ERROR;
928 goto done;
929 }
930 string_view_consume(&in, n);
931 r->value.update.time = ts;
932 if (in.len < 2) {
933 err = REFTABLE_FORMAT_ERROR;
934 goto done;
935 }
936
937 r->value.update.tz_offset = reftable_get_be16(in.buf);
938 string_view_consume(&in, 2);
939
940 n = decode_string(scratch, in);
941 if (n < 0) {
942 err = REFTABLE_FORMAT_ERROR;
943 goto done;
944 }
945 string_view_consume(&in, n);
946
947 REFTABLE_ALLOC_GROW_OR_NULL(r->value.update.message, scratch->len + 1,
948 r->value.update.message_cap);
949 if (!r->value.update.message) {
950 err = REFTABLE_OUT_OF_MEMORY_ERROR;
951 goto done;
952 }
953
954 memcpy(r->value.update.message, scratch->buf, scratch->len);
955 r->value.update.message[scratch->len] = 0;
956
957 return start.len - in.len;
958
959done:
960 return err;
961}
962
963static int null_streq(const char *a, const char *b)
964{
965 const char *empty = "";
966 if (!a)
967 a = empty;
968
969 if (!b)
970 b = empty;
971
972 return 0 == strcmp(a, b);
973}
974
975static int reftable_log_record_equal_void(const void *a,
976 const void *b, uint32_t hash_size)
977{
978 return reftable_log_record_equal((struct reftable_log_record *) a,
979 (struct reftable_log_record *) b,
980 hash_size);
981}
982
983static int reftable_log_record_cmp_void(const void *_a, const void *_b)
984{
985 const struct reftable_log_record *a = _a;
986 const struct reftable_log_record *b = _b;
987 int cmp = strcmp(a->refname, b->refname);
988 if (cmp)
989 return cmp;
990
991 /*
992 * Note that the comparison here is reversed. This is because the
993 * update index is reversed when comparing keys. For reference, see how
994 * we handle this in reftable_log_record_key()`.
995 */
996 return b->update_index - a->update_index;
997}
998
999int reftable_log_record_equal(const struct reftable_log_record *a,
1000 const struct reftable_log_record *b, uint32_t hash_size)
1001{
1002 // Implemented in reftable_record.rs
1003 if (!(null_streq(a->refname, b->refname) &&
1004 a->update_index == b->update_index &&
1005 a->value_type == b->value_type))
1006 return 0;
1007
1008 switch (a->value_type) {
1009 case REFTABLE_LOG_DELETION:
1010 return 1;
1011 case REFTABLE_LOG_UPDATE:
1012 return null_streq(a->value.update.name, b->value.update.name) &&
1013 a->value.update.time == b->value.update.time &&
1014 a->value.update.tz_offset == b->value.update.tz_offset &&
1015 null_streq(a->value.update.email,
1016 b->value.update.email) &&
1017 null_streq(a->value.update.message,
1018 b->value.update.message) &&
1019 !memcmp(a->value.update.old_hash,
1020 b->value.update.old_hash, hash_size) &&
1021 !memcmp(a->value.update.new_hash,
1022 b->value.update.new_hash, hash_size);
1023 }
1024
1025 abort();
1026}
1027
1028static int reftable_log_record_is_deletion_void(const void *p)
1029{
1030 return reftable_log_record_is_deletion(
1031 (const struct reftable_log_record *)p);
1032}
1033
1034static struct reftable_record_vtable reftable_log_record_vtable = {
1035 .key = &reftable_log_record_key,
1036 .type = REFTABLE_BLOCK_TYPE_LOG,
1037 .copy_from = &reftable_log_record_copy_from,
1038 .val_type = &reftable_log_record_val_type,
1039 .encode = &reftable_log_record_encode,
1040 .decode = &reftable_log_record_decode,
1041 .release = &reftable_log_record_release_void,
1042 .is_deletion = &reftable_log_record_is_deletion_void,
1043 .equal = &reftable_log_record_equal_void,
1044 .cmp = &reftable_log_record_cmp_void,
1045};
1046
1047static int reftable_index_record_key(const void *r, struct reftable_buf *dest)
1048{
1049 const struct reftable_index_record *rec = r;
1050 reftable_buf_reset(dest);
1051 return reftable_buf_add(dest, rec->last_key.buf, rec->last_key.len);
1052}
1053
1054static int reftable_index_record_copy_from(void *rec, const void *src_rec,
1055 uint32_t hash_size REFTABLE_UNUSED)
1056{
1057 struct reftable_index_record *dst = rec;
1058 const struct reftable_index_record *src = src_rec;
1059 int err;
1060
1061 reftable_buf_reset(&dst->last_key);
1062 err = reftable_buf_add(&dst->last_key, src->last_key.buf, src->last_key.len);
1063 if (err < 0)
1064 return err;
1065 dst->offset = src->offset;
1066
1067 return 0;
1068}
1069
1070static void reftable_index_record_release(void *rec)
1071{
1072 struct reftable_index_record *idx = rec;
1073 reftable_buf_release(&idx->last_key);
1074}
1075
1076static uint8_t reftable_index_record_val_type(const void *rec REFTABLE_UNUSED)
1077{
1078 return 0;
1079}
1080
1081static int reftable_index_record_encode(const void *rec, struct string_view out,
1082 uint32_t hash_size REFTABLE_UNUSED)
1083{
1084 const struct reftable_index_record *r =
1085 (const struct reftable_index_record *)rec;
1086 struct string_view start = out;
1087
1088 int n = put_var_int(&out, r->offset);
1089 if (n < 0)
1090 return n;
1091
1092 string_view_consume(&out, n);
1093
1094 return start.len - out.len;
1095}
1096
1097static int reftable_index_record_decode(void *rec, struct reftable_buf key,
1098 uint8_t val_type REFTABLE_UNUSED,
1099 struct string_view in,
1100 uint32_t hash_size REFTABLE_UNUSED,
1101 struct reftable_buf *scratch REFTABLE_UNUSED)
1102{
1103 struct string_view start = in;
1104 struct reftable_index_record *r = rec;
1105 int err, n = 0;
1106
1107 reftable_buf_reset(&r->last_key);
1108 err = reftable_buf_add(&r->last_key, key.buf, key.len);
1109 if (err < 0)
1110 return err;
1111
1112 n = get_var_int(&r->offset, &in);
1113 if (n < 0)
1114 return n;
1115
1116 string_view_consume(&in, n);
1117 return start.len - in.len;
1118}
1119
1120static int reftable_index_record_equal(const void *a, const void *b,
1121 uint32_t hash_size REFTABLE_UNUSED)
1122{
1123 struct reftable_index_record *ia = (struct reftable_index_record *) a;
1124 struct reftable_index_record *ib = (struct reftable_index_record *) b;
1125
1126 return ia->offset == ib->offset && !reftable_buf_cmp(&ia->last_key, &ib->last_key);
1127}
1128
1129static int reftable_index_record_cmp(const void *_a, const void *_b)
1130{
1131 const struct reftable_index_record *a = _a;
1132 const struct reftable_index_record *b = _b;
1133 return reftable_buf_cmp(&a->last_key, &b->last_key);
1134}
1135
1136static struct reftable_record_vtable reftable_index_record_vtable = {
1137 .key = &reftable_index_record_key,
1138 .type = REFTABLE_BLOCK_TYPE_INDEX,
1139 .copy_from = &reftable_index_record_copy_from,
1140 .val_type = &reftable_index_record_val_type,
1141 .encode = &reftable_index_record_encode,
1142 .decode = &reftable_index_record_decode,
1143 .release = &reftable_index_record_release,
1144 .is_deletion = ¬_a_deletion,
1145 .equal = &reftable_index_record_equal,
1146 .cmp = &reftable_index_record_cmp,
1147};
1148
1149int reftable_record_key(struct reftable_record *rec, struct reftable_buf *dest)
1150{
1151 return reftable_record_vtable(rec)->key(reftable_record_data(rec), dest);
1152}
1153
1154int reftable_record_encode(struct reftable_record *rec, struct string_view dest,
1155 uint32_t hash_size)
1156{
1157 return reftable_record_vtable(rec)->encode(reftable_record_data(rec),
1158 dest, hash_size);
1159}
1160
1161int reftable_record_copy_from(struct reftable_record *rec,
1162 struct reftable_record *src, uint32_t hash_size)
1163{
1164 assert(src->type == rec->type);
1165
1166 return reftable_record_vtable(rec)->copy_from(reftable_record_data(rec),
1167 reftable_record_data(src),
1168 hash_size);
1169}
1170
1171uint8_t reftable_record_val_type(struct reftable_record *rec)
1172{
1173 return reftable_record_vtable(rec)->val_type(reftable_record_data(rec));
1174}
1175
1176int reftable_record_decode(struct reftable_record *rec, struct reftable_buf key,
1177 uint8_t extra, struct string_view src, uint32_t hash_size,
1178 struct reftable_buf *scratch)
1179{
1180 return reftable_record_vtable(rec)->decode(reftable_record_data(rec),
1181 key, extra, src, hash_size,
1182 scratch);
1183}
1184
1185void reftable_record_release(struct reftable_record *rec)
1186{
1187 reftable_record_vtable(rec)->release(reftable_record_data(rec));
1188}
1189
1190int reftable_record_is_deletion(struct reftable_record *rec)
1191{
1192 return reftable_record_vtable(rec)->is_deletion(
1193 reftable_record_data(rec));
1194}
1195
1196int reftable_record_cmp(struct reftable_record *a, struct reftable_record *b,
1197 int *cmp)
1198{
1199 if (a->type != b->type)
1200 return -1;
1201 *cmp = reftable_record_vtable(a)->cmp(reftable_record_data(a),
1202 reftable_record_data(b));
1203 return 0;
1204}
1205
1206int reftable_record_equal(struct reftable_record *a, struct reftable_record *b, uint32_t hash_size)
1207{
1208 if (a->type != b->type)
1209 return 0;
1210 return reftable_record_vtable(a)->equal(
1211 reftable_record_data(a), reftable_record_data(b), hash_size);
1212}
1213
1214static int hash_equal(const unsigned char *a, const unsigned char *b, uint32_t hash_size)
1215{
1216 // Implemented in reftable_basics.rs
1217 if (a && b)
1218 return !memcmp(a, b, hash_size);
1219
1220 return a == b;
1221}
1222
1223int reftable_ref_record_equal(const struct reftable_ref_record *a,
1224 const struct reftable_ref_record *b, uint32_t hash_size)
1225{
1226 // Implemented in reftable_record.rs
1227 if (!null_streq(a->refname, b->refname))
1228 return 0;
1229
1230 if (a->update_index != b->update_index ||
1231 a->value_type != b->value_type)
1232 return 0;
1233
1234 switch (a->value_type) {
1235 case REFTABLE_REF_SYMREF:
1236 return !strcmp(a->value.symref, b->value.symref);
1237 case REFTABLE_REF_VAL2:
1238 return hash_equal(a->value.val2.value, b->value.val2.value,
1239 hash_size) &&
1240 hash_equal(a->value.val2.target_value,
1241 b->value.val2.target_value, hash_size);
1242 case REFTABLE_REF_VAL1:
1243 return hash_equal(a->value.val1, b->value.val1, hash_size);
1244 case REFTABLE_REF_DELETION:
1245 return 1;
1246 default:
1247 abort();
1248 }
1249}
1250
1251int reftable_ref_record_compare_name(const void *a, const void *b)
1252{
1253 return strcmp(((struct reftable_ref_record *)a)->refname,
1254 ((struct reftable_ref_record *)b)->refname);
1255}
1256
1257int reftable_ref_record_is_deletion(const struct reftable_ref_record *ref)
1258{
1259 return ref->value_type == REFTABLE_REF_DELETION;
1260}
1261
1262int reftable_log_record_compare_key(const void *a, const void *b)
1263{
1264 const struct reftable_log_record *la = a;
1265 const struct reftable_log_record *lb = b;
1266
1267 int cmp = strcmp(la->refname, lb->refname);
1268 if (cmp)
1269 return cmp;
1270 if (la->update_index > lb->update_index)
1271 return -1;
1272 return (la->update_index < lb->update_index) ? 1 : 0;
1273}
1274
1275int reftable_log_record_is_deletion(const struct reftable_log_record *log)
1276{
1277 // Implemented in reftable_record.rs
1278 return (log->value_type == REFTABLE_LOG_DELETION);
1279}
1280
1281static void *reftable_record_data(struct reftable_record *rec)
1282{
1283 switch (rec->type) {
1284 case REFTABLE_BLOCK_TYPE_REF:
1285 return &rec->u.ref;
1286 case REFTABLE_BLOCK_TYPE_LOG:
1287 return &rec->u.log;
1288 case REFTABLE_BLOCK_TYPE_INDEX:
1289 return &rec->u.idx;
1290 case REFTABLE_BLOCK_TYPE_OBJ:
1291 return &rec->u.obj;
1292 }
1293 abort();
1294}
1295
1296static struct reftable_record_vtable *
1297reftable_record_vtable(struct reftable_record *rec)
1298{
1299 switch (rec->type) {
1300 case REFTABLE_BLOCK_TYPE_REF:
1301 return &reftable_ref_record_vtable;
1302 case REFTABLE_BLOCK_TYPE_LOG:
1303 return &reftable_log_record_vtable;
1304 case REFTABLE_BLOCK_TYPE_INDEX:
1305 return &reftable_index_record_vtable;
1306 case REFTABLE_BLOCK_TYPE_OBJ:
1307 return &reftable_obj_record_vtable;
1308 }
1309 abort();
1310}
1311
1312int reftable_record_init(struct reftable_record *rec, uint8_t typ)
1313{
1314 memset(rec, 0, sizeof(*rec));
1315 rec->type = typ;
1316
1317 switch (typ) {
1318 case REFTABLE_BLOCK_TYPE_REF:
1319 case REFTABLE_BLOCK_TYPE_LOG:
1320 case REFTABLE_BLOCK_TYPE_OBJ:
1321 return 0;
1322 case REFTABLE_BLOCK_TYPE_INDEX:
1323 reftable_buf_init(&rec->u.idx.last_key);
1324 return 0;
1325 default:
1326 return REFTABLE_API_ERROR;
1327 }
1328}