📄 erl_db_tree.c
字号:
counterpos = tb->common.keypos + 1; res = db_do_update_counter(p, (DbTableCommon *) tb, (void *) bp, (*bp)->dbterm.tpl, counterpos, (int (*)(DbTableCommon *, void *, Uint, Eterm, int)) &realloc_counter, incr, warp, ret); if (*bp != b) /* May be reallocated in which case the saved stack is messed up, clear stck if so. */ tb->stack_pos = tb->slot_pos = 0; return res;}static int db_put_tree(Process *proc, DbTable *tbl, Eterm obj, Eterm *ret){ DbTableTree *tb = &tbl->tree; /* Non recursive insertion in AVL tree, building our own stack */ TreeDbTerm **tstack[STACK_NEED]; int tpos = 0; int dstack[STACK_NEED+1]; int dpos = 0; int state = 0; TreeDbTerm **this = &tb->root; Sint c; Eterm key; int dir; TreeDbTerm *p1, *p2, *p; key = GETKEY(tb, tuple_val(obj)); tb->stack_pos = tb->slot_pos = 0; dstack[dpos++] = DIR_END; for (;;) if (!*this) { /* Found our place */ state = 1; if (tb->common.nitems < TREE_MAX_ELEMENTS) tb->common.nitems++; else return DB_ERROR_SYSRES; *this = get_term(tb, NULL, obj); (*this)->balance = 0; (*this)->left = (*this)->right = NULL; break; } else if ((c = cmp(key,GETKEY(tb,(*this)->dbterm.tpl))) < 0) { /* go left */ 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 and this is a set, replace. */ *this = get_term(tb, *this, obj); break; } while (state && ( dir = dstack[--dpos] ) != DIR_END) { this = tstack[--tpos]; p = *this; if (dir == DIR_LEFT) { switch (p->balance) { case 1: p->balance = 0; state = 0; break; case 0: p->balance = -1; break; case -1: /* The icky case */ p1 = p->left; if (p1->balance == -1) { /* Single LL rotation */ p->left = p1->right; p1->right = p; p->balance = 0; (*this) = p1; } else { /* Double RR rotation */ p2 = p1->right; p1->right = p2->left; p2->left = p1; p->left = p2->right; p2->right = p; p->balance = (p2->balance == -1) ? +1 : 0; p1->balance = (p2->balance == 1) ? -1 : 0; (*this) = p2; } (*this)->balance = 0; state = 0; break; } } else { /* dir == DIR_RIGHT */ switch (p->balance) { case -1: p->balance = 0; state = 0; break; case 0: p->balance = 1; break; case 1: p1 = p->right; if (p1->balance == 1) { /* Single RR rotation */ p->right = p1->left; p1->left = p; p->balance = 0; (*this) = p1; } else { /* Double RL rotation */ p2 = p1->left; p1->left = p2->right; p2->right = p1; p->right = p2->left; p2->left = p; p->balance = (p2->balance == 1) ? -1 : 0; p1->balance = (p2->balance == -1) ? 1 : 0; (*this) = p2; } (*this)->balance = 0; state = 0; break; } } } *ret = am_true; return DB_ERROR_NONE;}static int db_get_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret){ DbTableTree *tb = &tbl->tree; Eterm copy; Eterm *hp; TreeDbTerm *this; /* * This is always a set, so we know exactly how large * the data is when we have found it. * The list created around it is purely for interface conformance. */ this = find_node(tb,key); if (this == NULL) { *ret = NIL; } else { hp = HAlloc(p, this->dbterm.size + 2); copy = copy_shallow(this->dbterm.v, this->dbterm.size, &hp, &MSO(p)); *ret = CONS(hp, copy, NIL); } return DB_ERROR_NONE;}static int db_member_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret){ DbTableTree *tb = &tbl->tree; TreeDbTerm *this; /* * This is always a set, so we know exactly how large * the data is when we have found it. * The list created around it is purely for interface conformance. */ this = find_node(tb,key); if (this == NULL) { *ret = am_false; } else { *ret = am_true; } return DB_ERROR_NONE;}static int db_get_element_tree(Process *p, DbTable *tbl, Eterm key, int ndex, Eterm *ret){ DbTableTree *tb = &tbl->tree; /* * Look the node up: */ Eterm *hp; TreeDbTerm *this; /* * This is always a set, so we know exactly how large * the data is when we have found it. * No list is created around elements in set's so there are no list * around the element here either. */ this = find_node(tb,key); if (this == NULL) { return DB_ERROR_BADKEY; } else { Eterm element; Uint sz; if (ndex > arityval(this->dbterm.tpl[0])) { return DB_ERROR_BADPARAM; } element = this->dbterm.tpl[ndex]; sz = size_object(element); hp = HAlloc(p, sz); *ret = copy_struct(element, sz, &hp, &MSO(p)); } return DB_ERROR_NONE;}static int db_erase_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret){ DbTableTree *tb = &tbl->tree; TreeDbTerm *res; *ret = am_true; if ((res = linkout_tree(tb, key)) != NULL) { free_term(tb, res); } return DB_ERROR_NONE;}static int db_erase_object_tree(Process *p, DbTable *tbl, Eterm object, Eterm *ret){ DbTableTree *tb = &tbl->tree; TreeDbTerm *res; *ret = am_true; if ((res = linkout_object_tree(tb, object)) != NULL) { free_term(tb, res); } return DB_ERROR_NONE;}static int db_slot_tree(Process *p, DbTable *tbl, Eterm slot_term, Eterm *ret){ DbTableTree *tb = &tbl->tree; Sint slot; TreeDbTerm *st; Eterm *hp; Eterm copy; /* * The notion of a "slot" is not natural in a tree, but we try to * simulate it by giving the n'th node in the tree instead. * Traversing a tree in this way is not very convenient, but by * using the saved stack we at least sometimes will get acceptable * performance. */ if (is_not_small(slot_term) || ((slot = signed_val(slot_term)) < 0) || (slot > tb->common.nitems)) return DB_ERROR_BADPARAM; if (slot == tb->common.nitems) { *ret = am_EOT; return DB_ERROR_NONE; } /* * We use the slot position and search from there, slot positions * are counted from 1 and up. */ ++slot; st = slot_search(p, tb, slot); if (st == NULL) { *ret = am_false; return DB_ERROR_UNSPEC; } hp = HAlloc(p, st->dbterm.size + 2); copy = copy_shallow(st->dbterm.v, st->dbterm.size, &hp, &MSO(p)); *ret = CONS(hp, copy, NIL); return DB_ERROR_NONE;}static BIF_RETTYPE ets_select_reverse(Process *p, Eterm a1, Eterm a2, Eterm a3){ Eterm list; Eterm result; Eterm* hp; Eterm* hend; int max_iter = CONTEXT_REDS * 10; if (is_nil(a1)) { hp = HAlloc(p, 3); BIF_RET(TUPLE2(hp,a2,a3)); } else if (is_not_list(a1)) { error: BIF_ERROR(p, BADARG); } list = a1; result = a2; hp = hend = NULL; while (is_list(list)) { Eterm* pair = list_val(list); if (--max_iter == 0) { BUMP_ALL_REDS(p); HRelease(p, hend, hp); BIF_TRAP3(&ets_select_reverse_exp, p, list, result, a3); } if (hp == hend) { hp = HAlloc(p, 64); hend = hp + 64; } result = CONS(hp, CAR(pair), result); hp += 2; list = CDR(pair); } if (is_not_nil(list)) { goto error; } HRelease(p, hend, hp); BUMP_REDS(p,CONTEXT_REDS - max_iter / 10); hp = HAlloc(p,3); BIF_RET(TUPLE2(hp, result, a3));}static BIF_RETTYPE bif_trap1(Export *bif, Process *p, Eterm p1) { BIF_TRAP1(bif, p, p1);} static BIF_RETTYPE bif_trap3(Export *bif, Process *p, Eterm p1, Eterm p2, Eterm p3) { BIF_TRAP3(bif, p, p1, p2, p3);} /*** This is called either when the select bif traps or when ets:select/1 ** is called. It does mostly the same as db_select_tree and may in either case** trap to itself again (via the ets:select/1 bif).** Note that this is common for db_select_tree and db_select_chunk_tree.*/static int db_select_continue_tree(Process *p, DbTable *tbl, Eterm continuation, Eterm *ret){ DbTableTree *tb = &tbl->tree; struct select_context sc; unsigned sz; Eterm *hp; Eterm lastkey; Eterm end_condition; Binary *mp; Eterm key; Eterm *tptr; Sint chunk_size; Sint reverse;#define RET_TO_BIF(Term, State) do { *ret = (Term); return State; } while(0); /* Decode continuation. We know it's a tuple but not the arity or anything else */ tptr = tuple_val(continuation); if (arityval(*tptr) != 8) RET_TO_BIF(NIL,DB_ERROR_BADPARAM); if (!is_small(tptr[4]) || !is_binary(tptr[5]) || !(is_list(tptr[6]) || tptr[6] == NIL) || !is_small(tptr[7]) || !is_small(tptr[8])) RET_TO_BIF(NIL,DB_ERROR_BADPARAM); lastkey = tptr[2]; end_condition = tptr[3]; if (!(thing_subtag(*binary_val(tptr[5])) == REFC_BINARY_SUBTAG)) RET_TO_BIF(NIL,DB_ERROR_BADPARAM); mp = ((ProcBin *) binary_val(tptr[5]))->val; if (!(mp->flags & BIN_FLAG_MATCH_PROG)) RET_TO_BIF(NIL,DB_ERROR_BADPARAM); chunk_size = signed_val(tptr[4]); sc.p = p; sc.accum = tptr[6]; sc.mp = mp; sc.end_condition = NIL; sc.lastobj = NULL; sc.max = 1000; sc.keypos = tb->common.keypos; sc.all_objects = mp->flags & BIN_FLAG_ALL_OBJECTS; sc.chunk_size = chunk_size; reverse = unsigned_val(tptr[7]); sc.got = signed_val(tptr[8]); if (chunk_size) { if (reverse) { traverse_backwards(tb, lastkey, &doit_select_chunk, &sc); } else { traverse_forward(tb, lastkey, &doit_select_chunk, &sc); } } else { if (reverse) { traverse_forward(tb, lastkey, &doit_select, &sc); } else { traverse_backwards(tb, lastkey, &doit_select, &sc); } } BUMP_REDS(p, 1000 - sc.max); if (sc.max > 0) { if (chunk_size) { Eterm *hp; unsigned sz; if (sc.got < chunk_size || sc.lastobj == NULL) { /* end of table, sc.lastobj may be NULL as we may have been at the very last object in the table when trapping. */ if (!sc.got) { RET_TO_BIF(am_EOT, DB_ERROR_NONE); } else { RET_TO_BIF(bif_trap3(&ets_select_reverse_exp, p, sc.accum, NIL, am_EOT), DB_ERROR_NONE); } } key = GETKEY(tb, sc.lastobj); sz = size_object(key); hp = HAlloc(p, 9 + sz); key = copy_struct(key, sz, &hp, &MSO(p)); continuation = TUPLE8 (hp, tptr[1], key, tptr[3], tptr[4], tptr[5], NIL, tptr[7], make_small(0)); RET_TO_BIF(bif_trap3(&ets_select_reverse_exp, p, sc.accum, NIL, continuation), DB_ERROR_NONE); } else { RET_TO_BIF(sc.accum, DB_ERROR_NONE); } } key = GETKEY(tb, sc.lastobj); if (chunk_size) { if (end_condition != NIL && ((!reverse && cmp_partly_bound(end_condition,key) < 0) || (reverse && cmp_partly_bound(end_condition,key) > 0))) { /* done anyway */ if (!sc.got) { RET_TO_BIF(am_EOT, DB_ERROR_NONE); } else { RET_TO_BIF(bif_trap3(&ets_select_reverse_exp, p, sc.accum, NIL, am_EOT), DB_ERROR_NONE); } } } else { if (end_condition != NIL && ((!reverse && cmp_partly_bound(end_condition,key) > 0) || (reverse && cmp_partly_bound(end_condition,key) < 0))) { /* done anyway */ RET_TO_BIF(sc.accum,DB_ERROR_NONE); } } /* Not done yet, let's trap. */ sz = size_object(key); hp = HAlloc(p, 9 + sz); key = copy_struct(key, sz, &hp, &MSO(p)); continuation = TUPLE8 (hp, tptr[1], key, tptr[3], tptr[4], tptr[5], sc.accum, tptr[7], make_small(sc.got)); RET_TO_BIF(bif_trap1(bif_export[BIF_ets_select_1], p, continuation), DB_ERROR_NONE);#undef RET_TO_BIF}static int db_select_tree(Process *p, DbTable *tbl, Eterm pattern, int reverse, Eterm *ret){ DbTableTree *tb = &tbl->tree; struct select_context sc; struct mp_info mpi; Eterm lastkey = NIL; Eterm key; Eterm continuation; unsigned sz; Eterm *hp; TreeDbTerm *this; int errcode; Eterm mpb;#define RET_TO_BIF(Term,RetVal) do { \ if (mpi.mp != NULL) { \ erts_match_set_free(mpi.mp); \ } \ *ret = (Term); \ return RetVal; \ } while(0) mpi.mp = NULL; sc.accum = NIL; sc.lastobj = NULL; sc.p = p; sc.max = 1000; sc.end_condition = NIL; sc.keypos = tb->common.keypos; sc.got = 0; sc.chunk_size = 0; if ((errcode = analyze_pattern(tb, pattern, &mpi)) != DB_ERROR_NONE) { RET_TO_BIF(NIL,errcode); } if (!mpi.something_can_match) { RET_TO_BIF(NIL,DB_ERROR_NONE); /* can't possibly match anything */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -