credential: add support for multistage credential rounds
[alt-git.git] / reftable / record.c
blob23b497adab8b3478026959a802bddb58fb1c6002
1 /*
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
7 */
9 /* record.c - methods for different types of records. */
11 #include "record.h"
13 #include "system.h"
14 #include "constants.h"
15 #include "reftable-error.h"
16 #include "basics.h"
18 static struct reftable_record_vtable *
19 reftable_record_vtable(struct reftable_record *rec);
20 static void *reftable_record_data(struct reftable_record *rec);
22 int get_var_int(uint64_t *dest, struct string_view *in)
24 int ptr = 0;
25 uint64_t val;
27 if (in->len == 0)
28 return -1;
29 val = in->buf[ptr] & 0x7f;
31 while (in->buf[ptr] & 0x80) {
32 ptr++;
33 if (ptr > in->len) {
34 return -1;
36 val = (val + 1) << 7 | (uint64_t)(in->buf[ptr] & 0x7f);
39 *dest = val;
40 return ptr + 1;
43 int put_var_int(struct string_view *dest, uint64_t val)
45 uint8_t buf[10] = { 0 };
46 int i = 9;
47 int n = 0;
48 buf[i] = (uint8_t)(val & 0x7f);
49 i--;
50 while (1) {
51 val >>= 7;
52 if (!val) {
53 break;
55 val--;
56 buf[i] = 0x80 | (uint8_t)(val & 0x7f);
57 i--;
60 n = sizeof(buf) - i - 1;
61 if (dest->len < n)
62 return -1;
63 memcpy(dest->buf, &buf[i + 1], n);
64 return n;
67 int reftable_is_block_type(uint8_t typ)
69 switch (typ) {
70 case BLOCK_TYPE_REF:
71 case BLOCK_TYPE_LOG:
72 case BLOCK_TYPE_OBJ:
73 case BLOCK_TYPE_INDEX:
74 return 1;
76 return 0;
79 const unsigned char *reftable_ref_record_val1(const struct reftable_ref_record *rec)
81 switch (rec->value_type) {
82 case REFTABLE_REF_VAL1:
83 return rec->value.val1;
84 case REFTABLE_REF_VAL2:
85 return rec->value.val2.value;
86 default:
87 return NULL;
91 const unsigned char *reftable_ref_record_val2(const struct reftable_ref_record *rec)
93 switch (rec->value_type) {
94 case REFTABLE_REF_VAL2:
95 return rec->value.val2.target_value;
96 default:
97 return NULL;
101 static int decode_string(struct strbuf *dest, struct string_view in)
103 int start_len = in.len;
104 uint64_t tsize = 0;
105 int n = get_var_int(&tsize, &in);
106 if (n <= 0)
107 return -1;
108 string_view_consume(&in, n);
109 if (in.len < tsize)
110 return -1;
112 strbuf_reset(dest);
113 strbuf_add(dest, in.buf, tsize);
114 string_view_consume(&in, tsize);
116 return start_len - in.len;
119 static int encode_string(char *str, struct string_view s)
121 struct string_view start = s;
122 int l = strlen(str);
123 int n = put_var_int(&s, l);
124 if (n < 0)
125 return -1;
126 string_view_consume(&s, n);
127 if (s.len < l)
128 return -1;
129 memcpy(s.buf, str, l);
130 string_view_consume(&s, l);
132 return start.len - s.len;
135 int reftable_encode_key(int *restart, struct string_view dest,
136 struct strbuf prev_key, struct strbuf key,
137 uint8_t extra)
139 struct string_view start = dest;
140 int prefix_len = common_prefix_size(&prev_key, &key);
141 uint64_t suffix_len = key.len - prefix_len;
142 int n = put_var_int(&dest, (uint64_t)prefix_len);
143 if (n < 0)
144 return -1;
145 string_view_consume(&dest, n);
147 *restart = (prefix_len == 0);
149 n = put_var_int(&dest, suffix_len << 3 | (uint64_t)extra);
150 if (n < 0)
151 return -1;
152 string_view_consume(&dest, n);
154 if (dest.len < suffix_len)
155 return -1;
156 memcpy(dest.buf, key.buf + prefix_len, suffix_len);
157 string_view_consume(&dest, suffix_len);
159 return start.len - dest.len;
162 int reftable_decode_key(struct strbuf *last_key, uint8_t *extra,
163 struct string_view in)
165 int start_len = in.len;
166 uint64_t prefix_len = 0;
167 uint64_t suffix_len = 0;
168 int n;
170 n = get_var_int(&prefix_len, &in);
171 if (n < 0)
172 return -1;
173 string_view_consume(&in, n);
175 n = get_var_int(&suffix_len, &in);
176 if (n <= 0)
177 return -1;
178 string_view_consume(&in, n);
180 *extra = (uint8_t)(suffix_len & 0x7);
181 suffix_len >>= 3;
183 if (in.len < suffix_len ||
184 prefix_len > last_key->len)
185 return -1;
187 strbuf_setlen(last_key, prefix_len);
188 strbuf_add(last_key, in.buf, suffix_len);
189 string_view_consume(&in, suffix_len);
191 return start_len - in.len;
194 static void reftable_ref_record_key(const void *r, struct strbuf *dest)
196 const struct reftable_ref_record *rec =
197 (const struct reftable_ref_record *)r;
198 strbuf_reset(dest);
199 strbuf_addstr(dest, rec->refname);
202 static void reftable_ref_record_copy_from(void *rec, const void *src_rec,
203 int hash_size)
205 struct reftable_ref_record *ref = rec;
206 const struct reftable_ref_record *src = src_rec;
207 char *refname = NULL;
208 size_t refname_cap = 0;
210 assert(hash_size > 0);
212 SWAP(refname, ref->refname);
213 SWAP(refname_cap, ref->refname_cap);
214 reftable_ref_record_release(ref);
215 SWAP(ref->refname, refname);
216 SWAP(ref->refname_cap, refname_cap);
218 if (src->refname) {
219 size_t refname_len = strlen(src->refname);
221 REFTABLE_ALLOC_GROW(ref->refname, refname_len + 1,
222 ref->refname_cap);
223 memcpy(ref->refname, src->refname, refname_len);
224 ref->refname[refname_len] = 0;
227 ref->update_index = src->update_index;
228 ref->value_type = src->value_type;
229 switch (src->value_type) {
230 case REFTABLE_REF_DELETION:
231 break;
232 case REFTABLE_REF_VAL1:
233 memcpy(ref->value.val1, src->value.val1, hash_size);
234 break;
235 case REFTABLE_REF_VAL2:
236 memcpy(ref->value.val2.value, src->value.val2.value, hash_size);
237 memcpy(ref->value.val2.target_value,
238 src->value.val2.target_value, hash_size);
239 break;
240 case REFTABLE_REF_SYMREF:
241 ref->value.symref = xstrdup(src->value.symref);
242 break;
246 static char hexdigit(int c)
248 if (c <= 9)
249 return '0' + c;
250 return 'a' + (c - 10);
253 static void hex_format(char *dest, const unsigned char *src, int hash_size)
255 assert(hash_size > 0);
256 if (src) {
257 int i = 0;
258 for (i = 0; i < hash_size; i++) {
259 dest[2 * i] = hexdigit(src[i] >> 4);
260 dest[2 * i + 1] = hexdigit(src[i] & 0xf);
262 dest[2 * hash_size] = 0;
266 static void reftable_ref_record_print_sz(const struct reftable_ref_record *ref,
267 int hash_size)
269 char hex[GIT_MAX_HEXSZ + 1] = { 0 }; /* BUG */
270 printf("ref{%s(%" PRIu64 ") ", ref->refname, ref->update_index);
271 switch (ref->value_type) {
272 case REFTABLE_REF_SYMREF:
273 printf("=> %s", ref->value.symref);
274 break;
275 case REFTABLE_REF_VAL2:
276 hex_format(hex, ref->value.val2.value, hash_size);
277 printf("val 2 %s", hex);
278 hex_format(hex, ref->value.val2.target_value,
279 hash_size);
280 printf("(T %s)", hex);
281 break;
282 case REFTABLE_REF_VAL1:
283 hex_format(hex, ref->value.val1, hash_size);
284 printf("val 1 %s", hex);
285 break;
286 case REFTABLE_REF_DELETION:
287 printf("delete");
288 break;
290 printf("}\n");
293 void reftable_ref_record_print(const struct reftable_ref_record *ref,
294 uint32_t hash_id) {
295 reftable_ref_record_print_sz(ref, hash_size(hash_id));
298 static void reftable_ref_record_release_void(void *rec)
300 reftable_ref_record_release(rec);
303 void reftable_ref_record_release(struct reftable_ref_record *ref)
305 switch (ref->value_type) {
306 case REFTABLE_REF_SYMREF:
307 reftable_free(ref->value.symref);
308 break;
309 case REFTABLE_REF_VAL2:
310 break;
311 case REFTABLE_REF_VAL1:
312 break;
313 case REFTABLE_REF_DELETION:
314 break;
315 default:
316 abort();
319 reftable_free(ref->refname);
320 memset(ref, 0, sizeof(struct reftable_ref_record));
323 static uint8_t reftable_ref_record_val_type(const void *rec)
325 const struct reftable_ref_record *r =
326 (const struct reftable_ref_record *)rec;
327 return r->value_type;
330 static int reftable_ref_record_encode(const void *rec, struct string_view s,
331 int hash_size)
333 const struct reftable_ref_record *r =
334 (const struct reftable_ref_record *)rec;
335 struct string_view start = s;
336 int n = put_var_int(&s, r->update_index);
337 assert(hash_size > 0);
338 if (n < 0)
339 return -1;
340 string_view_consume(&s, n);
342 switch (r->value_type) {
343 case REFTABLE_REF_SYMREF:
344 n = encode_string(r->value.symref, s);
345 if (n < 0) {
346 return -1;
348 string_view_consume(&s, n);
349 break;
350 case REFTABLE_REF_VAL2:
351 if (s.len < 2 * hash_size) {
352 return -1;
354 memcpy(s.buf, r->value.val2.value, hash_size);
355 string_view_consume(&s, hash_size);
356 memcpy(s.buf, r->value.val2.target_value, hash_size);
357 string_view_consume(&s, hash_size);
358 break;
359 case REFTABLE_REF_VAL1:
360 if (s.len < hash_size) {
361 return -1;
363 memcpy(s.buf, r->value.val1, hash_size);
364 string_view_consume(&s, hash_size);
365 break;
366 case REFTABLE_REF_DELETION:
367 break;
368 default:
369 abort();
372 return start.len - s.len;
375 static int reftable_ref_record_decode(void *rec, struct strbuf key,
376 uint8_t val_type, struct string_view in,
377 int hash_size, struct strbuf *scratch)
379 struct reftable_ref_record *r = rec;
380 struct string_view start = in;
381 uint64_t update_index = 0;
382 const char *refname = NULL;
383 size_t refname_cap = 0;
384 int n;
386 assert(hash_size > 0);
388 n = get_var_int(&update_index, &in);
389 if (n < 0)
390 return n;
391 string_view_consume(&in, n);
393 SWAP(refname, r->refname);
394 SWAP(refname_cap, r->refname_cap);
395 reftable_ref_record_release(r);
396 SWAP(r->refname, refname);
397 SWAP(r->refname_cap, refname_cap);
399 REFTABLE_ALLOC_GROW(r->refname, key.len + 1, r->refname_cap);
400 memcpy(r->refname, key.buf, key.len);
401 r->refname[key.len] = 0;
403 r->update_index = update_index;
404 r->value_type = val_type;
405 switch (val_type) {
406 case REFTABLE_REF_VAL1:
407 if (in.len < hash_size) {
408 return -1;
411 memcpy(r->value.val1, in.buf, hash_size);
412 string_view_consume(&in, hash_size);
413 break;
415 case REFTABLE_REF_VAL2:
416 if (in.len < 2 * hash_size) {
417 return -1;
420 memcpy(r->value.val2.value, in.buf, hash_size);
421 string_view_consume(&in, hash_size);
423 memcpy(r->value.val2.target_value, in.buf, hash_size);
424 string_view_consume(&in, hash_size);
425 break;
427 case REFTABLE_REF_SYMREF: {
428 int n = decode_string(scratch, in);
429 if (n < 0) {
430 return -1;
432 string_view_consume(&in, n);
433 r->value.symref = strbuf_detach(scratch, NULL);
434 } break;
436 case REFTABLE_REF_DELETION:
437 break;
438 default:
439 abort();
440 break;
443 return start.len - in.len;
446 static int reftable_ref_record_is_deletion_void(const void *p)
448 return reftable_ref_record_is_deletion(
449 (const struct reftable_ref_record *)p);
452 static int reftable_ref_record_equal_void(const void *a,
453 const void *b, int hash_size)
455 struct reftable_ref_record *ra = (struct reftable_ref_record *) a;
456 struct reftable_ref_record *rb = (struct reftable_ref_record *) b;
457 return reftable_ref_record_equal(ra, rb, hash_size);
460 static int reftable_ref_record_cmp_void(const void *_a, const void *_b)
462 const struct reftable_ref_record *a = _a;
463 const struct reftable_ref_record *b = _b;
464 return strcmp(a->refname, b->refname);
467 static void reftable_ref_record_print_void(const void *rec,
468 int hash_size)
470 reftable_ref_record_print_sz((struct reftable_ref_record *) rec, hash_size);
473 static struct reftable_record_vtable reftable_ref_record_vtable = {
474 .key = &reftable_ref_record_key,
475 .type = BLOCK_TYPE_REF,
476 .copy_from = &reftable_ref_record_copy_from,
477 .val_type = &reftable_ref_record_val_type,
478 .encode = &reftable_ref_record_encode,
479 .decode = &reftable_ref_record_decode,
480 .release = &reftable_ref_record_release_void,
481 .is_deletion = &reftable_ref_record_is_deletion_void,
482 .equal = &reftable_ref_record_equal_void,
483 .cmp = &reftable_ref_record_cmp_void,
484 .print = &reftable_ref_record_print_void,
487 static void reftable_obj_record_key(const void *r, struct strbuf *dest)
489 const struct reftable_obj_record *rec =
490 (const struct reftable_obj_record *)r;
491 strbuf_reset(dest);
492 strbuf_add(dest, rec->hash_prefix, rec->hash_prefix_len);
495 static void reftable_obj_record_release(void *rec)
497 struct reftable_obj_record *obj = rec;
498 FREE_AND_NULL(obj->hash_prefix);
499 FREE_AND_NULL(obj->offsets);
500 memset(obj, 0, sizeof(struct reftable_obj_record));
503 static void reftable_obj_record_print(const void *rec, int hash_size)
505 const struct reftable_obj_record *obj = rec;
506 char hex[GIT_MAX_HEXSZ + 1] = { 0 };
507 struct strbuf offset_str = STRBUF_INIT;
508 int i;
510 for (i = 0; i < obj->offset_len; i++)
511 strbuf_addf(&offset_str, "%" PRIu64 " ", obj->offsets[i]);
512 hex_format(hex, obj->hash_prefix, obj->hash_prefix_len);
513 printf("prefix %s (len %d), offsets [%s]\n",
514 hex, obj->hash_prefix_len, offset_str.buf);
515 strbuf_release(&offset_str);
518 static void reftable_obj_record_copy_from(void *rec, const void *src_rec,
519 int hash_size)
521 struct reftable_obj_record *obj = rec;
522 const struct reftable_obj_record *src =
523 (const struct reftable_obj_record *)src_rec;
525 reftable_obj_record_release(obj);
527 REFTABLE_ALLOC_ARRAY(obj->hash_prefix, src->hash_prefix_len);
528 obj->hash_prefix_len = src->hash_prefix_len;
529 if (src->hash_prefix_len)
530 memcpy(obj->hash_prefix, src->hash_prefix, obj->hash_prefix_len);
532 REFTABLE_ALLOC_ARRAY(obj->offsets, src->offset_len);
533 obj->offset_len = src->offset_len;
534 COPY_ARRAY(obj->offsets, src->offsets, src->offset_len);
537 static uint8_t reftable_obj_record_val_type(const void *rec)
539 const struct reftable_obj_record *r = rec;
540 if (r->offset_len > 0 && r->offset_len < 8)
541 return r->offset_len;
542 return 0;
545 static int reftable_obj_record_encode(const void *rec, struct string_view s,
546 int hash_size)
548 const struct reftable_obj_record *r = rec;
549 struct string_view start = s;
550 int i = 0;
551 int n = 0;
552 uint64_t last = 0;
553 if (r->offset_len == 0 || r->offset_len >= 8) {
554 n = put_var_int(&s, r->offset_len);
555 if (n < 0) {
556 return -1;
558 string_view_consume(&s, n);
560 if (r->offset_len == 0)
561 return start.len - s.len;
562 n = put_var_int(&s, r->offsets[0]);
563 if (n < 0)
564 return -1;
565 string_view_consume(&s, n);
567 last = r->offsets[0];
568 for (i = 1; i < r->offset_len; i++) {
569 int n = put_var_int(&s, r->offsets[i] - last);
570 if (n < 0) {
571 return -1;
573 string_view_consume(&s, n);
574 last = r->offsets[i];
576 return start.len - s.len;
579 static int reftable_obj_record_decode(void *rec, struct strbuf key,
580 uint8_t val_type, struct string_view in,
581 int hash_size, struct strbuf *scratch UNUSED)
583 struct string_view start = in;
584 struct reftable_obj_record *r = rec;
585 uint64_t count = val_type;
586 int n = 0;
587 uint64_t last;
588 int j;
590 reftable_obj_record_release(r);
592 REFTABLE_ALLOC_ARRAY(r->hash_prefix, key.len);
593 memcpy(r->hash_prefix, key.buf, key.len);
594 r->hash_prefix_len = key.len;
596 if (val_type == 0) {
597 n = get_var_int(&count, &in);
598 if (n < 0) {
599 return n;
602 string_view_consume(&in, n);
605 r->offsets = NULL;
606 r->offset_len = 0;
607 if (count == 0)
608 return start.len - in.len;
610 REFTABLE_ALLOC_ARRAY(r->offsets, count);
611 r->offset_len = count;
613 n = get_var_int(&r->offsets[0], &in);
614 if (n < 0)
615 return n;
616 string_view_consume(&in, n);
618 last = r->offsets[0];
619 j = 1;
620 while (j < count) {
621 uint64_t delta = 0;
622 int n = get_var_int(&delta, &in);
623 if (n < 0) {
624 return n;
626 string_view_consume(&in, n);
628 last = r->offsets[j] = (delta + last);
629 j++;
631 return start.len - in.len;
634 static int not_a_deletion(const void *p)
636 return 0;
639 static int reftable_obj_record_equal_void(const void *a, const void *b, int hash_size)
641 struct reftable_obj_record *ra = (struct reftable_obj_record *) a;
642 struct reftable_obj_record *rb = (struct reftable_obj_record *) b;
644 if (ra->hash_prefix_len != rb->hash_prefix_len
645 || ra->offset_len != rb->offset_len)
646 return 0;
648 if (ra->hash_prefix_len &&
649 memcmp(ra->hash_prefix, rb->hash_prefix, ra->hash_prefix_len))
650 return 0;
651 if (ra->offset_len &&
652 memcmp(ra->offsets, rb->offsets, ra->offset_len * sizeof(uint64_t)))
653 return 0;
655 return 1;
658 static int reftable_obj_record_cmp_void(const void *_a, const void *_b)
660 const struct reftable_obj_record *a = _a;
661 const struct reftable_obj_record *b = _b;
662 int cmp;
664 cmp = memcmp(a->hash_prefix, b->hash_prefix,
665 a->hash_prefix_len > b->hash_prefix_len ?
666 a->hash_prefix_len : b->hash_prefix_len);
667 if (cmp)
668 return cmp;
671 * When the prefix is the same then the object record that is longer is
672 * considered to be bigger.
674 return a->hash_prefix_len - b->hash_prefix_len;
677 static struct reftable_record_vtable reftable_obj_record_vtable = {
678 .key = &reftable_obj_record_key,
679 .type = BLOCK_TYPE_OBJ,
680 .copy_from = &reftable_obj_record_copy_from,
681 .val_type = &reftable_obj_record_val_type,
682 .encode = &reftable_obj_record_encode,
683 .decode = &reftable_obj_record_decode,
684 .release = &reftable_obj_record_release,
685 .is_deletion = &not_a_deletion,
686 .equal = &reftable_obj_record_equal_void,
687 .cmp = &reftable_obj_record_cmp_void,
688 .print = &reftable_obj_record_print,
691 static void reftable_log_record_print_sz(struct reftable_log_record *log,
692 int hash_size)
694 char hex[GIT_MAX_HEXSZ + 1] = { 0 };
696 switch (log->value_type) {
697 case REFTABLE_LOG_DELETION:
698 printf("log{%s(%" PRIu64 ") delete\n", log->refname,
699 log->update_index);
700 break;
701 case REFTABLE_LOG_UPDATE:
702 printf("log{%s(%" PRIu64 ") %s <%s> %" PRIu64 " %04d\n",
703 log->refname, log->update_index,
704 log->value.update.name ? log->value.update.name : "",
705 log->value.update.email ? log->value.update.email : "",
706 log->value.update.time,
707 log->value.update.tz_offset);
708 hex_format(hex, log->value.update.old_hash, hash_size);
709 printf("%s => ", hex);
710 hex_format(hex, log->value.update.new_hash, hash_size);
711 printf("%s\n\n%s\n}\n", hex,
712 log->value.update.message ? log->value.update.message : "");
713 break;
717 void reftable_log_record_print(struct reftable_log_record *log,
718 uint32_t hash_id)
720 reftable_log_record_print_sz(log, hash_size(hash_id));
723 static void reftable_log_record_key(const void *r, struct strbuf *dest)
725 const struct reftable_log_record *rec =
726 (const struct reftable_log_record *)r;
727 int len = strlen(rec->refname);
728 uint8_t i64[8];
729 uint64_t ts = 0;
730 strbuf_reset(dest);
731 strbuf_add(dest, (uint8_t *)rec->refname, len + 1);
733 ts = (~ts) - rec->update_index;
734 put_be64(&i64[0], ts);
735 strbuf_add(dest, i64, sizeof(i64));
738 static void reftable_log_record_copy_from(void *rec, const void *src_rec,
739 int hash_size)
741 struct reftable_log_record *dst = rec;
742 const struct reftable_log_record *src =
743 (const struct reftable_log_record *)src_rec;
745 reftable_log_record_release(dst);
746 *dst = *src;
747 if (dst->refname) {
748 dst->refname = xstrdup(dst->refname);
750 switch (dst->value_type) {
751 case REFTABLE_LOG_DELETION:
752 break;
753 case REFTABLE_LOG_UPDATE:
754 if (dst->value.update.email) {
755 dst->value.update.email =
756 xstrdup(dst->value.update.email);
758 if (dst->value.update.name) {
759 dst->value.update.name =
760 xstrdup(dst->value.update.name);
762 if (dst->value.update.message) {
763 dst->value.update.message =
764 xstrdup(dst->value.update.message);
767 memcpy(dst->value.update.new_hash,
768 src->value.update.new_hash, hash_size);
769 memcpy(dst->value.update.old_hash,
770 src->value.update.old_hash, hash_size);
771 break;
775 static void reftable_log_record_release_void(void *rec)
777 struct reftable_log_record *r = rec;
778 reftable_log_record_release(r);
781 void reftable_log_record_release(struct reftable_log_record *r)
783 reftable_free(r->refname);
784 switch (r->value_type) {
785 case REFTABLE_LOG_DELETION:
786 break;
787 case REFTABLE_LOG_UPDATE:
788 reftable_free(r->value.update.name);
789 reftable_free(r->value.update.email);
790 reftable_free(r->value.update.message);
791 break;
793 memset(r, 0, sizeof(struct reftable_log_record));
796 static uint8_t reftable_log_record_val_type(const void *rec)
798 const struct reftable_log_record *log =
799 (const struct reftable_log_record *)rec;
801 return reftable_log_record_is_deletion(log) ? 0 : 1;
804 static int reftable_log_record_encode(const void *rec, struct string_view s,
805 int hash_size)
807 const struct reftable_log_record *r = rec;
808 struct string_view start = s;
809 int n = 0;
810 if (reftable_log_record_is_deletion(r))
811 return 0;
813 if (s.len < 2 * hash_size)
814 return -1;
816 memcpy(s.buf, r->value.update.old_hash, hash_size);
817 memcpy(s.buf + hash_size, r->value.update.new_hash, hash_size);
818 string_view_consume(&s, 2 * hash_size);
820 n = encode_string(r->value.update.name ? r->value.update.name : "", s);
821 if (n < 0)
822 return -1;
823 string_view_consume(&s, n);
825 n = encode_string(r->value.update.email ? r->value.update.email : "",
827 if (n < 0)
828 return -1;
829 string_view_consume(&s, n);
831 n = put_var_int(&s, r->value.update.time);
832 if (n < 0)
833 return -1;
834 string_view_consume(&s, n);
836 if (s.len < 2)
837 return -1;
839 put_be16(s.buf, r->value.update.tz_offset);
840 string_view_consume(&s, 2);
842 n = encode_string(
843 r->value.update.message ? r->value.update.message : "", s);
844 if (n < 0)
845 return -1;
846 string_view_consume(&s, n);
848 return start.len - s.len;
851 static int reftable_log_record_decode(void *rec, struct strbuf key,
852 uint8_t val_type, struct string_view in,
853 int hash_size, struct strbuf *scratch)
855 struct string_view start = in;
856 struct reftable_log_record *r = rec;
857 uint64_t max = 0;
858 uint64_t ts = 0;
859 int n;
861 if (key.len <= 9 || key.buf[key.len - 9] != 0)
862 return REFTABLE_FORMAT_ERROR;
864 REFTABLE_ALLOC_GROW(r->refname, key.len - 8, r->refname_cap);
865 memcpy(r->refname, key.buf, key.len - 8);
866 ts = get_be64(key.buf + key.len - 8);
868 r->update_index = (~max) - ts;
870 if (val_type != r->value_type) {
871 switch (r->value_type) {
872 case REFTABLE_LOG_UPDATE:
873 FREE_AND_NULL(r->value.update.message);
874 r->value.update.message_cap = 0;
875 FREE_AND_NULL(r->value.update.email);
876 FREE_AND_NULL(r->value.update.name);
877 break;
878 case REFTABLE_LOG_DELETION:
879 break;
883 r->value_type = val_type;
884 if (val_type == REFTABLE_LOG_DELETION)
885 return 0;
887 if (in.len < 2 * hash_size)
888 return REFTABLE_FORMAT_ERROR;
890 memcpy(r->value.update.old_hash, in.buf, hash_size);
891 memcpy(r->value.update.new_hash, in.buf + hash_size, hash_size);
893 string_view_consume(&in, 2 * hash_size);
895 n = decode_string(scratch, in);
896 if (n < 0)
897 goto done;
898 string_view_consume(&in, n);
901 * In almost all cases we can expect the reflog name to not change for
902 * reflog entries as they are tied to the local identity, not to the
903 * target commits. As an optimization for this common case we can thus
904 * skip copying over the name in case it's accurate already.
906 if (!r->value.update.name ||
907 strcmp(r->value.update.name, scratch->buf)) {
908 r->value.update.name =
909 reftable_realloc(r->value.update.name, scratch->len + 1);
910 memcpy(r->value.update.name, scratch->buf, scratch->len);
911 r->value.update.name[scratch->len] = 0;
914 n = decode_string(scratch, in);
915 if (n < 0)
916 goto done;
917 string_view_consume(&in, n);
919 /* Same as above, but for the reflog email. */
920 if (!r->value.update.email ||
921 strcmp(r->value.update.email, scratch->buf)) {
922 r->value.update.email =
923 reftable_realloc(r->value.update.email, scratch->len + 1);
924 memcpy(r->value.update.email, scratch->buf, scratch->len);
925 r->value.update.email[scratch->len] = 0;
928 ts = 0;
929 n = get_var_int(&ts, &in);
930 if (n < 0)
931 goto done;
932 string_view_consume(&in, n);
933 r->value.update.time = ts;
934 if (in.len < 2)
935 goto done;
937 r->value.update.tz_offset = get_be16(in.buf);
938 string_view_consume(&in, 2);
940 n = decode_string(scratch, in);
941 if (n < 0)
942 goto done;
943 string_view_consume(&in, n);
945 REFTABLE_ALLOC_GROW(r->value.update.message, scratch->len + 1,
946 r->value.update.message_cap);
947 memcpy(r->value.update.message, scratch->buf, scratch->len);
948 r->value.update.message[scratch->len] = 0;
950 return start.len - in.len;
952 done:
953 return REFTABLE_FORMAT_ERROR;
956 static int null_streq(char *a, char *b)
958 char *empty = "";
959 if (!a)
960 a = empty;
962 if (!b)
963 b = empty;
965 return 0 == strcmp(a, b);
968 static int reftable_log_record_equal_void(const void *a,
969 const void *b, int hash_size)
971 return reftable_log_record_equal((struct reftable_log_record *) a,
972 (struct reftable_log_record *) b,
973 hash_size);
976 static int reftable_log_record_cmp_void(const void *_a, const void *_b)
978 const struct reftable_log_record *a = _a;
979 const struct reftable_log_record *b = _b;
980 int cmp = strcmp(a->refname, b->refname);
981 if (cmp)
982 return cmp;
985 * Note that the comparison here is reversed. This is because the
986 * update index is reversed when comparing keys. For reference, see how
987 * we handle this in reftable_log_record_key()`.
989 return b->update_index - a->update_index;
992 int reftable_log_record_equal(const struct reftable_log_record *a,
993 const struct reftable_log_record *b, int hash_size)
995 if (!(null_streq(a->refname, b->refname) &&
996 a->update_index == b->update_index &&
997 a->value_type == b->value_type))
998 return 0;
1000 switch (a->value_type) {
1001 case REFTABLE_LOG_DELETION:
1002 return 1;
1003 case REFTABLE_LOG_UPDATE:
1004 return null_streq(a->value.update.name, b->value.update.name) &&
1005 a->value.update.time == b->value.update.time &&
1006 a->value.update.tz_offset == b->value.update.tz_offset &&
1007 null_streq(a->value.update.email,
1008 b->value.update.email) &&
1009 null_streq(a->value.update.message,
1010 b->value.update.message) &&
1011 !memcmp(a->value.update.old_hash,
1012 b->value.update.old_hash, hash_size) &&
1013 !memcmp(a->value.update.new_hash,
1014 b->value.update.new_hash, hash_size);
1017 abort();
1020 static int reftable_log_record_is_deletion_void(const void *p)
1022 return reftable_log_record_is_deletion(
1023 (const struct reftable_log_record *)p);
1026 static void reftable_log_record_print_void(const void *rec, int hash_size)
1028 reftable_log_record_print_sz((struct reftable_log_record*)rec, hash_size);
1031 static struct reftable_record_vtable reftable_log_record_vtable = {
1032 .key = &reftable_log_record_key,
1033 .type = BLOCK_TYPE_LOG,
1034 .copy_from = &reftable_log_record_copy_from,
1035 .val_type = &reftable_log_record_val_type,
1036 .encode = &reftable_log_record_encode,
1037 .decode = &reftable_log_record_decode,
1038 .release = &reftable_log_record_release_void,
1039 .is_deletion = &reftable_log_record_is_deletion_void,
1040 .equal = &reftable_log_record_equal_void,
1041 .cmp = &reftable_log_record_cmp_void,
1042 .print = &reftable_log_record_print_void,
1045 static void reftable_index_record_key(const void *r, struct strbuf *dest)
1047 const struct reftable_index_record *rec = r;
1048 strbuf_reset(dest);
1049 strbuf_addbuf(dest, &rec->last_key);
1052 static void reftable_index_record_copy_from(void *rec, const void *src_rec,
1053 int hash_size)
1055 struct reftable_index_record *dst = rec;
1056 const struct reftable_index_record *src = src_rec;
1058 strbuf_reset(&dst->last_key);
1059 strbuf_addbuf(&dst->last_key, &src->last_key);
1060 dst->offset = src->offset;
1063 static void reftable_index_record_release(void *rec)
1065 struct reftable_index_record *idx = rec;
1066 strbuf_release(&idx->last_key);
1069 static uint8_t reftable_index_record_val_type(const void *rec)
1071 return 0;
1074 static int reftable_index_record_encode(const void *rec, struct string_view out,
1075 int hash_size)
1077 const struct reftable_index_record *r =
1078 (const struct reftable_index_record *)rec;
1079 struct string_view start = out;
1081 int n = put_var_int(&out, r->offset);
1082 if (n < 0)
1083 return n;
1085 string_view_consume(&out, n);
1087 return start.len - out.len;
1090 static int reftable_index_record_decode(void *rec, struct strbuf key,
1091 uint8_t val_type, struct string_view in,
1092 int hash_size, struct strbuf *scratch UNUSED)
1094 struct string_view start = in;
1095 struct reftable_index_record *r = rec;
1096 int n = 0;
1098 strbuf_reset(&r->last_key);
1099 strbuf_addbuf(&r->last_key, &key);
1101 n = get_var_int(&r->offset, &in);
1102 if (n < 0)
1103 return n;
1105 string_view_consume(&in, n);
1106 return start.len - in.len;
1109 static int reftable_index_record_equal(const void *a, const void *b, int hash_size)
1111 struct reftable_index_record *ia = (struct reftable_index_record *) a;
1112 struct reftable_index_record *ib = (struct reftable_index_record *) b;
1114 return ia->offset == ib->offset && !strbuf_cmp(&ia->last_key, &ib->last_key);
1117 static int reftable_index_record_cmp(const void *_a, const void *_b)
1119 const struct reftable_index_record *a = _a;
1120 const struct reftable_index_record *b = _b;
1121 return strbuf_cmp(&a->last_key, &b->last_key);
1124 static void reftable_index_record_print(const void *rec, int hash_size)
1126 const struct reftable_index_record *idx = rec;
1127 /* TODO: escape null chars? */
1128 printf("\"%s\" %" PRIu64 "\n", idx->last_key.buf, idx->offset);
1131 static struct reftable_record_vtable reftable_index_record_vtable = {
1132 .key = &reftable_index_record_key,
1133 .type = BLOCK_TYPE_INDEX,
1134 .copy_from = &reftable_index_record_copy_from,
1135 .val_type = &reftable_index_record_val_type,
1136 .encode = &reftable_index_record_encode,
1137 .decode = &reftable_index_record_decode,
1138 .release = &reftable_index_record_release,
1139 .is_deletion = &not_a_deletion,
1140 .equal = &reftable_index_record_equal,
1141 .cmp = &reftable_index_record_cmp,
1142 .print = &reftable_index_record_print,
1145 void reftable_record_key(struct reftable_record *rec, struct strbuf *dest)
1147 reftable_record_vtable(rec)->key(reftable_record_data(rec), dest);
1150 int reftable_record_encode(struct reftable_record *rec, struct string_view dest,
1151 int hash_size)
1153 return reftable_record_vtable(rec)->encode(reftable_record_data(rec),
1154 dest, hash_size);
1157 void reftable_record_copy_from(struct reftable_record *rec,
1158 struct reftable_record *src, int hash_size)
1160 assert(src->type == rec->type);
1162 reftable_record_vtable(rec)->copy_from(reftable_record_data(rec),
1163 reftable_record_data(src),
1164 hash_size);
1167 uint8_t reftable_record_val_type(struct reftable_record *rec)
1169 return reftable_record_vtable(rec)->val_type(reftable_record_data(rec));
1172 int reftable_record_decode(struct reftable_record *rec, struct strbuf key,
1173 uint8_t extra, struct string_view src, int hash_size,
1174 struct strbuf *scratch)
1176 return reftable_record_vtable(rec)->decode(reftable_record_data(rec),
1177 key, extra, src, hash_size,
1178 scratch);
1181 void reftable_record_release(struct reftable_record *rec)
1183 reftable_record_vtable(rec)->release(reftable_record_data(rec));
1186 int reftable_record_is_deletion(struct reftable_record *rec)
1188 return reftable_record_vtable(rec)->is_deletion(
1189 reftable_record_data(rec));
1192 int reftable_record_cmp(struct reftable_record *a, struct reftable_record *b)
1194 if (a->type != b->type)
1195 BUG("cannot compare reftable records of different type");
1196 return reftable_record_vtable(a)->cmp(
1197 reftable_record_data(a), reftable_record_data(b));
1200 int reftable_record_equal(struct reftable_record *a, struct reftable_record *b, int hash_size)
1202 if (a->type != b->type)
1203 return 0;
1204 return reftable_record_vtable(a)->equal(
1205 reftable_record_data(a), reftable_record_data(b), hash_size);
1208 static int hash_equal(const unsigned char *a, const unsigned char *b, int hash_size)
1210 if (a && b)
1211 return !memcmp(a, b, hash_size);
1213 return a == b;
1216 int reftable_ref_record_equal(const struct reftable_ref_record *a,
1217 const struct reftable_ref_record *b, int hash_size)
1219 assert(hash_size > 0);
1220 if (!null_streq(a->refname, b->refname))
1221 return 0;
1223 if (a->update_index != b->update_index ||
1224 a->value_type != b->value_type)
1225 return 0;
1227 switch (a->value_type) {
1228 case REFTABLE_REF_SYMREF:
1229 return !strcmp(a->value.symref, b->value.symref);
1230 case REFTABLE_REF_VAL2:
1231 return hash_equal(a->value.val2.value, b->value.val2.value,
1232 hash_size) &&
1233 hash_equal(a->value.val2.target_value,
1234 b->value.val2.target_value, hash_size);
1235 case REFTABLE_REF_VAL1:
1236 return hash_equal(a->value.val1, b->value.val1, hash_size);
1237 case REFTABLE_REF_DELETION:
1238 return 1;
1239 default:
1240 abort();
1244 int reftable_ref_record_compare_name(const void *a, const void *b)
1246 return strcmp(((struct reftable_ref_record *)a)->refname,
1247 ((struct reftable_ref_record *)b)->refname);
1250 int reftable_ref_record_is_deletion(const struct reftable_ref_record *ref)
1252 return ref->value_type == REFTABLE_REF_DELETION;
1255 int reftable_log_record_compare_key(const void *a, const void *b)
1257 const struct reftable_log_record *la = a;
1258 const struct reftable_log_record *lb = b;
1260 int cmp = strcmp(la->refname, lb->refname);
1261 if (cmp)
1262 return cmp;
1263 if (la->update_index > lb->update_index)
1264 return -1;
1265 return (la->update_index < lb->update_index) ? 1 : 0;
1268 int reftable_log_record_is_deletion(const struct reftable_log_record *log)
1270 return (log->value_type == REFTABLE_LOG_DELETION);
1273 static void *reftable_record_data(struct reftable_record *rec)
1275 switch (rec->type) {
1276 case BLOCK_TYPE_REF:
1277 return &rec->u.ref;
1278 case BLOCK_TYPE_LOG:
1279 return &rec->u.log;
1280 case BLOCK_TYPE_INDEX:
1281 return &rec->u.idx;
1282 case BLOCK_TYPE_OBJ:
1283 return &rec->u.obj;
1285 abort();
1288 static struct reftable_record_vtable *
1289 reftable_record_vtable(struct reftable_record *rec)
1291 switch (rec->type) {
1292 case BLOCK_TYPE_REF:
1293 return &reftable_ref_record_vtable;
1294 case BLOCK_TYPE_LOG:
1295 return &reftable_log_record_vtable;
1296 case BLOCK_TYPE_INDEX:
1297 return &reftable_index_record_vtable;
1298 case BLOCK_TYPE_OBJ:
1299 return &reftable_obj_record_vtable;
1301 abort();
1304 void reftable_record_init(struct reftable_record *rec, uint8_t typ)
1306 memset(rec, 0, sizeof(*rec));
1307 rec->type = typ;
1309 switch (typ) {
1310 case BLOCK_TYPE_REF:
1311 case BLOCK_TYPE_LOG:
1312 case BLOCK_TYPE_OBJ:
1313 return;
1314 case BLOCK_TYPE_INDEX:
1315 strbuf_init(&rec->u.idx.last_key, 0);
1316 return;
1317 default:
1318 BUG("unhandled record type");
1322 void reftable_record_print(struct reftable_record *rec, int hash_size)
1324 printf("'%c': ", rec->type);
1325 reftable_record_vtable(rec)->print(reftable_record_data(rec), hash_size);