Add TGK exec plan encoding

This commit is contained in:
Carl Niklas Rydberg 2026-01-18 06:13:07 +01:00
parent dde532d68f
commit 7878cd3702
5 changed files with 854 additions and 2 deletions

View file

@ -86,6 +86,7 @@ set(AMDUAT_ENC_SRCS
src/near_core/fer/receipt.c src/near_core/fer/receipt.c
src/near_core/enc/asl_log.c src/near_core/enc/asl_log.c
src/near_core/enc/asl_core_index.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.c
src/near_core/enc/pel_program_dag_desc.c src/near_core/enc/pel_program_dag_desc.c
src/near_core/enc/pel_trace_dag.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_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) add_executable(amduat_test_tgk1_edge tests/enc/test_tgk1_edge.c)
target_include_directories(amduat_test_tgk1_edge target_include_directories(amduat_test_tgk1_edge
PRIVATE ${AMDUAT_INTERNAL_DIR} PRIVATE ${AMDUAT_INTERNAL_DIR}

View file

@ -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 <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#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 */

View file

@ -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[2] = (uint8_t)(digest_len & 0xffu);
header[3] = (uint8_t)((digest_len >> 8) & 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, header, sizeof(header));
hash = amduat_asl_index_bloom_hash_update(hash, digest.data, digest.len); hash = amduat_asl_index_bloom_hash_update(hash, digest.data, digest.len);
*out_h1 = hash; *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, header, sizeof(header));
hash = amduat_asl_index_bloom_hash_update(hash, digest.data, digest.len); hash = amduat_asl_index_bloom_hash_update(hash, digest.data, digest.len);
if (hash == 0u) { if (hash == 0u) {

View file

@ -0,0 +1,494 @@
#include "amduat/enc/asl_tgk_exec_plan.h"
#include <limits.h>
#include <stdlib.h>
#include <string.h>
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, &params->segment_scan.segment_start_id) ||
!amduat_asl_tgk_exec_plan_read_u64_le(
cur, &params->segment_scan.segment_end_id)) {
return false;
}
if (!amduat_asl_tgk_exec_plan_read_u32_le(
cur, &params->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, &params->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, &params->tgk_traversal.start_node_id) ||
!amduat_asl_tgk_exec_plan_read_u32_le(
cur, &params->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, &params->limit_offset.limit) ||
!amduat_asl_tgk_exec_plan_read_u64_le(
cur, &params->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;
}

View file

@ -0,0 +1,231 @@
#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
};
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();
}