1 /*
2  * Hunt - A refined core library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.collection.TreeTrie;
13 
14 import hunt.collection.AbstractTrie;
15 import hunt.collection.ArrayList;
16 import hunt.io.ByteBuffer;
17 import hunt.collection.HashSet;
18 import hunt.collection.List;
19 import hunt.collection.Set;
20 
21 import hunt.Exceptions;
22 import hunt.text;
23 import hunt.util.Common;
24 
25 import std.conv;
26 
27 /* ------------------------------------------------------------ */
28 
29 /**
30  * A Trie string lookup data structure using a tree
31  * <p>
32  * This implementation is always case insensitive and is optimal for a variable
33  * number of fixed strings with few special characters.
34  * </p>
35  * <p>
36  * This Trie is stored in a Tree and is unlimited in capacity
37  * </p>
38  * <p>
39  * <p>
40  * This Trie is not Threadsafe and contains no mutual exclusion or deliberate
41  * memory barriers. It is intended for an ArrayTrie to be built by a single
42  * thread and then used concurrently by multiple threads and not mutated during
43  * that access. If concurrent mutations of the Trie is required external locks
44  * need to be applied.
45  * </p>
46  *
47  * @param (V) the entry type
48  */
49 class TreeTrie(V) : AbstractTrie!(V) {
50     private enum int[] __lookup =
51     [// 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
52    /*0*/-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
53    /*1*/-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
54    /*2*/31, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, -1, 27, 30, -1,
55    /*3*/-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 28, 29, -1, -1, -1, -1,
56    /*4*/-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
57    /*5*/15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
58    /*6*/-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
59    /*7*/15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
60     ];
61     private enum int INDEX = 32;
62     private TreeTrie!(V)[] _nextIndex;
63     private List!(TreeTrie!(V)) _nextOther;
64     private char _c;
65     private string _key;
66     private V _value;
67 
68     this() {
69         this('\0');
70     }
71 
72     private this(char c) {
73         _nextOther = new ArrayList!(TreeTrie!(V))();
74         _nextIndex = new TreeTrie[INDEX];
75         super(true);
76         this._c = c;
77     }
78 
79     override
80     void clear() {
81         _nextIndex[] = null;
82         _nextOther.clear();
83         _key = null;
84         _value = V.init;
85     }
86 
87     override
88     bool put(string s, V v) {
89         TreeTrie!(V) t = this;
90         size_t limit = s.length;
91         for (size_t k = 0; k < limit; k++) {
92             byte c = s[k];
93 
94             int index = c >= 0 && c < 0x7f ? __lookup[c] : -1;
95             if (index >= 0) {
96                 if (t._nextIndex[index] is null)
97                     t._nextIndex[index] = new TreeTrie!(V)(c);
98                 t = t._nextIndex[index];
99             } else {
100                 TreeTrie!(V) n = null;
101                 for (int i = t._nextOther.size(); i-- > 0; ) {
102                     n = t._nextOther.get(i);
103                     if (n._c == c)
104                         break;
105                     n = null;
106                 }
107                 if (n is null) {
108                     n = new TreeTrie!(V)(c);
109                     t._nextOther.add(n);
110                 }
111                 t = n;
112             }
113         }
114         static if(is(V == class)) {
115             t._key = v is null ? null : s;
116         } else {
117             t._key = v == V.init ? null : s;
118         }
119         
120         t._value = v;
121         return true;
122     }
123 
124     override
125     V get(string s, int offset, int len) {
126         TreeTrie!(V) t = this;
127         for (int i = 0; i < len; i++) {
128             byte c = s.charAt(offset + i);
129             int index = c >= 0 && c < 0x7f ? __lookup[c] : -1;
130             if (index >= 0) {
131                 if (t._nextIndex[index] is null)
132                     return V.init;
133                 t = t._nextIndex[index];
134             } else {
135                 TreeTrie!(V) n = null;
136                 for (int j = t._nextOther.size(); j-- > 0; ) {
137                     n = t._nextOther.get(j);
138                     if (n._c == c)
139                         break;
140                     n = null;
141                 }
142                 if (n is null)
143                     return V.init;
144                 t = n;
145             }
146         }
147         return t._value;
148     }
149 
150     override
151     V get(ByteBuffer b, int offset, int len) {
152         TreeTrie!(V) t = this;
153         for (int i = 0; i < len; i++) {
154             byte c = b.get(offset + i);
155             int index = c >= 0 && c < 0x7f ? __lookup[c] : -1;
156             if (index >= 0) {
157                 if (t._nextIndex[index] is null)
158                     return V.init;
159                 t = t._nextIndex[index];
160             } else {
161                 TreeTrie!(V) n = null;
162                 for (int j = t._nextOther.size(); j-- > 0; ) {
163                     n = t._nextOther.get(j);
164                     if (n._c == c)
165                         break;
166                     n = null;
167                 }
168                 if (n is null)
169                     return V.init;
170                 t = n;
171             }
172         }
173         return t._value;
174     }
175 
176     override
177     V getBest(byte[] b, int offset, int len) {
178         TreeTrie!(V) t = this;
179         for (int i = 0; i < len; i++) {
180             byte c = b[offset + i];
181             int index = c >= 0 && c < 0x7f ? __lookup[c] : -1;
182             if (index >= 0) {
183                 if (t._nextIndex[index] is null)
184                     break;
185                 t = t._nextIndex[index];
186             } else {
187                 TreeTrie!(V) n = null;
188                 for (int j = t._nextOther.size(); j-- > 0; ) {
189                     n = t._nextOther.get(j);
190                     if (n._c == c)
191                         break;
192                     n = null;
193                 }
194                 if (n is null)
195                     break;
196                 t = n;
197             }
198 
199             // Is the next Trie is a match
200             if (t._key !is null) {
201                 // Recurse so we can remember this possibility
202                 V best = t.getBest(b, offset + i + 1, len - i - 1);
203                 
204                 static if(is(V == class)) {
205                     if (best !is null)
206                         return best;
207                 } else {
208                     if (best != V.init)
209                         return best;
210                 }
211 
212                 break;
213             }
214         }
215         return t._value;
216     }
217 
218     override
219     V getBest(string s, int offset, int len) {
220         TreeTrie!(V) t = this;
221         for (int i = 0; i < len; i++) {
222             byte c = cast(byte) (0xff & s[offset + i]);
223             int index = c >= 0 && c < 0x7f ? __lookup[c] : -1;
224             if (index >= 0) {
225                 if (t._nextIndex[index] is null)
226                     break;
227                 t = t._nextIndex[index];
228             } else {
229                 TreeTrie!(V) n = null;
230                 for (int j = t._nextOther.size(); j-- > 0; ) {
231                     n = t._nextOther.get(j);
232                     if (n._c == c)
233                         break;
234                     n = null;
235                 }
236                 if (n is null)
237                     break;
238                 t = n;
239             }
240 
241             // Is the next Trie is a match
242             if (t._key !is null) {
243                 // Recurse so we can remember this possibility
244                 V best = t.getBest(s, offset + i + 1, len - i - 1);
245                 static if(is(V == class)) {
246                     if (best !is null)
247                         return best;
248                 } else {
249                     if (best != V.init)
250                         return best;
251                 }
252                 
253                 break;
254             }
255         }
256         return t._value;
257     }
258 
259     override
260     V getBest(ByteBuffer b, int offset, int len) {
261         if (b.hasArray())
262             return getBest(b.array(), b.arrayOffset() + b.position() + offset, len);
263         return getBestByteBuffer(b, offset, len);
264     }
265 
266     private V getBestByteBuffer(ByteBuffer b, int offset, int len) {
267         TreeTrie!(V) t = this;
268         int pos = b.position() + offset;
269         for (int i = 0; i < len; i++) {
270             byte c = b.get(pos++);
271             int index = c >= 0 && c < 0x7f ? __lookup[c] : -1;
272             if (index >= 0) {
273                 if (t._nextIndex[index] is null)
274                     break;
275                 t = t._nextIndex[index];
276             } else {
277                 TreeTrie!(V) n = null;
278                 for (int j = t._nextOther.size(); j-- > 0; ) {
279                     n = t._nextOther.get(j);
280                     if (n._c == c)
281                         break;
282                     n = null;
283                 }
284                 if (n is null)
285                     break;
286                 t = n;
287             }
288 
289             // Is the next Trie is a match
290             if (t._key !is null) {
291                 // Recurse so we can remember this possibility
292                 V best = t.getBest(b, offset + i + 1, len - i - 1);
293 
294                 static if(is(V == class)) {
295                     if (best !is null)
296                         return best;
297                 } else {
298                     if (best != V.init)
299                         return best;
300                 }
301 
302                 break;
303             }
304         }
305         return t._value;
306     }
307 
308 
309     override
310     string toString() {
311         StringBuilder buf = new StringBuilder();
312         toString(buf, this);
313 
314         if (buf.length == 0)
315             return "{}";
316 
317         buf.setCharAt(0, '{');
318         buf.append('}');
319         return buf.toString();
320     }
321 
322     private static void toString(V)(Appendable ot, TreeTrie!(V) t) {
323         if (t is null) 
324             return;
325 
326         void doAppend() {
327             try {
328                 ot.append(',');
329                 ot.append(t._key);
330                 ot.append('=');
331                 ot.append(t._value.to!string());
332             } catch (IOException e) {
333                 throw new RuntimeException(e);
334             }
335         }
336 
337         static if(is(V == class)) {
338             if (t._value !is null)
339                 doAppend();
340         } else {
341             if (t._value != V.init)
342                 doAppend();
343         }
344 
345         for (int i = 0; i < INDEX; i++) {
346             if (t._nextIndex[i] !is null)
347                 toString(ot, t._nextIndex[i]);
348         }
349         for (int i = t._nextOther.size(); i-- > 0; )
350             toString(ot, t._nextOther.get(i));
351     }
352 
353     override
354     Set!(string) keySet() {
355         Set!(string) keys = new HashSet!string();
356         keySet(keys, this);
357         return keys;
358     }
359 
360     private static void keySet(V)(Set!(string) set, TreeTrie!(V) t) {
361         if (t !is null) {
362             if (t._key !is null)
363                 set.add(t._key);
364 
365             for (int i = 0; i < INDEX; i++) {
366                 if (t._nextIndex[i] !is null)
367                     keySet(set, t._nextIndex[i]);
368             }
369             for (int i = t._nextOther.size(); i-- > 0; )
370                 keySet(set, t._nextOther.get(i));
371         }
372     }
373 
374     override
375     bool isFull() {
376         return false;
377     }
378 
379 }