amduat/src/near_core/fed/replay.c

305 lines
7.6 KiB
C
Raw Normal View History

#include "amduat/fed/replay.h"
#include <stdlib.h>
#include <string.h>
static int amduat_fed_octets_cmp(amduat_octets_t a, amduat_octets_t b) {
size_t min_len;
int cmp;
min_len = a.len < b.len ? a.len : b.len;
if (min_len > 0) {
cmp = memcmp(a.data, b.data, min_len);
if (cmp != 0) {
return cmp;
}
}
if (a.len < b.len) {
return -1;
}
if (a.len > b.len) {
return 1;
}
return 0;
}
static int amduat_fed_record_id_cmp(const amduat_fed_record_id_t *a,
const amduat_fed_record_id_t *b) {
if (a->type != b->type) {
return (a->type < b->type) ? -1 : 1;
}
if (a->ref.hash_id != b->ref.hash_id) {
return (a->ref.hash_id < b->ref.hash_id) ? -1 : 1;
}
return amduat_fed_octets_cmp(a->ref.digest, b->ref.digest);
}
static int amduat_fed_record_cmp(const void *lhs, const void *rhs) {
const amduat_fed_record_t *a = (const amduat_fed_record_t *)lhs;
const amduat_fed_record_t *b = (const amduat_fed_record_t *)rhs;
if (a->logseq != b->logseq) {
return (a->logseq < b->logseq) ? -1 : 1;
}
return amduat_fed_record_id_cmp(&a->id, &b->id);
}
static bool amduat_fed_record_clone(const amduat_fed_record_t *src,
amduat_fed_record_t *out) {
if (src == NULL || out == NULL) {
return false;
}
*out = *src;
if (!amduat_reference_clone(src->id.ref, &out->id.ref)) {
return false;
}
if (!amduat_reference_clone(src->location.ref, &out->location.ref)) {
amduat_reference_free(&out->id.ref);
return false;
}
return true;
}
static void amduat_fed_record_free(amduat_fed_record_t *record) {
if (record == NULL) {
return;
}
amduat_reference_free(&record->id.ref);
amduat_reference_free(&record->location.ref);
memset(record, 0, sizeof(*record));
}
static bool amduat_fed_bounds_ok(const amduat_fed_record_t *record,
uint64_t snapshot_id,
uint64_t log_prefix) {
if (record->snapshot_id < snapshot_id) {
return true;
}
if (record->snapshot_id > snapshot_id) {
return false;
}
return record->logseq <= log_prefix;
}
static bool amduat_fed_ref_eq(amduat_reference_t a, amduat_reference_t b) {
return amduat_reference_eq(a, b);
}
static bool amduat_fed_tombstone_matches(const amduat_fed_record_t *tombstone,
const amduat_fed_record_t *record) {
if (tombstone->id.type != AMDUAT_FED_REC_TOMBSTONE) {
return false;
}
if (record->id.type == AMDUAT_FED_REC_TOMBSTONE) {
return false;
}
return amduat_fed_ref_eq(tombstone->id.ref, record->id.ref);
}
static bool amduat_fed_tombstone_list_has(
const amduat_reference_t *refs,
size_t refs_len,
amduat_reference_t candidate) {
size_t i;
for (i = 0; i < refs_len; ++i) {
if (amduat_fed_ref_eq(refs[i], candidate)) {
return true;
}
}
return false;
}
static bool amduat_fed_tombstone_list_push(amduat_reference_t **refs,
size_t *len,
size_t *cap,
amduat_reference_t ref) {
amduat_reference_t *next;
if (*len == *cap) {
size_t next_cap = (*cap == 0u) ? 4u : (*cap * 2u);
next = (amduat_reference_t *)realloc(*refs,
next_cap * sizeof(*next));
if (next == NULL) {
return false;
}
*refs = next;
*cap = next_cap;
}
if (!amduat_reference_clone(ref, &(*refs)[*len])) {
return false;
}
(*len)++;
return true;
}
bool amduat_fed_record_validate(const amduat_fed_record_t *record) {
if (record == NULL) {
return false;
}
if (record->meta.visibility > 1u) {
return false;
}
if (record->meta.has_source > 1u) {
return false;
}
if (record->meta.has_source == 0u && record->meta.source_domain != 0u) {
return false;
}
if (record->id.type < AMDUAT_FED_REC_ARTIFACT ||
record->id.type > AMDUAT_FED_REC_TOMBSTONE) {
return false;
}
if (record->id.ref.digest.len != 0u &&
record->id.ref.digest.data == NULL) {
return false;
}
if (record->location.ref.digest.len != 0u &&
record->location.ref.digest.data == NULL) {
return false;
}
return true;
}
bool amduat_fed_replay_domain(const amduat_fed_record_t *records,
size_t count,
uint32_t domain_id,
uint64_t snapshot_id,
uint64_t log_prefix,
amduat_fed_replay_view_t *out_view) {
amduat_fed_record_t *scratch;
amduat_reference_t *tombstones;
size_t tombstones_len;
size_t tombstones_cap;
size_t scratch_len;
size_t i;
size_t out_len;
bool ok;
if (out_view == NULL) {
return false;
}
out_view->records = NULL;
out_view->len = 0;
if (records == NULL && count != 0u) {
return false;
}
if (count == 0u) {
return true;
}
scratch = (amduat_fed_record_t *)calloc(count, sizeof(*scratch));
if (scratch == NULL) {
return false;
}
tombstones = NULL;
tombstones_len = 0;
tombstones_cap = 0;
scratch_len = 0;
for (i = 0; i < count; ++i) {
const amduat_fed_record_t *record = &records[i];
if (record->meta.domain_id != domain_id) {
continue;
}
if (!amduat_fed_record_validate(record)) {
continue;
}
if (!amduat_fed_bounds_ok(record, snapshot_id, log_prefix)) {
continue;
}
if (!amduat_fed_record_clone(record, &scratch[scratch_len])) {
while (scratch_len > 0) {
scratch_len--;
amduat_fed_record_free(&scratch[scratch_len]);
}
free(scratch);
return false;
}
scratch_len++;
}
qsort(scratch, scratch_len, sizeof(*scratch), amduat_fed_record_cmp);
ok = true;
out_len = 0;
for (i = 0; i < scratch_len; ++i) {
amduat_fed_record_t current = scratch[i];
size_t j;
bool tombstoned;
scratch[i].id.ref.digest.data = NULL;
scratch[i].id.ref.digest.len = 0u;
if (current.id.type == AMDUAT_FED_REC_TOMBSTONE) {
for (j = 0; j < out_len; ++j) {
if (amduat_fed_tombstone_matches(&current, &scratch[j])) {
amduat_fed_record_free(&scratch[j]);
memmove(&scratch[j],
&scratch[j + 1],
(out_len - j - 1) * sizeof(*scratch));
out_len--;
j--;
}
}
if (!amduat_fed_tombstone_list_push(&tombstones,
&tombstones_len,
&tombstones_cap,
current.id.ref)) {
amduat_fed_record_free(&current);
ok = false;
break;
}
amduat_fed_record_free(&current);
continue;
}
tombstoned = amduat_fed_tombstone_list_has(tombstones,
tombstones_len,
current.id.ref);
if (tombstoned) {
amduat_fed_record_free(&current);
continue;
}
scratch[out_len++] = current;
}
for (i = 0; i < tombstones_len; ++i) {
amduat_reference_free(&tombstones[i]);
}
free(tombstones);
if (!ok) {
for (i = 0; i < scratch_len; ++i) {
amduat_fed_record_free(&scratch[i]);
}
free(scratch);
return false;
}
out_view->records = scratch;
out_view->len = out_len;
return true;
}
void amduat_fed_replay_view_free(amduat_fed_replay_view_t *view) {
size_t i;
if (view == NULL) {
return;
}
if (view->records != NULL) {
for (i = 0; i < view->len; ++i) {
amduat_fed_record_free(&view->records[i]);
}
free(view->records);
}
view->records = NULL;
view->len = 0;
}