diff --git a/critbit.c b/critbit.c index c0606f9..1815449 100644 --- a/critbit.c +++ b/critbit.c @@ -258,14 +258,23 @@ int tt_dict_foreach(tt_arena_t *a, } } +struct tt_dictset_union_context { + tt_arena_t *a; + tt_node_ptr_t result; +}; + +static int tt_dictset_union_f(void *context, tt_atom_t key, tt_node_ptr_t ignored_trie) { + struct tt_dictset_union_context *c = (struct tt_dictset_union_context *) context; + tt_node_ptr_t new_result = tt_grab(c->a, tt_dict_set(c->a, c->result, key, TT_EMPTY)); + tt_drop(c->a, c->result); + c->result = new_result; + return !TT_NO_PTR_P(c->result); +} + tt_node_ptr_t tt_dictset_union(tt_arena_t *a, tt_node_ptr_t s1, tt_node_ptr_t s2) { - tt_node_ptr_t result = TT_NO_PTR; /* grab'd */ - int f(void *context, tt_atom_t key, tt_node_ptr_t ignored_trie) { - tt_node_ptr_t new_result = tt_grab(a, tt_dict_set(a, result, key, TT_EMPTY)); - tt_drop(a, result); - result = new_result; - return !TT_NO_PTR_P(result); - } + struct tt_dictset_union_context context; + context.a = a; + context.result = TT_NO_PTR; /* grab'd */ if (tt_dict_size(a, s1) < tt_dict_size(a, s2)) { tt_node_ptr_t tmp = s2; @@ -273,55 +282,76 @@ tt_node_ptr_t tt_dictset_union(tt_arena_t *a, tt_node_ptr_t s1, tt_node_ptr_t s2 s1 = tmp; } /* Now s1 is larger, s2 is smaller. */ - result = tt_grab(a, s1); - tt_dict_foreach(a, s2, NULL, f); - return result; + context.result = tt_grab(a, s1); + tt_dict_foreach(a, s2, &context, tt_dictset_union_f); + return context.result; +} + +struct tt_dictset_intersection_context { + tt_arena_t *a; + tt_node_ptr_t s1; + tt_node_ptr_t result; +}; + +static int tt_dictset_intersection_f(void *context, tt_atom_t key, tt_node_ptr_t ignored_trie) { + struct tt_dictset_intersection_context *c = (struct tt_dictset_intersection_context *) context; + if (!TT_NO_PTR_P(tt_dict_get(c->a, c->s1, key))) { + tt_node_ptr_t new_result = tt_grab(c->a, tt_dict_set(c->a, c->result, key, TT_EMPTY)); + tt_drop(c->a, c->result); + c->result = new_result; + } + return !TT_NO_PTR_P(c->result); } tt_node_ptr_t tt_dictset_intersection(tt_arena_t *a, tt_node_ptr_t s1, tt_node_ptr_t s2) { - tt_node_ptr_t result = TT_NO_PTR; /* grab'd */ - int f(void *context, tt_atom_t key, tt_node_ptr_t ignored_trie) { - if (!TT_NO_PTR_P(tt_dict_get(a, s1, key))) { - tt_node_ptr_t new_result = tt_grab(a, tt_dict_set(a, result, key, TT_EMPTY)); - tt_drop(a, result); - result = new_result; - } - return !TT_NO_PTR_P(result); - } - + struct tt_dictset_intersection_context context; if (tt_dict_size(a, s1) < tt_dict_size(a, s2)) { tt_node_ptr_t tmp = s2; s2 = s1; s1 = tmp; } /* Now s1 is larger, s2 is smaller. */ - result = TT_EMPTY_DICT; - tt_dict_foreach(a, s2, NULL, f); - return result; + context.a = a; + context.s1 = s1; + context.result = TT_EMPTY_DICT; /* grab'd */ + tt_dict_foreach(a, s2, &context, tt_dictset_intersection_f); + return context.result; +} + +struct tt_dictset_difference_context { + tt_arena_t *a; + tt_node_ptr_t s2; + tt_node_ptr_t result; +}; + +static int tt_dictset_difference_f1(void *context, tt_atom_t key, tt_node_ptr_t ignored_trie) { + struct tt_dictset_difference_context *c = (struct tt_dictset_difference_context *) context; + if (!TT_NO_PTR_P(tt_dict_get(c->a, c->s2, key))) { + tt_node_ptr_t new_result = tt_grab(c->a, tt_dict_set(c->a, c->result, key, TT_EMPTY)); + tt_drop(c->a, c->result); + c->result = new_result; + } + return !TT_NO_PTR_P(c->result); +} + +static int tt_dictset_difference_f2(void *context, tt_atom_t key, tt_node_ptr_t ignored_trie) { + struct tt_dictset_difference_context *c = (struct tt_dictset_difference_context *) context; + tt_node_ptr_t new_result = tt_grab(c->a, tt_dict_remove(c->a, c->result, key)); + tt_drop(c->a, c->result); + c->result = new_result; + return !TT_NO_PTR_P(c->result); } tt_node_ptr_t tt_dictset_difference(tt_arena_t *a, tt_node_ptr_t s1, tt_node_ptr_t s2) { - tt_node_ptr_t result = TT_NO_PTR; + struct tt_dictset_difference_context context; + context.a = a; + context.s2 = s2; if (tt_dict_size(a, s1) < tt_dict_size(a, s2)) { - int f(void *context, tt_atom_t key, tt_node_ptr_t ignored_trie) { - if (!TT_NO_PTR_P(tt_dict_get(a, s2, key))) { - tt_node_ptr_t new_result = tt_grab(a, tt_dict_set(a, result, key, TT_EMPTY)); - tt_drop(a, result); - result = new_result; - } - return !TT_NO_PTR_P(result); - } - result = TT_EMPTY_DICT; - tt_dict_foreach(a, s1, NULL, f); + context.result = TT_EMPTY_DICT; + tt_dict_foreach(a, s1, &context, tt_dictset_difference_f1); } else { - int f(void *context, tt_atom_t key, tt_node_ptr_t ignored_trie) { - tt_node_ptr_t new_result = tt_grab(a, tt_dict_remove(a, result, key)); - tt_drop(a, result); - result = new_result; - return !TT_NO_PTR_P(result); - } - result = tt_grab(a, s1); - tt_dict_foreach(a, s2, NULL, f); + context.result = tt_grab(a, s1); + tt_dict_foreach(a, s2, &context, tt_dictset_difference_f2); } - return result; + return context.result; } diff --git a/route.c b/route.c index 1018c54..c58292b 100644 --- a/route.c +++ b/route.c @@ -160,6 +160,179 @@ static inline tt_node_ptr_t collapse(tt_arena_t *a, tt_node_ptr_t n) { return n; } +struct tt_trie_combine_context { + tt_arena_t *a; + int left_empty_keep; + int right_empty_keep; + int left_base_keep; + int right_base_keep; + void *f_context; + /* Should return a tt_grab'd result. */ + tt_node_ptr_t (*f)(void *f_context, tt_node_ptr_t r1, tt_node_ptr_t r2); +}; + +struct tt_trie_combine_examine_key_context { + struct tt_trie_combine_context *c; + tt_arena_t *a; /* the same as from tt_trie_combine_context */ + tt_node_ptr_t w1; + tt_node_ptr_t w2; + tt_node_ptr_t dict1; + tt_node_ptr_t dict2; + tt_node_ptr_t result_wild; /* grab'd */ + tt_node_ptr_t result_others; /* grab'd */ +}; + +/* Forward declaration (mutual recursion) */ +static tt_node_ptr_t tt_trie_combine_g(struct tt_trie_combine_context *c, + tt_node_ptr_t r1, + tt_node_ptr_t r2); + +static int tt_trie_combine_examine_key(void *examine_key_context, + tt_atom_t key, + tt_node_ptr_t ignored_trie) +{ + struct tt_trie_combine_examine_key_context *c = + (struct tt_trie_combine_examine_key_context *) examine_key_context; + tt_arena_t *a = c->a; + tt_node_ptr_t trie1, trie2; /* Looked up in r1 and r2 */ + tt_node_ptr_t new_trie; /* Combination of trie1 and trie2 */ + tt_node_ptr_t new_result_others; /* Updated dictionary with key---new_trie association */ + + trie1 = tt_grab(a, rlookup_dict(a, c->w1, c->dict1, key)); + if (TT_NO_PTR_P(trie1)) return 0; + trie2 = tt_grab(a, rlookup_dict(a, c->w2, c->dict2, key)); + if (TT_NO_PTR_P(trie2)) { + tt_drop(a, trie1); + return 0; + } + /* printf("descending into key %d, trie1 %u/%u trie2 %u/%u\n", */ + /* key, */ + /* tt_ptr_idx(trie1), */ + /* tt_ptr_tag(trie1), */ + /* tt_ptr_idx(trie2), */ + /* tt_ptr_tag(trie2)); */ + new_trie = tt_trie_combine_g(c->c, trie1, trie2); /* already grabbed */ + tt_drop(a, trie1); + tt_drop(a, trie2); + if (TT_NO_PTR_P(new_trie)) return 0; + new_result_others = tt_grab(a, rupdate_dict(a, c->result_wild, c->result_others, key, new_trie)); + tt_drop(a, new_trie); + if (TT_NO_PTR_P(new_result_others)) return 0; + tt_drop(a, c->result_others); + c->result_others = new_result_others; + /* printf("after update key %d, result_others %u/%u\n", */ + /* key, */ + /* tt_ptr_idx(c->result_others), */ + /* tt_ptr_tag(c->result_others)); */ + return 1; +} + +/* N.B. Returns a tt_grab'd result. */ +static tt_node_ptr_t tt_trie_combine_g(struct tt_trie_combine_context *c, + tt_node_ptr_t r1, + tt_node_ptr_t r2) +{ + tt_arena_t *a = c->a; + tt_tag_t t1, t2; + + if (TT_EMPTY_P(r1)) { + return c->left_empty_keep ? tt_grab(a, r2) : TT_EMPTY; + } + if (TT_EMPTY_P(r2)) { + return c->right_empty_keep ? tt_grab(a, r1) : TT_EMPTY; + } + + t1 = tt_ptr_tag(r1); + t2 = tt_ptr_tag(r2); + + if (t1 == TT_TAG_BRANCH && t2 == TT_TAG_BRANCH) { + tt_node_ptr_t result = TT_NO_PTR; /* grab'd */ + struct tt_trie_combine_examine_key_context ekc; + int ok = 1; + + ekc.c = c; + ekc.a = a; + ekc.w1 = TT_BRANCH_WILDCARD(a,r1); + ekc.w2 = TT_BRANCH_WILDCARD(a,r2); + ekc.dict1 = TT_BRANCH_OTHERS(a,r1); + ekc.dict2 = TT_BRANCH_OTHERS(a,r2); + ekc.result_wild = TT_EMPTY; /* grab'd */ + ekc.result_others = TT_EMPTY_DICT; /* grab'd */ + + /* printf("{{{ combining %u/%u with %u/%u\n", */ + /* tt_ptr_idx(r1), */ + /* tt_ptr_tag(r1), */ + /* tt_ptr_idx(r2), */ + /* tt_ptr_tag(r2)); */ + if (!TT_EMPTY_P(ekc.w1) && !TT_EMPTY_P(ekc.w2)) { + /* Two wildcards - worst case. Must loop over both dictionaries. */ + ekc.result_wild = tt_trie_combine_g(c, ekc.w1, ekc.w2); + ok = !TT_NO_PTR_P(ekc.result_wild); + ok = ok && tt_dict_foreach(a, ekc.dict1, &ekc, tt_trie_combine_examine_key); + ok = ok && tt_dict_foreach(a, ekc.dict2, &ekc, tt_trie_combine_examine_key); + } else if ((!TT_EMPTY_P(ekc.w1)) || + (TT_EMPTY_P(ekc.w2) && + (tt_dict_size(a, ekc.dict1) >= tt_dict_size(a, ekc.dict2)))) { + /* Either a wildcard on the left, or no wildcard at all but the left is larger */ + if (c->left_base_keep) { + ekc.result_wild = tt_grab(a, ekc.w1); + ekc.result_others = tt_grab(a, ekc.dict1); + } + ok = ok && tt_dict_foreach(a, ekc.dict2, &ekc, tt_trie_combine_examine_key); + } else { + /* Either a wildcard on the right, or no wildcard at all but the right is larger */ + if (c->right_base_keep) { + ekc.result_wild = tt_grab(a, ekc.w2); + ekc.result_others = tt_grab(a, ekc.dict2); + } + ok = ok && tt_dict_foreach(a, ekc.dict1, &ekc, tt_trie_combine_examine_key); + } + + if (ok) { + result = tt_grab(a, rbranch(a, ekc.result_wild, ekc.result_others)); + if (!TT_NO_PTR_P(result)) { + tt_node_ptr_t collapsed = tt_grab(a, collapse(a, result)); + tt_drop(a, result); + result = collapsed; + } + } + /* printf("}}} result %u/%u\n", */ + /* tt_ptr_idx(result), */ + /* tt_ptr_tag(result)); */ + + tt_drop(a, ekc.result_wild); + tt_drop(a, ekc.result_others); + return result; + } + + if (t1 == TT_TAG_TAIL) { + if (t2 == TT_TAG_TAIL) { + tt_node_ptr_t combined = + RET_IF_NO_PTR(tt_trie_combine_g(c, TT_TAIL_TRIE(a,r1), TT_TAIL_TRIE(a,r2))); + tt_node_ptr_t result = tt_grab(a, rwildseq(a, combined)); + tt_drop(a, combined); + return result; + } else { + tt_node_ptr_t r1_expanded = tt_grab(a, RET_IF_NO_PTR(expand(a, r1))); + tt_node_ptr_t result = tt_trie_combine_g(c, r1_expanded, r2); + tt_drop(a, r1_expanded); + return result; + } + } else if (t2 == TT_TAG_TAIL) { + tt_node_ptr_t r2_expanded = tt_grab(a, RET_IF_NO_PTR(expand(a, r2))); + tt_node_ptr_t result = tt_trie_combine_g(c, r1, r2_expanded); + tt_drop(a, r2_expanded); + return result; + } + + if (t1 == TT_TAG_OK || t2 == TT_TAG_OK) { + return c->f(c->f_context, r1, r2); + } + + /* There is no legitimate combination of tags that should let us get here. */ + assert(0); +} + /* N.B. Returns a tt_grab'd result. */ tt_node_ptr_t tt_trie_combine(tt_arena_t *a, tt_node_ptr_t r1, @@ -174,174 +347,64 @@ tt_node_ptr_t tt_trie_combine(tt_arena_t *a, tt_node_ptr_t r1, tt_node_ptr_t r2)) { - /* N.B. Returns a tt_grab'd result. */ - tt_node_ptr_t g(tt_node_ptr_t r1, tt_node_ptr_t r2) { - tt_tag_t t1, t2; + struct tt_trie_combine_context context; + context.a = a; + context.left_empty_keep = left_empty_keep; + context.right_empty_keep = right_empty_keep; + context.left_base_keep = left_base_keep; + context.right_base_keep = right_base_keep; + context.f_context = f_context; + context.f = f; + /* No need for tt_grab here - tt_trie_combine_g has already done that for us */ + return tt_trie_combine_g(&context, r1, r2); +} - if (TT_EMPTY_P(r1)) { - return left_empty_keep ? tt_grab(a, r2) : TT_EMPTY; - } - if (TT_EMPTY_P(r2)) { - return right_empty_keep ? tt_grab(a, r1) : TT_EMPTY; - } - - t1 = tt_ptr_tag(r1); - t2 = tt_ptr_tag(r2); - - if (t1 == TT_TAG_BRANCH && t2 == TT_TAG_BRANCH) { - tt_node_ptr_t w1 = TT_BRANCH_WILDCARD(a,r1); - tt_node_ptr_t w2 = TT_BRANCH_WILDCARD(a,r2); - tt_node_ptr_t dict1 = TT_BRANCH_OTHERS(a,r1); - tt_node_ptr_t dict2 = TT_BRANCH_OTHERS(a,r2); - tt_node_ptr_t result_wild = TT_EMPTY; /* grab'd */ - tt_node_ptr_t result_others = TT_EMPTY_DICT; /* grab'd */ - tt_node_ptr_t result = TT_NO_PTR; /* grab'd */ - int ok = 1; - - int examine_key(void *examine_key_context, tt_atom_t key, tt_node_ptr_t ignored_trie) { - tt_node_ptr_t trie1, trie2; /* Looked up in r1 and r2 */ - tt_node_ptr_t new_trie; /* Combination of trie1 and trie2 */ - tt_node_ptr_t new_result_others; /* Updated dictionary with key---new_trie association */ - trie1 = tt_grab(a, rlookup_dict(a, w1, dict1, key)); - if (TT_NO_PTR_P(trie1)) return 0; - trie2 = tt_grab(a, rlookup_dict(a, w2, dict2, key)); - if (TT_NO_PTR_P(trie2)) { - tt_drop(a, trie1); - return 0; - } - /* printf("descending into key %d, trie1 %u/%u trie2 %u/%u\n", */ - /* key, */ - /* tt_ptr_idx(trie1), */ - /* tt_ptr_tag(trie1), */ - /* tt_ptr_idx(trie2), */ - /* tt_ptr_tag(trie2)); */ - new_trie = g(trie1, trie2); /* already grabbed */ - tt_drop(a, trie1); - tt_drop(a, trie2); - if (TT_NO_PTR_P(new_trie)) return 0; - new_result_others = tt_grab(a, rupdate_dict(a, result_wild, result_others, key, new_trie)); - tt_drop(a, new_trie); - if (TT_NO_PTR_P(new_result_others)) return 0; - tt_drop(a, result_others); - result_others = new_result_others; - /* printf("after update key %d, result_others %u/%u\n", */ - /* key, */ - /* tt_ptr_idx(result_others), */ - /* tt_ptr_tag(result_others)); */ - return 1; - } - - /* printf("{{{ combining %u/%u with %u/%u\n", */ - /* tt_ptr_idx(r1), */ - /* tt_ptr_tag(r1), */ - /* tt_ptr_idx(r2), */ - /* tt_ptr_tag(r2)); */ - if (!TT_EMPTY_P(w1) && !TT_EMPTY_P(w2)) { - /* Two wildcards - worst case. Must loop over both dictionaries. */ - result_wild = g(w1, w2); - ok = !TT_NO_PTR_P(result_wild); - ok = ok && tt_dict_foreach(a, dict1, NULL, examine_key); - ok = ok && tt_dict_foreach(a, dict2, NULL, examine_key); - } else if ((!TT_EMPTY_P(w1)) || - (TT_EMPTY_P(w2) && - (tt_dict_size(a, dict1) >= tt_dict_size(a, dict2)))) { - /* Either a wildcard on the left, or no wildcard at all but the left is larger */ - if (left_base_keep) { - result_wild = tt_grab(a, w1); - result_others = tt_grab(a, dict1); - } - ok = ok && tt_dict_foreach(a, dict2, NULL, examine_key); - } else { - /* Either a wildcard on the right, or no wildcard at all but the right is larger */ - if (right_base_keep) { - result_wild = tt_grab(a, w2); - result_others = tt_grab(a, dict2); - } - ok = ok && tt_dict_foreach(a, dict1, NULL, examine_key); - } - - if (ok) { - result = tt_grab(a, rbranch(a, result_wild, result_others)); - if (!TT_NO_PTR_P(result)) { - tt_node_ptr_t collapsed = tt_grab(a, collapse(a, result)); - tt_drop(a, result); - result = collapsed; - } - } - /* printf("}}} result %u/%u\n", */ - /* tt_ptr_idx(result), */ - /* tt_ptr_tag(result)); */ - - tt_drop(a, result_wild); - tt_drop(a, result_others); - return result; - } - - if (t1 == TT_TAG_TAIL) { - if (t2 == TT_TAG_TAIL) { - tt_node_ptr_t combined = RET_IF_NO_PTR(g(TT_TAIL_TRIE(a,r1), TT_TAIL_TRIE(a,r2))); - tt_node_ptr_t result = tt_grab(a, rwildseq(a, combined)); - tt_drop(a, combined); - return result; - } else { - tt_node_ptr_t r1_expanded = tt_grab(a, RET_IF_NO_PTR(expand(a, r1))); - tt_node_ptr_t result = g(r1_expanded, r2); - tt_drop(a, r1_expanded); - return result; - } - } else if (t2 == TT_TAG_TAIL) { - tt_node_ptr_t r2_expanded = tt_grab(a, RET_IF_NO_PTR(expand(a, r2))); - tt_node_ptr_t result = g(r1, r2_expanded); - tt_drop(a, r2_expanded); - return result; - } - - if (t1 == TT_TAG_OK || t2 == TT_TAG_OK) { - return f(f_context, r1, r2); - } - - /* There is no legitimate combination of tags that should let us get here. */ - assert(0); +static tt_node_ptr_t f_union_map(void *f_context, tt_node_ptr_t r1, tt_node_ptr_t r2) { + tt_arena_t *a = (tt_arena_t *) f_context; + tt_tag_t t1 = tt_ptr_tag(r1); + tt_tag_t t2 = tt_ptr_tag(r2); + if (t1 != TT_TAG_OK) { + /* t2 must be ok. */ + return tt_grab(a, r2); + } else if (t2 != TT_TAG_OK) { + return tt_grab(a, r1); + } else { + tt_node_ptr_t s = RET_IF_NO_PTR(tt_dictset_union(a, TT_OK_DICT(a,r1), TT_OK_DICT(a,r2))); + tt_node_ptr_t result = tt_cons_ok(a, s); + tt_drop(a, s); + return tt_grab(a, result); } - - /* No need for tt_grab here - g has already done that for us */ - return g(r1, r2); } tt_node_ptr_t tt_trie_union_map(tt_arena_t *a, tt_node_ptr_t r1, tt_node_ptr_t r2) { - tt_node_ptr_t f_union_map(void *f_context, tt_node_ptr_t r1, tt_node_ptr_t r2) { - tt_tag_t t1 = tt_ptr_tag(r1); - tt_tag_t t2 = tt_ptr_tag(r2); - if (t1 != TT_TAG_OK) { - /* t2 must be ok. */ - return tt_grab(a, r2); - } else if (t2 != TT_TAG_OK) { - return tt_grab(a, r1); - } else { - tt_node_ptr_t s = RET_IF_NO_PTR(tt_dictset_union(a, TT_OK_DICT(a,r1), TT_OK_DICT(a,r2))); - tt_node_ptr_t result = tt_cons_ok(a, s); - tt_drop(a, s); - return tt_grab(a, result); - } - } - return tt_trie_combine(a, r1, r2, 1, 1, 1, 1, NULL, f_union_map); + return tt_trie_combine(a, r1, r2, 1, 1, 1, 1, a, f_union_map); +} + +struct f_union_set_context { + tt_arena_t *a; + tt_node_ptr_t emptyset; +}; + +static tt_node_ptr_t f_union_set(void *f_context, tt_node_ptr_t r1, tt_node_ptr_t r2) { + struct f_union_set_context *c = (struct f_union_set_context *) f_context; + return tt_grab(c->a, c->emptyset); } tt_node_ptr_t tt_trie_union_set(tt_arena_t *a, tt_node_ptr_t r1, tt_node_ptr_t r2) { - tt_node_ptr_t emptyset = tt_grab(a, RET_IF_NO_PTR(tt_cons_ok(a, TT_EMPTY_DICT))); + struct f_union_set_context context; tt_node_ptr_t result; - tt_node_ptr_t f_union_map(void *f_context, tt_node_ptr_t r1, tt_node_ptr_t r2) { - return tt_grab(a, emptyset); - } - result = tt_trie_combine(a, r1, r2, 1, 1, 1, 1, NULL, f_union_map); - tt_drop(a, emptyset); + context.a = a; + context.emptyset = tt_grab(a, RET_IF_NO_PTR(tt_cons_ok(a, TT_EMPTY_DICT))); + result = tt_trie_combine(a, r1, r2, 1, 1, 1, 1, &context, f_union_set); + tt_drop(a, context.emptyset); return result; } +static tt_node_ptr_t f_subtract_set(void *f_context, tt_node_ptr_t r1, tt_node_ptr_t r2) { + return TT_EMPTY; +} + tt_node_ptr_t tt_trie_subtract_set(tt_arena_t *a, tt_node_ptr_t r1, tt_node_ptr_t r2) { - tt_node_ptr_t f_subtract_set(void *f_context, tt_node_ptr_t r1, tt_node_ptr_t r2) { - return TT_EMPTY; - } return tt_trie_combine(a, r1, r2, 0, 1, 1, 0, NULL, f_subtract_set); } @@ -365,59 +428,74 @@ tt_node_ptr_t tt_trie_step(tt_arena_t *a, tt_node_ptr_t r, tt_atom_t key) { } } +struct tt_trie_relabel_visit_edge_context { + tt_arena_t *a; + void *f_context; + tt_node_ptr_t (*f)(void *f_context, tt_node_ptr_t oldlabel); + tt_node_ptr_t wild; + tt_node_ptr_t others; +}; + +static int tt_trie_relabel_visit_edge(void *context, tt_atom_t key, tt_node_ptr_t trie) { + struct tt_trie_relabel_visit_edge_context *c = + (struct tt_trie_relabel_visit_edge_context *) context; + tt_arena_t *a = c->a; + tt_node_ptr_t new_trie = tt_grab(a, tt_trie_relabel(a, trie, c->f_context, c->f)); + tt_node_ptr_t new_others; + if (TT_NO_PTR_P(new_trie)) return 0; + new_others = tt_grab(a, rupdate_dict(a, c->wild, c->others, key, new_trie)); + tt_drop(a, new_trie); + if (TT_NO_PTR_P(new_others)) return 0; + tt_drop(a, c->others); + c->others = new_others; + return 1; +} + tt_node_ptr_t tt_trie_relabel(tt_arena_t *a, tt_node_ptr_t r, void *f_context, tt_node_ptr_t (*f)(void *f_context, tt_node_ptr_t oldlabel)) { - tt_node_ptr_t walk(tt_node_ptr_t r) { - if (TT_EMPTY_P(r)) { - return r; - } - - switch (tt_ptr_tag(r)) { - case TT_TAG_OK: - return tt_cons_ok(a, RET_IF_NO_PTR(f(f_context, TT_OK_DICT(a, r)))); - - case TT_TAG_TAIL: - return tt_cons_tail(a, RET_IF_NO_PTR(walk(TT_TAIL_TRIE(a, r)))); - - case TT_TAG_BRANCH: { - tt_node_ptr_t wild = tt_grab(a, RET_IF_NO_PTR(walk(TT_BRANCH_WILDCARD(a, r)))); - tt_node_ptr_t others = TT_EMPTY_DICT; /* grab'd */ - tt_node_ptr_t result = TT_NO_PTR; - int visit_edge(void *context, tt_atom_t key, tt_node_ptr_t trie) { - tt_node_ptr_t new_trie = tt_grab(a, walk(trie)); - tt_node_ptr_t new_others; - if (TT_NO_PTR_P(new_trie)) return 0; - new_others = tt_grab(a, rupdate_dict(a, wild, others, key, new_trie)); - tt_drop(a, new_trie); - if (TT_NO_PTR_P(new_others)) return 0; - tt_drop(a, others); - others = new_others; - return 1; - } - if (tt_dict_foreach(a, TT_BRANCH_OTHERS(a, r), NULL, visit_edge)) { - result = rbranch(a, wild, others); - } - tt_drop(a, wild); - tt_drop(a, others); - return result; - } - - default: - assert(0); - } + if (TT_EMPTY_P(r)) { + return r; } - return walk(r); + + switch (tt_ptr_tag(r)) { + case TT_TAG_OK: + return tt_cons_ok(a, RET_IF_NO_PTR(f(f_context, TT_OK_DICT(a, r)))); + + case TT_TAG_TAIL: + return tt_cons_tail(a, RET_IF_NO_PTR(tt_trie_relabel(a, TT_TAIL_TRIE(a, r), f_context, f))); + + case TT_TAG_BRANCH: { + struct tt_trie_relabel_visit_edge_context context; + context.a = a; + context.f_context = f_context; + context.f = f; + context.wild = + tt_grab(a, RET_IF_NO_PTR(tt_trie_relabel(a, TT_BRANCH_WILDCARD(a, r), f_context, f))); + context.others = TT_EMPTY_DICT; /* grab'd */ + tt_node_ptr_t result = TT_NO_PTR; + if (tt_dict_foreach(a, TT_BRANCH_OTHERS(a, r), &context, tt_trie_relabel_visit_edge)) { + result = rbranch(a, context.wild, context.others); + } + tt_drop(a, context.wild); + tt_drop(a, context.others); + return result; + } + + default: + assert(0); + } +} + +static tt_node_ptr_t relabel_to_const(void *context, tt_node_ptr_t oldlabel) { + return *((tt_node_ptr_t *) context); } tt_node_ptr_t tt_trie_relabel_const(tt_arena_t *a, tt_node_ptr_t r, tt_node_ptr_t newlabel) { - tt_node_ptr_t relabel_to_const(void *context, tt_node_ptr_t oldlabel) { - return newlabel; - } - return tt_trie_relabel(a, r, NULL, relabel_to_const); + return tt_trie_relabel(a, r, &newlabel, relabel_to_const); } tt_node_ptr_t tt_begin_path(tt_arena_t *a, tt_node_ptr_t ok_dict) { @@ -450,73 +528,91 @@ static size_t gross_approximate_utf8_strlen(char const *c) { return count; } -void tt_dump_routingtable(tt_arena_t *a, tt_node_ptr_t r, int initial_indent) { - void walk(int indent, tt_node_ptr_t r) { - switch (tt_ptr_tag(r)) { - case TT_TAG_TAIL: - printf(" ...>"); - walk(indent + 5, TT_TAIL_TRIE(a, r)); - break; - case TT_TAG_OK: { - int need_space = 0; - int pkey(void *context, tt_atom_t key, tt_node_ptr_t ignored_trie) { - if (need_space) { - putchar(' '); - } else { - need_space = 1; - } - printf("%d", key); - return 1; - } - fputs(" {", stdout); - tt_dict_foreach(a, TT_OK_DICT(a, r), NULL, pkey); - putchar('}'); - break; - } - case TT_TAG_BRANCH: { - int need_sep = 0; - int pedge(void *context, tt_atom_t key, tt_node_ptr_t node) { - char keystr[256]; /* not very tight */ - if (need_sep) { - putchar('\n'); - print_indent(indent); - } else { - need_sep = 1; - } - keystr[sizeof(keystr) - 1] = '\0'; - switch (key) { - case TT_WILD: snprintf(keystr, sizeof(keystr) - 1, " ★"); break; - case TT_BOS: snprintf(keystr, sizeof(keystr) - 1, " <"); break; - case TT_EOS: snprintf(keystr, sizeof(keystr) - 1, " >"); break; - case TT_BOC: snprintf(keystr, sizeof(keystr) - 1, " {"); break; - case TT_EOC: snprintf(keystr, sizeof(keystr) - 1, " }"); break; - default: - snprintf(keystr, sizeof(keystr) - 1, " %d", key); - } - fputs(keystr, stdout); - walk(indent + gross_approximate_utf8_strlen(keystr), node); - return 1; - } - if (!TT_EMPTY_P(TT_BRANCH_WILDCARD(a, r))) { - pedge(NULL, TT_WILD, TT_BRANCH_WILDCARD(a, r)); - } - tt_dict_foreach(a, TT_BRANCH_OTHERS(a, r), NULL, pedge); - if (!need_sep) { - printf(" ::: no edges!"); - } - break; - } - case TT_TAG_SPECIAL: - if (TT_EMPTY_P(r)) { - printf(" ::: nothing"); - break; - } - /* fall through */ - default: - printf("?!?!?! %x", (unsigned int) r); - break; - } +static int tt_dump_routingtable_pkey(void *context, tt_atom_t key, tt_node_ptr_t ignored_trie) { + int *need_space = (int *) context; + if (*need_space) { + putchar(' '); + } else { + *need_space = 1; } - walk(initial_indent, r); + printf("%d", key); + return 1; +} + +/* Forward declaration (mutual recursion) */ +static void tt_dump_routingtable_walk(tt_arena_t *a, tt_node_ptr_t r, int indent); + +struct tt_dump_routingtable_pedge_context { + tt_arena_t *a; + int indent; + int need_sep; +}; + +static int tt_dump_routingtable_pedge(void *context, tt_atom_t key, tt_node_ptr_t node) { + struct tt_dump_routingtable_pedge_context *c = + (struct tt_dump_routingtable_pedge_context *) context; + char keystr[256]; /* not very tight */ + if (c->need_sep) { + putchar('\n'); + print_indent(c->indent); + } else { + c->need_sep = 1; + } + keystr[sizeof(keystr) - 1] = '\0'; + switch (key) { + case TT_WILD: snprintf(keystr, sizeof(keystr) - 1, " ★"); break; + case TT_BOS: snprintf(keystr, sizeof(keystr) - 1, " <"); break; + case TT_EOS: snprintf(keystr, sizeof(keystr) - 1, " >"); break; + case TT_BOC: snprintf(keystr, sizeof(keystr) - 1, " {"); break; + case TT_EOC: snprintf(keystr, sizeof(keystr) - 1, " }"); break; + default: + snprintf(keystr, sizeof(keystr) - 1, " %d", key); + } + fputs(keystr, stdout); + tt_dump_routingtable_walk(c->a, node, c->indent + gross_approximate_utf8_strlen(keystr)); + return 1; +} + +static void tt_dump_routingtable_walk(tt_arena_t *a, tt_node_ptr_t r, int indent) { + switch (tt_ptr_tag(r)) { + case TT_TAG_TAIL: + printf(" ...>"); + tt_dump_routingtable_walk(a, TT_TAIL_TRIE(a, r), indent + 5); + break; + case TT_TAG_OK: { + int need_space = 0; + fputs(" {", stdout); + tt_dict_foreach(a, TT_OK_DICT(a, r), &need_space, tt_dump_routingtable_pkey); + putchar('}'); + break; + } + case TT_TAG_BRANCH: { + struct tt_dump_routingtable_pedge_context context; + context.a = a; + context.indent = indent; + context.need_sep = 0; + if (!TT_EMPTY_P(TT_BRANCH_WILDCARD(a, r))) { + tt_dump_routingtable_pedge(&context, TT_WILD, TT_BRANCH_WILDCARD(a, r)); + } + tt_dict_foreach(a, TT_BRANCH_OTHERS(a, r), &context, tt_dump_routingtable_pedge); + if (!context.need_sep) { + printf(" ::: no edges!"); + } + break; + } + case TT_TAG_SPECIAL: + if (TT_EMPTY_P(r)) { + printf(" ::: nothing"); + break; + } + /* fall through */ + default: + printf("?!?!?! %x", (unsigned int) r); + break; + } +} + +void tt_dump_routingtable(tt_arena_t *a, tt_node_ptr_t r, int initial_indent) { + tt_dump_routingtable_walk(a, r, initial_indent); putchar('\n'); }