#include "inc/priv.h" #include "inc/equal.h" #include "inc/cvalues.h" #include "inc/types.h" #include "inc/print.h" #include "inc/table.h" const sl_type *sl_tabletype; static void print_htable(sl_v v) { sl_htable *h = cvalue_data(v); bool first = true; sl_print_str("#table(", true); for(usize i = 0, k = 0; k < h->live && i < h->size; i += 2){ if(h->table[i+1] == HT_NOTFOUND) continue; k++; if(!first) sl_print_str(" ", true); sl_print_child((sl_v)h->table[i]); sl_print_chr(' '); sl_print_child((sl_v)h->table[i+1]); first = false; } sl_print_chr(')'); } static void print_traverse_htable(sl_v self) { sl_htable *h = cvalue_data(self); for(usize i = 0; i < h->size; i += 2){ if(h->table[i+1] != HT_NOTFOUND){ sl_print_traverse((sl_v)h->table[i]); sl_print_traverse((sl_v)h->table[i+1]); } } } static void free_htable(sl_v self) { sl_htable *h = cvalue_data(self); htable_free(h); } static void relocate_htable(sl_v oldv, sl_v newv) { sl_htable *oldh = cvalue_data(oldv); sl_htable *h = cvalue_data(newv); if(oldh->table == &oldh->local[0]) h->table = &h->local[0]; h->i = oldh->i; h->num = oldh->num; h->live = oldh->live; for(usize i = 0, k = 0; k < h->live && i < h->size; i += 2){ if(oldh->table[i+1] == HT_NOTFOUND) continue; k++; h->table[i] = (void*)sl_relocate((sl_v)oldh->table[i]); h->table[i+1] = (void*)sl_relocate((sl_v)oldh->table[i+1]); } } static sl_v bounded_compare_htable(sl_v a, sl_v b, int bound, bool eq) { sl_htable *ha = totable(a), *hb = totable(b); usize na = ha->size; void **ta = ha->table; bound--; for(usize i = 0, k = 0; k < ha->live && i < na; i += 2){ if(ta[i+1] == HT_NOTFOUND) continue; k++; sl_v v = (sl_v)equalhash_get(hb, ta[i]); if(v == (sl_v)HT_NOTFOUND) return fixnum(1); v = bounded_compare((sl_v)ta[i+1], v, bound, eq); if(v != fixnum(0)) return v; } if(ha->live != hb->live) return ha->live > hb->live ? fixnum(1) : fixnum(-1); return fixnum(0); } static sl_cvtable table_vtable = { print_htable, relocate_htable, free_htable, print_traverse_htable, bounded_compare_htable }; bool ishashtable(sl_v v) { return iscvalue(v) && cvalue_class(v) == sl_tabletype; } BUILTIN("table?", tablep) { argcount(1); return ishashtable(args[0]) ? sl_t : sl_nil; } sl_returns_nonnull sl_htable * totable(sl_v v) { if(!ishashtable(v)) cthrow(type_error(nil, "table", v), v); return cvalue_data(v); } BUILTIN("table", table) { int cnt = nargs; if(cnt & 1) bthrow(error(sl_errarg, "arguments must come in pairs")); sl_v nt; // prevent small tables from being added to finalizer list if(cnt <= HT_N_INLINE) nt = cvalue_nofinalizer(sl_tabletype, sizeof(sl_htable)); else nt = cvalue(sl_tabletype, sl_offsetof(sl_htable, local[0])); sl_htable *h = cvalue_data(nt); htable_new(h, cnt/2); sl_v k = sl_nil, arg; int i; FOR_ARGS(i, 0, arg, args){ if(i & 1) equalhash_put(h, (void*)k, (void*)arg); else k = arg; } assert(h->table == &h->local[0] || cnt > HT_N_INLINE); return nt; } BUILTIN("put!", put) { argcount(3); sl_htable *h = totable(args[0]); void **table0 = h->table; equalhash_put(h, (void*)args[1], (void*)args[2]); // register finalizer if we outgrew inline space if(table0 == &h->local[0] && h->table != &h->local[0]){ sl_cv *cv = ptr(sl_cv, args[0]); add_finalizer(cv); cv->len = sl_offsetof(sl_htable, local[0]); } return args[0]; } _Noreturn static void sl_key_error(sl_v key) { sl_raise(mk_list2(sl_errkey, key)); } BUILTIN("get", get) { argcount_least(2); argcount_most(3); sl_htable *h = totable(args[0]); sl_v v = (sl_v)equalhash_get(h, (void*)args[1]); if(v == (sl_v)HT_NOTFOUND){ if(nargs == 3) return args[2]; bthrow(key_error(args[1])); } return v; } BUILTIN("has?", has) { argcount(2); sl_htable *h = totable(args[0]); return equalhash_has(h, (void*)args[1]) ? sl_t : sl_nil; } BUILTIN("del!", del) { argcount(2); sl_htable *h = totable(args[0]); equalhash_remove(h, (void*)args[1]); return args[0]; } BUILTIN("table-fold", table_fold) { argcount(3); sl_v f = args[0], zero = args[1], t = args[2]; sl_htable *h = totable(t); usize n = h->size; void **table = h->table; sl_gc_handle(&f); sl_gc_handle(&zero); sl_gc_handle(&t); for(usize i = 0, k = 0; k < h->live && i < n; i += 2){ if(table[i+1] == HT_NOTFOUND) continue; k++; zero = sl_applyn(3, f, (sl_v)table[i], (sl_v)table[i+1], zero); // reload pointer h = cvalue_data(t); n = h->size; table = h->table; } sl_free_gc_handles(3); return zero; } void table_init(void) { sl_tabletype = define_opaque_type(mk_sym("table", false), sizeof(sl_htable), &table_vtable, nil); }