📄 erl_db_tree.c
字号:
RET_TO_BIF(erts_make_integer(sc.accum,p),DB_ERROR_NONE); } if (mpi.some_limitation) { if ((this = find_next_from_pb_key(tb, mpi.most)) != NULL) { lastkey = GETKEY(tb, this->dbterm.tpl); } sc.end_condition = mpi.least; } traverse_backwards(tb, lastkey, &doit_select_delete, &sc); BUMP_REDS(p, 1000 - sc.max); if (sc.max > 0) { RET_TO_BIF(erts_make_integer(sc.accum,p), DB_ERROR_NONE); } key = GETKEY(tb, (sc.lastterm)->dbterm.tpl); sz = size_object(key); if (IS_USMALL(0, sc.accum)) { hp = HAlloc(p, sz + PROC_BIN_SIZE + 6); eaccsum = make_small(sc.accum); } else { hp = HAlloc(p, BIG_UINT_HEAP_SIZE + sz + PROC_BIN_SIZE + 6); eaccsum = uint_to_big(sc.accum, hp); hp += BIG_UINT_HEAP_SIZE; } key = copy_struct(key, sz, &hp, &MSO(p)); mpb = db_make_mp_binary(p,mpi.mp,&hp); continuation = TUPLE5 (hp, tb->common.id, key, sc.end_condition, /* From the match program, needn't be copied */ mpb, eaccsum); /* Don't free mpi.mp, so don't use macro */ if (sc.erase_lastterm) { free_term(tb, sc.lastterm); } *ret = bif_trap1(&ets_select_delete_continue_exp, p, continuation); return DB_ERROR_NONE;#undef RET_TO_BIF}/*** Other interface routines (not directly coupled to one bif)*//* Display hash table contents (for dump) */static void db_print_tree(int to, void *to_arg, int show, DbTable *tbl){ DbTableTree *tb = &tbl->tree;#ifdef TREE_DEBUG if (show) erts_print(to, to_arg, "\nTree data dump:\n" "------------------------------------------------\n"); do_dump_tree2(to, to_arg, show, tb->root, 0); if (show) erts_print(to, to_arg, "\n" "------------------------------------------------\n");#else erts_print(to, to_arg, "Ordered set (AVL tree), Elements: %d\n", tb->common.nitems); do_dump_tree(to, to_arg, tb->root);#endif}/* release all memory occupied by a single table */static int db_free_table_tree(DbTable *tbl){ DbTableTree *tb = &tbl->tree; if (!tb->deletion) { tb->stack_pos = 0; tb->deletion = 1; PUSH_NODE(tb, tb->root); } do_free_tree(tb); erts_db_free(ERTS_ALC_T_DB_STK, (DbTable *) tb, (void *) tb->stack, sizeof(TreeDbTerm *) * STACK_NEED); ASSERT(erts_smp_atomic_read(&tb->common.memory_size) == sizeof(DbTable)); return 1;}static int db_free_table_continue_tree(DbTable *tbl, int first){ DbTableTree *tb = &tbl->tree; int result; if (first) { tb->stack_pos = 0; tb->deletion = 1; PUSH_NODE(tb, tb->root); } result = do_free_tree_cont(tb, DELETE_RECORD_LIMIT); if (result) { /* Completely done. */ erts_db_free(ERTS_ALC_T_DB_STK, (DbTable *) tb, (void *) tb->stack, sizeof(TreeDbTerm *) * STACK_NEED); ASSERT(erts_smp_atomic_read(&tb->common.memory_size) == sizeof(DbTable)); } return result;}static int db_delete_all_objects_tree(Process* p, DbTable* tbl){ db_free_table_tree(tbl); db_create_tree(p, tbl); tbl->tree.common.nitems = 0; return 0;}static void do_db_tree_foreach_offheap(TreeDbTerm *, void (*)(ErlOffHeap *, void *), void *);static void db_foreach_offheap_tree(DbTable *tbl, void (*func)(ErlOffHeap *, void *), void * arg){ do_db_tree_foreach_offheap(tbl->tree.root, func, arg);}/*** Functions for internal use*/static voiddo_db_tree_foreach_offheap(TreeDbTerm *tdbt, void (*func)(ErlOffHeap *, void *), void * arg){ if(!tdbt) return; do_db_tree_foreach_offheap(tdbt->left, func, arg); (*func)(&(tdbt->dbterm.off_heap), arg); do_db_tree_foreach_offheap(tdbt->right, func, arg);}static TreeDbTerm *linkout_tree(DbTableTree *tb, Eterm key){ TreeDbTerm **tstack[STACK_NEED]; int tpos = 0; int dstack[STACK_NEED+1]; int dpos = 0; int state = 0; TreeDbTerm **this = &tb->root; Sint c; int dir; TreeDbTerm *q = NULL; /* * Somewhat complicated, deletion in an AVL tree, * The two helpers balance_left and balance_right are used to * keep the balance. As in insert, we do the stacking ourselves. */ tb->stack_pos = tb->slot_pos = 0; dstack[dpos++] = DIR_END; for (;;) { if (!*this) { /* Failure */ return NULL; } else if ((c = cmp(key,GETKEY(tb,(*this)->dbterm.tpl))) < 0) { dstack[dpos++] = DIR_LEFT; tstack[tpos++] = this; this = &((*this)->left); } else if (c > 0) { /* go right */ dstack[dpos++] = DIR_RIGHT; tstack[tpos++] = this; this = &((*this)->right); } else { /* Equal key, found the one to delete*/ q = (*this); if (q->right == NULL) { (*this) = q->left; state = 1; } else if (q->left == NULL) { (*this) = q->right; state = 1; } else { dstack[dpos++] = DIR_LEFT; tstack[tpos++] = this; state = delsub(this); } --(tb->common.nitems); break; } } while (state && ( dir = dstack[--dpos] ) != DIR_END) { this = tstack[--tpos]; if (dir == DIR_LEFT) { state = balance_left(this); } else { state = balance_right(this); } } return q;}static TreeDbTerm *linkout_object_tree(DbTableTree *tb, Eterm object){ TreeDbTerm **tstack[STACK_NEED]; int tpos = 0; int dstack[STACK_NEED+1]; int dpos = 0; int state = 0; TreeDbTerm **this = &tb->root; Sint c; int dir; TreeDbTerm *q = NULL; Eterm key; /* * Somewhat complicated, deletion in an AVL tree, * The two helpers balance_left and balance_right are used to * keep the balance. As in insert, we do the stacking ourselves. */ key = GETKEY(tb, tuple_val(object)); tb->stack_pos = tb->slot_pos = 0; dstack[dpos++] = DIR_END; for (;;) { if (!*this) { /* Failure */ return NULL; } else if ((c = cmp(key,GETKEY(tb,(*this)->dbterm.tpl))) < 0) { dstack[dpos++] = DIR_LEFT; tstack[tpos++] = this; this = &((*this)->left); } else if (c > 0) { /* go right */ dstack[dpos++] = DIR_RIGHT; tstack[tpos++] = this; this = &((*this)->right); } else { /* Equal key, found the only possible matching object*/ if (!eq(object,make_tuple((*this)->dbterm.tpl))) { return NULL; } q = (*this); if (q->right == NULL) { (*this) = q->left; state = 1; } else if (q->left == NULL) { (*this) = q->right; state = 1; } else { dstack[dpos++] = DIR_LEFT; tstack[tpos++] = this; state = delsub(this); } --(tb->common.nitems); break; } } while (state && ( dir = dstack[--dpos] ) != DIR_END) { this = tstack[--tpos]; if (dir == DIR_LEFT) { state = balance_left(this); } else { state = balance_right(this); } } return q;}/*** For the select functions, analyzes the pattern and determines which** part of the tree should be searched. Also compiles the match program*/static int analyze_pattern(DbTableTree *tb, Eterm pattern, struct mp_info *mpi){ Eterm lst, tpl, ttpl; Eterm *matches,*guards, *bodies; Eterm sbuff[30]; Eterm *buff = sbuff; Eterm *ptpl; int i; int num_heads = 0; Eterm key; Eterm partly_bound; int res; Eterm least = 0; Eterm most = 0; mpi->some_limitation = 1; mpi->got_partial = 0; mpi->something_can_match = 0; mpi->mp = NULL; mpi->all_objects = 1; mpi->save_term = NULL; for (lst = pattern; is_list(lst); lst = CDR(list_val(lst))) ++num_heads; if (lst != NIL) {/* proper list... */ return DB_ERROR_BADPARAM; } if (num_heads > 10) { buff = erts_alloc(ERTS_ALC_T_DB_TMP, sizeof(Eterm) * num_heads * 3); } matches = buff; guards = buff + num_heads; bodies = buff + (num_heads * 2); i = 0; for(lst = pattern; is_list(lst); lst = CDR(list_val(lst))) { Eterm body; ttpl = CAR(list_val(lst)); if (!is_tuple(ttpl)) { if (buff != sbuff) { erts_free(ERTS_ALC_T_DB_TMP, buff); } return DB_ERROR_BADPARAM; } ptpl = tuple_val(ttpl); if (ptpl[0] != make_arityval(3U)) { if (buff != sbuff) { erts_free(ERTS_ALC_T_DB_TMP, buff); } return DB_ERROR_BADPARAM; } matches[i] = tpl = ptpl[1]; guards[i] = ptpl[2]; bodies[i] = body = ptpl[3]; if (!is_list(body) || CDR(list_val(body)) != NIL || CAR(list_val(body)) != am_DollarUnderscore) { mpi->all_objects = 0; } ++i; partly_bound = NIL; res = key_given(tb, tpl, &mpi->save_term, &partly_bound); if ( res >= 0 ) { /* Can match something */ key = 0; mpi->something_can_match = 1; if (res > 0) { key = GETKEY(tb,tuple_val(tpl)); } else if (partly_bound != NIL) { mpi->got_partial = 1; key = partly_bound; } else { mpi->some_limitation = 0; } if (key != 0) { if (least == 0 || partly_bound_can_match_lesser(key,least)) { least = key; } if (most == 0 || partly_bound_can_match_greater(key,most)) { most = key; } } } } mpi->least = least; mpi->most = most; /* * It would be nice not to compile the match_spec if nothing could match, * but then the select calls would not fail like they should on bad * match specs that happen to specify non existent keys etc. */ if ((mpi->mp = db_match_compile(matches, guards, bodies, num_heads, DCOMP_TABLE, NULL)) == NULL) { if (buff != sbuff) { erts_free(ERTS_ALC_T_DB_TMP, buff); } return DB_ERROR_BADPARAM; } if (buff != sbuff) { erts_free(ERTS_ALC_T_DB_TMP, buff); } return DB_ERROR_NONE;}static void do_dump_tree(int to, void *to_arg, TreeDbTerm *t){ if (t != NULL) { do_dump_tree(to, to_arg, t->left); erts_print(to, to_arg, "%T\n", make_tuple(t->dbterm.tpl)); do_dump_tree(to, to_arg, t->right); }}static void free_term(DbTableTree *tb, TreeDbTerm* p){ db_free_term_data(&(p->dbterm)); erts_db_free(ERTS_ALC_T_DB_TERM, (DbTable *) tb, (void *) p, SIZ_DBTERM(p)*sizeof(Uint));}static void do_free_tree(DbTableTree *tb){ TreeDbTerm *root; TreeDbTerm *p; root = POP_NODE(tb); while (root != NULL) { if ((p = root->left) != NULL) { root->left = NULL; PUSH_NODE(tb, root); root = p; continue; } else if ((p = root->right) != NULL) { root->right = NULL; PUSH_NODE(tb, root); root = p; continue; } else { free_term(tb, root); root = POP_NODE(tb); } }}static int do_free_tree_cont(DbTableTree *tb, int num_left){ TreeDbTerm *root; TreeDbTerm *p; root = POP_NODE(tb); while (root != NULL) { if ((p = root->left) != NULL) { root->left = NULL; PUSH_NODE(tb, root); root = p; continue; } else if ((p = root->right) != NULL) { root->right = NULL; PUSH_NODE(tb, root); root = p; continue; } else { free_term(tb, root); if (--num_left > 0) { root = POP_NODE(tb); } else { return 0; /* Done enough for now */ } } } return 1;}static TreeDbTerm* get_term(DbTableTree *tb, TreeDbTerm* old, Eterm obj) { TreeDbTerm* p = db_get_term((DbTableCommon *) tb, (old != NULL) ? &(old->dbterm) : NULL, ((char *) &(old->dbterm)) - ((char *) old), obj); return p;}/* * Deletion helpers */static int balance_left(TreeDbTerm **this) { TreeDbTerm *p, *p1, *p2; int b1, b2, h = 1; p = *this; switch (p->balance) { case -1: p->balance = 0; break; case 0: p->balance = 1; h = 0; break; case 1: p1 = p->right; b1 = p1->balance; if (b1 >= 0) { /* Single RR rotation */ p->right = p1->left; p1->left = p; if (b1 == 0) { p->balance = 1; p1->balance = -1; h = 0; } else { p->balance = p1->balance = 0; } (*this) = p1; } else { /* Double RL rotation */ p2 = p1->left; b2 = p2->balance; p1->left = p2->right; p2->right = p1; p->right = p2->left; p2->left = p; p->balance = (b2 == 1) ? -1 : 0; p1->balance = (b2 == -1) ? 1 : 0; p2->balance = 0; (*this) = p2; } break; } return h;}static int balance_right(TreeDbTerm **this) { TreeDbTerm *p, *p1, *p2; int b1, b2, h = 1; p = *this; switch (p->balance) { case 1: p->balance = 0; break; case 0: p->balance = -1; h = 0; break; case -1: p1 = p->left; b1 = p1->balance; if (b1 <= 0) { /* Single LL rotation */ p->left = p1->right; p1->right = p; if (b1 == 0) { p->balance = -1; p1->balance = 1; h = 0; } else { p->balance = p1->balance = 0; } (*this) = p1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -