Implement TGK store API and in-memory adapter with tests

This commit is contained in:
Carl Niklas Rydberg 2025-12-21 08:44:04 +01:00
parent d7ac826407
commit 8b2a5f830a
6 changed files with 1488 additions and 1 deletions

View file

@ -241,6 +241,16 @@ target_link_libraries(amduat_test_tgk1_edge
)
add_test(NAME tgk1_edge COMMAND amduat_test_tgk1_edge)
add_executable(amduat_test_tgk_store_mem tests/tgk/test_tgk_store_mem.c)
target_include_directories(amduat_test_tgk_store_mem
PRIVATE ${AMDUAT_INTERNAL_DIR}
PRIVATE ${AMDUAT_INCLUDE_DIR}
)
target_link_libraries(amduat_test_tgk_store_mem
PRIVATE amduat_tgk_store_mem
)
add_test(NAME tgk_store_mem COMMAND amduat_test_tgk_store_mem)
add_executable(amduat_test_pel_program_dag_exec
tests/pel/test_pel_program_dag_exec.c)
target_include_directories(amduat_test_pel_program_dag_exec

View file

@ -0,0 +1,170 @@
#ifndef AMDUAT_TGK_STORE_H
#define AMDUAT_TGK_STORE_H
#include "amduat/asl/core.h"
#include "amduat/asl/store.h"
#include "amduat/tgk/core.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
GS_ERR_NOT_EDGE = 1,
GS_ERR_ARTIFACT_ERROR = 2,
GS_ERR_UNSUPPORTED = 3,
GS_ERR_INTEGRITY = 4
} amduat_tgk_graph_error_t;
typedef struct {
amduat_tgk_edge_type_id_t *types;
size_t types_len;
} amduat_tgk_edge_type_filter_t;
typedef enum {
AMDUAT_TGK_GRAPH_DIR_OUT = 1,
AMDUAT_TGK_GRAPH_DIR_IN = 2,
AMDUAT_TGK_GRAPH_DIR_BOTH = 3
} amduat_tgk_graph_direction_t;
typedef struct {
amduat_reference_t edge_ref;
amduat_tgk_edge_body_t body;
} amduat_tgk_graph_edge_view_t;
typedef struct {
amduat_tgk_graph_edge_view_t *edges;
size_t len;
} amduat_tgk_graph_edge_view_list_t;
typedef struct {
amduat_reference_t *nodes;
size_t len;
} amduat_tgk_node_list_t;
typedef struct {
amduat_tgk_graph_edge_view_list_t edges;
amduat_octets_t next_page_token;
bool has_next_page;
} amduat_tgk_graph_scan_result_t;
typedef struct {
amduat_asl_encoding_profile_id_t encoding_profile;
amduat_hash_id_t hash_id;
} amduat_tgk_identity_domain_t;
typedef struct {
amduat_tgk_identity_domain_t *domains;
size_t domains_len;
} amduat_tgk_id_space_config_t;
typedef struct {
amduat_octets_t description;
} amduat_tgk_artifact_scope_t;
typedef struct {
uint32_t *edge_tags;
size_t edge_tags_len;
amduat_tgk_edge_type_id_t *edge_types;
size_t edge_types_len;
amduat_asl_encoding_profile_id_t *encodings;
size_t encodings_len;
} amduat_tgk_profile_set_t;
typedef struct {
amduat_tgk_id_space_config_t id_space;
amduat_tgk_artifact_scope_t artifact_scope;
amduat_tgk_profile_set_t tgk_profiles;
} amduat_tgk_store_config_t;
typedef struct {
bool (*get_config)(void *ctx, amduat_tgk_store_config_t *out_config);
amduat_tgk_graph_error_t (*resolve_edge)(void *ctx,
amduat_reference_t ref,
amduat_tgk_edge_body_t *out_body);
bool (*edges_from)(void *ctx,
amduat_reference_t node,
amduat_tgk_edge_type_filter_t type_filter,
amduat_tgk_graph_edge_view_list_t *out_edges);
bool (*edges_to)(void *ctx,
amduat_reference_t node,
amduat_tgk_edge_type_filter_t type_filter,
amduat_tgk_graph_edge_view_list_t *out_edges);
bool (*edges_incident)(void *ctx,
amduat_reference_t node,
amduat_tgk_edge_type_filter_t type_filter,
amduat_tgk_graph_edge_view_list_t *out_edges);
bool (*scan_edges)(void *ctx,
amduat_tgk_edge_type_filter_t type_filter,
amduat_octets_t page_token,
bool has_page_token,
amduat_tgk_graph_scan_result_t *out_scan);
bool (*neighbors)(void *ctx,
amduat_reference_t node,
amduat_tgk_edge_type_filter_t type_filter,
amduat_tgk_graph_direction_t direction,
amduat_tgk_node_list_t *out_nodes);
} amduat_tgk_store_ops_t;
typedef struct {
amduat_tgk_store_config_t config;
amduat_tgk_store_ops_t ops;
void *ctx;
} amduat_tgk_store_t;
void amduat_tgk_store_init(amduat_tgk_store_t *store,
amduat_tgk_store_config_t config,
amduat_tgk_store_ops_t ops,
void *ctx);
bool amduat_tgk_store_get_config(amduat_tgk_store_t *store,
amduat_tgk_store_config_t *out_config);
amduat_tgk_graph_error_t amduat_tgk_store_resolve_edge(
amduat_tgk_store_t *store,
amduat_reference_t ref,
amduat_tgk_edge_body_t *out_body);
bool amduat_tgk_store_edges_from(amduat_tgk_store_t *store,
amduat_reference_t node,
amduat_tgk_edge_type_filter_t type_filter,
amduat_tgk_graph_edge_view_list_t *out_edges);
bool amduat_tgk_store_edges_to(amduat_tgk_store_t *store,
amduat_reference_t node,
amduat_tgk_edge_type_filter_t type_filter,
amduat_tgk_graph_edge_view_list_t *out_edges);
bool amduat_tgk_store_edges_incident(
amduat_tgk_store_t *store,
amduat_reference_t node,
amduat_tgk_edge_type_filter_t type_filter,
amduat_tgk_graph_edge_view_list_t *out_edges);
bool amduat_tgk_store_scan_edges(amduat_tgk_store_t *store,
amduat_tgk_edge_type_filter_t type_filter,
amduat_octets_t page_token,
bool has_page_token,
amduat_tgk_graph_scan_result_t *out_scan);
bool amduat_tgk_store_neighbors(amduat_tgk_store_t *store,
amduat_reference_t node,
amduat_tgk_edge_type_filter_t type_filter,
amduat_tgk_graph_direction_t direction,
amduat_tgk_node_list_t *out_nodes);
/* Query results are owned by the caller; free with helpers below. */
void amduat_tgk_graph_edge_view_list_free(
amduat_tgk_graph_edge_view_list_t *list);
void amduat_tgk_node_list_free(amduat_tgk_node_list_t *list);
void amduat_tgk_graph_scan_result_free(amduat_tgk_graph_scan_result_t *scan);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUAT_TGK_STORE_H */

