diff options
Diffstat (limited to 'nbt/value.ha')
-rw-r--r-- | nbt/value.ha | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/nbt/value.ha b/nbt/value.ha new file mode 100644 index 0000000..cf4aa17 --- /dev/null +++ b/nbt/value.ha @@ -0,0 +1,141 @@ +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); + }; +}; |