BIF_RETTYPE lists_reverse_2(BIF_ALIST_2) { /* Handle legal and illegal non-lists quickly. */ if (is_nil(BIF_ARG_1)) { BIF_RET(BIF_ARG_2); } else if (is_not_list(BIF_ARG_1)) { BIF_ERROR(BIF_P, BADARG); } /* We build the reversal on the unused part of the heap if possible to save * us the trouble of having to figure out the list size. We fall back to * lists_reverse_alloc when we run out of space. */ if (HeapWordsLeft(BIF_P) > 8) { return lists_reverse_onheap(BIF_P, BIF_ARG_1, BIF_ARG_2); } return lists_reverse_alloc(BIF_P, BIF_ARG_1, BIF_ARG_2); }
static void grow(Process *p) { unsigned int i,j; unsigned int steps = p->dictionary->homeSize / 5; Eterm l1,l2; Eterm l; Eterm *hp; unsigned int pos; unsigned int homeSize; int needed = 0; ProcDict *pd; #ifdef DEBUG Eterm *hp_limit; #endif HDEBUGF(("grow: steps = %d", steps)); if (steps == 0) steps = 1; /* Dont grow over MAX_HASH */ if ((MAX_HASH - steps) <= HASH_RANGE(p->dictionary)) { return; } /* * Calculate total number of heap words needed, and garbage collect * if necessary. */ pd = p->dictionary; pos = pd->splitPosition; homeSize = pd->homeSize; for (i = 0; i < steps; ++i) { if (pos == homeSize) { homeSize *= 2; pos = 0; } l = ARRAY_GET(pd, pos); pos++; if (is_not_tuple(l)) { while (l != NIL) { needed += 2; l = TCDR(l); } } } if (HeapWordsLeft(p) < needed) { BUMP_REDS(p, erts_garbage_collect(p, needed, 0, 0)); } #ifdef DEBUG hp_limit = p->htop + needed; #endif /* * Now grow. */ for (i = 0; i < steps; ++i) { ProcDict *pd = p->dictionary; if (pd->splitPosition == pd->homeSize) { pd->homeSize *= 2; pd->splitPosition = 0; } pos = pd->splitPosition; ++pd->splitPosition; /* For the hashes */ l = ARRAY_GET(pd, pos); if (is_tuple(l)) { if (pd_hash_value(pd, tuple_val(l)[1]) != pos) { array_put(&(p->dictionary), pos + p->dictionary->homeSize, l); array_put(&(p->dictionary), pos, NIL); } } else { l2 = NIL; l1 = l; for (j = 0; l1 != NIL; l1 = TCDR(l1)) j += 2; hp = HeapOnlyAlloc(p, j); while (l != NIL) { if (pd_hash_value(pd, tuple_val(TCAR(l))[1]) == pos) l1 = CONS(hp, TCAR(l), l1); else l2 = CONS(hp, TCAR(l), l2); hp += 2; l = TCDR(l); } if (l1 != NIL && TCDR(l1) == NIL) l1 = TCAR(l1); if (l2 != NIL && TCDR(l2) == NIL) l2 = TCAR(l2); ASSERT(hp <= hp_limit); /* After array_put pd is no longer valid */ array_put(&(p->dictionary), pos, l1); array_put(&(p->dictionary), pos + p->dictionary->homeSize, l2); } } #ifdef HARDDEBUG dictionary_dump(p->dictionary,CERR); #endif }
static void shrink(Process *p, Eterm* ret) { unsigned int range = HASH_RANGE(p->dictionary); unsigned int steps = (range*3) / 10; Eterm hi, lo, tmp; unsigned int i; Eterm *hp; #ifdef DEBUG Eterm *hp_limit; #endif if (range - steps < INITIAL_SIZE) { steps = range - INITIAL_SIZE; } for (i = 0; i < steps; ++i) { ProcDict *pd = p->dictionary; if (pd->splitPosition == 0) { pd->homeSize /= 2; pd->splitPosition = pd->homeSize; } --(pd->splitPosition); hi = ARRAY_GET(pd, (pd->splitPosition + pd->homeSize)); lo = ARRAY_GET(pd, pd->splitPosition); if (hi != NIL) { if (lo == NIL) { array_put(&(p->dictionary), pd->splitPosition, hi); } else { int needed = 4; if (is_list(hi) && is_list(lo)) { needed = 2*erts_list_length(hi); } if (HeapWordsLeft(p) < needed) { BUMP_REDS(p, erts_garbage_collect(p, needed, ret, 1)); hi = pd->data[(pd->splitPosition + pd->homeSize)]; lo = pd->data[pd->splitPosition]; } #ifdef DEBUG hp_limit = p->htop + needed; #endif if (is_tuple(lo)) { if (is_tuple(hi)) { hp = HeapOnlyAlloc(p, 4); tmp = CONS(hp, hi, NIL); hp += 2; array_put(&(p->dictionary), pd->splitPosition, CONS(hp,lo,tmp)); hp += 2; ASSERT(hp <= hp_limit); } else { /* hi is a list */ hp = HeapOnlyAlloc(p, 2); array_put(&(p->dictionary), pd->splitPosition, CONS(hp, lo, hi)); hp += 2; ASSERT(hp <= hp_limit); } } else { /* lo is a list */ if (is_tuple(hi)) { hp = HeapOnlyAlloc(p, 2); array_put(&(p->dictionary), pd->splitPosition, CONS(hp, hi, lo)); hp += 2; ASSERT(hp <= hp_limit); } else { /* Two lists */ hp = HeapOnlyAlloc(p, needed); for (tmp = hi; tmp != NIL; tmp = TCDR(tmp)) { lo = CONS(hp, TCAR(tmp), lo); hp += 2; } ASSERT(hp <= hp_limit); array_put(&(p->dictionary), pd->splitPosition, lo); } } } } array_put(&(p->dictionary), (pd->splitPosition + pd->homeSize), NIL); } if (HASH_RANGE(p->dictionary) <= (p->dictionary->size / 4)) { array_shrink(&(p->dictionary), (HASH_RANGE(p->dictionary) * 3) / 2); } }
static Eterm pd_hash_put(Process *p, Eterm id, Eterm value) { unsigned int hval; Eterm *hp; Eterm tpl; Eterm old; Eterm tmp; int needed; int i = 0; #ifdef DEBUG Eterm *hp_limit; #endif if (p->dictionary == NULL) { /* Create it */ array_put(&(p->dictionary), INITIAL_SIZE - 1, NIL); p->dictionary->homeSize = INITIAL_SIZE; } hval = pd_hash_value(p->dictionary, id); old = ARRAY_GET(p->dictionary, hval); /* * Calculate the number of heap words needed and garbage * collect if necessary. (Might be a slight overestimation.) */ needed = 3; /* {Key,Value} tuple */ if (is_boxed(old)) { /* * We don't want to compare keys twice, so we'll always * reserve the space for two CONS cells. */ needed += 2+2; } else if (is_list(old)) { i = 0; for (tmp = old; tmp != NIL && !EQ(tuple_val(TCAR(tmp))[1], id); tmp = TCDR(tmp)) { ++i; } if (is_nil(tmp)) { i = -1; needed += 2; } else { needed += 2*(i+1); } } if (HeapWordsLeft(p) < needed) { Eterm root[3]; root[0] = id; root[1] = value; root[2] = old; BUMP_REDS(p, erts_garbage_collect(p, needed, root, 3)); id = root[0]; value = root[1]; old = root[2]; } #ifdef DEBUG hp_limit = p->htop + needed; #endif /* * Create the {Key,Value} tuple. */ hp = HeapOnlyAlloc(p, 3); tpl = TUPLE2(hp, id, value); /* * Update the dictionary. */ if (is_nil(old)) { array_put(&(p->dictionary), hval, tpl); ++(p->dictionary->numElements); } else if (is_boxed(old)) { ASSERT(is_tuple(old)); if (EQ(tuple_val(old)[1],id)) { array_put(&(p->dictionary), hval, tpl); return tuple_val(old)[2]; } else { hp = HeapOnlyAlloc(p, 4); tmp = CONS(hp, old, NIL); hp += 2; ++(p->dictionary->numElements); array_put(&(p->dictionary), hval, CONS(hp, tpl, tmp)); hp += 2; ASSERT(hp <= hp_limit); } } else if (is_list(old)) { if (i == -1) { /* * New key. Simply prepend the tuple to the beginning of the list. */ hp = HeapOnlyAlloc(p, 2); array_put(&(p->dictionary), hval, CONS(hp, tpl, old)); hp += 2; ASSERT(hp <= hp_limit); ++(p->dictionary->numElements); } else { /* * i = Number of CDRs to skip to reach the changed element in the list. * * Replace old value in list. To avoid pointers from the old generation * to the new, we must rebuild the list from the beginning up to and * including the changed element. */ Eterm nlist; int j; hp = HeapOnlyAlloc(p, (i+1)*2); /* Find the list element to change. */ for (j = 0, nlist = old; j < i; j++, nlist = TCDR(nlist)) { ; } ASSERT(EQ(tuple_val(TCAR(nlist))[1], id)); nlist = TCDR(nlist); /* Unchanged part of list. */ /* Rebuild list before the updated element. */ for (tmp = old; i-- > 0; tmp = TCDR(tmp)) { nlist = CONS(hp, TCAR(tmp), nlist); hp += 2; } ASSERT(EQ(tuple_val(TCAR(tmp))[1], id)); /* Put the updated element first in the new list. */ nlist = CONS(hp, tpl, nlist); hp += 2; ASSERT(hp <= hp_limit); array_put(&(p->dictionary), hval, nlist); return tuple_val(TCAR(tmp))[2]; } } else { #ifdef DEBUG erts_fprintf(stderr, "Process dictionary for process %T is broken, trying to " "display term found in line %d:\n" "%T\n", p->common.id, __LINE__, old); #endif erl_exit(1, "Damaged process dictionary found during put/2."); } if (HASH_RANGE(p->dictionary) <= p->dictionary->numElements) { grow(p); } return am_undefined; }
BIF_RETTYPE lists_reverse_2(BIF_ALIST_2) { Eterm list; Eterm tmp_list; Eterm result; Eterm* hp; Uint n; int max_iter; /* * Handle legal and illegal non-lists quickly. */ if (is_nil(BIF_ARG_1)) { BIF_RET(BIF_ARG_2); } else if (is_not_list(BIF_ARG_1)) { error: BIF_ERROR(BIF_P, BADARG); } /* * First use the rest of the remaning heap space. */ list = BIF_ARG_1; result = BIF_ARG_2; hp = HEAP_TOP(BIF_P); n = HeapWordsLeft(BIF_P) / 2; while (n != 0 && is_list(list)) { Eterm* pair = list_val(list); result = CONS(hp, CAR(pair), result); list = CDR(pair); hp += 2; n--; } HEAP_TOP(BIF_P) = hp; if (is_nil(list)) { BIF_RET(result); } /* * Calculate length of remaining list (up to a suitable limit). */ max_iter = CONTEXT_REDS * 40; n = 0; tmp_list = list; while (max_iter-- > 0 && is_list(tmp_list)) { tmp_list = CDR(list_val(tmp_list)); n++; } if (is_not_nil(tmp_list) && is_not_list(tmp_list)) { goto error; } /* * Now do one HAlloc() and continue reversing. */ hp = HAlloc(BIF_P, 2*n); while (n != 0 && is_list(list)) { Eterm* pair = list_val(list); result = CONS(hp, CAR(pair), result); list = CDR(pair); hp += 2; n--; } if (is_nil(list)) { BIF_RET(result); } else { BUMP_ALL_REDS(BIF_P); BIF_TRAP2(bif_export[BIF_lists_reverse_2], BIF_P, list, result); } }
static BIF_RETTYPE append(Process* p, Eterm A, Eterm B) { Eterm list; Eterm copy; Eterm last; Eterm* hp = NULL; Sint i; list = A; if (is_nil(list)) { BIF_RET(B); } if (is_not_list(list)) { BIF_ERROR(p, BADARG); } /* optimistic append on heap first */ if ((i = HeapWordsLeft(p) / 2) < 4) { goto list_tail; } hp = HEAP_TOP(p); copy = last = CONS(hp, CAR(list_val(list)), make_list(hp+2)); list = CDR(list_val(list)); hp += 2; i -= 2; /* don't use the last 2 words (extra i--;) */ while(i-- && is_list(list)) { Eterm* listp = list_val(list); last = CONS(hp, CAR(listp), make_list(hp+2)); list = CDR(listp); hp += 2; } /* A is proper and B is NIL return A as-is, don't update HTOP */ if (is_nil(list) && is_nil(B)) { BIF_RET(A); } if (is_nil(list)) { HEAP_TOP(p) = hp; CDR(list_val(last)) = B; BIF_RET(copy); } list_tail: if ((i = erts_list_length(list)) < 0) { BIF_ERROR(p, BADARG); } /* remaining list was proper and B is NIL */ if (is_nil(B)) { BIF_RET(A); } if (hp) { /* Note: fall through case, already written * on the heap. * The last 2 words of the heap is not written yet */ Eterm *hp_save = hp; ASSERT(i != 0); HEAP_TOP(p) = hp + 2; if (i == 1) { hp[0] = CAR(list_val(list)); hp[1] = B; BIF_RET(copy); } hp = HAlloc(p, 2*(i - 1)); last = CONS(hp_save, CAR(list_val(list)), make_list(hp)); } else { hp = HAlloc(p, 2*i); copy = last = CONS(hp, CAR(list_val(list)), make_list(hp+2)); hp += 2; } list = CDR(list_val(list)); i--; ASSERT(i > -1); while(i--) { Eterm* listp = list_val(list); last = CONS(hp, CAR(listp), make_list(hp+2)); list = CDR(listp); hp += 2; } CDR(list_val(last)) = B; BIF_RET(copy); }