/* ********************************************************************************************** * File: maat_hierarchy.cpp * Description: * Authors: Zheng Chao * Date: 2022-10-31 * Copyright: (c) 2018-2022 Geedge Networks, Inc. All rights reserved. *********************************************************************************************** */ #include #include #include "utils.h" #include "maat_utils.h" #include "log/log.h" #include "uthash/utarray.h" #include "uthash/uthash.h" #include "bool_matcher.h" #include "igraph/igraph.h" #include "maat_hierarchy.h" #include "maat_garbage_collection.h" #define MODULE_HIERARCHY module_name_str("maat.hierarchy") struct maat_hierarchy_clause_state { unsigned long long clause_id; char not_flag; char in_use; UT_array *literal_ids; }; struct maat_hierarchy_compile { unsigned int magic; int compile_id; int actual_clause_num; int declared_clause_num; int not_clause_cnt; void *user_data; UT_hash_handle hh; struct maat_hierarchy_clause_state clause_states[MAX_ITEMS_PER_BOOL_EXPR]; }; struct maat_hierarchy_group { igraph_integer_t vertex_id; int group_id; int ref_by_compile_cnt; int ref_by_superior_group_cnt; int ref_by_subordinate_group_cnt; int ref_by_region_cnt; size_t top_group_cnt; int *top_group_ids; UT_hash_handle hh_group_id; UT_hash_handle hh_vertex_id; }; struct maat_hierarchy_region { int region_id; int group_id; int table_id; struct maat_hierarchy_group *ref_parent_group; UT_hash_handle hh; void *user_data; }; struct maat_hierarchy_literal_id { int group_id; int vt_id; }; struct maat_hierarchy_clause { long long clause_id; size_t n_literal_id; struct maat_hierarchy_literal_id *literal_ids; UT_hash_handle hh; }; struct region2clause_key { int region_id; int vt_id; }; struct group2region { int group_id; UT_array *region_ids; UT_hash_handle hh; //index to }; struct region2clause_value { struct region2clause_key key; UT_array *clause_ids; int group_id; UT_hash_handle hh; //index to }; struct maat_hierarchy_internal_hit_path { int Nth_scan; int Nth_hit_region; int region_id; int virtual_table_id; }; struct maat_hierarchy { pthread_rwlock_t rwlock; pthread_mutex_t mutex; time_t version; //After full update, clause id may indicate a different clause. Comparing hier->version and mid->hier_ver can prevent false positive match. int changed_flag; struct maat_hierarchy_compile *hash_compile_by_id; //key: compile_id, value: struct Maat_hierarchy_compile*. void (* compile_user_data_free)(void *compile_ud); struct maat_hierarchy_group *hash_group_by_id; //key: group_id, value: struct Maat_hierarchy_group*. struct maat_hierarchy_group *hash_group_by_vertex; //key:vetex_id, value: struct Maat_hierarchy_group*. Multimap (Items with multiple keys). struct maat_hierarchy_region *hash_region_by_id; //key: region_id, value: struct Maat_hierarchy_region*. struct maat_hierarchy_clause *hash_dedup_clause_by_literals; //key: literal combination, value: struct Maat_hierarchy_clause*. For generating unique clause_id. unsigned long long clause_id_generator; //Increasing number. void (* region_user_data_free)(void *region_ud); igraph_t group_graph; igraph_integer_t group_graph_vcount; igraph_vector_t dfs_vids; igraph_integer_t grp_vertex_id_generator; /*Following members are accessed from scan threads.*/ struct region2clause_value *hash_region2clause; //key: region_id+virtual_table_id, value: struct region2clause_value. struct bool_matcher *bm; int thread_num; struct maat_garbage_bin *ref_garbage_bin; struct log_handle *logger; struct bool_expr_match *expr_match_buff; }; struct maat_hierarchy_compile_mid { int thread_id; int Nth_scan; time_t hier_ver; size_t this_scan_region_hit_cnt; int not_clause_hitted_flag; int is_no_count_scan; size_t hit_path_cnt; UT_array *internal_hit_paths; UT_array *all_hit_clause_array; UT_array *this_scan_hit_clause_ids; }; struct maat_hierarchy *maat_hierarchy_new(int thread_num, struct maat_garbage_bin *bin, struct log_handle *logger) { struct maat_hierarchy *hier = ALLOC(struct maat_hierarchy, 1); UNUSED int ret = 0; hier->logger = logger; hier->thread_num = thread_num; hier->version = time(NULL); hier->hash_group_by_id = NULL; hier->hash_group_by_vertex = NULL; hier->hash_compile_by_id = NULL; hier->hash_region2clause = NULL; hier->hash_region_by_id = NULL; hier->hash_dedup_clause_by_literals = NULL; hier->clause_id_generator = 0; hier->ref_garbage_bin = bin; hier->expr_match_buff = ALLOC(struct bool_expr_match, thread_num * MAX_SCANNER_HIT_NUM); pthread_mutex_init(&hier->mutex, NULL); ret = pthread_rwlock_init(&hier->rwlock, NULL); assert(ret == 0); ret = igraph_empty(&hier->group_graph, 0, IGRAPH_DIRECTED); assert(ret == IGRAPH_SUCCESS); return hier; } UT_icd ut_literal_id_icd = {sizeof(struct maat_hierarchy_literal_id), NULL, NULL, NULL}; UT_icd ut_clause_id_icd = {sizeof(unsigned long long), NULL, NULL, NULL}; UT_icd ut_region_id_icd = {sizeof(int), NULL, NULL, NULL}; UT_icd ut_hit_path_icd = {sizeof(struct maat_hierarchy_internal_hit_path), NULL, NULL, NULL}; #define MAAT_HIER_COMPILE_MAGIC 0x4a5b6c7d static struct maat_hierarchy_compile *maat_hierarchy_compile_new(struct maat_hierarchy *hier, int compile_id) { struct maat_hierarchy_compile *compile = NULL; compile = ALLOC(struct maat_hierarchy_compile, 1); compile->magic = MAAT_HIER_COMPILE_MAGIC; compile->compile_id = compile_id; HASH_ADD_INT(hier->hash_compile_by_id, compile_id, compile); for(int i = 0; i < MAX_ITEMS_PER_BOOL_EXPR; i++) { utarray_new(compile->clause_states[i].literal_ids, &ut_literal_id_icd); compile->clause_states[i].in_use=0; } return compile; } static void maat_hierarchy_compile_free(struct maat_hierarchy_compile *compile) { struct maat_hierarchy_clause_state *clause_state = NULL; //user_data must be freed before calling this function. assert(compile->user_data == NULL); for (int i = 0; i < MAX_ITEMS_PER_BOOL_EXPR; i++) { clause_state = compile->clause_states + i; utarray_free(clause_state->literal_ids); clause_state->literal_ids = NULL; clause_state->in_use = 0; } compile->magic = 0; free(compile); } void maat_hierarchy_free_region2clause_hash(struct region2clause_value* hash) { struct region2clause_value *r2c_val = NULL, *tmp_r2c_val = NULL; HASH_ITER(hh, hash, r2c_val, tmp_r2c_val) { HASH_DEL(hash, r2c_val); utarray_free(r2c_val->clause_ids); free(r2c_val); } assert(hash == NULL); } static struct maat_hierarchy_region * maat_hierarchy_region_new(struct maat_hierarchy *hier, int region_id, int group_id, int table_id, struct maat_hierarchy_group *parent_group, void *user_data) { struct maat_hierarchy_region *region = NULL; region = ALLOC(struct maat_hierarchy_region, 1); region->group_id = group_id; region->region_id = region_id; region->table_id = table_id; region->ref_parent_group = parent_group; region->user_data = user_data; HASH_ADD_INT(hier->hash_region_by_id, region_id, region); parent_group->ref_by_region_cnt++; return region; } static void maat_hierarchy_region_free(struct maat_hierarchy *hier, struct maat_hierarchy_region *region) { HASH_DELETE(hh, hier->hash_region_by_id, region); region->ref_parent_group->ref_by_region_cnt--; if (hier->region_user_data_free && region->user_data) { hier->region_user_data_free(region->user_data); region->user_data = NULL; } free(region); } static const struct maat_hierarchy_clause * maat_hierarchy_clause_fetch(struct maat_hierarchy *hier, struct maat_hierarchy_literal_id *literal_ids, size_t n_literal_id) { struct maat_hierarchy_clause *clause = NULL; HASH_FIND(hh, hier->hash_dedup_clause_by_literals, literal_ids, n_literal_id * sizeof(struct maat_hierarchy_literal_id), clause); if (!clause) { clause = ALLOC(struct maat_hierarchy_clause, 1); clause->clause_id = hier->clause_id_generator; clause->n_literal_id = n_literal_id; clause->literal_ids = ALLOC(struct maat_hierarchy_literal_id, n_literal_id); memcpy(clause->literal_ids, literal_ids, n_literal_id * sizeof(struct maat_hierarchy_literal_id)); hier->clause_id_generator++; HASH_ADD_KEYPTR(hh, hier->hash_dedup_clause_by_literals, clause->literal_ids, n_literal_id * sizeof(struct maat_hierarchy_literal_id), clause); } return clause; } static void maat_hierarchy_clause_free(struct maat_hierarchy *hier, struct maat_hierarchy_clause *clause) { HASH_DELETE(hh, hier->hash_dedup_clause_by_literals, clause); free(clause->literal_ids); clause->n_literal_id = 0; free(clause); } static void group_vertex_free(struct maat_hierarchy_group *group) { free(group->top_group_ids); free(group); } void maat_hierarchy_free(struct maat_hierarchy *hier) { struct maat_hierarchy_compile *compile = NULL, *tmp_compile = NULL; struct maat_hierarchy_group *group = NULL, *tmp_group = NULL; struct region2clause_value *r2c_val = NULL, *tmp_r2c_val = NULL; struct maat_hierarchy_region *region = NULL, *tmp_region = NULL; struct maat_hierarchy_clause *clause = NULL, *tmp_clause = NULL; pthread_rwlock_wrlock(&hier->rwlock); //Reference: https://troydhanson.github.io/uthash/userguide.html#_what_can_it_do //Some have asked how uthash cleans up its internal memory. //The answer is simple: when you delete the final item from a hash table, //uthash releases all the internal memory associated with that hash table, //and sets its pointer to NULL. HASH_ITER(hh, hier->hash_compile_by_id, compile, tmp_compile) { if (hier->compile_user_data_free && compile->user_data) { hier->compile_user_data_free(compile->user_data); compile->user_data = NULL; } HASH_DEL(hier->hash_compile_by_id, compile); maat_hierarchy_compile_free(compile); } assert(hier->hash_compile_by_id == NULL); HASH_ITER(hh, hier->hash_region2clause, r2c_val, tmp_r2c_val) { HASH_DEL(hier->hash_region2clause, r2c_val); utarray_free(r2c_val->clause_ids); free(r2c_val); } maat_hierarchy_free_region2clause_hash(hier->hash_region2clause); HASH_ITER(hh, hier->hash_region_by_id, region, tmp_region) { maat_hierarchy_region_free(hier, region); } HASH_ITER(hh, hier->hash_dedup_clause_by_literals, clause, tmp_clause) { maat_hierarchy_clause_free(hier, clause); } //Free group as the last. HASH_CLEAR(hh_vertex_id, hier->hash_group_by_vertex);//No need group memory clean up. HASH_ITER(hh_group_id, hier->hash_group_by_id, group, tmp_group) { HASH_DELETE(hh_group_id, hier->hash_group_by_id, group); group_vertex_free(group); } assert(hier->hash_group_by_id == NULL); igraph_destroy(&hier->group_graph); bool_matcher_free(hier->bm); hier->bm = NULL; pthread_rwlock_unlock(&hier->rwlock); pthread_rwlock_destroy(&hier->rwlock); FREE(hier->expr_match_buff); FREE(hier); } size_t print_igraph_vector(igraph_vector_t *v, char *buff, size_t sz) { int printed = 0; for (long int i = 0; i < igraph_vector_size(v); i++) { printed += snprintf(buff + printed, sz - printed, " %li", (long int)VECTOR(*v)[i]); } return printed; } struct maat_hierarchy_group *maat_hierarchy_group_new(struct maat_hierarchy *hier, int group_id) { struct maat_hierarchy_group *group = NULL; group = ALLOC(struct maat_hierarchy_group, 1); group->group_id = group_id; group->vertex_id = hier->grp_vertex_id_generator++; assert(igraph_vcount(&hier->group_graph) == group->vertex_id); igraph_add_vertices(&hier->group_graph, 1, NULL); //Add 1 vertice. HASH_ADD(hh_group_id, hier->hash_group_by_id, group_id, sizeof(group->group_id), group); HASH_ADD(hh_vertex_id, hier->hash_group_by_vertex, vertex_id, sizeof(group->vertex_id), group); return group; } static void maat_hierarchy_group_free(struct maat_hierarchy *hier, struct maat_hierarchy_group *group) { igraph_vector_t v; char buff[4096] = {0}; assert(group->ref_by_compile_cnt == 0 && group->ref_by_superior_group_cnt == 0); igraph_vector_init(&v, 8); igraph_neighbors(&hier->group_graph, &v, group->vertex_id, IGRAPH_ALL); if (igraph_vector_size(&v) > 0) { print_igraph_vector(&v, buff, sizeof(buff)); log_error(hier->logger, MODULE_HIERARCHY, "Del group %d exception, still reached by %s.", group->vertex_id, buff); assert(0); } igraph_vector_destroy(&v); assert(group->top_group_ids==NULL); //We should not call igraph_delete_vertices, because this is function changes the ids of the vertices. //igraph_delete_vertices(&hier->group_graph, igraph_vss_1(group->vertex_id)); HASH_DELETE(hh_group_id, hier->hash_group_by_id, group); HASH_DELETE(hh_vertex_id, hier->hash_group_by_vertex, group); group_vertex_free(group); } static size_t effective_vertices_count(igraph_vector_t *vids) { size_t i = 0; int tmp_vid = 0; for (i = 0; i < (size_t)igraph_vector_size(vids); i++) { tmp_vid = (int) VECTOR(*vids)[i]; if (tmp_vid < 0) { break; } } return i; } static int maat_hierarchy_build_top_groups(struct maat_hierarchy *hier) { struct maat_hierarchy_group *group = NULL, *tmp = NULL; struct maat_hierarchy_group *superior_group = NULL; int tmp_vid=0; size_t i=0, top_group_cnt=0; int* temp_group_ids=NULL; igraph_bool_t is_dag; igraph_is_dag(&(hier->group_graph), &is_dag); if (!is_dag) { log_error(hier->logger, MODULE_HIERARCHY, "Sub group cycle detected!"); return -1; } hier->group_graph_vcount = igraph_vcount(&hier->group_graph); igraph_vector_init(&(hier->dfs_vids), hier->group_graph_vcount); HASH_ITER (hh_group_id, hier->hash_group_by_id, group, tmp) { top_group_cnt = 0; temp_group_ids = NULL; //Orphan, Not reference by any one, free it. if (0 == group->ref_by_compile_cnt && 0 == group->ref_by_superior_group_cnt && 0 == group->ref_by_subordinate_group_cnt && 0 == group->ref_by_region_cnt) { FREE(group->top_group_ids); maat_hierarchy_group_free(hier, group); continue; } //A group is need to build top groups when it has regions and referenced by superior groups or compiles. if (group->ref_by_region_cnt > 0 && (group->ref_by_compile_cnt > 0 || group->ref_by_superior_group_cnt > 0)) { if (0 == group->ref_by_superior_group_cnt) { //fast path, group is only referenced by compile rules. top_group_cnt = 1; temp_group_ids = ALLOC(int, top_group_cnt); temp_group_ids[0] = group->group_id; } else { igraph_vector_t *vids = &(hier->dfs_vids); igraph_dfs(&hier->group_graph, group->vertex_id, IGRAPH_OUT, 0, vids, NULL, NULL, NULL, NULL, NULL, NULL); temp_group_ids = ALLOC(int, effective_vertices_count(vids)); for (size_t i = 0; i < (size_t)igraph_vector_size(vids); i++) { tmp_vid = (int) VECTOR(*vids)[i]; if (tmp_vid < 0) { break; } HASH_FIND(hh_vertex_id, hier->hash_group_by_vertex, &tmp_vid, sizeof(tmp_vid), superior_group); //including itself if (superior_group->ref_by_compile_cnt > 0) { temp_group_ids[top_group_cnt] = superior_group->group_id; top_group_cnt++; } } } } free(group->top_group_ids); group->top_group_cnt = top_group_cnt; group->top_group_ids = ALLOC(int, group->top_group_cnt); memcpy(group->top_group_ids, temp_group_ids, sizeof(int)*group->top_group_cnt); FREE(temp_group_ids); } igraph_vector_destroy(&hier->dfs_vids); return 0; } static struct bool_matcher *maat_hierarchy_build_bool_matcher(struct maat_hierarchy *hier) { struct bool_matcher *bm = NULL; size_t compile_num = 0, expr_cnt = 0; struct bool_expr *bool_expr_array = NULL; struct maat_hierarchy_compile *compile = NULL, *tmp_compile = NULL; struct maat_hierarchy_clause_state *clause_state = NULL; const struct maat_hierarchy_clause *clause = NULL; size_t i = 0, j = 0; int has_clause_num = 0; compile_num = HASH_COUNT(hier->hash_compile_by_id); if (0 == compile_num) { log_error(hier->logger, MODULE_HIERARCHY, "No compile to build."); return NULL; } //STEP 1, update clause_id of each compile and literal struct maat_hierarchy_literal_id *literal_ids = NULL; size_t n_literal_id = 0; HASH_ITER(hh, hier->hash_compile_by_id, compile, tmp_compile) { has_clause_num = 0; for (i = 0; i < MAX_ITEMS_PER_BOOL_EXPR; i++) { clause_state = compile->clause_states + i; clause_state->clause_id = 0; if (!clause_state->in_use) { continue; } has_clause_num++; literal_ids = (struct maat_hierarchy_literal_id *)utarray_eltptr(clause_state->literal_ids, 0); n_literal_id = utarray_len(clause_state->literal_ids); clause = maat_hierarchy_clause_fetch(hier, literal_ids, n_literal_id); clause_state->clause_id = clause->clause_id; } assert(has_clause_num == compile->actual_clause_num); } //STEP 2, serial compile clause states to a bool expression array. compile_num = HASH_COUNT(hier->hash_compile_by_id); bool_expr_array = ALLOC(struct bool_expr, compile_num); HASH_ITER(hh, hier->hash_compile_by_id, compile, tmp_compile) { for (i = 0, j = 0; i < MAX_ITEMS_PER_BOOL_EXPR; i++) { if (compile->clause_states[i].in_use) { if (compile->clause_states[i].not_flag) { compile->not_clause_cnt++; } bool_expr_array[expr_cnt].items[j].item_id = compile->clause_states[i].clause_id; bool_expr_array[expr_cnt].items[j].not_flag = compile->clause_states[i].not_flag; j++; } } //some compile may have zero groups, e.g. default policy. if (j == (size_t)compile->declared_clause_num && j > 0) { bool_expr_array[expr_cnt].expr_id = compile->compile_id; bool_expr_array[expr_cnt].user_tag = compile; bool_expr_array[expr_cnt].item_num = j; expr_cnt++; } } //STEP 3, build the bool matcher. size_t mem_size = 0; if (0 == expr_cnt) { log_error(hier->logger, MODULE_HIERARCHY, "No bool expression to build."); goto error_out; } bm = bool_matcher_new(bool_expr_array, expr_cnt, &mem_size); if (bm != NULL) { log_info(hier->logger, MODULE_HIERARCHY, "Build bool matcher of %zu expressions with %zu bytes memory.", expr_cnt, mem_size); } else { log_error(hier->logger, MODULE_HIERARCHY, "Build bool matcher failed!"); } error_out: FREE(bool_expr_array); return bm; } static inline int compare_clause_id(const void *a, const void *b) { long long ret = *(const unsigned long long *)a - *(const unsigned long long *)b; if (0 == ret) { return 0; } else if(ret < 0) { return -1; } else { return 1; } } static inline int compare_region_id(const void *a, const void *b) { return (*(int*)a - *(int*)b); } struct region2clause_value *maat_hierarchy_build_region2clause_hash(struct maat_hierarchy *hier) { size_t i = 0, j = 0, k = 0; struct maat_hierarchy_compile *compile = NULL, *tmp_compile = NULL; struct maat_hierarchy_literal_id *literal_id = NULL; struct maat_hierarchy_clause_state *clause_state = NULL; struct maat_hierarchy_region *region = NULL, *tmp_region = NULL; struct maat_hierarchy_group *group = NULL; struct group2region *g2r_hash = NULL, *g2r = NULL, *g2r_tmp = NULL; struct region2clause_value *region2clause_hash = NULL, *r2c_val = NULL; struct region2clause_key r2c_key; //Build a temporary hash that maps group to its regions. HASH_ITER(hh, hier->hash_region_by_id, region, tmp_region) { group = region->ref_parent_group; for (i = 0; i < group->top_group_cnt; i++) { HASH_FIND_INT(g2r_hash, group->top_group_ids+i, g2r); if (!g2r) { g2r = ALLOC(struct group2region, 1); utarray_new(g2r->region_ids, &ut_region_id_icd); utarray_reserve(g2r->region_ids, group->ref_by_region_cnt); g2r->group_id = group->top_group_ids[i]; HASH_ADD_INT(g2r_hash, group_id, g2r); } //One region belongs to one group, one group may have many regions. So duplicate region check is unnecessary. //if(utarray_find(g2r->region_ids, &(region->region_id), compare_region_id)) assert(0); utarray_push_back(g2r->region_ids, &(region->region_id)); //utarray_sort(g2r->region_ids, compare_region_id); } } //Build short cut hash that maps region_id+vt_id to clause_ids. HASH_ITER(hh, hier->hash_compile_by_id, compile, tmp_compile) { for (i = 0; i < MAX_ITEMS_PER_BOOL_EXPR; i++) { clause_state = compile->clause_states + i; if (!clause_state->in_use) { continue; } for (j = 0; j < utarray_len(clause_state->literal_ids); j++) { literal_id = (struct maat_hierarchy_literal_id *)utarray_eltptr(clause_state->literal_ids, j); HASH_FIND(hh_group_id, hier->hash_group_by_id, &(literal_id->group_id), sizeof(literal_id->group_id), group); if (!group) { continue; } HASH_FIND_INT(g2r_hash, &(group->group_id), g2r); //group declared by compile, but has no subordinate or region. if (!g2r) { continue; } for (k = 0; k < utarray_len(g2r->region_ids); k++) { r2c_key.region_id = *((int*)utarray_eltptr(g2r->region_ids, k)); r2c_key.vt_id = literal_id->vt_id; HASH_FIND(hh, region2clause_hash, &r2c_key, sizeof(r2c_key), r2c_val); if (!r2c_val) { r2c_val = ALLOC(struct region2clause_value, 1); r2c_val->key = r2c_key; r2c_val->group_id = g2r->group_id; utarray_new(r2c_val->clause_ids, &ut_clause_id_icd); HASH_ADD(hh, region2clause_hash, key, sizeof(r2c_val->key), r2c_val); } if (utarray_find(r2c_val->clause_ids, &(clause_state->clause_id), compare_clause_id)) { continue; } utarray_push_back(r2c_val->clause_ids, &(clause_state->clause_id)); utarray_sort(r2c_val->clause_ids, compare_clause_id); } } } } int tmp1 = 0, tmp2 = 0; HASH_ITER(hh, g2r_hash, g2r, g2r_tmp) { HASH_DEL(g2r_hash, g2r); //Sanity Check utarray_sort(g2r->region_ids, compare_region_id); for (i = 1; i < utarray_len(g2r->region_ids); i++) { tmp1 = *((int*)utarray_eltptr(g2r->region_ids, i-1)); tmp2 = *((int*)utarray_eltptr(g2r->region_ids, i)); assert(tmp1!=tmp2); } utarray_free(g2r->region_ids); g2r->region_ids = NULL; free(g2r); } log_info(hier->logger, MODULE_HIERARCHY, "Build region2clause hash with %llu element.", HASH_COUNT(region2clause_hash)); return region2clause_hash; } int maat_hierarchy_rebuild(struct maat_hierarchy *hier) { int ret=0; struct bool_matcher *new_bm = NULL, *old_bm = NULL; struct region2clause_value *new_region2clause_hash = NULL, *old_region2clause_hash = NULL; //Read hier from update thread is OK. if (!hier->changed_flag) { return ret; } ret = maat_hierarchy_build_top_groups(hier); new_bm = maat_hierarchy_build_bool_matcher(hier); new_region2clause_hash = maat_hierarchy_build_region2clause_hash(hier); pthread_rwlock_wrlock(&hier->rwlock); old_bm = hier->bm; old_region2clause_hash = hier->hash_region2clause; hier->bm = new_bm; hier->hash_region2clause = new_region2clause_hash; hier->changed_flag = 0; pthread_rwlock_unlock(&hier->rwlock); maat_garbage_bagging(hier->ref_garbage_bin, old_bm, (void (*)(void*))bool_matcher_free); maat_garbage_bagging(hier->ref_garbage_bin, old_region2clause_hash, (void (*)(void*))maat_hierarchy_free_region2clause_hash); return ret; } size_t maat_hierarchy_get_hit_paths(struct maat_hierarchy *hier, struct maat_hierarchy_compile_mid *mid, struct maat_hit_path_t *hit_paths, size_t n_path) { } void maat_hierarchy_set_compile_user_data_free_func(struct maat_hierarchy *hier, void (* func)(void *)) { hier->compile_user_data_free = func; } void maat_hierarchy_set_region_user_data_free_func(struct maat_hierarchy *hier, void (* func)(void *)) { hier->region_user_data_free = func; } struct maat_hierarchy_compile_mid *maat_hierarchy_compile_mid_new(struct maat_hierarchy *hier, int thread_id) { struct maat_hierarchy_compile_mid *mid = ALLOC(struct maat_hierarchy_compile_mid, 1); mid->thread_id = thread_id; mid->hier_ver = hier->version; utarray_new(mid->internal_hit_paths, &ut_hit_path_icd); utarray_new(mid->all_hit_clause_array, &ut_clause_id_icd); utarray_new(mid->this_scan_hit_clause_ids, &ut_clause_id_icd); return mid; } void maat_hierarchy_compile_mid_free(struct maat_hierarchy_compile_mid *mid) { utarray_free(mid->internal_hit_paths); utarray_free(mid->all_hit_clause_array); utarray_free(mid->this_scan_hit_clause_ids); free(mid); } static int maat_hierarchy_hit_path_add(UT_array *hit_paths, int region_id, int virtual_table_id, int Nth_scan, int Nth_region_result) { struct maat_hierarchy_internal_hit_path new_path; new_path.region_id = region_id; new_path.Nth_hit_region = Nth_region_result; new_path.Nth_scan = Nth_scan; new_path.virtual_table_id = virtual_table_id; utarray_push_back(hit_paths, &new_path); return 1; } void maat_hierarchy_compile_mid_update(struct maat_hierarchy *hier, struct maat_hierarchy_compile_mid *mid, int region_id, int virtual_table_id, int Nth_scan, int Nth_region_result) { if (mid->Nth_scan != Nth_scan) { assert(mid->this_scan_region_hit_cnt == 0); mid->Nth_scan = Nth_scan; utarray_clear(mid->this_scan_hit_clause_ids); } int ret = maat_hierarchy_hit_path_add(mid->internal_hit_paths, region_id, virtual_table_id, Nth_scan, Nth_region_result); if (!ret) { return; } mid->hit_path_cnt++; mid->this_scan_region_hit_cnt++; struct region2clause_value* r2c_val = NULL; struct region2clause_key r2c_key; r2c_key.region_id = region_id; r2c_key.vt_id = virtual_table_id; HASH_FIND(hh, hier->hash_region2clause, &r2c_key, sizeof(r2c_key), r2c_val); if (!r2c_val) { return; } size_t i = 0; unsigned long long *clause_id = 0; size_t new_clause_idx = utarray_len(mid->this_scan_hit_clause_ids); for (size_t i = 0; i < utarray_len(r2c_val->clause_ids); i++) { clause_id = (unsigned long long *)utarray_eltptr(r2c_val->clause_ids, i); if (utarray_find(mid->all_hit_clause_array, clause_id, compare_clause_id)) { continue; } utarray_push_back(mid->this_scan_hit_clause_ids, clause_id); } if (utarray_len(mid->this_scan_hit_clause_ids) - new_clause_idx) { utarray_reserve(mid->all_hit_clause_array, utarray_len(mid->this_scan_hit_clause_ids) - new_clause_idx); for (i = new_clause_idx; i < utarray_len(mid->this_scan_hit_clause_ids); i++) { clause_id = (unsigned long long *)utarray_eltptr(mid->this_scan_hit_clause_ids, i); utarray_push_back(mid->all_hit_clause_array, clause_id); } utarray_sort(mid->all_hit_clause_array, compare_clause_id); } } int maat_hierarchy_compile_mid_has_NOT_clause(struct maat_hierarchy_compile_mid *mid) { return mid->not_clause_hitted_flag; } int maat_hierarchy_compile_add(struct maat_hierarchy *hier, int compile_id, int declared_clause_num, void *user_data) { int ret = 0; struct maat_hierarchy_compile *compile = NULL; pthread_rwlock_wrlock(&hier->rwlock); hier->changed_flag = 1; HASH_FIND_INT(hier->hash_compile_by_id, &compile_id, compile); if (!compile) { assert(declared_clause_num >= 0); compile = maat_hierarchy_compile_new(hier, compile_id); compile->declared_clause_num = declared_clause_num; compile->user_data = user_data; } else { if (compile->user_data != NULL) { log_error(hier->logger, MODULE_HIERARCHY, "Add compile %d failed, compile is already exisited.", compile_id); ret = -1; } else { compile->declared_clause_num = declared_clause_num; compile->user_data = user_data; } } pthread_rwlock_unlock(&hier->rwlock); return ret; } int maat_hierarchy_compile_remove(struct maat_hierarchy *hier, int compile_id) { int ret = 0; struct maat_hierarchy_compile *compile = NULL; pthread_rwlock_wrlock(&hier->rwlock); hier->changed_flag = 1; HASH_FIND_INT(hier->hash_compile_by_id, &compile_id, compile); if (compile) { if (hier->compile_user_data_free && compile->user_data) { hier->compile_user_data_free(compile->user_data); compile->user_data = NULL; } if (0 == compile->actual_clause_num) { HASH_DEL(hier->hash_compile_by_id, compile); maat_garbage_bagging(hier->ref_garbage_bin, compile, (void (*)(void*))maat_hierarchy_compile_free); } ret = 0; } else { log_error(hier->logger, MODULE_HIERARCHY, "Remove compile %d failed, compile is not exisited.", compile_id); ret = -1; } pthread_rwlock_unlock(&hier->rwlock); return ret; } static void *maat_hier_compile_get_user_data(struct maat_hierarchy* hier, int compile_id, int is_dettach) { struct maat_hierarchy_compile *compile = NULL; void *ret = NULL; pthread_rwlock_rdlock(&hier->rwlock); HASH_FIND_INT(hier->hash_compile_by_id, &compile_id, compile); if (compile) { ret = compile->user_data; if (is_dettach) { compile->user_data = NULL; } } pthread_rwlock_unlock(&hier->rwlock); return ret; } void *maat_hierarchy_compile_dettach_user_data(struct maat_hierarchy *hier, int compile_id) { return maat_hier_compile_get_user_data(hier, compile_id, 1); } void *maat_hierarchy_compile_read_user_data(struct maat_hierarchy *hier, int compile_id) { return maat_hier_compile_get_user_data(hier, compile_id, 0); } void maat_hierarchy_compile_user_data_iterate(struct maat_hierarchy *hier, void (*callback)(void *user_data, void *param), void *param) { struct maat_hierarchy_compile *compile = NULL, *tmp_compile = NULL; pthread_rwlock_rdlock(&hier->rwlock); HASH_ITER(hh, hier->hash_compile_by_id, compile, tmp_compile) { if (compile->user_data) { callback(compile->user_data, param); } } pthread_rwlock_unlock(&hier->rwlock); } static int maat_hierarchy_compile_has_clause(struct maat_hierarchy_compile* compile, unsigned long long clause_id) { struct maat_hierarchy_clause_state *clause_state = NULL; for (size_t i = 0; i < MAX_ITEMS_PER_BOOL_EXPR; i++) { clause_state = compile->clause_states + i; if (!clause_state->in_use) { continue; } if (clause_state->clause_id == clause_id) { return 1; } } return 0; } static size_t maat_hierarchy_compile_mid_if_new_hit_compile(struct maat_hierarchy_compile_mid *mid, struct maat_hierarchy_compile *compile) { size_t r_in_c_cnt = 0; unsigned long long new_hit_clause_id = 0; for(size_t i = 0; ithis_scan_hit_clause_ids); i++) { new_hit_clause_id = *(unsigned long long*)utarray_eltptr(mid->this_scan_hit_clause_ids, i); int ret = maat_hierarchy_compile_has_clause(compile, new_hit_clause_id); if (ret) { r_in_c_cnt++; } } return r_in_c_cnt; } int maat_hierarchy_region_compile(struct maat_hierarchy *hier, struct maat_hierarchy_compile_mid *mid, int is_last_compile, void **user_data_array, size_t ud_array_sz) { int bool_match_ret = 0, i = 0; struct maat_hierarchy_compile *compile = NULL; struct bool_expr_match *expr_match = hier->expr_match_buff + (mid->thread_id * MAX_SCANNER_HIT_NUM); size_t r_in_c_cnt = 0; size_t ud_result_cnt = 0; size_t this_scan_region_hits = mid->this_scan_region_hit_cnt; if (!hier->bm || 0 == utarray_len(mid->all_hit_clause_array) || hier->version != mid->hier_ver) { mid->this_scan_region_hit_cnt = 0; return 0; } bool_match_ret = bool_matcher_match(hier->bm, (unsigned long long *)utarray_eltptr(mid->all_hit_clause_array, 0), utarray_len(mid->all_hit_clause_array), expr_match, MAX_SCANNER_HIT_NUM); for (i = 0; i < bool_match_ret && ud_result_cnt < ud_array_sz; i++) { compile = (struct maat_hierarchy_compile *)expr_match[i].user_tag; assert(compile->magic == MAAT_HIER_COMPILE_MAGIC); assert((unsigned long long)compile->compile_id == expr_match[i].expr_id); if (0 == compile->actual_clause_num) { continue; } r_in_c_cnt = maat_hierarchy_compile_mid_if_new_hit_compile(mid, compile); if (compile->not_clause_cnt > 0 && !is_last_compile) { mid->not_clause_hitted_flag = 1; } else if(compile->user_data) { //For compile may be dettached by Maat_hierarchy_compile_dettach_user_data, only return non-NULL userdata. if (r_in_c_cnt > 0 || //compile hitted becasue of new reigon this_scan_region_hits == 0) //or hit a compile that refer a NOT-logic group in previous scan. { user_data_array[ud_result_cnt]=compile->user_data; ud_result_cnt++; } } } mid->this_scan_region_hit_cnt = 0; return ud_result_cnt; } void *maat_hierarchy_region_dettach_user_data(struct maat_hierarchy *hier, int region_id) { struct maat_hierarchy_region *region = NULL; void *ret = NULL; pthread_rwlock_wrlock(&hier->rwlock); hier->changed_flag = 1; HASH_FIND_INT(hier->hash_region_by_id, ®ion_id, region); if (region) { ret = region->user_data; region->user_data = NULL; } pthread_rwlock_unlock(&hier->rwlock); return ret; } int compare_literal_id(const void *pa, const void *pb) { struct maat_hierarchy_literal_id *la = (struct maat_hierarchy_literal_id *)pa; struct maat_hierarchy_literal_id *lb = (struct maat_hierarchy_literal_id *)pb; int ret = la->vt_id - lb->vt_id; if (ret == 0) { ret = la->group_id - lb->group_id; } return ret; } static int maat_hierarchy_compile_add_literal(struct maat_hierarchy_compile *compile, struct maat_hierarchy_literal_id *literal_id, int not_flag, int clause_index) { struct maat_hierarchy_literal_id *tmp = NULL; struct maat_hierarchy_clause_state *clause_state = compile->clause_states + clause_index; clause_state->not_flag = not_flag; if (!clause_state->in_use) { clause_state->in_use = 1; compile->actual_clause_num++; } tmp = (struct maat_hierarchy_literal_id *)utarray_find(clause_state->literal_ids, literal_id, compare_literal_id); if (tmp) { assert(*(unsigned long long*)tmp == *(unsigned long long*)(literal_id)); return -1; } else { utarray_push_back(clause_state->literal_ids, literal_id); utarray_sort(clause_state->literal_ids, compare_literal_id); } return 0; } static int maat_hierarchy_compile_remove_literal(struct maat_hierarchy_compile *compile, struct maat_hierarchy_literal_id *literal_id, int clause_index) { struct maat_hierarchy_literal_id *tmp = NULL; struct maat_hierarchy_clause_state *clause_state = compile->clause_states + clause_index; tmp = (struct maat_hierarchy_literal_id *)utarray_find(clause_state->literal_ids, literal_id , compare_literal_id); if (tmp) { assert(*(unsigned long long*)tmp == *(unsigned long long*)(literal_id)); } else { return -1; } size_t remove_idx = utarray_eltidx(clause_state->literal_ids, tmp); utarray_erase(clause_state->literal_ids, remove_idx, 1); if (0 == utarray_len(clause_state->literal_ids)) { clause_state->in_use = 0; compile->actual_clause_num--; } return 0; } int maat_hierarchy_add_group_to_compile(struct maat_hierarchy *hier, int group_id, int vt_id, int not_flag, int clause_index, int compile_id) { int ret = 0; struct maat_hierarchy_group *group = NULL; struct maat_hierarchy_literal_id literal_id = {group_id, vt_id}; struct maat_hierarchy_compile *compile = NULL; pthread_rwlock_wrlock(&hier->rwlock); hier->changed_flag = 1; HASH_FIND(hh_group_id, hier->hash_group_by_id, &group_id, sizeof(group_id), group); if (!group) { group = maat_hierarchy_group_new(hier, group_id); } HASH_FIND(hh, hier->hash_compile_by_id, &compile_id, sizeof(compile_id), compile); if (!compile) { compile = maat_hierarchy_compile_new(hier, compile_id); } ret = maat_hierarchy_compile_add_literal(compile, &literal_id, not_flag, clause_index); if (ret < 0) { log_error(hier->logger, MODULE_HIERARCHY, "Add group %d vt_id %d to clause %d of compile %d failed, group is already exisited.", group_id, vt_id, clause_index, compile_id); ret = -1; } else { ret = 0; group->ref_by_compile_cnt++; } pthread_rwlock_unlock(&hier->rwlock); return ret; } int maat_hierarchy_remove_group_from_compile(struct maat_hierarchy *hier, int group_id, int vt_id, int not_flag, int clause_index, int compile_id) { struct maat_hierarchy_group *group = NULL; struct maat_hierarchy_literal_id literal_id = {group_id, vt_id}; struct maat_hierarchy_compile *compile = NULL; int ret = 0; pthread_rwlock_wrlock(&hier->rwlock); hier->changed_flag = 1; HASH_FIND(hh_group_id, hier->hash_group_by_id, &group_id, sizeof(group_id), group); if (!group) { log_error(hier->logger, MODULE_HIERARCHY, "Remove group %d from compile %d failed, group is not exisited.", group_id, compile_id); goto error_out; } HASH_FIND(hh, hier->hash_compile_by_id, &compile_id, sizeof(compile_id), compile); if (!compile) { log_error(hier->logger, MODULE_HIERARCHY, "Remove group %d from compile %d failed, compile is not exisited.", group_id, compile_id); goto error_out; } ret = maat_hierarchy_compile_remove_literal(compile, &literal_id, clause_index); if (ret < 0) { log_error(hier->logger, MODULE_HIERARCHY, "Remove group %d vt_id %d from clause %d of compile %d failed, literal is not in compile.", group_id, vt_id, clause_index, compile_id); goto error_out; } if (0 == compile->actual_clause_num && !compile->user_data) { HASH_DEL(hier->hash_compile_by_id, compile); maat_garbage_bagging(hier->ref_garbage_bin, compile, (void (*)(void*))maat_hierarchy_compile_free); } pthread_rwlock_unlock(&hier->rwlock); return 0; error_out: pthread_rwlock_unlock(&hier->rwlock); return -1; } int maat_hierarchy_add_group_to_group(struct maat_hierarchy *hier, int group_id, int superior_group_id) { igraph_integer_t edge_id; struct maat_hierarchy_group *group = NULL, *superior_group = NULL; pthread_rwlock_wrlock(&hier->rwlock); hier->changed_flag = 1; HASH_FIND(hh_group_id, hier->hash_group_by_id, &group_id, sizeof(group_id), group); if (!group) { group = maat_hierarchy_group_new(hier, group_id); } HASH_FIND(hh_group_id, hier->hash_group_by_id, &superior_group_id, sizeof(superior_group_id), superior_group); if (!superior_group) { superior_group = maat_hierarchy_group_new(hier, superior_group_id); } int ret = igraph_get_eid(&hier->group_graph, &edge_id, group->vertex_id, superior_group->vertex_id, IGRAPH_DIRECTED, /*error*/ 0); //No duplicated edges between two groups. if (edge_id > 0) { log_error(hier->logger, MODULE_HIERARCHY, "Add group %d to group %d failed, relation already exisited.", group->group_id, superior_group->group_id); ret = -1; } else { igraph_add_edge(&hier->group_graph, group->vertex_id, superior_group->vertex_id); group->ref_by_superior_group_cnt++; superior_group->ref_by_subordinate_group_cnt++; ret = 0; } pthread_rwlock_unlock(&hier->rwlock); return ret; } int maat_hierarchy_remove_group_from_group(struct maat_hierarchy *hier, int group_id, int superior_group_id) { struct maat_hierarchy_group *group = NULL, *superior_group = NULL; //No hash write operation, LOCK protection is unnecessary. hier->changed_flag = 1; HASH_FIND(hh_group_id, hier->hash_group_by_id, &group_id, sizeof(group_id), group); if (NULL == group) { log_error(hier->logger, MODULE_HIERARCHY, "Del group %d from group %d failed, group %d not exisited.", group_id, superior_group_id, group_id); return -1; } HASH_FIND(hh_group_id, hier->hash_group_by_id, &superior_group_id, sizeof(superior_group_id), superior_group); if (NULL == superior_group) { log_error(hier->logger, MODULE_HIERARCHY, "Del group %d from group %d failed, superior group %d not exisited.", group_id, superior_group_id, superior_group_id); return -1; } igraph_es_t es; igraph_integer_t edge_num_before = 0, edge_num_after = 0; edge_num_before = igraph_ecount(&hier->group_graph); // The edges between the given pairs of vertices will be included in the edge selection. //The vertex pairs must be given as the arguments of the function call, the third argument //is the first vertex of the first edge, the fourth argument is the second vertex of the //first edge, the fifth is the first vertex of the second edge and so on. The last element //of the argument list must be -1 to denote the end of the argument list. //https://igraph.org/c/doc/igraph-Iterators.html#igraph_es_pairs_small int ret = igraph_es_pairs_small(&es, IGRAPH_DIRECTED, group->vertex_id, superior_group->vertex_id, -1); assert(ret == IGRAPH_SUCCESS); // ignore no such edge to abort(). igraph_set_error_handler(igraph_error_handler_ignore); ret = igraph_delete_edges(&hier->group_graph, es); edge_num_after = igraph_ecount(&hier->group_graph); igraph_es_destroy(&es); if (ret != IGRAPH_SUCCESS || edge_num_before - edge_num_after != 1) { assert(0); return -1; } group->ref_by_superior_group_cnt--; superior_group->ref_by_subordinate_group_cnt--; return 0; } int maat_hierarchy_add_region_to_group(struct maat_hierarchy *hier, int group_id, int region_id, int table_id, void *user_data) { //A region rule belongs to ONE group only. struct maat_hierarchy_group *group = NULL; struct maat_hierarchy_region *region = NULL; int ret = 0; pthread_rwlock_wrlock(&hier->rwlock); hier->changed_flag = 1; HASH_FIND(hh_group_id, hier->hash_group_by_id, &group_id, sizeof(group_id), group); if (!group) { group = maat_hierarchy_group_new(hier, group_id); } HASH_FIND_INT(hier->hash_region_by_id, ®ion_id, region); if (region) { log_error(hier->logger, MODULE_HIERARCHY, "Add region %d to group %d failed, region already in group %d.", region_id, group_id, region->ref_parent_group->group_id); ret = -1; } else { region = maat_hierarchy_region_new(hier, region_id, group_id, table_id, group, user_data); ret = 0; } pthread_rwlock_unlock(&hier->rwlock); return ret; } int maat_hierarchy_remove_region_from_group(struct maat_hierarchy *hier, int group_id, int region_id) { struct maat_hierarchy_group *group = NULL; struct maat_hierarchy_region *region = NULL; pthread_rwlock_wrlock(&hier->rwlock); hier->changed_flag = 1; HASH_FIND(hh_group_id, hier->hash_group_by_id, &group_id, sizeof(group_id), group); if (!group) { log_error(hier->logger, MODULE_HIERARCHY, "Remove region %d from group %d failed, group is not existed.", region_id, group_id); goto error_out; } HASH_FIND_INT(hier->hash_region_by_id, ®ion_id, region); if (!region) { log_error(hier->logger, MODULE_HIERARCHY, "Remove region %d from group %d failed, region is not exisited.", region_id, group_id); goto error_out; } assert(region->group_id == group->group_id); maat_hierarchy_region_free(hier, region); pthread_rwlock_unlock(&hier->rwlock); return 0; error_out: pthread_rwlock_unlock(&hier->rwlock); return -1; }