amduat/tests/enc/test_asl_core_index.c

337 lines
9.4 KiB
C
Raw Permalink Normal View History

2026-01-17 13:10:07 +01:00
#include "amduat/enc/asl_core_index.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void store_u16_le(uint8_t *out, uint16_t value) {
out[0] = (uint8_t)(value & 0xffu);
out[1] = (uint8_t)((value >> 8) & 0xffu);
}
static void store_u64_le(uint8_t *out, uint64_t value) {
out[0] = (uint8_t)(value & 0xffu);
out[1] = (uint8_t)((value >> 8) & 0xffu);
out[2] = (uint8_t)((value >> 16) & 0xffu);
out[3] = (uint8_t)((value >> 24) & 0xffu);
out[4] = (uint8_t)((value >> 32) & 0xffu);
out[5] = (uint8_t)((value >> 40) & 0xffu);
out[6] = (uint8_t)((value >> 48) & 0xffu);
out[7] = (uint8_t)((value >> 56) & 0xffu);
}
static uint64_t load_u64_le(const uint8_t *data) {
return (uint64_t)data[0] | ((uint64_t)data[1] << 8) |
((uint64_t)data[2] << 16) | ((uint64_t)data[3] << 24) |
((uint64_t)data[4] << 32) | ((uint64_t)data[5] << 40) |
((uint64_t)data[6] << 48) | ((uint64_t)data[7] << 56);
}
static uint64_t crc64_ecma(const uint8_t *data, size_t len) {
uint64_t crc = 0u;
size_t i;
for (i = 0; i < len; ++i) {
uint64_t bit;
uint8_t value = data[i];
crc ^= ((uint64_t)value) << 56;
for (bit = 0; bit < 8; ++bit) {
if (crc & 0x8000000000000000ull) {
crc = (crc << 1) ^ 0x42f0e1eba9ea3693ull;
} else {
crc <<= 1;
}
}
}
return crc;
}
static void update_crc(amduat_octets_t bytes) {
size_t footer_offset = bytes.len - AMDUAT_ASL_CORE_INDEX_FOOTER_SIZE;
uint64_t crc = crc64_ecma(bytes.data, footer_offset);
store_u64_le((uint8_t *)bytes.data + footer_offset, crc);
}
static void build_simple_segment(amduat_asl_core_index_segment_t *segment,
amduat_asl_index_record_t *record,
amduat_asl_extent_record_t *extent,
uint8_t digest_bytes[8]) {
memset(segment, 0, sizeof(*segment));
memset(record, 0, sizeof(*record));
memset(extent, 0, sizeof(*extent));
digest_bytes[0] = 0x01;
digest_bytes[1] = 0x02;
digest_bytes[2] = 0x03;
digest_bytes[3] = 0x04;
digest_bytes[4] = 0x05;
digest_bytes[5] = 0x06;
digest_bytes[6] = 0x07;
digest_bytes[7] = 0x08;
segment->header.snapshot_min = 5;
segment->header.snapshot_max = 6;
segment->header.segment_domain_id = 7;
segment->header.segment_visibility = 1;
segment->header.federation_version = 0;
segment->header.flags = 0;
segment->header.reserved0 = 0;
record->hash_id = 9;
record->digest_len = 8;
record->extent_count = 1;
record->total_length = 3;
record->domain_id = 7;
record->visibility = 1;
record->has_cross_domain_source = 0;
record->cross_domain_source = 0;
record->flags = 0;
extent->block_id = 123;
extent->offset = 0;
extent->length = 3;
segment->records = record;
segment->record_count = 1;
segment->digests = amduat_octets(digest_bytes, 8);
segment->extents = extent;
segment->extent_count = 1;
segment->footer.seal_snapshot = 10;
segment->footer.seal_time_ns = 11;
}
static int test_round_trip(void) {
amduat_asl_core_index_segment_t segment;
amduat_asl_index_record_t record;
amduat_asl_extent_record_t extent;
uint8_t digest_bytes[8];
amduat_octets_t encoded;
amduat_asl_core_index_segment_t decoded;
int exit_code = 1;
build_simple_segment(&segment, &record, &extent, digest_bytes);
if (!amduat_enc_asl_core_index_encode_v1(&segment, &encoded)) {
fprintf(stderr, "encode failed\n");
return exit_code;
}
if (!amduat_enc_asl_core_index_decode_v1(encoded, &decoded)) {
fprintf(stderr, "decode failed\n");
goto cleanup;
}
if (decoded.header.version != AMDUAT_ASL_CORE_INDEX_VERSION ||
decoded.header.header_size != AMDUAT_ASL_CORE_INDEX_HEADER_SIZE) {
fprintf(stderr, "header mismatch\n");
goto cleanup_decoded;
}
if (decoded.record_count != 1 || decoded.extent_count != 1) {
fprintf(stderr, "count mismatch\n");
goto cleanup_decoded;
}
if (decoded.header.segment_domain_id != 7 ||
decoded.header.segment_visibility != 1) {
fprintf(stderr, "segment federation mismatch\n");
goto cleanup_decoded;
}
if (decoded.records[0].hash_id != record.hash_id ||
decoded.records[0].digest_len != record.digest_len ||
decoded.records[0].total_length != record.total_length ||
decoded.records[0].domain_id != record.domain_id ||
decoded.records[0].visibility != record.visibility ||
decoded.records[0].flags != record.flags) {
fprintf(stderr, "record mismatch\n");
goto cleanup_decoded;
}
if (decoded.digests.len != sizeof(digest_bytes) ||
memcmp(decoded.digests.data, digest_bytes, sizeof(digest_bytes)) != 0) {
fprintf(stderr, "digest mismatch\n");
goto cleanup_decoded;
}
if (decoded.extents[0].block_id != extent.block_id ||
decoded.extents[0].offset != extent.offset ||
decoded.extents[0].length != extent.length) {
fprintf(stderr, "extent mismatch\n");
goto cleanup_decoded;
}
if (decoded.footer.seal_snapshot != segment.footer.seal_snapshot ||
decoded.footer.seal_time_ns != segment.footer.seal_time_ns) {
fprintf(stderr, "footer mismatch\n");
goto cleanup_decoded;
}
exit_code = 0;
cleanup_decoded:
amduat_enc_asl_core_index_free(&decoded);
cleanup:
free((void *)encoded.data);
return exit_code;
}
static int test_crc_mismatch(void) {
amduat_asl_core_index_segment_t segment;
amduat_asl_index_record_t record;
amduat_asl_extent_record_t extent;
uint8_t digest_bytes[8];
amduat_octets_t encoded;
amduat_asl_core_index_segment_t decoded;
uint64_t digests_offset;
int exit_code = 1;
build_simple_segment(&segment, &record, &extent, digest_bytes);
if (!amduat_enc_asl_core_index_encode_v1(&segment, &encoded)) {
fprintf(stderr, "encode failed\n");
return exit_code;
}
digests_offset = load_u64_le(encoded.data + 64);
((uint8_t *)encoded.data)[digests_offset] ^= 0xffu;
if (amduat_enc_asl_core_index_decode_v1(encoded, &decoded)) {
fprintf(stderr, "decode unexpectedly succeeded\n");
amduat_enc_asl_core_index_free(&decoded);
goto cleanup;
}
exit_code = 0;
cleanup:
free((void *)encoded.data);
return exit_code;
}
static int test_invalid_offsets(void) {
amduat_asl_core_index_segment_t segment;
amduat_asl_index_record_t record;
amduat_asl_extent_record_t extent;
uint8_t digest_bytes[8];
amduat_octets_t encoded;
amduat_asl_core_index_segment_t decoded;
int exit_code = 1;
build_simple_segment(&segment, &record, &extent, digest_bytes);
if (!amduat_enc_asl_core_index_encode_v1(&segment, &encoded)) {
fprintf(stderr, "encode failed\n");
return exit_code;
}
store_u64_le((uint8_t *)encoded.data + 72, 0);
update_crc(encoded);
if (amduat_enc_asl_core_index_decode_v1(encoded, &decoded)) {
fprintf(stderr, "decode unexpectedly succeeded\n");
amduat_enc_asl_core_index_free(&decoded);
goto cleanup;
}
exit_code = 0;
cleanup:
free((void *)encoded.data);
return exit_code;
}
static int test_invalid_federation_fields(void) {
amduat_asl_core_index_segment_t segment;
amduat_asl_index_record_t record;
amduat_asl_extent_record_t extent;
uint8_t digest_bytes[8];
amduat_octets_t encoded;
amduat_asl_core_index_segment_t decoded;
uint64_t records_offset;
int exit_code = 1;
build_simple_segment(&segment, &record, &extent, digest_bytes);
if (!amduat_enc_asl_core_index_encode_v1(&segment, &encoded)) {
fprintf(stderr, "encode failed\n");
return exit_code;
}
records_offset = load_u64_le(encoded.data + 40);
((uint8_t *)encoded.data)[records_offset + 36] = 2;
update_crc(encoded);
if (amduat_enc_asl_core_index_decode_v1(encoded, &decoded)) {
fprintf(stderr, "decode unexpectedly succeeded\n");
amduat_enc_asl_core_index_free(&decoded);
goto cleanup;
}
exit_code = 0;
cleanup:
free((void *)encoded.data);
return exit_code;
}
static int test_legacy_defaults(void) {
amduat_asl_core_index_segment_t segment;
amduat_asl_index_record_t record;
amduat_asl_extent_record_t extent;
uint8_t digest_bytes[8];
amduat_octets_t encoded;
amduat_asl_core_index_segment_t decoded;
uint64_t records_offset;
int exit_code = 1;
build_simple_segment(&segment, &record, &extent, digest_bytes);
if (!amduat_enc_asl_core_index_encode_v1(&segment, &encoded)) {
fprintf(stderr, "encode failed\n");
return exit_code;
}
store_u16_le((uint8_t *)encoded.data + 8, 2);
((uint8_t *)encoded.data)[100] = 1;
records_offset = load_u64_le(encoded.data + 40);
((uint8_t *)encoded.data)[records_offset + 36] = 1;
update_crc(encoded);
if (!amduat_enc_asl_core_index_decode_v1(encoded, &decoded)) {
fprintf(stderr, "decode failed\n");
goto cleanup;
}
if (decoded.header.segment_visibility != 0 ||
decoded.records[0].visibility != 0 ||
decoded.records[0].domain_id != 0 ||
decoded.records[0].has_cross_domain_source != 0 ||
decoded.records[0].cross_domain_source != 0) {
fprintf(stderr, "legacy defaults not applied\n");
goto cleanup_decoded;
}
exit_code = 0;
cleanup_decoded:
amduat_enc_asl_core_index_free(&decoded);
cleanup:
free((void *)encoded.data);
return exit_code;
}
int main(void) {
if (test_round_trip() != 0) {
return 1;
}
if (test_crc_mismatch() != 0) {
return 1;
}
if (test_invalid_offsets() != 0) {
return 1;
}
if (test_invalid_federation_fields() != 0) {
return 1;
}
if (test_legacy_defaults() != 0) {
return 1;
}
return 0;
}