Coverage Report

Created: 2026-01-09 07:10

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