1 /* 2 Copyright (c) 2015-2021 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license (the "Software") to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 */ 28 29 /** 30 * Trie-based dictionary (associative array) that can use any type as a key 31 * 32 * Copyright: Timur Gafarov 2015-2021. 33 * License: $(LINK2 boost.org/LICENSE_1_0.txt, Boost License 1.0). 34 * Authors: Timur Gafarov, Andrey Penechko, Roman Chistokhodov, ijet 35 */ 36 module dlib.container.dict; 37 38 import std.stdio; 39 import std.traits; 40 import std.format; 41 import std..string; 42 import dlib.core.memory; 43 import dlib.container.array; 44 45 size_t dataSize(T)(T v) 46 { 47 static if (is(T == class) || is(T == interface)) 48 return (void*).sizeof; 49 else 50 static if (isArray!T) 51 return v.length * (ForeachType!T).sizeof; 52 else 53 return T.sizeof; 54 } 55 56 auto byteRange(T)(T v) 57 { 58 struct R 59 { 60 T value; 61 size_t size; 62 size_t offset; 63 64 this(T v) 65 { 66 value = v; 67 size = dataSize(v); 68 offset = 0; 69 } 70 71 @property bool empty() 72 { 73 return (offset >= size); 74 } 75 76 @property ubyte front() 77 { 78 ubyte* ptr; 79 static if (isArray!T) { ptr = (cast(ubyte[])value).ptr;} 80 else { ptr = cast(ubyte*)&value; } 81 return ptr[offset]; 82 } 83 84 void popFront() 85 { 86 offset++; 87 } 88 } 89 90 return R(v); 91 } 92 93 /** 94 * Trie-based dictionary (associative array) that can use any type as a key. No hash functions are required. 95 */ 96 class Trie(T, K) 97 { 98 T value; 99 K key; 100 ubyte symbol; 101 Array!(Trie!(T, K)) children; 102 bool active = false; 103 size_t length = 0; 104 105 this() 106 { 107 } 108 109 this(ubyte s) 110 { 111 symbol = s; 112 } 113 114 /// Set value by key. 115 void set(K k, T v) 116 { 117 Trie!(T, K) current = this; 118 foreach(s; byteRange(k)) 119 { 120 bool found = false; 121 foreach(c; current.children) 122 { 123 if (c.symbol == s) 124 { 125 current = c; 126 found = true; 127 break; 128 } 129 } 130 131 if (!found) 132 { 133 auto n = New!(Trie!(T, K))(s); 134 current.children.append(n); 135 current = n; 136 137 //current.children.append(n); 138 //current = n; 139 } 140 } 141 142 if (current !is this) 143 { 144 current.value = v; 145 current.key = k; 146 147 if (!current.active) 148 { 149 current.active = true; 150 length++; 151 } 152 } 153 } 154 155 /// Get value by key. Returns null if the element does not exist in trie. 156 T* get(K k) 157 { 158 Trie!(T, K) current = this; 159 foreach(ubyte s; byteRange(k)) 160 { 161 bool found = false; 162 foreach(c; current.children) 163 { 164 if (c.symbol == s) 165 { 166 found = true; 167 current = c; 168 break; 169 } 170 } 171 172 if (!found) 173 return null; 174 } 175 176 if (current !is this) 177 { 178 if (current.active && 179 current.key == k) 180 { 181 return ¤t.value; 182 } 183 } 184 185 return null; 186 } 187 188 /// Remove element by key. 189 void remove(K k) 190 { 191 Trie!(T, K) current = this; 192 foreach(ubyte s; byteRange(k)) 193 { 194 bool found = false; 195 foreach(c; current.children) 196 { 197 if (c.symbol == s) 198 { 199 found = true; 200 current = c; 201 break; 202 } 203 } 204 205 if (!found) 206 return; 207 } 208 209 if (current !is this) 210 { 211 if (current.active && 212 current.key == k) 213 { 214 current.active = false; 215 length--; 216 } 217 } 218 } 219 220 /// Get value by key. It's an error to access non-existing key. 221 T opIndex(K k) 222 { 223 T* v = get(k); 224 if (v !is null) 225 return *v; 226 else 227 assert(0, format("Non-existing key in Trie.opIndex: %s", k)); 228 } 229 230 /// Set value by key 231 T opIndexAssign(T v, K k) 232 { 233 set(k, v); 234 return v; 235 } 236 237 /// 238 T* opBinaryRight(string op)(K k) if (op == "in") 239 { 240 return get(k); 241 } 242 243 int opApply(scope int delegate(K, ref T) dg) 244 { 245 int result = 0; 246 247 foreach(c; children) 248 { 249 if (c.active) 250 result = dg(c.key, c.value); 251 252 if (result) 253 break; 254 255 result = c.opApply(dg); 256 257 if (result) 258 break; 259 } 260 261 return result; 262 } 263 264 /// Remove all elements. 265 void clear() 266 { 267 foreach(c; children) 268 { 269 Delete(c); 270 } 271 272 children.free(); 273 length = 0; 274 } 275 276 ~this() 277 { 278 clear(); 279 } 280 281 /// Trie must be manually freed when it's no longer needed. 282 void free() 283 { 284 Delete(this); 285 } 286 } 287 288 /// Convenient alias 289 alias Dict = Trie; 290 291 /// Convenient function for dict creation. 292 Dict!(T, K) dict(T, K)() 293 { 294 return New!(Dict!(T, K))(); 295 } 296 297 /// 298 unittest 299 { 300 auto d = dict!(string, string)(); 301 scope(exit) d.free(); 302 d["Hell"] = "No"; 303 d["Hello"] = "World"; 304 d["Help"] = "Me"; 305 d["Something"] = "Else"; 306 assert(d["Hell"] == "No"); 307 assert(d["Hello"] == "World"); 308 assert(d["Help"] == "Me"); 309 assert(d["Something"] == "Else"); 310 assert("Held" !in d); 311 assert(d.length == 4); 312 313 string[string] elements; 314 foreach(key, value; d) 315 { 316 elements[key] = value; 317 } 318 assert(elements["Hell"] == "No"); 319 assert(elements["Hello"] == "World"); 320 assert(elements["Help"] == "Me"); 321 assert(elements["Something"] == "Else"); 322 assert(elements.length == d.length); 323 324 d["Something"] = "New"; 325 assert(d["Something"] == "New"); 326 327 d.remove("Hell"); 328 assert(d.length == 3); 329 assert(d.get("Hell") is null); 330 331 d.clear(); 332 assert(d.length == 0); 333 assert("Hello" !in d); 334 assert("Help" !in d); 335 assert("Something" !in d); 336 337 d["Held"] = "Fire"; 338 assert(d["Held"] == "Fire"); 339 340 auto di = dict!(string, int); 341 scope(exit) di.free(); 342 di[0xBEAF] = "BEAF"; 343 di[0xDEADBEAF] = "DEADBEAF"; 344 di[0xDEAD] = "DEAD"; 345 assert(di[0xBEAF] == "BEAF"); 346 assert(di[0xDEADBEAF] == "DEADBEAF"); 347 assert(di[0xDEAD] == "DEAD"); 348 }