1 /** 2 DataNode Key-Value Pair Abstraction 3 4 Copyright: 5 Copyright © 2020-2026, Inochi2D Project 6 7 License: 8 $(LINK2 https://github.com/Inochi2D/inochi2d/blob/main/LICENSE, BSD 2-clause License) 9 10 Authors: 11 Luna Nielsen 12 */ 13 module inp.format.dict; 14 import nulib.collections.internal.marray; 15 import numem.core.traits; 16 import numem.rc; 17 import numem; 18 19 /** 20 An ordered refcounted dictionary. 21 */ 22 struct RcOrderedDictionary(TKey, TValue) { 23 private: 24 @nogc: 25 alias KVT = KV!(TKey, TValue); 26 alias KVStoreT = ManagedArray!(KV!(TKey, TValue)); 27 Rc!KVStoreT values; 28 29 /// Helper that finds an entry by its key. 30 pragma(inline, true) 31 ptrdiff_t findEntry()(auto ref inout(TKey) key) inout nothrow { 32 if (!values) return -1; 33 34 foreach(i, ref entry; values) { 35 if (entry.key == key) 36 return i; 37 } 38 return -1; 39 } 40 41 public: 42 43 /** 44 Makes a new ordered dictionary. 45 */ 46 static typeof(this) make() { 47 typeof(this) result; 48 result.values = Rc!(KVStoreT)(KVStoreT()); 49 return result; 50 } 51 52 /// Destructor 53 ~this() nothrow @trusted { 54 if (values) { 55 values.release(); 56 } 57 } 58 59 /** 60 Copy-constructor 61 */ 62 this()(auto ref return scope inout(typeof(this)) rhs) pure nothrow @trusted { 63 nu_memmove(&values, &rhs.values, typeof(values).sizeof); 64 if (values) 65 values.retain(); 66 } 67 68 /** 69 Length of the node. 70 */ 71 @property size_t length() pure => values ? values.length : 0; 72 73 /** 74 Assignment operator 75 */ 76 void opAssign()(auto ref return scope inout(typeof(this)) rhs) { 77 if (values) 78 values.release(); 79 80 nu_memmove(&values, &rhs.values, typeof(values).sizeof); 81 if (values) 82 values.retain(); 83 } 84 85 /** 86 Removes the given key from the object. 87 88 Params: 89 key = The key to remove. 90 */ 91 void remove()(auto ref TKey key) { 92 ptrdiff_t idx = findEntry(key); 93 if (idx >= 0) { 94 values.deleteRange(values[idx..idx+1]); 95 } 96 } 97 98 /** 99 Assigns an element of the dictionary. 100 101 Params: 102 key = The key to query. 103 value = The value to set. 104 */ 105 void opIndexAssign()(auto ref TValue value, auto ref TKey key) { 106 if (!values) 107 this.values = Rc!(KVStoreT)(KVStoreT()); 108 109 ptrdiff_t idx = findEntry(key); 110 if (idx >= 0) { 111 values[idx] = KV!(TKey, TValue)(key, value); 112 return; 113 } 114 115 // Append our new entry. 116 values.value.resize(values.length+1); 117 values[$-1] = KV!(TKey, TValue)(key, value); 118 } 119 120 /** 121 Gets whether the given key is present in the object. 122 123 Params: 124 key = The key to query. 125 126 Returns: 127 $(D true) if the object contains a value with the given key, 128 $(D false) otherwise. 129 */ 130 inout(TValue)* opBinaryRight(string op)(auto ref TKey key) inout nothrow 131 if (op == "in") { 132 inout idx = findEntry(key); 133 return idx != -1 ? &(values[idx].value) : null; 134 } 135 136 /** 137 Gets the given entry in the object. 138 139 Params: 140 key = The key to query. 141 142 Returns: 143 The $(D DataNode) with the given key. 144 */ 145 ref TValue opIndex()(auto ref TKey key) { 146 ptrdiff_t idx = findEntry(key); 147 assert(idx >= 0); 148 149 return values[idx].value; 150 } 151 152 /** 153 Dict-iterator 154 */ 155 int opApply(scope int delegate(size_t i, ref TKey key, ref TValue value) dg) { 156 if (!values) 157 return 0; 158 159 auto dgf = cast(int delegate(size_t i, ref TKey key, ref TValue value) @nogc scope)dg; 160 foreach (i; 0..values.length) { 161 int result = dgf(i, values[i].key, values[i].value); 162 if (result) 163 return result; 164 } 165 return 0; 166 } 167 168 /** 169 Dict-iterator 170 */ 171 int opApply(scope int delegate(ref TKey key, ref TValue value) dg) { 172 if (!values) 173 return 0; 174 175 auto dgf = cast(int delegate(ref TKey key, ref TValue value) @nogc scope)dg; 176 foreach (i; 0..values.length) { 177 int result = dgf(values[i].key, values[i].value); 178 if (result) 179 return result; 180 } 181 return 0; 182 } 183 184 /** 185 Dict-iterator 186 */ 187 int opApply(scope int delegate(ref TValue value) dg) { 188 if (!values) 189 return 0; 190 191 auto dgf = cast(int delegate(ref TValue value) @nogc scope)dg; 192 foreach (i; 0..values.length) { 193 int result = dgf(values[i].value); 194 if (result) 195 return result; 196 } 197 return 0; 198 } 199 } 200 201 202 // 203 // IMPLEMENTATION DETAILS 204 // 205 private: 206 207 /** 208 DataNode Key-Value Pair. 209 */ 210 struct KV(TKey, TValue) { 211 @nogc: 212 TKey key; 213 TValue value; 214 215 pragma(inline, true) 216 static void kvassign(T)(ref T dst, ref T src) { 217 static if (is(T == string)) { 218 dst = src.nu_dup(); 219 } else static if (is(T == U[], U)) { 220 dst = src.nu_dup(); 221 } else static if (hasElaborateMove!T) { 222 dst = src.move(); 223 } else static if (hasElaborateCopyConstructor!T) { 224 dst = T(src); 225 } else { 226 dst = cast(T)src; 227 } 228 } 229 230 pragma(inline, true) 231 static void kvdelete(T)(ref T dst) { 232 static if (hasElaborateDestructor!T) { 233 nogc_trydelete(dst); 234 } else static if (is(T == U[], U)) { 235 nu_freea(dst); 236 } else { 237 nogc_initialize(dst); 238 } 239 } 240 241 ~this() nothrow { 242 kvdelete(key); 243 kvdelete(value); 244 } 245 246 this()(auto ref TKey key, auto ref TValue value) @trusted { 247 kvassign!TKey(this.key, key); 248 kvassign!TValue(this.value, value); 249 } 250 251 /// Copy-constructor 252 this(ref return scope inout(typeof(this)) rhs) @trusted { 253 kvassign!TKey(this.key, cast(TKey)rhs.key); 254 kvassign!TValue(this.value, cast(TValue)rhs.value); 255 } 256 }