View file

@ -3,4 +3,40 @@
/* In-memory TGK store adapter public API. */
#include "amduat/tgk/store.h"
#include <stdbool.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
amduat_reference_t ref;
amduat_artifact_t artifact;
} amduat_tgk_store_mem_artifact_t;
typedef struct {
amduat_tgk_store_config_t config;
const amduat_tgk_store_mem_artifact_t *artifacts;
size_t artifacts_len;
amduat_tgk_graph_edge_view_t *edges;
size_t edges_len;
} amduat_tgk_store_mem_t;
/* Artifacts are borrowed; decoded edges are owned by the store. */
bool amduat_tgk_store_mem_init(amduat_tgk_store_mem_t *mem,
amduat_tgk_store_config_t config,
const amduat_tgk_store_mem_artifact_t *artifacts,
size_t artifacts_len);
void amduat_tgk_store_mem_free(amduat_tgk_store_mem_t *mem);
amduat_tgk_store_ops_t amduat_tgk_store_mem_ops(void);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUAT_TGK_TGK_STORE_MEM_H */

View file

@ -1,3 +1,695 @@
#include "amduat/tgk/tgk_store_mem.h"
/* TODO: implement in-memory TGK store adapter. */
#include "amduat/enc/tgk1_edge.h"
#include <stdlib.h>
#include <string.h>
static bool amduat_tgk_store_mem_hash_id_supported(
const amduat_tgk_store_mem_t *mem,
amduat_hash_id_t hash_id) {
size_t i;
if (mem == NULL) {
return false;
}
for (i = 0; i < mem->config.id_space.domains_len; ++i) {
if (mem->config.id_space.domains[i].hash_id == hash_id) {
return true;
}
}
return false;
}
static bool amduat_tgk_store_mem_edge_tag_supported(
const amduat_tgk_store_mem_t *mem,
uint32_t tag_id) {
size_t i;
if (mem == NULL) {
return false;
}
for (i = 0; i < mem->config.tgk_profiles.edge_tags_len; ++i) {
if (mem->config.tgk_profiles.edge_tags[i] == tag_id) {
return true;
}
}
return false;
}
static bool amduat_tgk_store_mem_edge_type_supported(
const amduat_tgk_store_mem_t *mem,
amduat_tgk_edge_type_id_t type_id) {
size_t i;
if (mem == NULL) {
return false;
}
for (i = 0; i < mem->config.tgk_profiles.edge_types_len; ++i) {
if (mem->config.tgk_profiles.edge_types[i] == type_id) {
return true;
}
}
return false;
}
static bool amduat_tgk_store_mem_encoding_supported(
const amduat_tgk_store_mem_t *mem,
amduat_asl_encoding_profile_id_t profile_id) {
size_t i;
if (mem == NULL) {
return false;
}
for (i = 0; i < mem->config.tgk_profiles.encodings_len; ++i) {
if (mem->config.tgk_profiles.encodings[i] == profile_id) {
return true;
}
}
return false;
}
static bool amduat_tgk_store_mem_type_filter_match(
amduat_tgk_edge_type_filter_t filter,
amduat_tgk_edge_type_id_t type_id) {
size_t i;
if (filter.types_len == 0 || filter.types == NULL) {
return true;
}
for (i = 0; i < filter.types_len; ++i) {
if (filter.types[i] == type_id) {
return true;
}
}
return false;
}
static bool amduat_tgk_store_mem_node_in_list(
amduat_reference_t node,
const amduat_reference_t *list,
size_t len) {
size_t i;
for (i = 0; i < len; ++i) {
if (amduat_reference_eq(node, list[i])) {
return true;
}
}
return false;
}
static int amduat_tgk_store_mem_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 && a.data != NULL && b.data != NULL) {
cmp = memcmp(a.data, b.data, min_len);
if (cmp != 0) {
return cmp;
}
} else if (min_len != 0) {
return (a.data == NULL) ? -1 : 1;
}
if (a.len < b.len) {
return -1;
}
if (a.len > b.len) {
return 1;
}
return 0;
}
static int amduat_tgk_store_mem_ref_cmp(amduat_reference_t a,
amduat_reference_t b) {
if (a.hash_id < b.hash_id) {
return -1;
}
if (a.hash_id > b.hash_id) {
return 1;
}
return amduat_tgk_store_mem_octets_cmp(a.digest, b.digest);
}
static int amduat_tgk_store_mem_ref_ptr_cmp(const void *a, const void *b) {
const amduat_reference_t *ref_a = (const amduat_reference_t *)a;
const amduat_reference_t *ref_b = (const amduat_reference_t *)b;
return amduat_tgk_store_mem_ref_cmp(*ref_a, *ref_b);
}
static int amduat_tgk_store_mem_edge_cmp(const void *a, const void *b) {
const amduat_tgk_graph_edge_view_t *edge_a =
(const amduat_tgk_graph_edge_view_t *)a;
const amduat_tgk_graph_edge_view_t *edge_b =
(const amduat_tgk_graph_edge_view_t *)b;
return amduat_tgk_store_mem_ref_cmp(edge_a->edge_ref, edge_b->edge_ref);
}
static bool amduat_tgk_store_mem_reference_clone(amduat_reference_t src,
amduat_reference_t *dst) {
uint8_t *digest_copy;
if (dst == NULL) {
return false;
}
*dst = amduat_reference(src.hash_id, amduat_octets(NULL, 0));
if (src.digest.len == 0) {
return true;
}
if (src.digest.data == NULL) {
return false;
}
digest_copy = (uint8_t *)malloc(src.digest.len);
if (digest_copy == NULL) {
return false;
}
memcpy(digest_copy, src.digest.data, src.digest.len);
dst->digest = amduat_octets(digest_copy, src.digest.len);
return true;
}
static void amduat_tgk_store_mem_reference_free(amduat_reference_t *ref) {
if (ref == NULL) {
return;
}
free((void *)ref->digest.data);
ref->digest.data = NULL;
ref->digest.len = 0;
}
static bool amduat_tgk_store_mem_edge_body_clone(
const amduat_tgk_edge_body_t *src,
amduat_tgk_edge_body_t *dst) {
size_t i;
if (src == NULL || dst == NULL) {
return false;
}
memset(dst, 0, sizeof(*dst));
dst->type = src->type;
if (src->from_len != 0) {
dst->from =
(amduat_reference_t *)calloc(src->from_len,
sizeof(amduat_reference_t));
if (dst->from == NULL) {
return false;
}
}
for (i = 0; i < src->from_len; ++i) {
if (!amduat_tgk_store_mem_reference_clone(src->from[i], &dst->from[i])) {
dst->from_len = i;
amduat_tgk_edge_body_free(dst);
return false;
}
}
dst->from_len = src->from_len;
if (src->to_len != 0) {
dst->to =
(amduat_reference_t *)calloc(src->to_len,
sizeof(amduat_reference_t));
if (dst->to == NULL) {
amduat_tgk_edge_body_free(dst);
return false;
}
}
for (i = 0; i < src->to_len; ++i) {
if (!amduat_tgk_store_mem_reference_clone(src->to[i], &dst->to[i])) {
dst->to_len = i;
amduat_tgk_edge_body_free(dst);
return false;
}
}
dst->to_len = src->to_len;
if (!amduat_tgk_store_mem_reference_clone(src->payload, &dst->payload)) {
amduat_tgk_edge_body_free(dst);
return false;
}
return true;
}
static bool amduat_tgk_store_mem_edge_view_clone(
const amduat_tgk_graph_edge_view_t *src,
amduat_tgk_graph_edge_view_t *dst) {
if (src == NULL || dst == NULL) {
return false;
}
memset(dst, 0, sizeof(*dst));
if (!amduat_tgk_store_mem_reference_clone(src->edge_ref, &dst->edge_ref)) {
return false;
}
if (!amduat_tgk_store_mem_edge_body_clone(&src->body, &dst->body)) {
amduat_tgk_store_mem_reference_free(&dst->edge_ref);
return false;
}
return true;
}
static const amduat_tgk_store_mem_artifact_t *
amduat_tgk_store_mem_lookup_artifact(const amduat_tgk_store_mem_t *mem,
amduat_reference_t ref) {
size_t i;
if (mem == NULL) {
return NULL;
}
for (i = 0; i < mem->artifacts_len; ++i) {
if (amduat_reference_eq(mem->artifacts[i].ref, ref)) {
return &mem->artifacts[i];
}
}
return NULL;
}
static amduat_tgk_graph_error_t amduat_tgk_store_mem_decode_edge(
const amduat_tgk_store_mem_t *mem,
const amduat_tgk_store_mem_artifact_t *entry,
amduat_tgk_edge_body_t *out_body) {
if (out_body != NULL) {
memset(out_body, 0, sizeof(*out_body));
}
if (mem == NULL || entry == NULL || out_body == NULL) {
return GS_ERR_UNSUPPORTED;
}
if (!entry->artifact.has_type_tag ||
!amduat_tgk_store_mem_edge_tag_supported(
mem, entry->artifact.type_tag.tag_id)) {
return GS_ERR_NOT_EDGE;
}
if (!amduat_tgk_store_mem_encoding_supported(mem, TGK1_EDGE_ENC_V1)) {
return GS_ERR_UNSUPPORTED;
}
if (entry->artifact.bytes.len != 0 &&
entry->artifact.bytes.data == NULL) {
return GS_ERR_INTEGRITY;
}
if (!amduat_enc_tgk1_edge_decode_v1(entry->artifact.bytes, out_body)) {
return GS_ERR_INTEGRITY;
}
if (!amduat_tgk_edge_body_has_endpoints(out_body)) {
amduat_tgk_edge_body_free(out_body);
return GS_ERR_INTEGRITY;
}
if (!amduat_tgk_store_mem_edge_type_supported(mem, out_body->type)) {
amduat_tgk_edge_body_free(out_body);
return GS_ERR_NOT_EDGE;
}
return 0;
}
static bool amduat_tgk_store_mem_build_edge_list(
const amduat_tgk_store_mem_t *mem,
amduat_tgk_edge_type_filter_t type_filter,
const amduat_reference_t *node,
bool check_from,
bool check_to,
amduat_tgk_graph_edge_view_list_t *out_edges) {
size_t count = 0;
size_t i;
size_t idx;
if (mem == NULL || out_edges == NULL) {
return false;
}
out_edges->edges = NULL;
out_edges->len = 0;
for (i = 0; i < mem->edges_len; ++i) {
const amduat_tgk_graph_edge_view_t *edge = &mem->edges[i];
bool matched = false;
if (!amduat_tgk_store_mem_type_filter_match(type_filter, edge->body.type)) {
continue;
}
if (node == NULL) {
matched = true;
} else {
if (check_from &&
amduat_tgk_store_mem_node_in_list(*node, edge->body.from,
edge->body.from_len)) {
matched = true;
}
if (check_to &&
amduat_tgk_store_mem_node_in_list(*node, edge->body.to,
edge->body.to_len)) {
matched = true;
}
}
if (matched) {
count++;
}
}
if (count == 0) {
return true;
}
out_edges->edges =
(amduat_tgk_graph_edge_view_t *)calloc(count,
sizeof(amduat_tgk_graph_edge_view_t));
if (out_edges->edges == NULL) {
return false;
}
idx = 0;
for (i = 0; i < mem->edges_len; ++i) {
const amduat_tgk_graph_edge_view_t *edge = &mem->edges[i];
bool matched = false;
if (!amduat_tgk_store_mem_type_filter_match(type_filter, edge->body.type)) {
continue;
}
if (node == NULL) {
matched = true;
} else {
if (check_from &&
amduat_tgk_store_mem_node_in_list(*node, edge->body.from,
edge->body.from_len)) {
matched = true;
}
if (check_to &&
amduat_tgk_store_mem_node_in_list(*node, edge->body.to,
edge->body.to_len)) {
matched = true;
}
}
if (matched) {
if (!amduat_tgk_store_mem_edge_view_clone(edge,
&out_edges->edges[idx])) {
out_edges->len = idx;
amduat_tgk_graph_edge_view_list_free(out_edges);
return false;
}
idx++;
}
}
out_edges->len = idx;
return true;
}
static bool amduat_tgk_store_mem_get_config(void *ctx,
amduat_tgk_store_config_t *out_config) {
amduat_tgk_store_mem_t *mem = (amduat_tgk_store_mem_t *)ctx;
if (mem == NULL || out_config == NULL) {
return false;
}
*out_config = mem->config;
return true;
}
static amduat_tgk_graph_error_t amduat_tgk_store_mem_resolve_edge(
void *ctx,
amduat_reference_t ref,
amduat_tgk_edge_body_t *out_body) {
amduat_tgk_store_mem_t *mem = (amduat_tgk_store_mem_t *)ctx;
const amduat_tgk_store_mem_artifact_t *entry;
if (out_body != NULL) {
memset(out_body, 0, sizeof(*out_body));
}
if (mem == NULL || out_body == NULL) {
return GS_ERR_UNSUPPORTED;
}
if (!amduat_tgk_store_mem_hash_id_supported(mem, ref.hash_id)) {
return GS_ERR_UNSUPPORTED;
}
entry = amduat_tgk_store_mem_lookup_artifact(mem, ref);
if (entry == NULL) {
return GS_ERR_ARTIFACT_ERROR;
}
return amduat_tgk_store_mem_decode_edge(mem, entry, out_body);
}
static bool amduat_tgk_store_mem_edges_from(
void *ctx,
amduat_reference_t node,
amduat_tgk_edge_type_filter_t type_filter,
amduat_tgk_graph_edge_view_list_t *out_edges) {
amduat_tgk_store_mem_t *mem = (amduat_tgk_store_mem_t *)ctx;
return amduat_tgk_store_mem_build_edge_list(mem, type_filter, &node, true,
false, out_edges);
}
static bool amduat_tgk_store_mem_edges_to(
void *ctx,
amduat_reference_t node,
amduat_tgk_edge_type_filter_t type_filter,
amduat_tgk_graph_edge_view_list_t *out_edges) {
amduat_tgk_store_mem_t *mem = (amduat_tgk_store_mem_t *)ctx;
return amduat_tgk_store_mem_build_edge_list(mem, type_filter, &node, false,
true, out_edges);
}
static bool amduat_tgk_store_mem_edges_incident(
void *ctx,
amduat_reference_t node,
amduat_tgk_edge_type_filter_t type_filter,
amduat_tgk_graph_edge_view_list_t *out_edges) {
amduat_tgk_store_mem_t *mem = (amduat_tgk_store_mem_t *)ctx;
return amduat_tgk_store_mem_build_edge_list(mem, type_filter, &node, true,
true, out_edges);
}
static bool amduat_tgk_store_mem_scan_edges(
void *ctx,
amduat_tgk_edge_type_filter_t type_filter,
amduat_octets_t page_token,
bool has_page_token,
amduat_tgk_graph_scan_result_t *out_scan) {
amduat_tgk_store_mem_t *mem = (amduat_tgk_store_mem_t *)ctx;
(void)page_token;
(void)has_page_token;
if (out_scan == NULL) {
return false;
}
out_scan->edges.edges = NULL;
out_scan->edges.len = 0;
out_scan->next_page_token.data = NULL;
out_scan->next_page_token.len = 0;
out_scan->has_next_page = false;
return amduat_tgk_store_mem_build_edge_list(mem, type_filter, NULL, false,
false, &out_scan->edges);
}
static bool amduat_tgk_store_mem_node_list_add(
amduat_reference_t node,
amduat_reference_t **nodes,
size_t *nodes_len,
size_t *nodes_cap) {
size_t i;
size_t new_cap;
amduat_reference_t *next;
for (i = 0; i < *nodes_len; ++i) {
if (amduat_reference_eq((*nodes)[i], node)) {
return true;
}
}
if (*nodes_len == *nodes_cap) {
new_cap = (*nodes_cap == 0) ? 4u : (*nodes_cap * 2u);
next = (amduat_reference_t *)realloc(*nodes,
new_cap * sizeof(amduat_reference_t));
if (next == NULL) {
return false;
}
*nodes = next;
*nodes_cap = new_cap;
}
if (!amduat_tgk_store_mem_reference_clone(node, &(*nodes)[*nodes_len])) {
return false;
}
(*nodes_len)++;
return true;
}
static bool amduat_tgk_store_mem_neighbors(
void *ctx,
amduat_reference_t node,
amduat_tgk_edge_type_filter_t type_filter,
amduat_tgk_graph_direction_t direction,
amduat_tgk_node_list_t *out_nodes) {
amduat_tgk_store_mem_t *mem = (amduat_tgk_store_mem_t *)ctx;
amduat_reference_t *nodes = NULL;
size_t nodes_len = 0;
size_t nodes_cap = 0;
size_t i;
size_t j;
if (mem == NULL || out_nodes == NULL) {
return false;
}
out_nodes->nodes = NULL;
out_nodes->len = 0;
for (i = 0; i < mem->edges_len; ++i) {
const amduat_tgk_graph_edge_view_t *edge = &mem->edges[i];
bool use_out = false;
bool use_in = false;
if (!amduat_tgk_store_mem_type_filter_match(type_filter, edge->body.type)) {
continue;
}
if (direction == AMDUAT_TGK_GRAPH_DIR_OUT ||
direction == AMDUAT_TGK_GRAPH_DIR_BOTH) {
if (amduat_tgk_store_mem_node_in_list(node, edge->body.from,
edge->body.from_len)) {
use_out = true;
}
}
if (direction == AMDUAT_TGK_GRAPH_DIR_IN ||
direction == AMDUAT_TGK_GRAPH_DIR_BOTH) {
if (amduat_tgk_store_mem_node_in_list(node, edge->body.to,
edge->body.to_len)) {
use_in = true;
}
}
if (use_out) {
for (j = 0; j < edge->body.to_len; ++j) {
if (!amduat_tgk_store_mem_node_list_add(edge->body.to[j], &nodes,
&nodes_len, &nodes_cap)) {
goto cleanup;
}
}
}
if (use_in) {
for (j = 0; j < edge->body.from_len; ++j) {
if (!amduat_tgk_store_mem_node_list_add(edge->body.from[j], &nodes,
&nodes_len, &nodes_cap)) {
goto cleanup;
}
}
}
}
if (nodes_len > 1) {
qsort(nodes, nodes_len, sizeof(amduat_reference_t),
amduat_tgk_store_mem_ref_ptr_cmp);
}
out_nodes->nodes = nodes;
out_nodes->len = nodes_len;
return true;
cleanup:
for (i = 0; i < nodes_len; ++i) {
amduat_tgk_store_mem_reference_free(&nodes[i]);
}
free(nodes);
return false;
}
bool amduat_tgk_store_mem_init(amduat_tgk_store_mem_t *mem,
amduat_tgk_store_config_t config,
const amduat_tgk_store_mem_artifact_t *artifacts,
size_t artifacts_len) {
size_t i;
size_t count = 0;
amduat_tgk_graph_edge_view_t *edges;
if (mem == NULL) {
return false;
}
mem->config = config;
mem->artifacts = artifacts;
mem->artifacts_len = artifacts_len;
mem->edges = NULL;
mem->edges_len = 0;
if (artifacts_len == 0) {
return true;
}
if (artifacts == NULL) {
return false;
}
edges = (amduat_tgk_graph_edge_view_t *)calloc(
artifacts_len, sizeof(amduat_tgk_graph_edge_view_t));
if (edges == NULL) {
return false;
}
for (i = 0; i < artifacts_len; ++i) {
amduat_tgk_edge_body_t body;
amduat_tgk_graph_error_t err;
const amduat_tgk_store_mem_artifact_t *entry = &artifacts[i];
if (!amduat_tgk_store_mem_hash_id_supported(mem, entry->ref.hash_id)) {
continue;
}
err = amduat_tgk_store_mem_decode_edge(mem, entry, &body);
if (err != 0) {
continue;
}
if (!amduat_tgk_store_mem_reference_clone(entry->ref,
&edges[count].edge_ref)) {
amduat_tgk_edge_body_free(&body);
goto cleanup;
}
edges[count].body = body;
count++;
}
if (count > 1) {
qsort(edges, count, sizeof(amduat_tgk_graph_edge_view_t),
amduat_tgk_store_mem_edge_cmp);
}
mem->edges = edges;
mem->edges_len = count;
return true;
cleanup:
for (i = 0; i < count; ++i) {
amduat_tgk_store_mem_reference_free(&edges[i].edge_ref);
amduat_tgk_edge_body_free(&edges[i].body);
}
free(edges);
mem->edges = NULL;
mem->edges_len = 0;
return false;
}
void amduat_tgk_store_mem_free(amduat_tgk_store_mem_t *mem) {
size_t i;
if (mem == NULL) {
return;
}
for (i = 0; i < mem->edges_len; ++i) {
amduat_tgk_store_mem_reference_free(&mem->edges[i].edge_ref);
amduat_tgk_edge_body_free(&mem->edges[i].body);
}
free(mem->edges);
mem->edges = NULL;
mem->edges_len = 0;
mem->artifacts = NULL;
mem->artifacts_len = 0;
}
amduat_tgk_store_ops_t amduat_tgk_store_mem_ops(void) {
amduat_tgk_store_ops_t ops;
memset(&ops, 0, sizeof(ops));
ops.get_config = amduat_tgk_store_mem_get_config;
ops.resolve_edge = amduat_tgk_store_mem_resolve_edge;
ops.edges_from = amduat_tgk_store_mem_edges_from;
ops.edges_to = amduat_tgk_store_mem_edges_to;
ops.edges_incident = amduat_tgk_store_mem_edges_incident;
ops.scan_edges = amduat_tgk_store_mem_scan_edges;
ops.neighbors = amduat_tgk_store_mem_neighbors;
return ops;
}

