use hash::fnv; use htab; use strings; export type Value = ( i8 | i16 | i32 | i64 | f32 | f64 | []i8 | []i32 | []i64 | str | []Value | Object); export type Object = struct { table: htab::table, }; type Entry = (str, Value); fn eq_fn(ctx: *opaque, key: *opaque) bool = *(ctx: *str) == *(key: *str); fn _get(object: *Object, hash: u64, key: str) nullable *Entry = htab::get(&object.table, hash, &eq_fn, &key, size(Entry)): nullable *Entry; export fn newobject(cap: size) Object = Object { table = htab::new(cap, size(Entry)) }; export fn count(object: *Object) size = return htab::count(&object.table); export fn get(object: *Object, key: str) nullable *Value = { const hash = fnv::string64(key); match (_get(object, hash, key)) { case let entry: *Entry => return &entry.1; case null => return null; }; }; export fn set(object: *Object, key: str, value: Value) void = { const hash = fnv::string64(key); match (_get(object, hash, key)) { case let entry: *Entry => finish(entry.1); entry.1 = value; case null => const entry = htab::add(&object.table, hash, size(Entry)): *Entry; *entry = (strings::dup(key), value); }; }; export fn del(object: *Object, key: str) (Value | void) = { const hash = fnv::string64(key); match (_get(object, hash, key)) { case let entry: *Entry => free(entry.0); const value = entry.1; htab::del(&object.table, entry, size(Entry)); return value; case null => void; }; }; export fn _clear(object: *Object) void = { let it = htab::iter(&object.table); for (true) match (htab::next(&it, size(Entry)): nullable *Entry) { case let entry: *Entry => free(entry.0); finish(entry.1); case null => break; }; }; export fn clear(object: *Object) void = { _clear(object); htab::clear(&object.table, size(Entry)); }; export type Iterator = struct { iter: htab::iterator, }; export fn iter(object: *Object) Iterator = Iterator { iter = htab::iter(&object.table) }; export fn next(it: *Iterator) ((str, *Value) | void) = { match (htab::next(&it.iter, size(Entry)): nullable *Entry) { case let entry: *Entry => return (entry.0, &entry.1); case null => void; }; }; export fn dup(value: *Value) Value = { match (*value) { case let array: []i8 => return alloc(array...); case let array: []i32 => return alloc(array...); case let array: []i64 => return alloc(array...); case let string: str => return strings::dup(string); case let list: []Value => let list_ = alloc([]: [0]Value, len(list)); for (let i = 0z; i < len(list); i += 1) { append(list_, dup(&list[i])); }; return list_; case let object: Object => let object_ = newobject(count(&object)); let it = iter(&object); for (true) match (next(&it)) { case let entry: (str, *Value) => set(&object_, entry.0, dup(entry.1)); case void => break; }; return object_; }; }; export fn finish(value: Value) void = { match (value) { case (i8 | i16 | i32 | i64 | f32 | f64) => void; case let array: []i8 => free(array); case let array: []i32 => free(array); case let array: []i64 => free(array); case let string: str => free(string); case let list: []Value => for (let i = 0z; i < len(list); i += 1) { finish(list[i]); }; free(list); case let object: Object => _clear(&object); }; };