#ifndef sc_included_sph_dg_test_helper #define sc_included_sph_dg_test_helper #define increment(a) a = (1 + a) #define decrement(a) a = (a - 1) #ifndef sc_included_stdio_h #include #define sc_included_stdio_h #endif #ifndef sc_included_stdlib_h #include #define sc_included_stdlib_h #endif #ifndef sc_included_errno_h #include #define sc_included_errno_h #endif #ifndef sc_included_pthread_h #include #define sc_included_pthread_h #endif #define debug_log_p 1 #ifndef sc_included_sph_dg #define sc_included_sph_dg /* this file contains declarations and macros to include for using sph-dg as a * shared library. */ #ifndef sc_included_lmdb_h #include #define sc_included_lmdb_h #endif #ifndef sc_included_sph #define sc_included_sph #ifndef sc_included_inttypes_h #include #define sc_included_inttypes_h #endif #ifndef sc_included_stdio_h #include #define sc_included_stdio_h #endif #define boolean b8 #define pointer_t uintptr_t #define b0 void #define b8 uint8_t #define b16 uint16_t #define b32 uint32_t #define b64 uint64_t #define b8_s int8_t #define b16_s int16_t #define b32_s int32_t #define b64_s int64_t #define f32_s float #define f64_s double /** writes values with current routine name and line info to standard output. example: (debug-log "%d" 1) otherwise like printf */ #define debug_log(format, ...) \ fprintf(stdout, "%s:%d " format "\n", __func__, __LINE__, __VA_ARGS__) ; #define null ((b0)(0)) #define zero_p(a) (0 == a) #endif #ifndef sc_included_sph_dg_status #define sc_included_sph_dg_status #ifndef sc_included_sph_status #define sc_included_sph_status /* return status code and error handling. uses a local variable named "status" and a goto label named "exit". a status has an identifier and a group to discern between status identifiers of different libraries. status id 0 is success, everything else can be considered a failure or special case. status ids are 32 bit signed integers for compatibility with error return codes from many other existing libraries. bindings with a ! suffix update the status from an expression */ typedef b32_s status_i_t; typedef struct { status_i_t id; b8 group; } status_t; #define status_id_success 0 #define status_group_undefined 0 #define status_init \ status_t status = {status_id_success, status_group_undefined} /** like status init but sets a default group */ #define status_init_group(group) status_t status = {status_id_success, group} ; #define status_reset status_set_both(status_group_undefined, status_id_success) #define status_success_p (status_id_success == status.id) #define status_failure_p !status_success_p #define status_goto goto exit #define status_set_group(group_id) status.group = group_id #define status_set_id(status_id) status.id = status_id #define status_set_both(group_id, status_id) \ status_set_group(group_id); \ status_set_id(status_id) #define status_require \ if (status_failure_p) { \ status_goto; \ } /** update status with the result of expression, check for failure and goto * error if so */ #define status_require_x(expression) \ status = expression; \ if (status_failure_p) { \ status_goto; \ } ; /** set the status id and goto error */ #define status_set_id_goto(status_id) \ status_set_id(status_id); \ status_goto ; #define status_set_group_goto(group_id) \ status_set_group(group_id); \ status_goto #define status_set_both_goto(group_id, status_id) \ status_set_both(group_id, status_id); \ status_goto #define status_id_is_p(status_id) (status_id == status.id) /** update status with the result of expression, check for failure and goto * error if so */ #define status_i_require_x(expression) \ status.id = expression; \ if (status_failure_p) { \ status_goto; \ } ; #endif enum { dg_status_id_undefined, dg_status_id_input_type, dg_status_id_max_id, dg_status_id_data_length, dg_status_id_not_implemented, dg_status_id_duplicate, dg_status_id_memory, dg_status_id_condition_unfulfilled, dg_status_id_missing_argument_dg_root, dg_status_id_path_not_accessible_dg_root, dg_status_id_no_more_data, dg_status_group_dg, dg_status_group_lmdb, dg_status_group_libc }; b8 *dg_status_group_id_to_name(status_i_t a) { return (((dg_status_group_dg == a) ? "sph-dg" : ((dg_status_group_lmdb == a) ? "lmdb" : ((dg_status_group_libc == a) ? "libc" : "")))); }; b8 *dg_status_description(status_t a) { return (( (dg_status_group_dg == a.group) ? ((b8 *)(( (dg_status_id_input_type == a.id) ? "input argument is of wrong type" : ((dg_status_id_data_length == a.id) ? "intern data too large" : ((dg_status_id_duplicate == a.id) ? "element already exists" : ((dg_status_id_not_implemented == a.id) ? "not implemented" : ((dg_status_id_missing_argument_dg_root == a.id) ? "missing argument 'dg-root'" : ((dg_status_id_path_not_accessible_dg_root == a.id) ? "root not accessible" : ((dg_status_id_memory == a.id) ? "not enough " "memory or " "other memory " "allocation " "error" : ((dg_status_id_max_id == a.id) ? "maximum" " identi" "fier " "value " "has " "been " "reached" : ((dg_status_id_condition_unfulfilled == a.id) ? "condition unfulfilled" : ((dg_status_id_no_more_data == a.id) ? "no more data to read" : "")))))))))))) : ((dg_status_group_lmdb == a.group) ? ((b8 *)(mdb_strerror(a.id))) : ((b8 *)(""))))); }; b8 *dg_status_name(status_t a) { return (( (dg_status_group_dg == a.group) ? ((b8 *)(( (dg_status_id_input_type == a.id) ? "input-type" : ((dg_status_id_data_length == a.id) ? "data-length" : ((dg_status_id_duplicate == a.id) ? "duplicate" : ((dg_status_id_not_implemented == a.id) ? "not-implemented" : ((dg_status_id_missing_argument_dg_root == a.id) ? "missing-argument-dg-root" : ((dg_status_id_path_not_accessible_dg_root == a.id) ? "path-not-accessible-" "dg-root" : ((dg_status_id_memory == a.id) ? "memory" : ((dg_status_id_max_id == a.id) ? "max-id-" "reached" : ((dg_status_id_condition_unfulfilled == a.id) ? "condition-unfulfilled" : ((dg_status_id_no_more_data == a.id) ? "no-more-data" : "unknown")))))))))))) : ((dg_status_group_lmdb == a.group) ? ((b8 *)(mdb_strerror(a.id))) : ((b8 *)("unknown"))))); }; #define dg_status_set_id_goto(status_id) \ status_set_both_goto(dg_status_group_dg, status_id) #define dg_status_require_read_x(expression) \ status = expression; \ if (!(status_success_p || status_id_is_p(dg_status_id_no_more_data))) { \ status_goto; \ } #define dg_status_no_more_data_if_mdb_notfound \ if (dg_mdb_status_notfound_p) { \ status_set_both(dg_status_group_dg, dg_status_id_no_more_data); \ } #define dg_status_success_if_mdb_notfound \ if (dg_mdb_status_notfound_p) { \ status_set_id(status_id_success); \ } #define dg_status_success_if_no_more_data \ if (status_id_is_p(dg_status_id_no_more_data)) { \ status.id = status_id_success; \ } #define dg_mdb_status_success_p status_id_is_p(MDB_SUCCESS) #define dg_mdb_status_failure_p !dg_mdb_status_success_p #define dg_mdb_status_notfound_p status_id_is_p(MDB_NOTFOUND) #define dg_mdb_status_set_id_goto(id) \ status_set_both_goto(dg_status_group_lmdb, id) #define dg_mdb_status_require_x(expression) \ status_set_id(expression); \ if (dg_mdb_status_failure_p) { \ status_set_group_goto(dg_status_group_lmdb); \ } #define dg_mdb_status_require \ if (dg_mdb_status_failure_p) { \ status_set_group_goto(dg_status_group_lmdb); \ } #define dg_mdb_status_require_read \ if (!(dg_mdb_status_success_p || dg_mdb_status_notfound_p)) { \ status_set_group_goto(dg_status_group_lmdb); \ } #define dg_mdb_status_require_read_x(expression) \ status_set_id(expression); \ dg_mdb_status_require_read #define dg_mdb_status_require_notfound \ if (!dg_mdb_status_notfound_p) { \ status_set_group_goto(dg_status_group_lmdb); \ } #endif #ifndef sc_included_sph_dg_config #define sc_included_sph_dg_config typedef b64 dg_id_t; typedef b16 dg_type_id_t; typedef b32 dg_ordinal_t; #define dg_id_max UINT64_MAX #define dg_size_id sizeof(dg_id_t) #define dg_size_type_id sizeof(dg_type_id_t) #define dg_size_ordinal sizeof(dg_ordinal_t) #define dg_id_equal_p(a, b) (a == b) #define dg_id_compare(a, b) ((a < b) ? -1 : (a > b)) #endif /* data size is limited by lmdb max keysize, which is 511 byte by lmdb default * but can be adjusted with recompiling lmdb */ #ifndef dg_id_t #define dg_id_t b64 #endif #ifndef dg_ordinal_t #define dg_ordinal_t b32 #endif #ifndef dg_id_max #define dg_id_max UINT64_MAX #endif #ifndef dg_size_octets_data_max #define dg_size_octets_data_max 511 #endif #ifndef dg_size_octets_data_min #define dg_size_octets_data_min 1 #endif #ifndef dg_size_octets_id #define dg_size_octets_id sizeof(dg_id_t) #endif #ifndef dg_size_octets_ordinal #define dg_size_octets_ordinal sizeof(dg_ordinal_t) #endif #ifndef dg_id_equal_p #define dg_id_equal_p(a, b) (a == b) #endif #ifndef dg_id_compare #define dg_id_compare(a, b) ((a < b) ? -1 : (a > b)) #endif #define dg_ordinal_compare dg_id_compare #ifndef dg_pointer_to_id #define dg_pointer_to_id(a, index) (*(index + ((dg_id_t *)(a)))) #endif #define imht_set_key_t dg_id_t #define dg_type_id 0 #define dg_type_intern 1 #define dg_type_extern 2 #define dg_type_intern_small 3 #define dg_type_mask 3 #define dg_id_type_step 4 #define dg_size_octets_relation_data \ (dg_size_octets_ordinal + dg_size_octets_id) #define dg_size_octets_relation_key (2 * dg_size_octets_id) #define dg_read_option_skip 1 #define dg_read_option_is_set_left 2 #define dg_read_option_is_set_right 4 #define dg_read_option_initialised 8 #define dg_null 0 #define dg_type_bit_id 1 #define dg_type_bit_intern 2 #define dg_type_bit_extern 4 #define dg_type_bit_intern_small 8 #ifndef sc_included_stdlib_h #include #define sc_included_stdlib_h #endif #ifndef sc_included_inttypes_h #include #define sc_included_inttypes_h #endif #ifndef imht_set_key_t #define imht_set_key_t uint64_t #endif #ifndef imht_set_can_contain_zero_p #define imht_set_can_contain_zero_p 1 #endif #ifndef imht_set_size_factor #define imht_set_size_factor 2 #endif uint16_t imht_set_primes[] = { 0, 3, 7, 13, 19, 29, 37, 43, 53, 61, 71, 79, 89, 101, 107, 113, 131, 139, 151, 163, 173, 181, 193, 199, 223, 229, 239, 251, 263, 271, 281, 293, 311, 317, 337, 349, 359, 373, 383, 397, 409, 421, 433, 443, 457, 463, 479, 491, 503, 521, 541, 557, 569, 577, 593, 601, 613, 619, 641, 647, 659, 673, 683, 701, 719, 733, 743, 757, 769, 787, 809, 821, 827, 839, 857, 863, 881, 887, 911, 929, 941, 953, 971, 983, 997}; uint16_t *imht_set_primes_end = (imht_set_primes + 83); typedef struct { size_t size; imht_set_key_t *content; } imht_set_t; size_t imht_set_calculate_hash_table_size(size_t min_size) { min_size = (imht_set_size_factor * min_size); uint16_t *primes = imht_set_primes; while ((primes < imht_set_primes_end)) { if ((min_size <= (*primes))) { return ((*primes)); } else { primes = (1 + primes); }; }; if ((min_size <= (*primes))) { return ((*primes)); }; return ((1 | min_size)); }; uint8_t imht_set_create(size_t min_size, imht_set_t **result) { (*result) = malloc(sizeof(imht_set_t)); if (!(*result)) { return (0); }; min_size = imht_set_calculate_hash_table_size(min_size); (*(*result)).content = calloc(min_size, sizeof(imht_set_key_t)); (*(*result)).size = min_size; return (((*(*result)).content ? 1 : 0)); }; void imht_set_destroy(imht_set_t *a) { if (a) { free((*a).content); free(a); }; }; #if imht_set_can_contain_zero_p #define imht_set_hash(value, hash_table) \ (value ? (1 + (value % (hash_table.size - 1))) : 0) #else #define imht_set_hash(value, hash_table) (value % hash_table.size) #endif /** returns the address of the element in the set, 0 if it was not found. caveat: if imht-set-can-contain-zero? is defined, which is the default, dereferencing a returned address for the found value 0 will return 1 instead */ imht_set_key_t *imht_set_find(imht_set_t *a, imht_set_key_t value) { imht_set_key_t *h = ((*a).content + imht_set_hash(value, (*a))); if ((*h)) { #if imht_set_can_contain_zero_p if (((((*h) == value)) || ((0 == value)))) { return (h); }; #else if (((*h) == value)) { return (h); }; #endif imht_set_key_t *content_end = ((*a).content + ((*a).size - 1)); imht_set_key_t *h2 = (1 + h); while ((h2 < content_end)) { if (!(*h2)) { return (0); } else { if ((value == (*h2))) { return (h2); }; }; h2 = (1 + h2); }; if (!(*h2)) { return (0); } else { if ((value == (*h2))) { return (h2); }; }; h2 = (*a).content; while ((h2 < h)) { if (!(*h2)) { return (0); } else { if ((value == (*h2))) { return (h2); }; }; h2 = (1 + h2); }; }; return (0); }; #define imht_set_contains_p(a, value) ((0 == imht_set_find(a, value)) ? 0 : 1) /** returns 1 if the element was removed, 0 if it was not found */ uint8_t imht_set_remove(imht_set_t *a, imht_set_key_t value) { imht_set_key_t *value_address = imht_set_find(a, value); if (value_address) { (*value_address) = 0; return (1); } else { return (0); }; }; /** returns the address of the added or already included element, 0 if there is * no space left in the set */ imht_set_key_t *imht_set_add(imht_set_t *a, imht_set_key_t value) { imht_set_key_t *h = ((*a).content + imht_set_hash(value, (*a))); if ((*h)) { #if imht_set_can_contain_zero_p if ((((value == (*h))) || ((0 == value)))) { return (h); }; #else if ((value == (*h))) { return (h); }; #endif imht_set_key_t *content_end = ((*a).content + ((*a).size - 1)); imht_set_key_t *h2 = (1 + h); while ((((h2 <= content_end)) && (*h2))) { h2 = (1 + h2); }; if ((h2 > content_end)) { h2 = (*a).content; while (((h2 < h) && (*h2))) { h2 = (1 + h2); }; if ((h2 == h)) { return (0); } else { #if imht_set_can_contain_zero_p (*h2) = ((0 == value) ? 1 : value); #else (*h2) = value; #endif }; } else { #if imht_set_can_contain_zero_p (*h2) = ((0 == value) ? 1 : value); #else (*h2) = value; #endif }; } else { #if imht_set_can_contain_zero_p (*h) = ((0 == value) ? 1 : value); #else (*h) = value; #endif return (h); }; }; typedef struct { b0 *data; size_t size; } dg_data_t; typedef struct { dg_id_t id; size_t size; b0 *data; } dg_data_record_t; typedef struct { dg_id_t left; dg_id_t right; dg_id_t label; dg_ordinal_t ordinal; } dg_relation_record_t; #define mi_list_name_prefix dg_ids #define mi_list_element_t dg_id_t /* a minimal linked list with custom element types. this file can be included multiple times to create differently typed versions, depending the value of the preprocessor variables mi-list-name-infix and mi-list-element-t before inclusion */ #ifndef sc_included_stdlib_h #include #define sc_included_stdlib_h #endif #ifndef sc_included_inttypes_h #include #define sc_included_inttypes_h #endif #ifndef mi_list_name_prefix #define mi_list_name_prefix mi_list_64 #endif #ifndef mi_list_element_t #define mi_list_element_t uint64_t #endif /* there does not seem to be a simpler way for identifier concatenation in c in * this case */ #ifndef mi_list_name_concat #define mi_list_name_concat(a, b) a##_##b #define mi_list_name_concatenator(a, b) mi_list_name_concat(a, b) #define mi_list_name(name) mi_list_name_concatenator(mi_list_name_prefix, name) #endif #define mi_list_struct_name mi_list_name(struct) #define mi_list_t mi_list_name(t) typedef struct mi_list_struct_name { struct mi_list_struct_name *link; mi_list_element_t data; } mi_list_t; #ifndef mi_list_first #define mi_list_first(a) (*a).data #define mi_list_first_address(a) &(*a).data #define mi_list_rest(a) (*a).link #endif mi_list_t *mi_list_name(drop)(mi_list_t *a) { mi_list_t *a_next = mi_list_rest(a); free(a); return (a_next); }; /** it would be nice to set the pointer to zero, but that would require more * indirection with a pointer-pointer */ void mi_list_name(destroy)(mi_list_t *a) { mi_list_t *a_next = 0; while (a) { a_next = (*a).link; free(a); a = a_next; }; }; mi_list_t *mi_list_name(add)(mi_list_t *a, mi_list_element_t value) { mi_list_t *element = calloc(1, sizeof(mi_list_t)); if (!element) { return (0); }; (*element).data = value; (*element).link = a; return (element); }; size_t mi_list_name(length)(mi_list_t *a) { size_t result = 0; while (a) { result = (1 + result); a = mi_list_rest(a); }; return (result); }; #undef mi_list_name_prefix #undef mi_list_element_t #undef mi_list_struct_name #undef mi_list_t ; #define mi_list_name_prefix dg_data_list #define mi_list_element_t dg_data_t /* a minimal linked list with custom element types. this file can be included multiple times to create differently typed versions, depending the value of the preprocessor variables mi-list-name-infix and mi-list-element-t before inclusion */ #ifndef sc_included_stdlib_h #include #define sc_included_stdlib_h #endif #ifndef sc_included_inttypes_h #include #define sc_included_inttypes_h #endif #ifndef mi_list_name_prefix #define mi_list_name_prefix mi_list_64 #endif #ifndef mi_list_element_t #define mi_list_element_t uint64_t #endif /* there does not seem to be a simpler way for identifier concatenation in c in * this case */ #ifndef mi_list_name_concat #define mi_list_name_concat(a, b) a##_##b #define mi_list_name_concatenator(a, b) mi_list_name_concat(a, b) #define mi_list_name(name) mi_list_name_concatenator(mi_list_name_prefix, name) #endif #define mi_list_struct_name mi_list_name(struct) #define mi_list_t mi_list_name(t) typedef struct mi_list_struct_name { struct mi_list_struct_name *link; mi_list_element_t data; } mi_list_t; #ifndef mi_list_first #define mi_list_first(a) (*a).data #define mi_list_first_address(a) &(*a).data #define mi_list_rest(a) (*a).link #endif mi_list_t *mi_list_name(drop)(mi_list_t *a) { mi_list_t *a_next = mi_list_rest(a); free(a); return (a_next); }; /** it would be nice to set the pointer to zero, but that would require more * indirection with a pointer-pointer */ void mi_list_name(destroy)(mi_list_t *a) { mi_list_t *a_next = 0; while (a) { a_next = (*a).link; free(a); a = a_next; }; }; mi_list_t *mi_list_name(add)(mi_list_t *a, mi_list_element_t value) { mi_list_t *element = calloc(1, sizeof(mi_list_t)); if (!element) { return (0); }; (*element).data = value; (*element).link = a; return (element); }; size_t mi_list_name(length)(mi_list_t *a) { size_t result = 0; while (a) { result = (1 + result); a = mi_list_rest(a); }; return (result); }; #undef mi_list_name_prefix #undef mi_list_element_t #undef mi_list_struct_name #undef mi_list_t ; #define mi_list_name_prefix dg_data_records #define mi_list_element_t dg_data_record_t /* a minimal linked list with custom element types. this file can be included multiple times to create differently typed versions, depending the value of the preprocessor variables mi-list-name-infix and mi-list-element-t before inclusion */ #ifndef sc_included_stdlib_h #include #define sc_included_stdlib_h #endif #ifndef sc_included_inttypes_h #include #define sc_included_inttypes_h #endif #ifndef mi_list_name_prefix #define mi_list_name_prefix mi_list_64 #endif #ifndef mi_list_element_t #define mi_list_element_t uint64_t #endif /* there does not seem to be a simpler way for identifier concatenation in c in * this case */ #ifndef mi_list_name_concat #define mi_list_name_concat(a, b) a##_##b #define mi_list_name_concatenator(a, b) mi_list_name_concat(a, b) #define mi_list_name(name) mi_list_name_concatenator(mi_list_name_prefix, name) #endif #define mi_list_struct_name mi_list_name(struct) #define mi_list_t mi_list_name(t) typedef struct mi_list_struct_name { struct mi_list_struct_name *link; mi_list_element_t data; } mi_list_t; #ifndef mi_list_first #define mi_list_first(a) (*a).data #define mi_list_first_address(a) &(*a).data #define mi_list_rest(a) (*a).link #endif mi_list_t *mi_list_name(drop)(mi_list_t *a) { mi_list_t *a_next = mi_list_rest(a); free(a); return (a_next); }; /** it would be nice to set the pointer to zero, but that would require more * indirection with a pointer-pointer */ void mi_list_name(destroy)(mi_list_t *a) { mi_list_t *a_next = 0; while (a) { a_next = (*a).link; free(a); a = a_next; }; }; mi_list_t *mi_list_name(add)(mi_list_t *a, mi_list_element_t value) { mi_list_t *element = calloc(1, sizeof(mi_list_t)); if (!element) { return (0); }; (*element).data = value; (*element).link = a; return (element); }; size_t mi_list_name(length)(mi_list_t *a) { size_t result = 0; while (a) { result = (1 + result); a = mi_list_rest(a); }; return (result); }; #undef mi_list_name_prefix #undef mi_list_element_t #undef mi_list_struct_name #undef mi_list_t ; #define mi_list_name_prefix dg_relation_records #define mi_list_element_t dg_relation_record_t /* a minimal linked list with custom element types. this file can be included multiple times to create differently typed versions, depending the value of the preprocessor variables mi-list-name-infix and mi-list-element-t before inclusion */ #ifndef sc_included_stdlib_h #include #define sc_included_stdlib_h #endif #ifndef sc_included_inttypes_h #include #define sc_included_inttypes_h #endif #ifndef mi_list_name_prefix #define mi_list_name_prefix mi_list_64 #endif #ifndef mi_list_element_t #define mi_list_element_t uint64_t #endif /* there does not seem to be a simpler way for identifier concatenation in c in * this case */ #ifndef mi_list_name_concat #define mi_list_name_concat(a, b) a##_##b #define mi_list_name_concatenator(a, b) mi_list_name_concat(a, b) #define mi_list_name(name) mi_list_name_concatenator(mi_list_name_prefix, name) #endif #define mi_list_struct_name mi_list_name(struct) #define mi_list_t mi_list_name(t) typedef struct mi_list_struct_name { struct mi_list_struct_name *link; mi_list_element_t data; } mi_list_t; #ifndef mi_list_first #define mi_list_first(a) (*a).data #define mi_list_first_address(a) &(*a).data #define mi_list_rest(a) (*a).link #endif mi_list_t *mi_list_name(drop)(mi_list_t *a) { mi_list_t *a_next = mi_list_rest(a); free(a); return (a_next); }; /** it would be nice to set the pointer to zero, but that would require more * indirection with a pointer-pointer */ void mi_list_name(destroy)(mi_list_t *a) { mi_list_t *a_next = 0; while (a) { a_next = (*a).link; free(a); a = a_next; }; }; mi_list_t *mi_list_name(add)(mi_list_t *a, mi_list_element_t value) { mi_list_t *element = calloc(1, sizeof(mi_list_t)); if (!element) { return (0); }; (*element).data = value; (*element).link = a; return (element); }; size_t mi_list_name(length)(mi_list_t *a) { size_t result = 0; while (a) { result = (1 + result); a = mi_list_rest(a); }; return (result); }; #undef mi_list_name_prefix #undef mi_list_element_t #undef mi_list_struct_name #undef mi_list_t ; #define dg_ids_first mi_list_first #define dg_ids_first_address mi_list_first_address #define dg_ids_rest mi_list_rest #define dg_data_list_first mi_list_first #define dg_data_list_first_address mi_list_first_address #define dg_data_list_rest mi_list_rest #define dg_data_records_first mi_list_first #define dg_data_records_first_address mi_list_first_address #define dg_data_records_rest mi_list_rest #define dg_relation_records_first mi_list_first #define dg_relation_records_first_address mi_list_first_address #define dg_relation_records_rest mi_list_rest typedef MDB_txn dg_txn_t; #define dg_txn_introduce dg_txn_t *dg_txn = 0 #define dg_txn_begin \ dg_mdb_status_require_x(mdb_txn_begin(dg_mdb_env, 0, MDB_RDONLY, &dg_txn)) #define dg_txn_abort \ mdb_txn_abort(dg_txn); \ dg_txn = 0 #define dg_txn_commit \ dg_mdb_status_require_x(mdb_txn_commit(dg_txn)); \ dg_txn = 0 #define dg_txn_write_begin \ dg_mdb_status_require_x(mdb_txn_begin(dg_mdb_env, 0, 0, &dg_txn)) #define dg_intern_small_id_to_data(id) (id >> 2) #define dg_intern_small_data_to_id(data) (dg_type_intern_small & (data << 2)) typedef struct { MDB_stat id_to_data; MDB_stat data_intern_to_id; MDB_stat data_extern_to_extern; MDB_stat left_to_right; MDB_stat right_to_left; MDB_stat label_to_left; } dg_statistics_t; typedef struct { b8 read_only_p; size_t maximum_size_octets; b32 maximum_reader_count; b8 filesystem_has_ordered_writes_p; b32 env_open_flags; b16 file_permissions; } dg_init_options_t; typedef struct { boolean errors_p; dg_relation_records_t *missing_right_left; dg_relation_records_t *missing_label_left; dg_relation_records_t *excess_right_left; dg_relation_records_t *excess_label_left; } dg_index_errors_relation_t; typedef struct { boolean errors_p; dg_ids_t *different_data_id; dg_ids_t *excess_data_id; dg_ids_t *different_id_data; dg_ids_t *missing_id_data; } dg_index_errors_intern_t; typedef struct { boolean errors_p; dg_ids_t *different_data_extern; dg_ids_t *excess_data_extern; dg_ids_t *different_id_data; dg_ids_t *missing_id_data; } dg_index_errors_extern_t; dg_index_errors_relation_t dg_index_errors_relation_null = {0, 0, 0, 0, 0}; dg_index_errors_intern_t dg_index_errors_intern_null = {0, 0, 0, 0, 0}; dg_index_errors_extern_t dg_index_errors_extern_null = {0, 0, 0, 0, 0}; typedef struct { dg_ordinal_t min; dg_ordinal_t max; } dg_ordinal_match_data_t; typedef struct { status_t status; MDB_cursor *restrict cursor; b8 types; b8 options; } dg_node_read_state_t; typedef struct { status_t status; MDB_cursor *restrict cursor; b8 options; } dg_intern_read_state_t; typedef struct { status_t status; MDB_cursor *restrict cursor; MDB_cursor *restrict cursor_2; b0 *left; b0 *right; b0 *label; dg_ids_t *left_first; dg_ids_t *right_first; dg_ordinal_match_data_t *ordinal; b8 options; b0 *reader; } dg_relation_read_state_t; typedef status_t (*dg_relation_reader_t)(dg_relation_read_state_t *, b32, dg_relation_records_t **); typedef dg_ordinal_t (*dg_relation_ordinal_generator_t)(b0 *); #define dg_type_p(dg_type_name, id) (dg_type_name == (id & dg_type_mask)) status_t dg_node_read(dg_node_read_state_t *state, b32 count, dg_data_records_t **result); status_t dg_node_select(dg_txn_t *txn, b8 types, b32 offset, dg_node_read_state_t *state); b0 dg_node_selection_destroy(dg_node_read_state_t *state); b0 dg_relation_selection_destroy(dg_relation_read_state_t *state); status_t dg_intern_data_to_id(dg_txn_t *txn, dg_data_list_t *data, boolean every_p, dg_ids_t **result); status_t dg_intern_ensure(dg_txn_t *txn, dg_data_list_t *data, dg_ids_t **result); status_t dg_intern_update(dg_txn_t *txn, dg_id_t id, dg_data_t data); status_t dg_extern_update(dg_txn_t *txn, dg_id_t id, dg_data_t data); status_t dg_intern_id_to_data(dg_txn_t *txn, dg_ids_t *ids, boolean every_p, dg_data_list_t **result); b0 dg_exit(); status_t dg_extern_create(dg_txn_t *txn, b32 count, dg_data_t *data, dg_ids_t **result); status_t dg_extern_id_to_data(dg_txn_t *txn, dg_ids_t *ids, boolean every_p, dg_data_list_t **result); status_t dg_extern_data_to_id(dg_txn_t *txn, dg_data_t data, dg_ids_t **result); status_t dg_id_create(dg_txn_t *txn, b32 count, dg_ids_t **result); status_t dg_exists_p(dg_txn_t *txn, dg_ids_t *ids, boolean *result); boolean dg_extern_p(dg_id_t id); boolean dg_id_p(dg_id_t id); boolean dg_intern_small_p(dg_id_t id); status_t dg_identify(dg_txn_t *txn, dg_ids_t *ids, dg_ids_t **result); boolean dg_intern_p(dg_id_t id); boolean dg_relation_p(dg_id_t id); status_t dg_index_errors_extern(dg_txn_t *txn, dg_index_errors_extern_t *result); status_t dg_index_errors_intern(dg_txn_t *txn, dg_index_errors_intern_t *result); status_t dg_index_errors_relation(dg_txn_t *txn, dg_index_errors_relation_t *result); status_t dg_index_recreate_extern(); status_t dg_index_recreate_intern(); status_t dg_index_recreate_relation(); status_t dg_init(b8 *dg_root_argument, dg_init_options_t *options); dg_init_options_t dg_init_options_set_defaults(dg_init_options_t *a); status_t dg_relation_ensure(dg_txn_t *txn, dg_ids_t *left, dg_ids_t *right, dg_ids_t *label, dg_relation_ordinal_generator_t ordinal_generator, b0 *ordinal_generator_state); status_t dg_statistics(dg_txn_t *txn, dg_statistics_t *result); status_t dg_delete(dg_txn_t *txn, dg_ids_t *ids); status_t dg_relation_delete(dg_txn_t *txn, dg_ids_t *left, dg_ids_t *right, dg_ids_t *label, dg_ordinal_match_data_t *ordinal); b8 *dg_status_description(status_t a); b8 *dg_status_name(status_t a); b8 *dg_status_group_id_to_name(status_i_t a); status_t dg_relation_select(dg_txn_t *txn, dg_ids_t *left, dg_ids_t *right, dg_ids_t *label, dg_ordinal_match_data_t *ordinal, b32 offset, dg_relation_read_state_t *result); status_t dg_relation_read(dg_relation_read_state_t *state, b32 count, dg_relation_records_t **result); b8 *dg_root; boolean dg_initialised; MDB_env *dg_mdb_env; #define dg_pointer_allocation_set(result, expression, result_temp) \ result_temp = expression; \ if (result_temp) { \ result = result_temp; \ } else { \ dg_status_set_id_goto(dg_status_id_memory); \ } #define dg_ids_add_x(target, source, ids_temp) \ dg_pointer_allocation_set(target, dg_ids_add(target, source), ids_temp) #define dg_define_ids(name) dg_ids_t *name = 0 #define dg_define_ids_2(name_1, name_2) \ dg_define_ids(name_1); \ dg_define_ids(name_2) #define dg_define_ids_3(name_1, name_2, name_3) \ dg_define_ids_2(name_1, name_2); \ dg_define_ids(name_3) #define dg_relation_data_to_id(a) \ dg_pointer_to_id((1 + ((dg_ordinal_t *)(a))), 0) #define dg_relation_data_to_ordinal(a) (*((dg_ordinal_t *)(a))) #define dg_relation_data_set_id(a, value) dg_relation_data_to_id(a) = value #define dg_relation_data_set_ordinal(a, value) \ dg_relation_data_to_ordinal(a) = value #define dg_relation_data_set_both(a, ordinal, id) \ dg_relation_data_set_ordinal(ordinal); \ dg_relation_data_set_id(id) b0 dg_debug_log_ids(dg_ids_t *a); b0 dg_debug_log_ids_set(imht_set_t a); b0 dg_debug_display_relation_records(dg_relation_records_t *records); status_t dg_debug_count_all_btree_entries(MDB_txn *txn, b32 *result); status_t dg_debug_display_btree_counts(MDB_txn *txn); status_t dg_debug_display_content_left_to_right(dg_txn_t *txn); status_t dg_debug_display_content_right_to_left(dg_txn_t *txn); #endif #ifndef sc_included_sph_dg_lmdb #define sc_included_sph_dg_lmdb #ifndef sc_included_string_h #include #define sc_included_string_h #endif #define dg_mdb_declare_cursor(name) MDB_cursor *name = 0 #define dg_mdb_compare_get_mv_data(mdb_val) (*mdb_val).mv_data #define dg_mdb_cursor_close_2(a, b) \ mdb_cursor_close(a); \ mdb_cursor_close(b) #define dg_mdb_cursor_close_3(a, b, c) \ dg_mdb_cursor_close_2(a, b); \ mdb_cursor_close(c) MDB_val val_null; #define dg_mdb_cursor_get_x(cursor, val_1, val_2, cursor_operation) \ status_set_id(mdb_cursor_get(cursor, &val_1, &val_2, cursor_operation)) #define dg_mdb_cursor_next_dup_x(cursor, val_1, val_2) \ dg_mdb_cursor_get_x(cursor, val_1, val_2, MDB_NEXT_DUP) #define dg_mdb_cursor_next_nodup_x(cursor, val_1, val_2) \ dg_mdb_cursor_get_x(cursor, val_1, val_2, MDB_NEXT_NODUP) #define dg_mdb_cursor_del_x(cursor, flags) \ status_set_id(mdb_cursor_del(cursor, flags)) #define dg_mdb_declare_cursor_2(name_1, name_2) \ dg_mdb_declare_cursor(name_1); \ dg_mdb_declare_cursor(name_2) #define dg_mdb_declare_cursor_3(name_1, name_2, name_3) \ dg_mdb_declare_cursor_2(name_1, name_2); \ dg_mdb_declare_cursor(name_3) #define dg_mdb_initialise_cursor(txn, name) \ dg_mdb_status_require_x(mdb_cursor_open(txn, dbi_##name, &name)) #define dg_mdb_initialise_cursor_2(txn, name_1, name_2) \ dg_mdb_initialise_cursor(txn, name_1); \ dg_mdb_initialise_cursor(txn, name_2) #define dg_mdb_initialise_cursor_3(txn, name_1, name_2, name_3) \ dg_mdb_initialise_cursor_2(txn, name_1, name_2); \ dg_mdb_initialise_cursor(txn, name_3) #define dg_mdb_introduce_cursor(txn, name) \ dg_mdb_declare_cursor(name); \ dg_mdb_initialise_cursor(txn, name) #define dg_mdb_introduce_cursor_2(txn, name_1, name_2) \ dg_mdb_declare_cursor_2(name_1, name_2); \ dg_mdb_initialise_cursor_2(txn, name_1, name_2) #define dg_mdb_introduce_cursor_3(txn, name_1, name_2, name_3) \ dg_mdb_declare_cursor_3(name_1, name_2, name_3); \ dg_mdb_initialise_cursor_3(txn, name_1, name_2, name_3) #define dg_mdb_val_to_id_at(a, index) dg_pointer_to_id(a.mv_data, index) #define dg_mdb_val_to_id(a) dg_pointer_to_id(a.mv_data, 0) #define dg_mdb_introduce_val(name, size) \ MDB_val name; \ name.mv_size = size #define dg_mdb_introduce_val_id dg_mdb_introduce_val(val_id, dg_size_octets_id) #define dg_mdb_introduce_val_id_2 \ dg_mdb_introduce_val(val_id_2, dg_size_octets_id) #define dg_mdb_introduce_val_id_3 \ dg_mdb_introduce_val(val_id_3, dg_size_octets_id) #define dg_mdb_introduce_val_data MDB_val val_data #define dg_mdb_introduce_val_data_2 MDB_val val_data_2 #define dg_mdb_reset_val_null val_null.mv_size = 0 #define dg_mdb_introduce_val_relation_data \ MDB_val val_relation_data; \ val_relation_data.mv_size = dg_size_octets_relation_data #define dg_mdb_introduce_val_relation_key \ MDB_val val_relation_key; \ val_relation_key.mv_size = dg_size_octets_relation_key #define dg_mdb_val_relation_data_to_id(a) dg_relation_data_to_id(a.mv_data) #define dg_mdb_val_relation_data_to_ordinal(a) \ dg_relation_data_to_ordinal(a.mv_data) static int dg_mdb_compare_id(const MDB_val *a, const MDB_val *b) { return (dg_id_compare(dg_pointer_to_id(dg_mdb_compare_get_mv_data(a), 0), dg_pointer_to_id(dg_mdb_compare_get_mv_data(b), 0))); }; static int dg_mdb_compare_relation_key(const MDB_val *a, const MDB_val *b) { return (((dg_mdb_val_to_id_at((*a), 0) < dg_mdb_val_to_id_at((*b), 0)) ? -1 : ((dg_mdb_val_to_id_at((*a), 0) > dg_mdb_val_to_id_at((*b), 0)) ? 1 : ((dg_mdb_val_to_id_at((*a), 1) < dg_mdb_val_to_id_at((*b), 1)) ? -1 : (dg_mdb_val_to_id_at((*a), 1) > dg_mdb_val_to_id_at((*b), 1)))))); }; static int dg_mdb_compare_relation_data(const MDB_val *a, const MDB_val *b) { return (((dg_mdb_val_relation_data_to_ordinal((*a)) < dg_mdb_val_relation_data_to_ordinal((*b))) ? -1 : ((dg_mdb_val_relation_data_to_ordinal((*a)) > dg_mdb_val_relation_data_to_ordinal((*b))) ? 1 : ((dg_mdb_val_relation_data_to_id((*a)) < dg_mdb_val_relation_data_to_id((*b))) ? -1 : (dg_mdb_val_relation_data_to_id((*a)) > dg_mdb_val_relation_data_to_id((*b))))))); }; static int dg_mdb_compare_data(const MDB_val *a, const MDB_val *b) { ssize_t length_difference = (((ssize_t)((*a).mv_size)) - ((ssize_t)((*b).mv_size))); return ((length_difference ? ((length_difference < 0) ? -1 : 1) : memcmp((*a).mv_data, (*b).mv_data, (*a).mv_size))); }; #define dg_mdb_cursor_each_key(cursor, val_key, val_value, body) \ dg_mdb_cursor_get_x(cursor, val_key, val_value, MDB_FIRST); \ while (dg_mdb_status_success_p) { \ body; \ dg_mdb_cursor_next_nodup_x(cursor, val_key, val_value); \ }; \ dg_mdb_status_require_notfound #define dg_mdb_cursor_set_first_x(cursor) \ dg_mdb_status_require_x( \ mdb_cursor_get(cursor, &val_null, &val_null, MDB_FIRST)) #define dg_mdb_val_to_relation_key(a) ((dg_id_t *)(a.mv_data)) #endif #ifndef sc_included_sph_dg_debug #define sc_included_sph_dg_debug b0 dg_debug_log_ids(dg_ids_t *a) { while (a) { debug_log("%lu", dg_ids_first(a)); a = dg_ids_rest(a); }; }; b0 dg_debug_log_ids_set(imht_set_t a) { b32 index = 0; while ((index < a.size)) { debug_log("%lu", (*(a.content + index))); index = (1 + index); }; }; b0 dg_debug_display_relation_records(dg_relation_records_t *records) { dg_relation_record_t record; printf("relation records\n"); while (records) { record = dg_relation_records_first(records); printf(" lcor %lu %lu %lu %lu\n", record.left, record.label, record.ordinal, record.right); records = dg_relation_records_rest(records); }; }; status_t dg_debug_count_all_btree_entries(MDB_txn *txn, b32 *result) { status_init; dg_statistics_t stat; status_require_x(dg_statistics(txn, &stat)); (*result) = (stat.id_to_data.ms_entries + stat.data_intern_to_id.ms_entries + stat.data_extern_to_extern.ms_entries + stat.left_to_right.ms_entries + stat.right_to_left.ms_entries + stat.label_to_left.ms_entries); exit: return (status); }; status_t dg_debug_display_btree_counts(MDB_txn *txn) { status_init; dg_statistics_t stat; status_require_x(dg_statistics(txn, &stat)); printf("btree entry count\n id->data %d data-intern->id %d\n " "data-extern->extern %d left->right %d\n right->left %d label->left " "%d\n", stat.id_to_data.ms_entries, stat.data_intern_to_id.ms_entries, stat.data_extern_to_extern.ms_entries, stat.left_to_right.ms_entries, stat.right_to_left.ms_entries, stat.label_to_left.ms_entries); exit: return (status); }; #endif #ifndef sc_included_sph_one #define sc_included_sph_one /* depends on sph.sc */ #ifndef sc_included_string_h #include #define sc_included_string_h #endif #ifndef sc_included_stdlib_h #include #define sc_included_stdlib_h #endif /** set result to a new string with a trailing slash added, or the given string if it already has a trailing slash. returns 0 if result is the given string, 1 if new memory could not be allocated, 2 if result is a new string */ b8 ensure_trailing_slash(b8 *a, b8 **result) { b32 a_len = strlen(a); if ((!a_len || (('/' == (*(a + (a_len - 1))))))) { (*result) = a; return (0); } else { char *new_a = malloc((2 + a_len)); if (!new_a) { return (1); }; memcpy(new_a, a, a_len); memcpy((new_a + a_len), "/", 1); (*(new_a + (1 + a_len))) = 0; (*result) = new_a; return (2); }; }; /** always returns a new string */ b8 *string_append(b8 *a, b8 *b) { size_t a_length = strlen(a); size_t b_length = strlen(b); b8 *result = malloc((1 + a_length + b_length)); if (result) { memcpy(result, a, a_length); memcpy((result + a_length), b, (1 + b_length)); }; return (result); }; /** return a new string with the same contents as the given string. return 0 if * the memory allocation failed */ b8 *string_clone(b8 *a) { size_t a_size = (1 + strlen(a)); b8 *result = malloc(a_size); if (result) { memcpy(result, a, a_size); }; return (result); }; #ifndef sc_included_unistd_h #include #define sc_included_unistd_h #endif #ifndef sc_included_sys_stat_h #include #define sc_included_sys_stat_h #endif #ifndef sc_included_libgen_h #include #define sc_included_libgen_h #endif #ifndef sc_included_errno_h #include #define sc_included_errno_h #endif #define file_exists_p(path) !(access(path, F_OK) == -1) /** like posix dirname, but never modifies its argument and always returns a new * string */ b8 *dirname_2(b8 *a) { b8 *path_copy = string_clone(a); return (dirname(path_copy)); }; /** return 1 if the path exists or has been successfully created */ boolean ensure_directory_structure(b8 *path, mode_t mkdir_mode) { if (file_exists_p(path)) { return (1); } else { b8 *path_dirname = dirname_2(path); boolean status = ensure_directory_structure(path_dirname, mkdir_mode); free(path_dirname); return ((status && ((((EEXIST == errno)) || ((0 == mkdir(path, mkdir_mode))))))); }; }; #endif status_t dg_ids_reverse(dg_ids_t *source, dg_ids_t **result) { status_init; dg_ids_t *ids_temp; while (source) { dg_ids_add_x((*result), dg_ids_first(source), ids_temp); source = dg_ids_rest(source); }; exit: return (status); }; boolean dg_ids_contains_p(dg_ids_t *ids, dg_id_t id) { while (ids) { if ((id == dg_ids_first(ids))) { return (1); }; ids = dg_ids_rest(ids); }; return (0); }; status_t dg_debug_display_all_relations(MDB_txn *txn) { status_init; dg_relation_records_t *records; dg_relation_read_state_t state; records = 0; dg_status_require_read_x(dg_relation_select(txn, 0, 0, 0, 0, 0, &state)); dg_status_require_read_x(dg_relation_read(&state, 0, &records)); printf("all "); dg_relation_selection_destroy(&state); dg_debug_display_relation_records(records); dg_relation_records_destroy(records); exit: return (status); }; #define dg_debug_define_relation_records_contains_at_p(field) \ boolean dg_debug_relation_records_contains_at_##field##_p( \ dg_relation_records_t *records, dg_id_t id) { \ while (records) { \ if ((id == dg_relation_records_first(records).field)) { \ return (1); \ }; \ records = dg_relation_records_rest(records); \ }; \ return (0); \ } dg_debug_define_relation_records_contains_at_p(left); dg_debug_define_relation_records_contains_at_p(right); dg_debug_define_relation_records_contains_at_p(label); #define test_helper_dg_root "/tmp/test-sph-dg" #define test_helper_path_data test_helper_dg_root "/data" #define test_helper_assert(description, expression) \ if (!expression) { \ printf("%s failed\n", description); \ status_set_id_goto(1); \ } #define test_helper_filter_ids_to_reader_suffix_integer(left, right, label, \ ordinal) \ ((left ? 8 : 0) | (right ? 4 : 0) | (label ? 2 : 0) | (ordinal ? 1 : 0)) #define test_helper_test_one(func) \ printf("%s\n", #func); \ status_require_x(test_helper_dg_reset(0)); \ status_require_x(func()) status_t test_helper_create_ids(b32 count, dg_ids_t **result) { status_init; dg_define_ids(ids_temp); dg_txn_introduce; dg_txn_write_begin; dg_id_create(dg_txn, count, &ids_temp); dg_txn_commit; status_require(dg_ids_reverse(ids_temp, result)); exit: return (status); }; status_t test_helper_create_interns(b32 count, dg_ids_t **result) { status_init; dg_data_t data_element = {.size = dg_size_octets_id, .data = 0}; dg_data_list_t *data_list = 0; while (count) { data_element.data = calloc(1, sizeof(dg_ids_t)); (*((dg_id_t *)(data_element.data))) = (123 + count); data_list = dg_data_list_add(data_list, data_element); count = (count - 1); }; dg_txn_introduce; dg_txn_write_begin; status_require_x(dg_intern_ensure(dg_txn, data_list, result)); dg_txn_commit; exit: if (dg_txn) { dg_txn_abort; }; return (status); }; dg_ordinal_t test_helper_default_ordinal_generator(b0 *state) { dg_ordinal_t *ordinal_pointer = state; dg_ordinal_t result = (1 + (*ordinal_pointer)); (*ordinal_pointer) = result; return (result); }; status_t test_helper_create_relations(b32 count_left, b32 count_right, b32 count_label, dg_ids_t **left, dg_ids_t **right, dg_ids_t **label) { status_init; status_require_x(test_helper_create_ids(count_left, left)); status_require_x(test_helper_create_ids(count_right, right)); status_require_x(test_helper_create_ids(count_label, label)); dg_ordinal_t ordinal_state_value = 0; dg_txn_introduce; dg_txn_write_begin; status_require_x(dg_relation_ensure(dg_txn, (*left), (*right), (*label), test_helper_default_ordinal_generator, &ordinal_state_value)); dg_txn_commit; exit: if (dg_txn) { dg_txn_abort; }; return (status); }; b32 test_helper_calculate_relation_count(b32 left_count, b32 right_count, b32 label_count) { return ((left_count * right_count * label_count)); }; b32 test_helper_calculate_relation_count_from_ids(dg_ids_t *left, dg_ids_t *right, dg_ids_t *label) { return (test_helper_calculate_relation_count( dg_ids_length(left), dg_ids_length(right), dg_ids_length(label))); }; b32 test_helper_estimate_relation_read_result_count( b32 left_count, b32 right_count, b32 label_count, dg_ordinal_match_data_t *ordinal) { b32 count = (left_count * right_count * label_count); b32 max; b32 min; if (ordinal) { min = ((*ordinal).min ? ((*ordinal).min - 1) : 0); max = (*ordinal).max; ((max > count) ? (max = count) : 0); } else { min = 0; max = count; }; return ((count - min - (count - max))); }; b32 test_helper_estimate_relation_read_btree_entry_count( b32 existing_left_count, b32 existing_right_count, b32 existing_label_count, dg_ordinal_match_data_t *ordinal) { b32 ordinal_min = 0; b32 ordinal_max = 0; if (ordinal) { ordinal_min = (*ordinal).min; ordinal_max = (*ordinal).max; }; b32 label_left_count = 0; b32 left_right_count = 0; b32 right_left_count = 0; b32 ordinal_value = 1; b32 left_count = 0; b32 right_count = 0; b32 label_count = 0; while ((label_count < existing_label_count)) { while ((left_count < existing_left_count)) { if ((((ordinal_value <= ordinal_max)) && ((ordinal_value >= ordinal_min)))) { increment(label_left_count); }; while ((right_count < existing_right_count)) { if ((((ordinal_value <= ordinal_max)) && ((ordinal_value >= ordinal_min)))) { increment(ordinal_value); increment(left_right_count); increment(right_left_count); }; increment(right_count); }; increment(left_count); }; increment(label_count); }; return ((left_right_count + right_left_count + label_left_count)); }; status_t test_helper_ids_add_new_ids(dg_ids_t *ids_old, dg_ids_t **result) { status_init; dg_define_ids(ids_new); (*result) = 0; status_require_x(test_helper_create_ids(dg_ids_length(ids_old), &ids_new)); b32 target_count = (2 * dg_ids_length(ids_old)); b32 start_mixed = (target_count / 4); b32 start_new = (target_count - start_mixed); b32 count = 0; while ((count < target_count)) { if ((count < start_mixed)) { (*result) = dg_ids_add((*result), dg_ids_first(ids_old)); ids_old = dg_ids_rest(ids_old); } else { if ((count < start_new)) { if ((1 & count)) { (*result) = dg_ids_add((*result), dg_ids_first(ids_old)); ids_old = dg_ids_rest(ids_old); } else { (*result) = dg_ids_add((*result), dg_ids_first(ids_new)); ids_new = dg_ids_rest(ids_new); }; } else { (*result) = dg_ids_add((*result), dg_ids_first(ids_new)); ids_new = dg_ids_rest(ids_new); }; }; count = (1 + count); }; exit: return (status); }; b8 *test_helper_reader_suffix_integer_to_string(b8 a) { b8 *result = malloc(40); (*(result + 0)) = ((8 & a) ? '1' : '0'); (*(result + 1)) = ((4 & a) ? '1' : '0'); (*(result + 2)) = ((2 & a) ? '1' : '0'); (*(result + 3)) = ((1 & a) ? '1' : '0'); (*(result + 4)) = 0; return (result); }; status_t test_helper_dg_reset(boolean re_use) { status_init; if (dg_initialised) { dg_exit(); }; if ((!re_use && file_exists_p(test_helper_path_data))) { status_set_id(system("rm " test_helper_path_data)); }; status_require; status_require_x(dg_init(test_helper_dg_root, 0)); exit: return (status); }; #endif b32 common_element_count = 40; b32 common_label_count = 40; #define test_relation_read_records_validate_one(name) \ records_temp = records; \ while (records_temp) { \ if (!dg_ids_contains_p(existing_##name, \ dg_relation_records_first(records_temp).name)) { \ printf("\n result records contain inexistant %s ids\n", #name); \ dg_debug_display_relation_records(records); \ status_set_id_goto(1); \ }; \ records_temp = dg_relation_records_rest(records_temp); \ }; \ ids_temp = existing_##name; \ while (ids_temp) { \ if (!dg_debug_relation_records_contains_at_##name##_p( \ records, dg_ids_first(ids_temp))) { \ printf("\n %s result records do not contain all existing-ids\n", \ #name); \ dg_debug_display_relation_records(records); \ status_set_id_goto(2); \ }; \ ids_temp = dg_ids_rest(ids_temp); \ } status_t test_relation_read_records_validate( dg_relation_records_t *records, dg_ids_t *left, dg_ids_t *existing_left, dg_ids_t *right, dg_ids_t *existing_right, dg_ids_t *label, dg_ids_t *existing_label, dg_ordinal_match_data_t *ordinal) { status_init; dg_relation_records_t *records_temp; dg_ids_t *ids_temp; test_relation_read_records_validate_one(left); test_relation_read_records_validate_one(right); test_relation_read_records_validate_one(label); exit: return (status); }; #define test_relation_read_header \ status_init; \ dg_relation_read_state_t state; \ b32 ordinal_min = 2; \ b32 ordinal_max = 5; \ dg_ordinal_match_data_t ordinal_match_data = {ordinal_min, ordinal_max}; \ dg_ordinal_match_data_t *ordinal = &ordinal_match_data; \ dg_relation_records_t *records = 0; \ b32 existing_left_count = common_label_count; \ b32 existing_right_count = common_element_count; \ b32 existing_label_count = common_label_count; \ b32 expected_count; \ b8 reader_suffix; \ b8 *reader_suffix_string; \ dg_define_ids_3(existing_left, existing_right, existing_label); \ dg_define_ids_3(left, right, label); \ status_require_x(test_helper_create_relations( \ existing_left_count, existing_right_count, existing_label_count, \ &existing_left, &existing_right, &existing_label)); \ status_require_x(test_helper_ids_add_new_ids(existing_left, &left)); \ status_require_x(test_helper_ids_add_new_ids(existing_right, &right)); \ status_require_x(test_helper_ids_add_new_ids(existing_label, &label)); \ dg_txn_introduce; \ dg_txn_begin; \ printf(" ") #define test_relation_read_one(left, right, label, ordinal, offset) \ reader_suffix = test_helper_filter_ids_to_reader_suffix_integer( \ left, right, label, ordinal); \ reader_suffix_string = \ test_helper_reader_suffix_integer_to_string(reader_suffix); \ printf(" %s", reader_suffix_string); \ free(reader_suffix_string); \ records = 0; \ status_require_x(dg_relation_select(dg_txn, left, right, label, ordinal, \ offset, &state)); \ dg_status_require_read_x(dg_relation_read(&state, 2, &records)); \ dg_status_require_read_x(dg_relation_read(&state, 0, &records)); \ if (status_id_is_p(dg_status_id_no_more_data)) { \ status_set_id(status_id_success); \ } else { \ printf( \ "\n final read result does not indicate that there is no more data"); \ status_set_id_goto(1); \ }; \ expected_count = test_helper_estimate_relation_read_result_count( \ existing_left_count, existing_right_count, existing_label_count, \ ordinal); \ if (!(dg_relation_records_length(records) == expected_count)) { \ printf("\n expected %lu read %lu. ordinal min %d max %d\n", \ expected_count, dg_relation_records_length(records), \ (ordinal ? ordinal_min : 0), (ordinal ? ordinal_max : 0)); \ printf("the read "); \ dg_debug_display_relation_records(records); \ dg_debug_display_all_relations(dg_txn); \ status_set_id_goto(1); \ }; \ if (!ordinal) { \ status_require_x(test_relation_read_records_validate( \ records, left, existing_left, right, existing_right, label, \ existing_label, ordinal)); \ }; \ dg_status_success_if_no_more_data; \ dg_relation_selection_destroy(&state); \ dg_relation_records_destroy(records) #define test_relation_delete_header \ status_init; \ dg_relation_read_state_t state; \ dg_relation_records_t *records = 0; \ dg_define_ids_3(left, right, label); \ dg_ordinal_match_data_t ordinal_match_data = {2, 5}; \ dg_ordinal_match_data_t *ordinal = &ordinal_match_data; \ b32 read_count_before_expected; \ b32 btree_count_after_delete; \ b32 existing_left_count = common_label_count; \ b32 btree_count_before_delete; \ b32 btree_count_deleted_expected; \ b32 btree_count_after_expected; \ b32 existing_right_count = common_element_count; \ b32 existing_label_count = common_label_count; \ dg_txn_introduce; \ printf(" ") #define test_relation_delete_one(left_p, right_p, label_p, ordinal_p) \ printf(" %d%d%d%d", left_p, right_p, label_p, ordinal_p); \ dg_txn_begin; \ dg_debug_count_all_btree_entries(dg_txn, &btree_count_before_delete); \ dg_txn_abort; \ status_require_x(test_helper_create_relations( \ common_label_count, common_element_count, common_label_count, &left, \ &right, &label)); \ dg_txn_write_begin; \ status_require_x( \ dg_relation_delete(dg_txn, (left_p ? left : 0), (right_p ? right : 0), \ (label_p ? label : 0), (ordinal_p ? ordinal : 0))); \ dg_debug_count_all_btree_entries(dg_txn, &btree_count_after_delete); \ dg_status_require_read_x(dg_relation_select( \ dg_txn, (left_p ? left : 0), (right_p ? right : 0), \ (label_p ? label : 0), (ordinal_p ? ordinal : 0), 0, &state)); \ dg_status_require_read_x(dg_relation_read(&state, 0, &records)); \ dg_relation_selection_destroy(&state); \ dg_txn_commit; \ read_count_before_expected = \ test_helper_estimate_relation_read_result_count( \ existing_left_count, existing_right_count, existing_label_count, \ ordinal); \ if (!(0 == dg_relation_records_length(records))) { \ printf("\n failed deletion. %lu relations not deleted\n", \ dg_relation_records_length(records)); \ dg_debug_display_relation_records(records); \ dg_txn_begin; \ dg_txn_abort; \ status_set_id_goto(1); \ }; \ dg_relation_records_destroy(records); \ records = 0; \ btree_count_before_delete = \ (btree_count_before_delete + existing_left_count + \ existing_right_count + existing_label_count); \ btree_count_deleted_expected = \ test_helper_estimate_relation_read_btree_entry_count( \ existing_left_count, existing_right_count, existing_label_count, \ ordinal); \ btree_count_after_expected = \ (btree_count_after_delete - btree_count_deleted_expected); \ if (!(((btree_count_after_expected == btree_count_after_delete)) && \ ((ordinal_p \ ? 1 \ : (btree_count_after_delete == btree_count_before_delete))))) { \ printf( \ "\n failed deletion. %lu btree entries remaining, expected %lu\n", \ btree_count_after_delete, btree_count_after_expected); \ dg_txn_begin; \ dg_debug_display_btree_counts(dg_txn); \ dg_status_require_read_x( \ dg_relation_select(dg_txn, 0, 0, 0, 0, 0, &state)); \ dg_status_require_read_x(dg_relation_read(&state, 0, &records)); \ printf("all remaining "); \ dg_debug_display_relation_records(records); \ dg_relation_selection_destroy(&state); \ dg_txn_abort; \ status_set_id_goto(1); \ }; \ dg_ids_destroy(left); \ dg_ids_destroy(right); \ dg_ids_destroy(label); \ records = 0; \ left = 0; \ right = 0; \ label = 0 status_t test_id_create_identify_exists() { status_init; dg_ids_t *ids_result = 0; dg_txn_introduce; dg_txn_write_begin; status_require_x(dg_id_create(dg_txn, 3, &ids_result)); if (!(ids_result && ((3 == dg_ids_length(ids_result))))) { status_set_id_goto(1); }; boolean boolean_result; status_require_x(dg_exists_p(dg_txn, ids_result, &boolean_result)); test_helper_assert("dg-exists?", boolean_result); dg_ids_t *ids = ids_result; ids_result = 0; status_require_x(dg_identify(dg_txn, ids, &ids_result)); test_helper_assert("result length", (ids_result && ((3 == dg_ids_length(ids_result))))); exit: if (dg_txn) { dg_txn_abort; }; dg_ids_destroy(ids); dg_ids_destroy(ids_result); return (status); }; status_t test_statistics() { status_init; dg_txn_introduce; dg_txn_begin; dg_statistics_t stat; status_require_x(dg_statistics(dg_txn, &stat)); exit: if (dg_txn) { dg_txn_abort; }; return (status); }; status_t test_extern() { dg_ids_t *data_ids = 0; status_init; dg_txn_introduce; dg_txn_write_begin; dg_id_t data_element_value = 2; dg_data_t data_element = {.size = dg_size_octets_id, .data = &data_element_value}; status_require_x(dg_extern_create(dg_txn, 50, &data_element, &data_ids)); test_helper_assert("dg-extern? 1", (data_ids && dg_extern_p(dg_ids_first(data_ids)))); dg_data_list_t *data_list = 0; status_require_x(dg_extern_id_to_data(dg_txn, data_ids, 1, &data_list)); test_helper_assert("data-equal?", ((*((dg_id_t *)(data_element.data))) == (*((dg_id_t *)(dg_data_list_first(data_list).data))))); dg_ids_t *data_ids_2 = 0; dg_status_require_read_x( dg_extern_data_to_id(dg_txn, data_element, &data_ids_2)); test_helper_assert("dg-extern? 2", dg_extern_p(dg_ids_first(data_ids))); test_helper_assert("dg-extern? 3 ", dg_extern_p(dg_ids_first(data_ids_2))); dg_txn_commit; exit: if (dg_txn) { dg_txn_abort; }; return (status); }; status_t test_intern() { dg_ids_t *data_ids = 0; dg_id_t data_element_value = 2; dg_data_t data_element = {.size = dg_size_octets_id, .data = &data_element_value}; dg_data_list_t *data = dg_data_list_add(0, data_element); status_init; dg_txn_introduce; dg_txn_write_begin; status_require_x(dg_intern_ensure(dg_txn, data, &data_ids)); test_helper_assert("dg-intern-ensure result length", (dg_ids_length(data_ids) == dg_data_list_length(data))); dg_id_t id_first = dg_ids_first(data_ids); test_helper_assert("dg-intern?", (data_ids && dg_intern_p(id_first))); dg_data_list_t *data_2 = 0; dg_ids_t *data_ids_2 = 0; status_require_x(dg_intern_id_to_data(dg_txn, data_ids, 1, &data_2)); test_helper_assert( "id->data return length", (data && ((dg_ids_length(data_ids) == dg_data_list_length(data_2))))); status_require_x(dg_intern_data_to_id(dg_txn, data, 1, &data_ids_2)); dg_txn_commit; test_helper_assert( "data->id return length", (data && ((dg_ids_length(data_ids_2) == dg_data_list_length(data))))); dg_txn_write_begin; data_element_value = 9; status_require_x(dg_intern_update(dg_txn, id_first, data_element)); status_t status_2 = {0, 0}; status_2 = dg_intern_update(dg_txn, id_first, data_element); test_helper_assert("duplicate update prevention", (dg_status_id_duplicate == status_2.id)); dg_txn_commit; dg_txn_begin; dg_ids_t *data_ids_3 = dg_ids_add(0, id_first); dg_data_list_t *data_3 = 0; status_require_x(dg_intern_id_to_data(dg_txn, data_ids_3, 1, &data_3)); dg_txn_abort; exit: if (dg_txn) { dg_txn_abort; }; dg_ids_destroy(data_ids); dg_ids_destroy(data_ids_2); dg_data_list_destroy(data); dg_data_list_destroy(data_2); return (status); }; status_t test_index() { status_init; dg_ids_t *ids; dg_define_ids_3(left, right, label); status_require_x( test_helper_create_relations(common_label_count, common_element_count, common_label_count, &left, &right, &label)); status_require_x(test_helper_create_interns(common_element_count, &ids)); dg_txn_introduce; status_require_x(dg_index_recreate_intern()); status_require_x(dg_index_recreate_extern()); dg_index_errors_extern_t index_errors_extern; dg_index_errors_intern_t index_errors_intern; dg_index_errors_relation_t index_errors_relation; dg_txn_begin; status_require_x(dg_index_errors_intern(dg_txn, &index_errors_intern)); status_require_x(dg_index_errors_extern(dg_txn, &index_errors_extern)); status_require_x(dg_index_errors_relation(dg_txn, &index_errors_relation)); test_helper_assert("errors-intern?", !index_errors_intern.errors_p); test_helper_assert("errors-extern?", !index_errors_extern.errors_p); test_helper_assert("errors-relation?", !index_errors_relation.errors_p); exit: if (dg_txn) { dg_txn_abort; }; return (status); }; status_t test_relation() { status_init; dg_define_ids_3(right, left, label); status_require_x( test_helper_create_relations(common_label_count, common_element_count, common_label_count, &left, &right, &label)); dg_txn_introduce; dg_txn_write_begin; status_require_x(dg_relation_ensure(dg_txn, left, right, label, 0, 0)); dg_txn_commit; exit: if (dg_txn) { dg_txn_abort; }; return (status); }; status_t test_node_read() { status_init; dg_ids_t *ids_intern = 0; dg_ids_t *ids_id = 0; status_require_x( test_helper_create_interns(common_element_count, &ids_intern)); status_require_x(test_helper_create_ids(common_element_count, &ids_id)); dg_txn_introduce; dg_txn_begin; dg_node_read_state_t state; status_require_x(dg_node_select(dg_txn, 0, 0, &state)); dg_data_records_t *records = 0; dg_status_require_read_x(dg_node_read(&state, 0, &records)); dg_node_selection_destroy(&state); test_helper_assert("result length", (dg_data_records_length(records) == (2 * common_element_count))); dg_data_records_destroy(records); records = 0; status_require_x(dg_node_select(dg_txn, 1, 0, &state)); dg_status_require_read_x(dg_node_read(&state, 0, &records)); dg_node_selection_destroy(&state); test_helper_assert("result length with type filter", (dg_data_records_length(records) == common_element_count)); dg_data_records_destroy(records); exit: if (dg_txn) { dg_txn_abort; }; dg_status_success_if_no_more_data; return (status); }; #define test_relation_read_body \ test_relation_read_one(left, 0, 0, 0, 0); \ test_relation_read_one(left, 0, label, 0, 0); \ test_relation_read_one(left, right, 0, 0, 0); \ test_relation_read_one(left, right, label, 0, 0); \ test_relation_read_one(0, 0, 0, 0, 0); \ test_relation_read_one(0, 0, label, 0, 0); \ test_relation_read_one(0, right, 0, 0, 0); \ test_relation_read_one(0, right, label, 0, 0); \ test_relation_read_one(left, 0, 0, ordinal, 0); \ test_relation_read_one(left, 0, label, ordinal, 0); \ test_relation_read_one(left, right, 0, ordinal, 0); \ test_relation_read_one(left, right, label, ordinal, 0); \ dg_status_success_if_no_more_data status_t test_relation_read() { test_relation_read_header; test_relation_read_body; exit: printf("\n"); if (dg_txn) { dg_txn_abort; }; return (status); }; status_t test_relation_delete() { test_relation_delete_header; test_relation_delete_one(1, 0, 0, 0); test_relation_delete_one(1, 0, 1, 0); test_relation_delete_one(1, 1, 0, 0); test_relation_delete_one(1, 1, 1, 0); test_relation_delete_one(0, 0, 1, 0); test_relation_delete_one(0, 1, 0, 0); test_relation_delete_one(0, 1, 1, 0); test_relation_delete_one(1, 0, 0, 1); test_relation_delete_one(1, 0, 1, 1); test_relation_delete_one(1, 1, 0, 1); test_relation_delete_one(1, 1, 1, 1); exit: printf("\n"); return (status); }; b0 *test_concurrent_write_or_read_thread(b0 *status_pointer) { status_init; status = (*((status_t *)(status_pointer))); dg_relation_read_state_t state; dg_relation_records_t *records = 0; dg_txn_introduce; dg_txn_begin; records = 0; status_require_x(dg_relation_select(dg_txn, 0, 0, 0, 0, 0, &state)); dg_status_require_read_x(dg_relation_read(&state, 2, &records)); dg_status_require_read_x(dg_relation_read(&state, 0, &records)); dg_txn_abort; exit: dg_status_success_if_no_more_data; (*((status_t *)(status_pointer))) = status; }; status_t test_concurrent_write_or_read() { status_init; pthread_t thread_two; pthread_t thread_three; status_require_x(test_helper_dg_reset(0)); dg_define_ids_3(left, right, label); status_require_x( test_helper_create_relations(common_element_count, common_element_count, common_label_count, &left, &right, &label)); status_t thread_two_result = {0, 0}; status_t thread_three_result = {0, 0}; if (pthread_create(&thread_two, 0, test_concurrent_write_or_read_thread, &thread_two_result)) { printf("error creating thread"); status_set_id_goto(1); }; if (pthread_create(&thread_three, 0, test_concurrent_write_or_read_thread, &thread_three_result)) { printf("error creating thread"); status_set_id_goto(1); }; test_concurrent_write_or_read_thread(&status); status_require; if (pthread_join(thread_two, 0)) { printf("error joining thread"); status_set_id_goto(2); }; if (pthread_join(thread_three, 0)) { printf("error joining thread"); status_set_id_goto(2); }; status = thread_two_result; status_require; status = thread_three_result; exit: return (status); }; status_t test_id_creation() { status_init; dg_txn_introduce; b32 count = 32; dg_ids_t *ids = 0; while (count) { dg_txn_write_begin; dg_id_create(dg_txn, 2, &ids); dg_txn_commit; test_helper_dg_reset(1); count = (count - 1); }; dg_ids_t *ids_a = ids; dg_ids_t *ids_b = 0; dg_ids_t *ids_c = ids_b; while (ids_a) { while (ids_c) { if ((dg_ids_first(ids_a) == dg_ids_first(ids_c))) { status_set_id_goto(1); }; ids_c = dg_ids_rest(ids_c); }; ids_b = dg_ids_add(ids_b, dg_ids_first(ids_a)); ids_c = ids_b; ids_a = dg_ids_rest(ids_a); }; dg_ids_destroy(ids); exit: return (status); }; int main() { status_init; test_helper_test_one(test_id_creation); test_helper_test_one(test_concurrent_write_or_read); test_helper_test_one(test_relation_read); test_helper_test_one(test_relation_delete); test_helper_test_one(test_statistics); test_helper_test_one(test_id_create_identify_exists); test_helper_test_one(test_intern); test_helper_test_one(test_extern); test_helper_test_one(test_node_read); test_helper_test_one(test_relation); test_helper_test_one(test_index); exit: if (dg_initialised) { dg_exit(); }; if (status_success_p) { printf("--\ntests finished successfully.\n"); } else { printf("\ntests failed. %d %s\n", status.id, dg_status_description(status)); }; return (status.id); };