View file

@ -0,0 +1,146 @@
#include "amduat/tgk/store.h"
#include <stdlib.h>
static void amduat_tgk_store_reference_free(amduat_reference_t *ref) {
if (ref == NULL) {
return;
}
free((void *)ref->digest.data);
ref->digest.data = NULL;
ref->digest.len = 0;
}
void amduat_tgk_store_init(amduat_tgk_store_t *store,
amduat_tgk_store_config_t config,
amduat_tgk_store_ops_t ops,
void *ctx) {
if (store == NULL) {
return;
}
store->config = config;
store->ops = ops;
store->ctx = ctx;
}
bool amduat_tgk_store_get_config(amduat_tgk_store_t *store,
amduat_tgk_store_config_t *out_config) {
if (store == NULL || store->ops.get_config == NULL) {
return false;
}
return store->ops.get_config(store->ctx, out_config);
}
amduat_tgk_graph_error_t amduat_tgk_store_resolve_edge(
amduat_tgk_store_t *store,
amduat_reference_t ref,
amduat_tgk_edge_body_t *out_body) {
if (store == NULL || store->ops.resolve_edge == NULL) {
return GS_ERR_UNSUPPORTED;
}
return store->ops.resolve_edge(store->ctx, ref, out_body);
}
bool amduat_tgk_store_edges_from(amduat_tgk_store_t *store,
amduat_reference_t node,
amduat_tgk_edge_type_filter_t type_filter,
amduat_tgk_graph_edge_view_list_t *out_edges) {
if (store == NULL || store->ops.edges_from == NULL) {
return false;
}
return store->ops.edges_from(store->ctx, node, type_filter, out_edges);
}
bool amduat_tgk_store_edges_to(amduat_tgk_store_t *store,
amduat_reference_t node,
amduat_tgk_edge_type_filter_t type_filter,
amduat_tgk_graph_edge_view_list_t *out_edges) {
if (store == NULL || store->ops.edges_to == NULL) {
return false;
}
return store->ops.edges_to(store->ctx, node, type_filter, out_edges);
}
bool amduat_tgk_store_edges_incident(
amduat_tgk_store_t *store,
amduat_reference_t node,
amduat_tgk_edge_type_filter_t type_filter,
amduat_tgk_graph_edge_view_list_t *out_edges) {
if (store == NULL || store->ops.edges_incident == NULL) {
return false;
}
return store->ops.edges_incident(store->ctx, node, type_filter, out_edges);
}
bool amduat_tgk_store_scan_edges(amduat_tgk_store_t *store,
amduat_tgk_edge_type_filter_t type_filter,
amduat_octets_t page_token,
bool has_page_token,
amduat_tgk_graph_scan_result_t *out_scan) {
if (store == NULL || store->ops.scan_edges == NULL) {
return false;
}
return store->ops.scan_edges(store->ctx, type_filter, page_token,
has_page_token, out_scan);
}
bool amduat_tgk_store_neighbors(amduat_tgk_store_t *store,
amduat_reference_t node,
amduat_tgk_edge_type_filter_t type_filter,
amduat_tgk_graph_direction_t direction,
amduat_tgk_node_list_t *out_nodes) {
if (store == NULL || store->ops.neighbors == NULL) {
return false;
}
return store->ops.neighbors(store->ctx, node, type_filter, direction,
out_nodes);
}
void amduat_tgk_graph_edge_view_list_free(
amduat_tgk_graph_edge_view_list_t *list) {
size_t i;
if (list == NULL) {
return;
}
if (list->edges == NULL) {
list->len = 0;
return;
}
for (i = 0; i < list->len; ++i) {
amduat_tgk_store_reference_free(&list->edges[i].edge_ref);
amduat_tgk_edge_body_free(&list->edges[i].body);
}
free(list->edges);
list->edges = NULL;
list->len = 0;
}
void amduat_tgk_node_list_free(amduat_tgk_node_list_t *list) {
size_t i;
if (list == NULL) {
return;
}
if (list->nodes == NULL) {
list->len = 0;
return;
}
for (i = 0; i < list->len; ++i) {
amduat_tgk_store_reference_free(&list->nodes[i]);
}
free(list->nodes);
list->nodes = NULL;
list->len = 0;
}
void amduat_tgk_graph_scan_result_free(amduat_tgk_graph_scan_result_t *scan) {
if (scan == NULL) {
return;
}
amduat_tgk_graph_edge_view_list_free(&scan->edges);
free((void *)scan->next_page_token.data);
scan->next_page_token.data = NULL;
scan->next_page_token.len = 0;
scan->has_next_page = false;
}

