value.ha (4759B)
1 // License: MPL-2.0 2 // (c) 2022 Drew DeVault <sir@cmpwn.com> 3 use hash::fnv; 4 use strings; 5 6 // TODO: Resize table as appropriate 7 export def OBJECT_BUCKETS: size = 32; 8 9 export type object = struct { 10 buckets: [OBJECT_BUCKETS][](str, value), 11 }; 12 13 // A JSON value. 14 export type value = (f64 | str | bool | _null | []value | object); 15 16 // Initializes a new (empty) JSON object. Call [[finish]] to free associated 17 // resources when you're done using it. 18 export fn newobject() object = { 19 return object { ... }; 20 }; 21 22 // Gets a value from a JSON object. The return value is borrowed from the 23 // object. 24 export fn get(obj: *object, key: str) (*value | void) = { 25 const hash = fnv::string(key); 26 const bucket = &obj.buckets[hash % len(obj.buckets)]; 27 for (let i = 0z; i < len(bucket); i += 1) { 28 if (bucket[i].0 == key) { 29 return &bucket[i].1; 30 }; 31 }; 32 }; 33 34 // Sets a value in a JSON object. The key and value will be duplicated. 35 export fn set(obj: *object, key: const str, val: const value) void = { 36 const hash = fnv::string(key); 37 const bucket = &obj.buckets[hash % len(obj.buckets)]; 38 for (let i = 0z; i < len(bucket); i += 1) { 39 if (bucket[i].0 == key) { 40 finish(bucket[i].1); 41 bucket[i].1 = dup(val); 42 return; 43 }; 44 }; 45 append(bucket, (strings::dup(key), dup(val))); 46 }; 47 48 // Deletes values from a JSON object, if they are present. 49 export fn del(obj: *object, keys: const str...) void = { 50 for (let i = 0z; i < len(keys); i += 1) { 51 const key = keys[i]; 52 const hash = fnv::string(key); 53 const bucket = &obj.buckets[hash % len(obj.buckets)]; 54 for (let i = 0z; i < len(bucket); i += 1) { 55 if (bucket[i].0 == key) { 56 free(bucket[i].0); 57 finish(bucket[i].1); 58 delete(bucket[i]); 59 break; 60 }; 61 }; 62 }; 63 }; 64 65 // Clears all values from a JSON object, leaving it empty. 66 export fn reset(obj: *object) void = { 67 let it = iter(obj); 68 for (true) match (next(&it)) { 69 case void => 70 break; 71 case let v: (const str, const *value) => 72 del(obj, v.0); 73 }; 74 }; 75 76 export type iterator = struct { 77 obj: *object, 78 i: size, 79 j: size, 80 }; 81 82 // Creates an iterator that enumerates over the key/value pairs in an 83 // [[object]]. 84 export fn iter(obj: *object) iterator = { 85 return iterator { obj = obj, ... }; 86 }; 87 88 // Returns the next key/value pair from this iterator, or void if none remain. 89 export fn next(iter: *iterator) ((const str, const *value) | void) = { 90 for (iter.i < len(iter.obj.buckets); iter.i += 1) { 91 const bucket = &iter.obj.buckets[iter.i]; 92 for (iter.j < len(bucket)) { 93 const key = bucket[iter.j].0; 94 const val = &bucket[iter.j].1; 95 iter.j += 1; 96 return (key, val); 97 }; 98 iter.j = 0; 99 }; 100 }; 101 102 // Duplicates a JSON value. The caller must pass the return value to [[finish]] 103 // to free associated resources when they're done using it. 104 export fn dup(val: value) value = { 105 match (val) { 106 case let s: str => 107 return strings::dup(s); 108 case let v: []value => 109 let new: []value = alloc([], len(v)); 110 for (let i = 0z; i < len(v); i += 1) { 111 append(new, dup(v[i])); 112 }; 113 return new; 114 case let o: object => 115 let new = newobject(); 116 const i = iter(&o); 117 for (true) { 118 const pair = match (next(&i)) { 119 case void => 120 break; 121 case let pair: (const str, const *value) => 122 yield pair; 123 }; 124 set(&new, pair.0, *pair.1); 125 }; 126 return new; 127 case => 128 return val; 129 }; 130 }; 131 132 // Checks two JSON values for equality. 133 export fn equal(a: value, b: value) bool = { 134 match (a) { 135 case _null => 136 return b is _null; 137 case let a: bool => 138 return b is bool && a == b as bool; 139 case let a: f64 => 140 return b is f64 && a == b as f64; 141 case let a: str => 142 return b is str && a == b as str; 143 case let a: []value => 144 if (!(b is []value)) return false; 145 const b = b as []value; 146 if (len(a) != len(b)) return false; 147 for (let i = 0z; i < len(a); i += 1) { 148 if (!equal(a[i], b[i])) { 149 return false; 150 }; 151 }; 152 return true; 153 case let a: object => 154 if (!(b is object)) return false; 155 let a = iter(&a), b = iter(&(b as object)); 156 for (true) match (next(&a)) { 157 case let a: (const str, const *value) => 158 match (next(&b)) { 159 case let b: (const str, const *value) => 160 if (a.0 != b.0 || !equal(*a.1, *b.1)) { 161 return false; 162 }; 163 }; 164 case void => 165 return next(&b) is void; 166 }; 167 return true; 168 }; 169 }; 170 171 // Frees state associated with a JSON value. 172 export fn finish(val: value) void = { 173 match (val) { 174 case let s: str => 175 free(s); 176 case let v: []value => 177 for (let i = 0z; i < len(v); i += 1) { 178 finish(v[i]); 179 }; 180 free(v); 181 case let o: object => 182 for (let i = 0z; i < len(o.buckets); i += 1) { 183 const bucket = &o.buckets[i]; 184 for (let j = 0z; j < len(bucket); j += 1) { 185 free(bucket[j].0); 186 finish(bucket[j].1); 187 }; 188 // TODO: https://todo.sr.ht/~sircmpwn/hare/690 189 //free(bucket); 190 }; 191 case => void; 192 }; 193 };