diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ec2184..d10eb0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,6 +86,7 @@ set(AMDUAT_ENC_SRCS src/near_core/fer/receipt.c src/near_core/enc/asl_log.c src/near_core/enc/asl_core_index.c + src/near_core/enc/asl_tgk_exec_plan.c src/near_core/enc/pel_program_dag.c src/near_core/enc/pel_program_dag_desc.c src/near_core/enc/pel_trace_dag.c @@ -292,6 +293,17 @@ target_link_libraries(amduat_test_asl_core_index ) add_test(NAME asl_core_index COMMAND amduat_test_asl_core_index) +add_executable(amduat_test_asl_tgk_exec_plan + tests/enc/test_asl_tgk_exec_plan.c) +target_include_directories(amduat_test_asl_tgk_exec_plan + PRIVATE ${AMDUAT_INTERNAL_DIR} + PRIVATE ${AMDUAT_INCLUDE_DIR} +) +target_link_libraries(amduat_test_asl_tgk_exec_plan + PRIVATE amduat_enc amduat_hash_asl1 amduat_asl amduat_util +) +add_test(NAME asl_tgk_exec_plan COMMAND amduat_test_asl_tgk_exec_plan) + add_executable(amduat_test_tgk1_edge tests/enc/test_tgk1_edge.c) target_include_directories(amduat_test_tgk1_edge PRIVATE ${AMDUAT_INTERNAL_DIR} diff --git a/include/amduat/enc/asl_tgk_exec_plan.h b/include/amduat/enc/asl_tgk_exec_plan.h new file mode 100644 index 0000000..c344a58 --- /dev/null +++ b/include/amduat/enc/asl_tgk_exec_plan.h @@ -0,0 +1,115 @@ +#ifndef AMDUAT_ENC_ASL_TGK_EXEC_PLAN_H +#define AMDUAT_ENC_ASL_TGK_EXEC_PLAN_H + +#include "amduat/asl/core.h" + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum { AMDUAT_ASL_TGK_EXEC_PLAN_VERSION = 1 }; + +typedef enum { + AMDUAT_ASL_TGK_EXEC_OP_SEGMENT_SCAN = 0, + AMDUAT_ASL_TGK_EXEC_OP_INDEX_FILTER = 1, + AMDUAT_ASL_TGK_EXEC_OP_MERGE = 2, + AMDUAT_ASL_TGK_EXEC_OP_PROJECTION = 3, + AMDUAT_ASL_TGK_EXEC_OP_TGK_TRAVERSAL = 4, + AMDUAT_ASL_TGK_EXEC_OP_AGGREGATION = 5, + AMDUAT_ASL_TGK_EXEC_OP_LIMIT_OFFSET = 6, + AMDUAT_ASL_TGK_EXEC_OP_SHARD_DISPATCH = 7, + AMDUAT_ASL_TGK_EXEC_OP_SIMD_FILTER = 8, + AMDUAT_ASL_TGK_EXEC_OP_TOMBSTONE_SHADOW = 9 +} amduat_asl_tgk_exec_operator_type_t; + +typedef enum { + AMDUAT_ASL_TGK_EXEC_OP_FLAG_NONE = 0x00, + AMDUAT_ASL_TGK_EXEC_OP_FLAG_PARALLEL = 0x01, + AMDUAT_ASL_TGK_EXEC_OP_FLAG_OPTIONAL = 0x02 +} amduat_asl_tgk_exec_operator_flags_t; + +typedef struct { + uint64_t logseq_min; + uint64_t logseq_max; +} amduat_asl_tgk_exec_snapshot_range_t; + +typedef struct { + struct { + uint8_t is_asl_segment; + uint64_t segment_start_id; + uint64_t segment_end_id; + } segment_scan; + + struct { + uint32_t artifact_type_tag; + uint8_t has_type_tag; + uint32_t edge_type_key; + uint8_t has_edge_type; + uint8_t role; + } index_filter; + + struct { + uint8_t deterministic; + } merge; + + struct { + uint8_t project_artifact_id; + uint8_t project_tgk_edge_id; + uint8_t project_node_id; + uint8_t project_type_tag; + } projection; + + struct { + uint64_t start_node_id; + uint32_t traversal_depth; + uint8_t direction; + } tgk_traversal; + + struct { + uint8_t agg_count; + uint8_t agg_union; + uint8_t agg_sum; + } aggregation; + + struct { + uint64_t limit; + uint64_t offset; + } limit_offset; +} amduat_asl_tgk_exec_operator_params_t; + +typedef struct { + uint32_t op_id; + amduat_asl_tgk_exec_operator_type_t op_type; + amduat_asl_tgk_exec_operator_flags_t flags; + amduat_asl_tgk_exec_snapshot_range_t snapshot; + amduat_asl_tgk_exec_operator_params_t params; + uint32_t input_count; + uint32_t inputs[8]; +} amduat_asl_tgk_exec_operator_def_t; + +typedef struct { + uint32_t plan_version; + uint32_t operator_count; + amduat_asl_tgk_exec_operator_def_t *operators; +} amduat_asl_tgk_exec_plan_t; + +bool amduat_enc_asl_tgk_exec_plan_encode_v1( + const amduat_asl_tgk_exec_plan_t *plan, + amduat_octets_t *out_bytes); + +bool amduat_enc_asl_tgk_exec_plan_decode_v1( + amduat_octets_t bytes, + amduat_asl_tgk_exec_plan_t *out_plan); + +void amduat_enc_asl_tgk_exec_plan_free( + amduat_asl_tgk_exec_plan_t *plan); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* AMDUAT_ENC_ASL_TGK_EXEC_PLAN_H */ diff --git a/src/near_core/asl/index_bloom.c b/src/near_core/asl/index_bloom.c index 4ed979c..8eb555a 100644 --- a/src/near_core/asl/index_bloom.c +++ b/src/near_core/asl/index_bloom.c @@ -44,12 +44,12 @@ static bool amduat_asl_index_bloom_hash_pair(amduat_hash_id_t hash_id, header[2] = (uint8_t)(digest_len & 0xffu); header[3] = (uint8_t)((digest_len >> 8) & 0xffu); - hash = 1469598103934665603ull ^ 0x9e3779b97f4a7c15ull; + hash = 14695981039346656037ull ^ 0x9e3779b97f4a7c15ull; hash = amduat_asl_index_bloom_hash_update(hash, header, sizeof(header)); hash = amduat_asl_index_bloom_hash_update(hash, digest.data, digest.len); *out_h1 = hash; - hash = 1469598103934665603ull ^ 0xbf58476d1ce4e5b9ull; + hash = 14695981039346656037ull ^ 0xbf58476d1ce4e5b9ull; hash = amduat_asl_index_bloom_hash_update(hash, header, sizeof(header)); hash = amduat_asl_index_bloom_hash_update(hash, digest.data, digest.len); if (hash == 0u) { diff --git a/src/near_core/enc/asl_tgk_exec_plan.c b/src/near_core/enc/asl_tgk_exec_plan.c new file mode 100644 index 0000000..4a99b69 --- /dev/null +++ b/src/near_core/enc/asl_tgk_exec_plan.c @@ -0,0 +1,494 @@ +#include "amduat/enc/asl_tgk_exec_plan.h" + +#include +#include +#include + +enum { + AMDUAT_ASL_TGK_EXEC_PLAN_HEADER_SIZE = 8, + AMDUAT_ASL_TGK_EXEC_PLAN_INPUT_CAP = 8, + AMDUAT_ASL_TGK_EXEC_PLAN_PARAMS_SIZE = 65, + AMDUAT_ASL_TGK_EXEC_PLAN_OPERATOR_SIZE = 129, + AMDUAT_ASL_TGK_EXEC_PLAN_OP_TYPE_MAX = + AMDUAT_ASL_TGK_EXEC_OP_TOMBSTONE_SHADOW, + AMDUAT_ASL_TGK_EXEC_PLAN_OP_FLAG_MASK = + AMDUAT_ASL_TGK_EXEC_OP_FLAG_PARALLEL | + AMDUAT_ASL_TGK_EXEC_OP_FLAG_OPTIONAL +}; + +typedef struct { + const uint8_t *data; + size_t len; + size_t offset; +} amduat_asl_tgk_exec_plan_cursor_t; + +static void amduat_asl_tgk_exec_plan_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 amduat_asl_tgk_exec_plan_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 bool amduat_asl_tgk_exec_plan_read_u32_le( + amduat_asl_tgk_exec_plan_cursor_t *cur, + uint32_t *out) { + const uint8_t *data; + + if (cur->len - cur->offset < 4u) { + return false; + } + data = cur->data + cur->offset; + *out = (uint32_t)data[0] | ((uint32_t)data[1] << 8) | + ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24); + cur->offset += 4u; + return true; +} + +static bool amduat_asl_tgk_exec_plan_read_u64_le( + amduat_asl_tgk_exec_plan_cursor_t *cur, + uint64_t *out) { + const uint8_t *data; + + if (cur->len - cur->offset < 8u) { + return false; + } + data = cur->data + cur->offset; + *out = (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); + cur->offset += 8u; + return true; +} + +static bool amduat_asl_tgk_exec_plan_add_size(size_t *acc, size_t add) { + if (*acc > SIZE_MAX - add) { + return false; + } + *acc += add; + return true; +} + +static bool amduat_asl_tgk_exec_plan_is_bool(uint8_t value) { + return value == 0u || value == 1u; +} + +static bool amduat_asl_tgk_exec_plan_validate_params( + amduat_asl_tgk_exec_operator_type_t op_type, + const amduat_asl_tgk_exec_operator_params_t *params) { + if (!amduat_asl_tgk_exec_plan_is_bool(params->segment_scan.is_asl_segment)) { + return false; + } + if (!amduat_asl_tgk_exec_plan_is_bool(params->index_filter.has_type_tag)) { + return false; + } + if (!amduat_asl_tgk_exec_plan_is_bool(params->index_filter.has_edge_type)) { + return false; + } + if (params->index_filter.role > 3u) { + return false; + } + if (!amduat_asl_tgk_exec_plan_is_bool(params->merge.deterministic)) { + return false; + } + if (!amduat_asl_tgk_exec_plan_is_bool( + params->projection.project_artifact_id) || + !amduat_asl_tgk_exec_plan_is_bool( + params->projection.project_tgk_edge_id) || + !amduat_asl_tgk_exec_plan_is_bool(params->projection.project_node_id) || + !amduat_asl_tgk_exec_plan_is_bool(params->projection.project_type_tag)) { + return false; + } + if (params->tgk_traversal.direction > 3u) { + return false; + } + if (op_type == AMDUAT_ASL_TGK_EXEC_OP_TGK_TRAVERSAL && + params->tgk_traversal.direction == 0u) { + return false; + } + if (!amduat_asl_tgk_exec_plan_is_bool(params->aggregation.agg_count) || + !amduat_asl_tgk_exec_plan_is_bool(params->aggregation.agg_union) || + !amduat_asl_tgk_exec_plan_is_bool(params->aggregation.agg_sum)) { + return false; + } + return true; +} + +static bool amduat_asl_tgk_exec_plan_has_op_id( + const amduat_asl_tgk_exec_plan_t *plan, + uint32_t op_id) { + uint32_t i; + + for (i = 0; i < plan->operator_count; ++i) { + if (plan->operators[i].op_id == op_id) { + return true; + } + } + return false; +} + +static bool amduat_asl_tgk_exec_plan_unique_op_ids( + const amduat_asl_tgk_exec_plan_t *plan) { + uint32_t i; + uint32_t j; + + for (i = 0; i < plan->operator_count; ++i) { + for (j = i + 1u; j < plan->operator_count; ++j) { + if (plan->operators[i].op_id == plan->operators[j].op_id) { + return false; + } + } + } + return true; +} + +static bool amduat_asl_tgk_exec_plan_validate_operator( + const amduat_asl_tgk_exec_plan_t *plan, + const amduat_asl_tgk_exec_operator_def_t *op) { + uint32_t i; + + if ((uint32_t)op->op_type > AMDUAT_ASL_TGK_EXEC_PLAN_OP_TYPE_MAX) { + return false; + } + if (((uint32_t)op->flags & ~AMDUAT_ASL_TGK_EXEC_PLAN_OP_FLAG_MASK) != 0u) { + return false; + } + if (op->input_count > AMDUAT_ASL_TGK_EXEC_PLAN_INPUT_CAP) { + return false; + } + if (!amduat_asl_tgk_exec_plan_validate_params(op->op_type, &op->params)) { + return false; + } + for (i = 0; i < op->input_count; ++i) { + if (!amduat_asl_tgk_exec_plan_has_op_id(plan, op->inputs[i])) { + return false; + } + } + return true; +} + +static void amduat_asl_tgk_exec_plan_encode_params( + const amduat_asl_tgk_exec_operator_params_t *params, + uint8_t *buffer, + size_t *offset) { + buffer[(*offset)++] = params->segment_scan.is_asl_segment; + amduat_asl_tgk_exec_plan_store_u64_le( + buffer + *offset, params->segment_scan.segment_start_id); + *offset += 8u; + amduat_asl_tgk_exec_plan_store_u64_le( + buffer + *offset, params->segment_scan.segment_end_id); + *offset += 8u; + + amduat_asl_tgk_exec_plan_store_u32_le( + buffer + *offset, params->index_filter.artifact_type_tag); + *offset += 4u; + buffer[(*offset)++] = params->index_filter.has_type_tag; + amduat_asl_tgk_exec_plan_store_u32_le( + buffer + *offset, params->index_filter.edge_type_key); + *offset += 4u; + buffer[(*offset)++] = params->index_filter.has_edge_type; + buffer[(*offset)++] = params->index_filter.role; + + buffer[(*offset)++] = params->merge.deterministic; + + buffer[(*offset)++] = params->projection.project_artifact_id; + buffer[(*offset)++] = params->projection.project_tgk_edge_id; + buffer[(*offset)++] = params->projection.project_node_id; + buffer[(*offset)++] = params->projection.project_type_tag; + + amduat_asl_tgk_exec_plan_store_u64_le( + buffer + *offset, params->tgk_traversal.start_node_id); + *offset += 8u; + amduat_asl_tgk_exec_plan_store_u32_le( + buffer + *offset, params->tgk_traversal.traversal_depth); + *offset += 4u; + buffer[(*offset)++] = params->tgk_traversal.direction; + + buffer[(*offset)++] = params->aggregation.agg_count; + buffer[(*offset)++] = params->aggregation.agg_union; + buffer[(*offset)++] = params->aggregation.agg_sum; + + amduat_asl_tgk_exec_plan_store_u64_le(buffer + *offset, + params->limit_offset.limit); + *offset += 8u; + amduat_asl_tgk_exec_plan_store_u64_le(buffer + *offset, + params->limit_offset.offset); + *offset += 8u; +} + +static bool amduat_asl_tgk_exec_plan_decode_params( + amduat_asl_tgk_exec_plan_cursor_t *cur, + amduat_asl_tgk_exec_operator_params_t *params) { + if (cur->len - cur->offset < AMDUAT_ASL_TGK_EXEC_PLAN_PARAMS_SIZE) { + return false; + } + + params->segment_scan.is_asl_segment = cur->data[cur->offset++]; + if (!amduat_asl_tgk_exec_plan_read_u64_le( + cur, ¶ms->segment_scan.segment_start_id) || + !amduat_asl_tgk_exec_plan_read_u64_le( + cur, ¶ms->segment_scan.segment_end_id)) { + return false; + } + + if (!amduat_asl_tgk_exec_plan_read_u32_le( + cur, ¶ms->index_filter.artifact_type_tag)) { + return false; + } + params->index_filter.has_type_tag = cur->data[cur->offset++]; + if (!amduat_asl_tgk_exec_plan_read_u32_le( + cur, ¶ms->index_filter.edge_type_key)) { + return false; + } + params->index_filter.has_edge_type = cur->data[cur->offset++]; + params->index_filter.role = cur->data[cur->offset++]; + + params->merge.deterministic = cur->data[cur->offset++]; + + params->projection.project_artifact_id = cur->data[cur->offset++]; + params->projection.project_tgk_edge_id = cur->data[cur->offset++]; + params->projection.project_node_id = cur->data[cur->offset++]; + params->projection.project_type_tag = cur->data[cur->offset++]; + + if (!amduat_asl_tgk_exec_plan_read_u64_le( + cur, ¶ms->tgk_traversal.start_node_id) || + !amduat_asl_tgk_exec_plan_read_u32_le( + cur, ¶ms->tgk_traversal.traversal_depth)) { + return false; + } + params->tgk_traversal.direction = cur->data[cur->offset++]; + + params->aggregation.agg_count = cur->data[cur->offset++]; + params->aggregation.agg_union = cur->data[cur->offset++]; + params->aggregation.agg_sum = cur->data[cur->offset++]; + + if (!amduat_asl_tgk_exec_plan_read_u64_le( + cur, ¶ms->limit_offset.limit) || + !amduat_asl_tgk_exec_plan_read_u64_le( + cur, ¶ms->limit_offset.offset)) { + return false; + } + + return true; +} + +bool amduat_enc_asl_tgk_exec_plan_encode_v1( + const amduat_asl_tgk_exec_plan_t *plan, + amduat_octets_t *out_bytes) { + size_t total_len; + size_t offset; + uint8_t *buffer; + uint32_t i; + + if (out_bytes == NULL) { + return false; + } + out_bytes->data = NULL; + out_bytes->len = 0u; + + if (plan == NULL) { + return false; + } + if (plan->operator_count != 0u && plan->operators == NULL) { + return false; + } + if (plan->operator_count > SIZE_MAX / AMDUAT_ASL_TGK_EXEC_PLAN_OPERATOR_SIZE) { + return false; + } + + total_len = AMDUAT_ASL_TGK_EXEC_PLAN_HEADER_SIZE; + if (!amduat_asl_tgk_exec_plan_add_size( + &total_len, + (size_t)plan->operator_count * + AMDUAT_ASL_TGK_EXEC_PLAN_OPERATOR_SIZE)) { + return false; + } + + if (plan->operator_count > UINT32_MAX) { + return false; + } + + for (i = 0; i < plan->operator_count; ++i) { + if (!amduat_asl_tgk_exec_plan_validate_operator(plan, + &plan->operators[i])) { + return false; + } + } + if (!amduat_asl_tgk_exec_plan_unique_op_ids(plan)) { + return false; + } + + buffer = (uint8_t *)malloc(total_len); + if (buffer == NULL) { + return false; + } + + offset = 0u; + amduat_asl_tgk_exec_plan_store_u32_le(buffer + offset, plan->plan_version); + offset += 4u; + amduat_asl_tgk_exec_plan_store_u32_le(buffer + offset, + plan->operator_count); + offset += 4u; + + for (i = 0; i < plan->operator_count; ++i) { + const amduat_asl_tgk_exec_operator_def_t *op = &plan->operators[i]; + uint32_t j; + + amduat_asl_tgk_exec_plan_store_u32_le(buffer + offset, op->op_id); + offset += 4u; + amduat_asl_tgk_exec_plan_store_u32_le(buffer + offset, + (uint32_t)op->op_type); + offset += 4u; + amduat_asl_tgk_exec_plan_store_u32_le(buffer + offset, + (uint32_t)op->flags); + offset += 4u; + amduat_asl_tgk_exec_plan_store_u64_le(buffer + offset, + op->snapshot.logseq_min); + offset += 8u; + amduat_asl_tgk_exec_plan_store_u64_le(buffer + offset, + op->snapshot.logseq_max); + offset += 8u; + + amduat_asl_tgk_exec_plan_encode_params(&op->params, buffer, &offset); + + amduat_asl_tgk_exec_plan_store_u32_le(buffer + offset, op->input_count); + offset += 4u; + for (j = 0; j < AMDUAT_ASL_TGK_EXEC_PLAN_INPUT_CAP; ++j) { + amduat_asl_tgk_exec_plan_store_u32_le(buffer + offset, op->inputs[j]); + offset += 4u; + } + } + + out_bytes->data = buffer; + out_bytes->len = total_len; + return true; +} + +bool amduat_enc_asl_tgk_exec_plan_decode_v1( + amduat_octets_t bytes, + amduat_asl_tgk_exec_plan_t *out_plan) { + amduat_asl_tgk_exec_plan_cursor_t cur; + uint32_t operator_count; + size_t expected_len; + amduat_asl_tgk_exec_operator_def_t *operators; + uint32_t i; + + if (out_plan == NULL) { + return false; + } + out_plan->plan_version = 0u; + out_plan->operator_count = 0u; + out_plan->operators = NULL; + + if (bytes.len < AMDUAT_ASL_TGK_EXEC_PLAN_HEADER_SIZE || + bytes.data == NULL) { + return false; + } + + cur.data = bytes.data; + cur.len = bytes.len; + cur.offset = 0u; + + if (!amduat_asl_tgk_exec_plan_read_u32_le(&cur, &out_plan->plan_version) || + !amduat_asl_tgk_exec_plan_read_u32_le(&cur, &operator_count)) { + return false; + } + + if (operator_count > SIZE_MAX / AMDUAT_ASL_TGK_EXEC_PLAN_OPERATOR_SIZE) { + return false; + } + + expected_len = AMDUAT_ASL_TGK_EXEC_PLAN_HEADER_SIZE + + (size_t)operator_count * + AMDUAT_ASL_TGK_EXEC_PLAN_OPERATOR_SIZE; + if (expected_len != bytes.len) { + return false; + } + + if (operator_count == 0u) { + out_plan->operator_count = 0u; + return true; + } + + operators = (amduat_asl_tgk_exec_operator_def_t *)calloc( + operator_count, sizeof(*operators)); + if (operators == NULL) { + return false; + } + + for (i = 0; i < operator_count; ++i) { + amduat_asl_tgk_exec_operator_def_t *op = &operators[i]; + uint32_t j; + uint32_t op_type_raw; + uint32_t flags_raw; + + if (!amduat_asl_tgk_exec_plan_read_u32_le(&cur, &op->op_id) || + !amduat_asl_tgk_exec_plan_read_u32_le(&cur, &op_type_raw) || + !amduat_asl_tgk_exec_plan_read_u32_le(&cur, &flags_raw) || + !amduat_asl_tgk_exec_plan_read_u64_le(&cur, + &op->snapshot.logseq_min) || + !amduat_asl_tgk_exec_plan_read_u64_le(&cur, + &op->snapshot.logseq_max)) { + free(operators); + return false; + } + op->op_type = (amduat_asl_tgk_exec_operator_type_t)op_type_raw; + op->flags = (amduat_asl_tgk_exec_operator_flags_t)flags_raw; + + if (!amduat_asl_tgk_exec_plan_decode_params(&cur, &op->params) || + !amduat_asl_tgk_exec_plan_read_u32_le(&cur, &op->input_count)) { + free(operators); + return false; + } + if (op->input_count > AMDUAT_ASL_TGK_EXEC_PLAN_INPUT_CAP) { + free(operators); + return false; + } + for (j = 0; j < AMDUAT_ASL_TGK_EXEC_PLAN_INPUT_CAP; ++j) { + if (!amduat_asl_tgk_exec_plan_read_u32_le(&cur, &op->inputs[j])) { + free(operators); + return false; + } + } + } + + out_plan->operator_count = operator_count; + out_plan->operators = operators; + + if (!amduat_asl_tgk_exec_plan_unique_op_ids(out_plan)) { + amduat_enc_asl_tgk_exec_plan_free(out_plan); + return false; + } + for (i = 0; i < operator_count; ++i) { + if (!amduat_asl_tgk_exec_plan_validate_operator(out_plan, + &operators[i])) { + amduat_enc_asl_tgk_exec_plan_free(out_plan); + return false; + } + } + + return true; +} + +void amduat_enc_asl_tgk_exec_plan_free(amduat_asl_tgk_exec_plan_t *plan) { + if (plan == NULL) { + return; + } + free(plan->operators); + plan->operators = NULL; + plan->operator_count = 0u; + plan->plan_version = 0u; +} diff --git a/tests/enc/test_asl_tgk_exec_plan.c b/tests/enc/test_asl_tgk_exec_plan.c new file mode 100644 index 0000000..6798b3f --- /dev/null +++ b/tests/enc/test_asl_tgk_exec_plan.c @@ -0,0 +1,231 @@ +#include "amduat/enc/asl_tgk_exec_plan.h" + +#include +#include +#include +#include +#include + +enum { + k_plan_header_size = 8, + k_params_size = 65, + k_operator_size = 129, + k_input_offset = 97 +}; + +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; +} + +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; + } + return test_invalid_input_count(); +}