#include "amduat/fed/replay.h" #include #include 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(¤t, &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(¤t); ok = false; break; } amduat_fed_record_free(¤t); continue; } tombstoned = amduat_fed_tombstone_list_has(tombstones, tombstones_len, current.id.ref); if (tombstoned) { amduat_fed_record_free(¤t); 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; }