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