View file

@ -0,0 +1,433 @@
#include "amduat/enc/asl1_core.h"
#include "amduat/enc/tgk1_edge.h"
#include "amduat/hash/asl1.h"
#include "amduat/tgk/tgk_store_mem.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static const uint8_t k_non_edge_bytes[] = {0xde, 0xad, 0xbe, 0xef};
typedef struct {
amduat_tgk_store_t store;
amduat_tgk_store_mem_t mem;
amduat_tgk_store_mem_artifact_t artifacts[5];
amduat_octets_t edge_bytes[4];
amduat_reference_t ref_edge1;
amduat_reference_t ref_edge2;
amduat_reference_t ref_edge3;
amduat_reference_t ref_edge_bad;
amduat_reference_t ref_non_edge;
amduat_reference_t node_a;
amduat_reference_t node_b;
amduat_reference_t node_c;
amduat_reference_t payload1;
amduat_reference_t payload2;
amduat_reference_t payload3;
amduat_reference_t payload_bad;
uint8_t digest_edge1[32];
uint8_t digest_edge2[32];
uint8_t digest_edge3[32];
uint8_t digest_edge_bad[32];
uint8_t digest_non_edge[32];
uint8_t digest_node_a[32];
uint8_t digest_node_b[32];
uint8_t digest_node_c[32];
uint8_t digest_payload1[32];
uint8_t digest_payload2[32];
uint8_t digest_payload3[32];
uint8_t digest_payload_bad[32];
amduat_tgk_identity_domain_t domains[1];
uint32_t edge_tags[1];
amduat_tgk_edge_type_id_t edge_types[2];
amduat_asl_encoding_profile_id_t encodings[1];
amduat_tgk_store_config_t config;
} test_env_t;
static void fill_digest(uint8_t *out, uint8_t value) {
memset(out, value, 32);
}
static amduat_reference_t make_ref(uint8_t value, uint8_t *storage) {
fill_digest(storage, value);
return amduat_reference(AMDUAT_HASH_ASL1_ID_SHA256,
amduat_octets(storage, 32));
}
static void free_edge_bytes(test_env_t *env) {
size_t i;
for (i = 0; i < 4; ++i) {
free((void *)env->edge_bytes[i].data);
env->edge_bytes[i].data = NULL;
env->edge_bytes[i].len = 0;
}
}
static bool init_env(test_env_t *env) {
amduat_tgk_edge_body_t edge1;
amduat_tgk_edge_body_t edge2;
amduat_tgk_edge_body_t edge3;
amduat_tgk_edge_body_t edge_bad;
amduat_reference_t edge1_from[1];
amduat_reference_t edge1_to[1];
amduat_reference_t edge2_from[1];
amduat_reference_t edge2_to[1];
amduat_reference_t edge3_from[1];
amduat_reference_t edge3_to[1];
amduat_reference_t edge_bad_from[1];
amduat_reference_t edge_bad_to[1];
memset(env, 0, sizeof(*env));
env->domains[0].encoding_profile = AMDUAT_ENC_ASL1_CORE_V1;
env->domains[0].hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
env->edge_tags[0] = TYPE_TAG_TGK1_EDGE_V1;
env->edge_types[0] = 0x10;
env->edge_types[1] = 0x20;
env->encodings[0] = TGK1_EDGE_ENC_V1;
env->config.id_space.domains = env->domains;
env->config.id_space.domains_len = 1;
env->config.artifact_scope.description = amduat_octets(NULL, 0);
env->config.tgk_profiles.edge_tags = env->edge_tags;
env->config.tgk_profiles.edge_tags_len = 1;
env->config.tgk_profiles.edge_types = env->edge_types;
env->config.tgk_profiles.edge_types_len = 2;
env->config.tgk_profiles.encodings = env->encodings;
env->config.tgk_profiles.encodings_len = 1;
env->ref_edge1 = make_ref(0x20, env->digest_edge1);
env->ref_edge2 = make_ref(0x10, env->digest_edge2);
env->ref_edge3 = make_ref(0x30, env->digest_edge3);
env->ref_edge_bad = make_ref(0x40, env->digest_edge_bad);
env->ref_non_edge = make_ref(0x50, env->digest_non_edge);
env->node_a = make_ref(0xa1, env->digest_node_a);
env->node_b = make_ref(0xb1, env->digest_node_b);
env->node_c = make_ref(0xc1, env->digest_node_c);
env->payload1 = make_ref(0xe1, env->digest_payload1);
env->payload2 = make_ref(0xe2, env->digest_payload2);
env->payload3 = make_ref(0xe3, env->digest_payload3);
env->payload_bad = make_ref(0xe4, env->digest_payload_bad);
memset(&edge1, 0, sizeof(edge1));
edge1.type = 0x10;
edge1_from[0] = env->node_a;
edge1.from = edge1_from;
edge1.from_len = 1;
edge1_to[0] = env->node_b;
edge1.to = edge1_to;
edge1.to_len = 1;
edge1.payload = env->payload1;
memset(&edge2, 0, sizeof(edge2));
edge2.type = 0x10;
edge2_from[0] = env->node_a;
edge2.from = edge2_from;
edge2.from_len = 1;
edge2_to[0] = env->node_c;
edge2.to = edge2_to;
edge2.to_len = 1;
edge2.payload = env->payload2;
memset(&edge3, 0, sizeof(edge3));
edge3.type = 0x20;
edge3_from[0] = env->node_b;
edge3.from = edge3_from;
edge3.from_len = 1;
edge3_to[0] = env->node_a;
edge3.to = edge3_to;
edge3.to_len = 1;
edge3.payload = env->payload3;
memset(&edge_bad, 0, sizeof(edge_bad));
edge_bad.type = 0x99;
edge_bad_from[0] = env->node_a;
edge_bad.from = edge_bad_from;
edge_bad.from_len = 1;
edge_bad_to[0] = env->node_b;
edge_bad.to = edge_bad_to;
edge_bad.to_len = 1;
edge_bad.payload = env->payload_bad;
if (!amduat_enc_tgk1_edge_encode_v1(&edge1, &env->edge_bytes[0]) ||
!amduat_enc_tgk1_edge_encode_v1(&edge2, &env->edge_bytes[1]) ||
!amduat_enc_tgk1_edge_encode_v1(&edge3, &env->edge_bytes[2]) ||
!amduat_enc_tgk1_edge_encode_v1(&edge_bad, &env->edge_bytes[3])) {
free_edge_bytes(env);
return false;
}
env->artifacts[0].ref = env->ref_edge1;
env->artifacts[0].artifact =
amduat_artifact_with_type(env->edge_bytes[0],
amduat_type_tag(TYPE_TAG_TGK1_EDGE_V1));
env->artifacts[1].ref = env->ref_edge2;
env->artifacts[1].artifact =
amduat_artifact_with_type(env->edge_bytes[1],
amduat_type_tag(TYPE_TAG_TGK1_EDGE_V1));
env->artifacts[2].ref = env->ref_edge3;
env->artifacts[2].artifact =
amduat_artifact_with_type(env->edge_bytes[2],
amduat_type_tag(TYPE_TAG_TGK1_EDGE_V1));
env->artifacts[3].ref = env->ref_edge_bad;
env->artifacts[3].artifact =
amduat_artifact_with_type(env->edge_bytes[3],
amduat_type_tag(TYPE_TAG_TGK1_EDGE_V1));
env->artifacts[4].ref = env->ref_non_edge;
env->artifacts[4].artifact =
amduat_artifact(amduat_octets(k_non_edge_bytes, sizeof(k_non_edge_bytes)));
if (!amduat_tgk_store_mem_init(&env->mem, env->config, env->artifacts, 5)) {
free_edge_bytes(env);
return false;
}
amduat_tgk_store_init(&env->store, env->config, amduat_tgk_store_mem_ops(),
&env->mem);
return true;
}
static void free_env(test_env_t *env) {
amduat_tgk_store_mem_free(&env->mem);
free_edge_bytes(env);
}
static int test_resolve_edge_ok(const test_env_t *env) {
amduat_tgk_edge_body_t body;
amduat_tgk_graph_error_t err;
err = amduat_tgk_store_resolve_edge((amduat_tgk_store_t *)&env->store,
env->ref_edge1, &body);
if (err != 0) {
fprintf(stderr, "resolve_edge ok failed: %d\n", err);
return 1;
}
if (body.type != 0x10 || body.from_len != 1 || body.to_len != 1) {
fprintf(stderr, "resolve_edge body mismatch\n");
amduat_tgk_edge_body_free(&body);
return 1;
}
if (!amduat_reference_eq(body.from[0], env->node_a) ||
!amduat_reference_eq(body.to[0], env->node_b)) {
fprintf(stderr, "resolve_edge endpoints mismatch\n");
amduat_tgk_edge_body_free(&body);
return 1;
}
amduat_tgk_edge_body_free(&body);
return 0;
}
static int test_resolve_edge_not_edge(const test_env_t *env) {
amduat_tgk_edge_body_t body;
amduat_tgk_graph_error_t err;
err = amduat_tgk_store_resolve_edge((amduat_tgk_store_t *)&env->store,
env->ref_edge_bad, &body);
if (err != GS_ERR_NOT_EDGE) {
fprintf(stderr, "resolve_edge not_edge mismatch: %d\n", err);
return 1;
}
return 0;
}
static int test_type_filter(const test_env_t *env) {
amduat_tgk_edge_type_id_t types[1] = {0x10};
amduat_tgk_edge_type_filter_t filter;
amduat_tgk_graph_edge_view_list_t edges;
size_t i;
filter.types = types;
filter.types_len = 1;
if (!amduat_tgk_store_edges_incident((amduat_tgk_store_t *)&env->store,
env->node_a, filter, &edges)) {
fprintf(stderr, "edges_incident type filter failed\n");
return 1;
}
if (edges.len != 2) {
fprintf(stderr, "edges_incident type filter count mismatch\n");
amduat_tgk_graph_edge_view_list_free(&edges);
return 1;
}
for (i = 0; i < edges.len; ++i) {
if (edges.edges[i].body.type != 0x10) {
fprintf(stderr, "edges_incident type filter type mismatch\n");
amduat_tgk_graph_edge_view_list_free(&edges);
return 1;
}
}
amduat_tgk_graph_edge_view_list_free(&edges);
return 0;
}
static int test_ordering(const test_env_t *env) {
amduat_tgk_edge_type_filter_t filter;
amduat_tgk_graph_edge_view_list_t edges;
filter.types = NULL;
filter.types_len = 0;
if (!amduat_tgk_store_edges_from((amduat_tgk_store_t *)&env->store,
env->node_a, filter, &edges)) {
fprintf(stderr, "edges_from ordering failed\n");
return 1;
}
if (edges.len != 2) {
fprintf(stderr, "edges_from ordering count mismatch\n");
amduat_tgk_graph_edge_view_list_free(&edges);
return 1;
}
if (!amduat_reference_eq(edges.edges[0].edge_ref, env->ref_edge2) ||
!amduat_reference_eq(edges.edges[1].edge_ref, env->ref_edge1)) {
fprintf(stderr, "edges_from ordering mismatch\n");
amduat_tgk_graph_edge_view_list_free(&edges);
return 1;
}
amduat_tgk_graph_edge_view_list_free(&edges);
return 0;
}
static int test_adjacency(const test_env_t *env) {
amduat_tgk_edge_type_filter_t filter;
amduat_tgk_graph_edge_view_list_t edges;
filter.types = NULL;
filter.types_len = 0;
if (!amduat_tgk_store_edges_to((amduat_tgk_store_t *)&env->store,
env->node_a, filter, &edges)) {
fprintf(stderr, "edges_to failed\n");
return 1;
}
if (edges.len != 1 ||
!amduat_reference_eq(edges.edges[0].edge_ref, env->ref_edge3)) {
fprintf(stderr, "edges_to mismatch\n");
amduat_tgk_graph_edge_view_list_free(&edges);
return 1;
}
amduat_tgk_graph_edge_view_list_free(&edges);
if (!amduat_tgk_store_edges_incident((amduat_tgk_store_t *)&env->store,
env->node_a, filter, &edges)) {
fprintf(stderr, "edges_incident failed\n");
return 1;
}
if (edges.len != 3 ||
!amduat_reference_eq(edges.edges[0].edge_ref, env->ref_edge2) ||
!amduat_reference_eq(edges.edges[1].edge_ref, env->ref_edge1) ||
!amduat_reference_eq(edges.edges[2].edge_ref, env->ref_edge3)) {
fprintf(stderr, "edges_incident mismatch\n");
amduat_tgk_graph_edge_view_list_free(&edges);
return 1;
}
amduat_tgk_graph_edge_view_list_free(&edges);
return 0;
}
static int test_neighbors(const test_env_t *env) {
amduat_tgk_edge_type_filter_t filter;
amduat_tgk_node_list_t nodes;
filter.types = NULL;
filter.types_len = 0;
if (!amduat_tgk_store_neighbors((amduat_tgk_store_t *)&env->store,
env->node_a, filter,
AMDUAT_TGK_GRAPH_DIR_OUT, &nodes)) {
fprintf(stderr, "neighbors out failed\n");
return 1;
}
if (nodes.len != 2 ||
!amduat_reference_eq(nodes.nodes[0], env->node_b) ||
!amduat_reference_eq(nodes.nodes[1], env->node_c)) {
fprintf(stderr, "neighbors out mismatch\n");
amduat_tgk_node_list_free(&nodes);
return 1;
}
amduat_tgk_node_list_free(&nodes);
if (!amduat_tgk_store_neighbors((amduat_tgk_store_t *)&env->store,
env->node_a, filter,
AMDUAT_TGK_GRAPH_DIR_IN, &nodes)) {
fprintf(stderr, "neighbors in failed\n");
return 1;
}
if (nodes.len != 1 ||
!amduat_reference_eq(nodes.nodes[0], env->node_b)) {
fprintf(stderr, "neighbors in mismatch\n");
amduat_tgk_node_list_free(&nodes);
return 1;
}
amduat_tgk_node_list_free(&nodes);
if (!amduat_tgk_store_neighbors((amduat_tgk_store_t *)&env->store,
env->node_a, filter,
AMDUAT_TGK_GRAPH_DIR_BOTH, &nodes)) {
fprintf(stderr, "neighbors both failed\n");
return 1;
}
if (nodes.len != 2 ||
!amduat_reference_eq(nodes.nodes[0], env->node_b) ||
!amduat_reference_eq(nodes.nodes[1], env->node_c)) {
fprintf(stderr, "neighbors both mismatch\n");
amduat_tgk_node_list_free(&nodes);
return 1;
}
amduat_tgk_node_list_free(&nodes);
return 0;
}
static int test_scan_edges(const test_env_t *env) {
amduat_tgk_edge_type_filter_t filter;
amduat_tgk_graph_scan_result_t scan;
filter.types = NULL;
filter.types_len = 0;
if (!amduat_tgk_store_scan_edges((amduat_tgk_store_t *)&env->store, filter,
amduat_octets(NULL, 0), false, &scan)) {
fprintf(stderr, "scan_edges failed\n");
return 1;
}
if (scan.edges.len != 3 ||
!amduat_reference_eq(scan.edges.edges[0].edge_ref, env->ref_edge2) ||
!amduat_reference_eq(scan.edges.edges[1].edge_ref, env->ref_edge1) ||
!amduat_reference_eq(scan.edges.edges[2].edge_ref, env->ref_edge3)) {
fprintf(stderr, "scan_edges ordering mismatch\n");
amduat_tgk_graph_scan_result_free(&scan);
return 1;
}
amduat_tgk_graph_scan_result_free(&scan);
return 0;
}
int main(void) {
test_env_t env;
if (!init_env(&env)) {
fprintf(stderr, "failed to init tgk store mem test env\n");
return 1;
}
if (test_resolve_edge_ok(&env) != 0 ||
test_resolve_edge_not_edge(&env) != 0 ||
test_type_filter(&env) != 0 ||
test_ordering(&env) != 0 ||
test_adjacency(&env) != 0 ||
test_neighbors(&env) != 0 ||
test_scan_edges(&env) != 0) {
free_env(&env);
return 1;
}
free_env(&env);
return 0;
}