2026-01-18 06:13:07 +01:00
|
|
|
#include "amduat/enc/asl_tgk_exec_plan.h"
|
|
|
|
|
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
k_plan_header_size = 8,
|
|
|
|
|
k_params_size = 65,
|
|
|
|
|
k_operator_size = 129,
|
|
|
|
|
k_input_offset = 97
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-22 00:25:56 +01:00
|
|
|
static bool read_file(const char *path, uint8_t **out_bytes, size_t *out_len) {
|
|
|
|
|
FILE *fp;
|
|
|
|
|
long size;
|
|
|
|
|
uint8_t *buffer;
|
|
|
|
|
size_t read_len;
|
|
|
|
|
|
|
|
|
|
if (path == NULL || out_bytes == NULL || out_len == NULL) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
*out_bytes = NULL;
|
|
|
|
|
*out_len = 0u;
|
|
|
|
|
|
|
|
|
|
fp = fopen(path, "rb");
|
|
|
|
|
if (fp == NULL) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (fseek(fp, 0, SEEK_END) != 0) {
|
|
|
|
|
fclose(fp);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
size = ftell(fp);
|
|
|
|
|
if (size < 0) {
|
|
|
|
|
fclose(fp);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (fseek(fp, 0, SEEK_SET) != 0) {
|
|
|
|
|
fclose(fp);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buffer = (uint8_t *)malloc((size_t)size + 1u);
|
|
|
|
|
if (buffer == NULL) {
|
|
|
|
|
fclose(fp);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
read_len = fread(buffer, 1u, (size_t)size, fp);
|
|
|
|
|
fclose(fp);
|
|
|
|
|
if (read_len != (size_t)size) {
|
|
|
|
|
free(buffer);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
buffer[size] = '\0';
|
|
|
|
|
|
|
|
|
|
*out_bytes = buffer;
|
|
|
|
|
*out_len = (size_t)size;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool build_fixture_path(char *out, size_t out_len) {
|
|
|
|
|
const char *needle = "/tests/enc/";
|
|
|
|
|
const char *path = __FILE__;
|
|
|
|
|
const char *pos = strstr(path, needle);
|
|
|
|
|
size_t prefix_len;
|
|
|
|
|
|
|
|
|
|
if (pos == NULL) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
prefix_len = (size_t)(pos - path);
|
|
|
|
|
if (prefix_len + strlen("/tests/fixtures/conformance/"
|
|
|
|
|
"asl_tgk_exec_plan/artifacts/plan0.bin") + 1u >
|
|
|
|
|
out_len) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memcpy(out, path, prefix_len);
|
|
|
|
|
out[prefix_len] = '\0';
|
|
|
|
|
strcat(out, "/tests/fixtures/conformance/"
|
|
|
|
|
"asl_tgk_exec_plan/artifacts/plan0.bin");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-18 06:13:07 +01:00
|
|
|
static void store_u32_le(uint8_t *out, uint32_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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void fill_default_params(amduat_asl_tgk_exec_operator_params_t *params) {
|
|
|
|
|
memset(params, 0, sizeof(*params));
|
|
|
|
|
params->tgk_traversal.direction = 1u;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int test_round_trip(void) {
|
|
|
|
|
amduat_asl_tgk_exec_operator_def_t operators[2];
|
|
|
|
|
amduat_asl_tgk_exec_plan_t plan;
|
|
|
|
|
amduat_octets_t encoded;
|
|
|
|
|
amduat_asl_tgk_exec_plan_t decoded;
|
|
|
|
|
int exit_code = 1;
|
|
|
|
|
|
|
|
|
|
memset(operators, 0, sizeof(operators));
|
|
|
|
|
fill_default_params(&operators[0].params);
|
|
|
|
|
fill_default_params(&operators[1].params);
|
|
|
|
|
|
|
|
|
|
operators[0].op_id = 1u;
|
|
|
|
|
operators[0].op_type = AMDUAT_ASL_TGK_EXEC_OP_SEGMENT_SCAN;
|
|
|
|
|
operators[0].flags = AMDUAT_ASL_TGK_EXEC_OP_FLAG_NONE;
|
|
|
|
|
operators[0].snapshot.logseq_min = 0u;
|
|
|
|
|
operators[0].snapshot.logseq_max = 10u;
|
|
|
|
|
operators[0].input_count = 0u;
|
|
|
|
|
|
|
|
|
|
operators[1].op_id = 2u;
|
|
|
|
|
operators[1].op_type = AMDUAT_ASL_TGK_EXEC_OP_TGK_TRAVERSAL;
|
|
|
|
|
operators[1].flags = AMDUAT_ASL_TGK_EXEC_OP_FLAG_PARALLEL;
|
|
|
|
|
operators[1].snapshot.logseq_min = 1u;
|
|
|
|
|
operators[1].snapshot.logseq_max = 10u;
|
|
|
|
|
operators[1].params.tgk_traversal.direction = 2u;
|
|
|
|
|
operators[1].params.tgk_traversal.traversal_depth = 3u;
|
|
|
|
|
operators[1].params.tgk_traversal.start_node_id = 42u;
|
|
|
|
|
operators[1].input_count = 1u;
|
|
|
|
|
operators[1].inputs[0] = 1u;
|
|
|
|
|
|
|
|
|
|
plan.plan_version = AMDUAT_ASL_TGK_EXEC_PLAN_VERSION;
|
|
|
|
|
plan.operator_count = 2u;
|
|
|
|
|
plan.operators = operators;
|
|
|
|
|
|
|
|
|
|
if (!amduat_enc_asl_tgk_exec_plan_encode_v1(&plan, &encoded)) {
|
|
|
|
|
fprintf(stderr, "encode failed\n");
|
|
|
|
|
return exit_code;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!amduat_enc_asl_tgk_exec_plan_decode_v1(encoded, &decoded)) {
|
|
|
|
|
fprintf(stderr, "decode failed\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (decoded.plan_version != plan.plan_version ||
|
|
|
|
|
decoded.operator_count != plan.operator_count) {
|
|
|
|
|
fprintf(stderr, "header mismatch\n");
|
|
|
|
|
goto cleanup_decoded;
|
|
|
|
|
}
|
|
|
|
|
if (decoded.operators[1].op_type != operators[1].op_type ||
|
|
|
|
|
decoded.operators[1].params.tgk_traversal.direction != 2u ||
|
|
|
|
|
decoded.operators[1].params.tgk_traversal.traversal_depth != 3u ||
|
|
|
|
|
decoded.operators[1].params.tgk_traversal.start_node_id != 42u) {
|
|
|
|
|
fprintf(stderr, "operator fields mismatch\n");
|
|
|
|
|
goto cleanup_decoded;
|
|
|
|
|
}
|
|
|
|
|
if (decoded.operators[1].input_count != 1u ||
|
|
|
|
|
decoded.operators[1].inputs[0] != 1u) {
|
|
|
|
|
fprintf(stderr, "inputs mismatch\n");
|
|
|
|
|
goto cleanup_decoded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exit_code = 0;
|
|
|
|
|
|
|
|
|
|
cleanup_decoded:
|
|
|
|
|
amduat_enc_asl_tgk_exec_plan_free(&decoded);
|
|
|
|
|
cleanup:
|
|
|
|
|
free((void *)encoded.data);
|
|
|
|
|
return exit_code;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int test_invalid_input_ref(void) {
|
|
|
|
|
amduat_asl_tgk_exec_operator_def_t operators[1];
|
|
|
|
|
amduat_asl_tgk_exec_plan_t plan;
|
|
|
|
|
amduat_octets_t encoded;
|
|
|
|
|
int exit_code = 1;
|
|
|
|
|
size_t op_offset;
|
|
|
|
|
size_t input_offset;
|
|
|
|
|
|
|
|
|
|
memset(operators, 0, sizeof(operators));
|
|
|
|
|
fill_default_params(&operators[0].params);
|
|
|
|
|
operators[0].op_id = 1u;
|
|
|
|
|
operators[0].op_type = AMDUAT_ASL_TGK_EXEC_OP_SEGMENT_SCAN;
|
|
|
|
|
operators[0].input_count = 0u;
|
|
|
|
|
|
|
|
|
|
plan.plan_version = AMDUAT_ASL_TGK_EXEC_PLAN_VERSION;
|
|
|
|
|
plan.operator_count = 1u;
|
|
|
|
|
plan.operators = operators;
|
|
|
|
|
|
|
|
|
|
if (!amduat_enc_asl_tgk_exec_plan_encode_v1(&plan, &encoded)) {
|
|
|
|
|
fprintf(stderr, "encode failed\n");
|
|
|
|
|
return exit_code;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
op_offset = k_plan_header_size;
|
|
|
|
|
if (encoded.len < op_offset + k_input_offset + 4u) {
|
|
|
|
|
fprintf(stderr, "encoded buffer too small\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
store_u32_le((uint8_t *)encoded.data + op_offset + k_params_size + 28u, 1u);
|
|
|
|
|
input_offset = op_offset + k_input_offset;
|
|
|
|
|
store_u32_le((uint8_t *)encoded.data + input_offset, 99u);
|
|
|
|
|
|
|
|
|
|
if (amduat_enc_asl_tgk_exec_plan_decode_v1(encoded, &(amduat_asl_tgk_exec_plan_t){0})) {
|
|
|
|
|
fprintf(stderr, "decode unexpectedly succeeded\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exit_code = 0;
|
|
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
|
free((void *)encoded.data);
|
|
|
|
|
return exit_code;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int test_truncated_decode(void) {
|
|
|
|
|
amduat_asl_tgk_exec_operator_def_t operators[1];
|
|
|
|
|
amduat_asl_tgk_exec_plan_t plan;
|
|
|
|
|
amduat_octets_t encoded;
|
|
|
|
|
amduat_octets_t truncated;
|
|
|
|
|
int exit_code = 1;
|
|
|
|
|
|
|
|
|
|
memset(operators, 0, sizeof(operators));
|
|
|
|
|
fill_default_params(&operators[0].params);
|
|
|
|
|
operators[0].op_id = 1u;
|
|
|
|
|
operators[0].op_type = AMDUAT_ASL_TGK_EXEC_OP_SEGMENT_SCAN;
|
|
|
|
|
|
|
|
|
|
plan.plan_version = AMDUAT_ASL_TGK_EXEC_PLAN_VERSION;
|
|
|
|
|
plan.operator_count = 1u;
|
|
|
|
|
plan.operators = operators;
|
|
|
|
|
|
|
|
|
|
if (!amduat_enc_asl_tgk_exec_plan_encode_v1(&plan, &encoded)) {
|
|
|
|
|
fprintf(stderr, "encode failed\n");
|
|
|
|
|
return exit_code;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
truncated = amduat_octets(encoded.data, encoded.len - 1u);
|
|
|
|
|
if (amduat_enc_asl_tgk_exec_plan_decode_v1(truncated,
|
|
|
|
|
&(amduat_asl_tgk_exec_plan_t){0})) {
|
|
|
|
|
fprintf(stderr, "decode unexpectedly succeeded\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exit_code = 0;
|
|
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
|
free((void *)encoded.data);
|
|
|
|
|
return exit_code;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int test_invalid_input_count(void) {
|
|
|
|
|
amduat_asl_tgk_exec_operator_def_t operators[1];
|
|
|
|
|
amduat_asl_tgk_exec_plan_t plan;
|
|
|
|
|
amduat_octets_t encoded;
|
|
|
|
|
int exit_code = 1;
|
|
|
|
|
size_t op_offset;
|
|
|
|
|
size_t input_count_offset;
|
|
|
|
|
|
|
|
|
|
memset(operators, 0, sizeof(operators));
|
|
|
|
|
fill_default_params(&operators[0].params);
|
|
|
|
|
operators[0].op_id = 1u;
|
|
|
|
|
operators[0].op_type = AMDUAT_ASL_TGK_EXEC_OP_SEGMENT_SCAN;
|
|
|
|
|
|
|
|
|
|
plan.plan_version = AMDUAT_ASL_TGK_EXEC_PLAN_VERSION;
|
|
|
|
|
plan.operator_count = 1u;
|
|
|
|
|
plan.operators = operators;
|
|
|
|
|
|
|
|
|
|
if (!amduat_enc_asl_tgk_exec_plan_encode_v1(&plan, &encoded)) {
|
|
|
|
|
fprintf(stderr, "encode failed\n");
|
|
|
|
|
return exit_code;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
op_offset = k_plan_header_size;
|
|
|
|
|
input_count_offset = op_offset + k_params_size + 28u;
|
|
|
|
|
if (encoded.len < input_count_offset + 4u) {
|
|
|
|
|
fprintf(stderr, "encoded buffer too small\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
store_u32_le((uint8_t *)encoded.data + input_count_offset, 9u);
|
|
|
|
|
|
|
|
|
|
if (amduat_enc_asl_tgk_exec_plan_decode_v1(encoded,
|
|
|
|
|
&(amduat_asl_tgk_exec_plan_t){0})) {
|
|
|
|
|
fprintf(stderr, "decode unexpectedly succeeded\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exit_code = 0;
|
|
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
|
free((void *)encoded.data);
|
|
|
|
|
return exit_code;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-22 00:25:56 +01:00
|
|
|
static int test_invalid_snapshot_range(void) {
|
|
|
|
|
amduat_asl_tgk_exec_operator_def_t operators[1];
|
|
|
|
|
amduat_asl_tgk_exec_plan_t plan;
|
|
|
|
|
|
|
|
|
|
memset(operators, 0, sizeof(operators));
|
|
|
|
|
fill_default_params(&operators[0].params);
|
|
|
|
|
operators[0].op_id = 1u;
|
|
|
|
|
operators[0].op_type = AMDUAT_ASL_TGK_EXEC_OP_SEGMENT_SCAN;
|
|
|
|
|
operators[0].snapshot.logseq_min = 10u;
|
|
|
|
|
operators[0].snapshot.logseq_max = 1u;
|
|
|
|
|
|
|
|
|
|
plan.plan_version = AMDUAT_ASL_TGK_EXEC_PLAN_VERSION;
|
|
|
|
|
plan.operator_count = 1u;
|
|
|
|
|
plan.operators = operators;
|
|
|
|
|
|
|
|
|
|
if (amduat_enc_asl_tgk_exec_plan_encode_v1(&plan,
|
|
|
|
|
&(amduat_octets_t){0})) {
|
|
|
|
|
fprintf(stderr, "encode unexpectedly succeeded\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int test_fixture_plan0(void) {
|
|
|
|
|
char path[512];
|
|
|
|
|
uint8_t *bytes = NULL;
|
|
|
|
|
size_t len = 0u;
|
|
|
|
|
amduat_asl_tgk_exec_plan_t plan;
|
|
|
|
|
int exit_code = 1;
|
|
|
|
|
|
|
|
|
|
if (!build_fixture_path(path, sizeof(path))) {
|
|
|
|
|
fprintf(stderr, "fixture path build failed\n");
|
|
|
|
|
return exit_code;
|
|
|
|
|
}
|
|
|
|
|
if (!read_file(path, &bytes, &len)) {
|
|
|
|
|
fprintf(stderr, "fixture read failed: %s\n", path);
|
|
|
|
|
return exit_code;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!amduat_enc_asl_tgk_exec_plan_decode_v1(amduat_octets(bytes, len),
|
|
|
|
|
&plan)) {
|
|
|
|
|
fprintf(stderr, "fixture decode failed\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (plan.plan_version != AMDUAT_ASL_TGK_EXEC_PLAN_VERSION ||
|
|
|
|
|
plan.operator_count != 1u ||
|
|
|
|
|
plan.operators[0].op_type != AMDUAT_ASL_TGK_EXEC_OP_SEGMENT_SCAN ||
|
|
|
|
|
plan.operators[0].snapshot.logseq_max != 10u) {
|
|
|
|
|
fprintf(stderr, "fixture contents mismatch\n");
|
|
|
|
|
goto cleanup_plan;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exit_code = 0;
|
|
|
|
|
|
|
|
|
|
cleanup_plan:
|
|
|
|
|
amduat_enc_asl_tgk_exec_plan_free(&plan);
|
|
|
|
|
cleanup:
|
|
|
|
|
free(bytes);
|
|
|
|
|
return exit_code;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-18 06:13:07 +01:00
|
|
|
int main(void) {
|
|
|
|
|
if (test_round_trip() != 0) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
if (test_invalid_input_ref() != 0) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
if (test_truncated_decode() != 0) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2026-02-22 00:25:56 +01:00
|
|
|
if (test_invalid_input_count() != 0) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
if (test_invalid_snapshot_range() != 0) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return test_fixture_plan0();
|
2026-01-18 06:13:07 +01:00
|
|
|
}
|