/* * BIF implementations */ static void pd_hash_erase(Process *p, Eterm id, Eterm *ret) { unsigned int hval; Eterm old; Eterm tmp; unsigned int range; *ret = am_undefined; if (p->dictionary == NULL) { return; } hval = pd_hash_value(p->dictionary, id); old = ARRAY_GET(p->dictionary, hval); if (is_boxed(old)) { /* Tuple */ ASSERT(is_tuple(old)); if (EQ(tuple_val(old)[1], id)) { array_put(&(p->dictionary), hval, NIL); --(p->dictionary->numElements); *ret = tuple_val(old)[2]; } } else if (is_list(old)) { /* Find cons cell for identical value */ Eterm* prev = &p->dictionary->data[hval]; for (tmp = *prev; tmp != NIL; prev = &TCDR(tmp), tmp = *prev) { if (EQ(tuple_val(TCAR(tmp))[1], id)) { *prev = TCDR(tmp); *ret = tuple_val(TCAR(tmp))[2]; --(p->dictionary->numElements); } } /* If there is only one element left in the list we must remove the list. */ old = ARRAY_GET(p->dictionary, hval); ASSERT(is_list(old)); if (is_nil(TCDR(old))) { array_put(&p->dictionary, hval, TCAR(old)); } } else if (is_not_nil(old)) { #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 erase/1."); } if ((range = HASH_RANGE(p->dictionary)) > INITIAL_SIZE && range / 2 > (p->dictionary->numElements)) { shrink(p, ret); } }
static void rm_watch(int wd, bool update_parent) { watch_node* node = table_get(watches, wd); if (node == NULL) { return; } userlog(LOG_DEBUG, "unwatching %s: %d (%p)", node->path, node->wd, node); if (inotify_rm_watch(inotify_fd, node->wd) < 0) { userlog(LOG_DEBUG, "inotify_rm_watch(%d:%s): %s", node->wd, node->path, strerror(errno)); } for (int i=0; i<array_size(node->kids); i++) { watch_node* kid = array_get(node->kids, i); if (kid != NULL) { rm_watch(kid->wd, false); } } if (update_parent && node->parent != NULL) { for (int i=0; i<array_size(node->parent->kids); i++) { if (array_get(node->parent->kids, i) == node) { array_put(node->parent->kids, i, NULL); break; } } } array_delete(node->kids); free(node); table_put(watches, wd, NULL); }
struct array* array_read(char * filename) { int i; FILE * h; int num; value value; struct array * res; h = fopen(filename, "r"); i = read_value(h, &num); assert(i > 0); res = array_alloc(num); num = 1; for (i = 0; i < res->capacity && num > 0; i++) { num = read_value(h, &value); array_put(res, value); } fclose(h); return res; }
uint32_t watch_add(watch_t * self, const char *path, uint32_t events, watch_callback_t * cb) { if (unlikely(self == NULL)) throw_unexpected(WATCH_NULL); if (access(path, F_OK) != 0) throw_errno(errno); uint32_t wd = inotify_add_watch(self->fd, path, events); if (unlikely((int)wd == -1)) throw_errno(errno); if (cb != NULL) array_put(&self->callbacks, wd, cb, 1); return wd; }
static bool process_inotify_event(struct inotify_event* event) { watch_node* node = table_get(watches, event->wd); if (node == NULL) { return true; } bool is_dir = (event->mask & IN_ISDIR) == IN_ISDIR; userlog(LOG_DEBUG, "inotify: wd=%d mask=%d dir=%d name=%s", event->wd, event->mask & (~IN_ISDIR), is_dir, node->path); memcpy(path_buf, node->path, node->path_len + 1); int path_len = node->path_len; if (event->len > 0) { path_buf[node->path_len] = '/'; int name_len = strlen(event->name); memcpy(path_buf + path_len + 1, event->name, name_len + 1); path_len += name_len + 1; } if (is_dir && event->mask & (IN_CREATE | IN_MOVED_TO)) { int result = walk_tree(path_len, node, true, NULL); if (result < 0 && result != ERR_IGNORE && result != ERR_CONTINUE) { return false; } } if (is_dir && event->mask & (IN_DELETE | IN_MOVED_FROM)) { for (int i=0; i<array_size(node->kids); i++) { watch_node* kid = array_get(node->kids, i); if (kid != NULL && strncmp(path_buf, kid->path, kid->path_len) == 0) { rm_watch(kid->wd, false); array_put(node->kids, i, NULL); break; } } } if (callback != NULL) { (*callback)(path_buf, event->mask); } return true; }
int main(int argc, char ** argv) { int i, max, min, count, amplitude; struct array * array; srandom(rdtsc() + time(NULL)); if(argc < 4) { printf("Usage: %s <min_value> <max_value> <number of values>\n", argv[0]); return EXIT_FAILURE; } else { array_init(argc, argv); min = atoi(argv[1]); max = atoi(argv[2]); count = atoi(argv[3]); array = array_alloc(count); amplitude = max - min + 1; for(i = 0; i < count; i++) { array_put(array, random() % amplitude + min); } printf("%d ", count); for(i = 0; i < count; i++) { printf("%d ", array_get(array, i)); } array_free(array); } return 0; }
static bool process_inotify_event(struct inotify_event* event) { watch_node* node = table_get(watches, event->wd); if (node == NULL) { return true; } userlog(LOG_DEBUG, "inotify: wd=%d mask=%d dir=%d name=%s", event->wd, event->mask & (~IN_ISDIR), (event->mask & IN_ISDIR) != 0, node->name); char path[PATH_MAX]; strcpy(path, node->name); if (event->len > 0) { if (path[strlen(path) - 1] != '/') { strcat(path, "/"); } strcat(path, event->name); } if ((event->mask & IN_CREATE || event->mask & IN_MOVED_TO) && event->mask & IN_ISDIR) { int result = walk_tree(path, node, NULL); if (result < 0 && result != ERR_IGNORE) { return false; } } if ((event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) && event->mask & IN_ISDIR) { for (int i=0; i<array_size(node->kids); i++) { watch_node* kid = array_get(node->kids, i); if (kid != NULL && strcmp(kid->name, path) == 0) { rm_watch(kid->wd, false); array_put(node->kids, i, NULL); break; } } } if (callback != NULL) { (*callback)(path, event->mask); } return true; }
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; }
int main(int argc, char *argv[]) { struct array a; struct array_iterator ai; int i; int *t, *t_arr; while ((i = getopt(argc, argv, "vi:")) != -1) { switch (i) { case 'v': ++verbose; break; case 'i': MAX_ITEMS = atoi(optarg); break; default: fprintf(stderr, "bad option\n"); exit(1); } } assert(t_arr = malloc(MAX_ITEMS * sizeof(int))); array_init(&a, 100); verbose_print(1, "start\n"); for (i = 0; i < MAX_ITEMS; i++) { t = t_arr + i; *t = i; assert(array_put(&a, t) == i); } for (i = 0; i < MAX_ITEMS; i += 2) { t = t_arr + i; assert(array_remove(&a, t) == t); } for (i = 1; i < MAX_ITEMS; i += 2) { t = t_arr + i; assert(array_pop(&a) == t); } for (i = 0; i < MAX_ITEMS; i += 2) { t = t_arr + i; assert(array_put_at(&a, i, t) == NULL); } for (i = 1; i < MAX_ITEMS; i += 2) { t = t_arr + i; assert(array_put(&a, t) == i); } for (i = 0; i < MAX_ITEMS; i += 2) { t = t_arr + i; assert(array_remove_at(&a, i) == t); } array_iter_set(&ai, &a); i = 1; while ((t = array_iter_get(&ai))) { assert(*t == i); i += 2; verbose_print(2, "%d ", *t); } array_iter_end(&ai); printf("\n"); array_free(&a); return 0; }