SourceMod SDK  1.7
sm_trie_tpl.h
Go to the documentation of this file.
1 
32 #ifndef _INCLUDE_SOURCEMOD_TEMPLATED_TRIE_H_
33 #define _INCLUDE_SOURCEMOD_TEMPLATED_TRIE_H_
34 
35 #include <new>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <assert.h>
39 
41 {
42  Node_Unused = 0, /* Node is not being used (sparse) */
43  Node_Arc, /* Node is part of an arc and does not terminate */
44  Node_Term, /* Node is a terminator */
45 };
46 
59 template <typename K>
60 class KTrie
61 {
62  class KTrieNode;
63 public:
67  void clear()
68  {
69  run_destructors();
70  internal_clear();
71  }
72 
79  bool remove(const char *key)
80  {
81  KTrieNode *node = internal_retrieve(key);
82  if (!node || !node->valset)
83  {
84  return false;
85  }
86 
87  node->value.~K();
88  node->valset = false;
89 
90  m_numElements--;
91 
92  return true;
93  }
94 
101  K * retrieve(const char *key)
102  {
103  KTrieNode *node = internal_retrieve(key);
104  if (!node || !node->valset)
105  {
106  return NULL;
107  }
108  return &node->value;
109  }
110 
118  bool retrieve(const char *key, K *result)
119  {
120  KTrieNode *node = internal_retrieve(key);
121  if (!node || !node->valset)
122  return false;
123  *result = node->value;
124  return true;
125  }
126 
134  bool replace(const char *key, const K & obj)
135  {
136  KTrieNode *prev_node = internal_retrieve(key);
137  if (!prev_node)
138  {
139  return insert(key, obj);
140  }
141 
142  if (prev_node->valset)
143  {
144  prev_node->value.~K();
145  }
146 
147  new (&prev_node->value) K(obj);
148 
149  return true;
150  }
151 
160  bool insert(const char *key, const K & obj)
161  {
162  unsigned int lastidx = 1; /* the last node index */
163  unsigned int curidx; /* current node index */
164  const char *keyptr = key; /* input stream at current token */
165  KTrieNode *node = NULL; /* current node being processed */
166  KTrieNode *basenode = NULL; /* current base node being processed */
167  unsigned int q; /* temporary var for x_check results */
168  unsigned int curoffs; /* current offset */
169 
174  if (*key == '\0')
175  {
176  if (m_empty != NULL && m_empty->valset)
177  {
178  return false;
179  }
180 
181  if (m_empty == NULL)
182  {
183  m_empty = (KTrieNode *)malloc(sizeof(KTrieNode));
184  }
185 
186  m_empty->valset = true;
187  new (&m_empty->value) K(obj);
188 
189  m_numElements++;
190 
191  return true;
192  }
193 
194  /* Start traversing at the root node (1) */
195  do
196  {
197  /* Find where the next character is, then advance */
198  curidx = m_base[lastidx].idx;
199  basenode = &m_base[curidx];
200  curoffs = charval(*keyptr);
201  curidx += curoffs;
202  node = &m_base[curidx];
203  keyptr++;
204 
205  /* Check if this slot is supposed to be empty. If so, we need to handle CASES 1/2:
206  * Insertion without collisions
207  */
208  if ( (curidx > m_baseSize) || (node->mode == Node_Unused) )
209  {
210  if (curidx > m_baseSize)
211  {
212  if (!grow())
213  {
214  return false;
215  }
216  node = &m_base[curidx];
217  }
218  node->parent = lastidx;
219  if (*keyptr == '\0')
220  {
221  node->mode = Node_Arc;
222  }
223  else
224  {
225  node->idx = x_addstring(keyptr);
226  node->mode = Node_Term;
227  }
228  node->valset = true;
229  new (&node->value) K(obj);
230 
231  m_numElements++;
232 
233  return true;
234  }
235  else if (node->parent != lastidx)
236  {
237  /* Collision! We have to split up the tree here. CASE 4:
238  * Insertion when a new word is inserted with a collision.
239  * NOTE: This is the hardest case to handle. All below examples are based on:
240  * BACHELOR, BADGE, inserting BABY.
241  * The problematic production here is A -> B, where B is already being used.
242  *
243  * This process has to rotate one half of the 'A' arc. We generate two lists:
244  * Outgoing Arcs - Anything leaving this 'A'
245  * Incoming Arcs - Anything going to this 'A'
246  * Whichever list is smaller will be moved. Note that this works because the intersection
247  * affects both arc chains, and moving one will make the slot available to either.
248  */
249  KTrieNode *cur;
250 
251  /* Find every node arcing from the last node.
252  * I.e. for BACHELOR, BADGE, BABY,
253  * The arcs leaving A will be C and D, but our current node is B -> *.
254  * Thus, we use the last index (A) to find the base for arcs leaving A.
255  */
256  unsigned int outgoing_base = m_base[lastidx].idx;
257  unsigned int outgoing_list[256];
258  unsigned int outgoing_count = 0; /* count the current index here */
259  cur = &m_base[outgoing_base] + 1;
260  unsigned int outgoing_limit = 255;
261 
262  if (outgoing_base + outgoing_limit > m_baseSize)
263  {
264  outgoing_limit = m_baseSize - outgoing_base;
265  }
266 
267  for (unsigned int i=1; i<=outgoing_limit; i++,cur++)
268  {
269  if (cur->mode == Node_Unused || cur->parent != lastidx)
270  {
271  continue;
272  }
273  outgoing_list[outgoing_count++] = i;
274  }
275  outgoing_list[outgoing_count++] = curidx - outgoing_base;
276 
277  /* Now we need to find all the arcs leaving our parent...
278  * Note: the inconsistency is the base of our parent.
279  */
280  assert(m_base[node->parent].mode == Node_Arc);
281  unsigned int incoming_list[256];
282  unsigned int incoming_base = m_base[node->parent].idx;
283  unsigned int incoming_count = 0;
284  unsigned int incoming_limit = 255;
285  cur = &m_base[incoming_base] + 1;
286 
287  if (incoming_base + incoming_limit > m_baseSize)
288  {
289  incoming_limit = m_baseSize - incoming_base;
290  }
291 
292  assert(incoming_limit > 0 && incoming_limit <= 255);
293 
294  for (unsigned int i=1; i<=incoming_limit; i++,cur++)
295  {
296  if (cur->mode == Node_Arc || cur->mode == Node_Term)
297  {
298  if (cur->parent == node->parent)
299  {
300  incoming_list[incoming_count++] = i;
301  }
302  }
303  }
304 
305  if (incoming_count < outgoing_count + 1)
306  {
307  unsigned int q = x_check_multi(incoming_list, incoming_count);
308 
309  node = &m_base[curidx];
310 
311  /* If we're incoming, we need to modify our parent */
312  m_base[node->parent].idx = q;
313 
314  /* For each node in the "to move" list,
315  * Relocate the node's info to the new position.
316  */
317  unsigned int idx, newidx, oldidx;
318  for (unsigned int i=0; i<incoming_count; i++)
319  {
320  idx = incoming_list[i];
321  newidx = q + idx;
322  oldidx = incoming_base + idx;
323  if (oldidx == lastidx)
324  {
325  /* Important! Make sure we're not invalidating our sacred lastidx */
326  lastidx = newidx;
327  }
328  /* Fully copy the node */
329  memcpy(&m_base[newidx], &m_base[oldidx], sizeof(KTrieNode));
330  if (m_base[oldidx].valset)
331  {
332  new (&m_base[newidx].value) K(m_base[oldidx].value);
333  m_base[oldidx].value.~K();
334  }
335  assert(m_base[m_base[newidx].parent].mode == Node_Arc);
336  /* Erase old data */
337  memset(&m_base[oldidx], 0, sizeof(KTrieNode));
338  /* If we are not a terminator, we have children we must take care of */
339  if (m_base[newidx].mode == Node_Arc)
340  {
341  KTrieNode *check_base = &m_base[m_base[newidx].idx] + 1;
342  outgoing_limit = (m_base + m_baseSize + 1) - check_base;
343  if (outgoing_limit > 255)
344  {
345  outgoing_limit = 255;
346  }
347  for (unsigned int j=1; j<=outgoing_limit; j++, check_base++)
348  {
349  if (check_base->parent == oldidx)
350  {
351  check_base->parent = newidx;
352  }
353  }
354  }
355  }
356  }
357  else
358  {
359  unsigned int q = x_check_multi(outgoing_list, outgoing_count);
360 
361  node = &m_base[curidx];
362 
363  /* If we're outgoing, we need to modify our own base */
364  m_base[lastidx].idx = q;
365 
366  /* Take the last index (curidx) out of the list. Technically we are not moving this,
367  * since it's already being used by something else.
368  */
369  outgoing_count--;
370 
371  /* For each node in the "to move" list,
372  * Relocate the node's info to the new position.
373  */
374  unsigned int idx, newidx, oldidx;
375  for (unsigned int i=0; i<outgoing_count; i++)
376  {
377  idx = outgoing_list[i];
378  newidx = q + idx;
379  oldidx = outgoing_base + idx;
380  if (oldidx == lastidx)
381  {
382  /* Important! Make sure we're not invalidating our sacred lastidx */
383  lastidx = newidx;
384  }
385  /* Fully copy the node */
386  memcpy(&m_base[newidx], &m_base[oldidx], sizeof(KTrieNode));
387  if (m_base[oldidx].valset)
388  {
389  new (&m_base[newidx].value) K(m_base[oldidx].value);
390  m_base[oldidx].value.~K();
391  }
392  assert(m_base[m_base[newidx].parent].mode == Node_Arc);
393  /* Erase old data */
394  memset(&m_base[oldidx], 0, sizeof(KTrieNode));
395  /* If we are not a terminator, we have children we must take care of */
396  if (m_base[newidx].mode == Node_Arc)
397  {
398  KTrieNode *check_base = &m_base[m_base[newidx].idx] + 1;
399  outgoing_limit = (m_base + m_baseSize + 1) - check_base;
400  if (outgoing_limit > 255)
401  {
402  outgoing_limit = 255;
403  }
404  for (unsigned int j=1; j<=outgoing_limit; j++, check_base++)
405  {
406  if (check_base->parent == oldidx)
407  {
408  check_base->parent = newidx;
409  }
410  }
411  }
412  }
413 
414  /* Take the invisible node and use it as our new node */
415  node = &m_base[q + outgoing_list[outgoing_count]];
416  }
417 
418  /* We're finally done! */
419  node->parent = lastidx;
420  if (*keyptr == '\0')
421  {
422  node->mode = Node_Arc;
423  }
424  else
425  {
426  node->idx = x_addstring(keyptr);
427  node->mode = Node_Term;
428  }
429  node->valset = true;
430  new (&node->value) K(obj);
431 
432  m_numElements++;
433 
434  return true;
435  }
436  else
437  {
438  /* See what's in the next node - special case if terminator! */
439  if (node->mode == Node_Term)
440  {
441  /* If we're a terminator, we need to handle CASE 3:
442  * Insertion when a terminating collision occurs
443  */
444  char *term = &m_stringtab[node->idx];
445  /* Do an initial browsing to make sure they're not the same string */
446  if (strcmp(keyptr, term) == 0)
447  {
448  if (!node->valset)
449  {
450  node->valset = true;
451  new (&node->value) K(obj);
452  m_numElements++;
453  return true;
454  }
455  /* Same string. We can't insert. */
456  return false;
457  }
458  /* For each matching character pair, we need to disband the terminator.
459  * This splits the similar prefix into a single arc path.
460  * First, save the old values so we can move them to a new node.
461  * Next, for each loop:
462  * Take the current (invalid) node, and point it to the next arc base.
463  * Set the current node to the node at the next arc.
464  */
465  K oldvalue;
466  bool oldvalset = node->valset;
467  if (oldvalset)
468  {
469  oldvalue = node->value;
470  }
471  if (*term == *keyptr)
472  {
473  while (*term == *keyptr)
474  {
475  /* Find the next free slot in the check array.
476  * This is the "vector base" essentially
477  */
478  q = x_check(*term);
479  node = &m_base[curidx];
480  /* Point the node to the next new base */
481  node->idx = q;
482  node->mode = Node_Arc;
483  if (node->valset == true)
484  {
485  node->value.~K();
486  node->valset = false;
487  }
488  /* Advance the input stream and local variables */
489  lastidx = curidx;
490  curidx = q + charval(*term);
491  node = &m_base[curidx];
492  /* Make sure the new current node has its parent set. */
493  node->parent = lastidx;
494  node->mode = Node_Arc; /* Just in case we run x_check again */
495  *term = '\0'; /* Unmark the string table here */
496  term++;
497  keyptr++;
498  }
499  }
500  else if (node->valset)
501  {
502  node->valset = false;
503  node->value.~K();
504  }
505  /* We're done inserting new pairs. If one of them is exhausted,
506  * we take special shortcuts.
507  */
508  if (*term == '\0') //EX: BADGERHOUSE added over B -> ADGER.
509  {
510  /* First backpatch the current node - it ends the newly split terminator.
511  * In the example, this would mean the node is the production from R -> ?
512  * This node ends the old BADGER, so we set it here.
513  */
514  node->valset = oldvalset;
515  if (node->valset)
516  {
517  new (&node->value) K(oldvalue);
518  }
519 
520  /* The terminator was split up, but pieces of keyptr remain.
521  * We need to generate a new production, in this example, R -> H,
522  * with H being a terminator to OUSE. Thus we get:
523  * B,A,D,G,E,R*,H*->OUSE (* = value set).
524  * NOTE: parent was last set at the end of the while loop.
525  */
526  /* Get the new base and apply re-basing */
527  q = x_check(*keyptr);
528  node = &m_base[curidx];
529 
530  node->idx = q;
531  node->mode = Node_Arc;
532  lastidx = curidx;
533  /* Finish the final node */
534  curidx = q + charval(*keyptr);
535  node = &m_base[curidx];
536  keyptr++;
537  /* Optimize - don't add to string table if there's nothing more to eat */
538  if (*keyptr == '\0')
539  {
540  node->mode = Node_Arc;
541  }
542  else
543  {
544  node->idx = x_addstring(keyptr);
545  node->mode = Node_Term;
546  }
547  node->parent = lastidx;
548  node->valset = true;
549  new (&node->value) K(obj);
550  }
551  else if (*keyptr == '\0')
552  { //EX: BADGER added over B -> ADGERHOUSE
553  /* First backpatch the current node - it ends newly split input string.
554  * This is the exact opposite of the above procedure.
555  */
556  node->valset = true;
557  new (&node->value) K(obj);
558 
559  /* Get the new base and apply re-basing */
560  q = x_check(*term);
561  node = &m_base[curidx];
562 
563  node->idx = q;
564  node->mode = Node_Arc;
565  lastidx = curidx;
566  /* Finish the final node */
567  curidx = q + charval(*term);
568  node = &m_base[curidx];
569  term++;
570  /* Optimize - don't add to string table if there's nothing more to eat */
571  if (*term == '\0')
572  {
573  node->mode = Node_Arc;
574  }
575  else
576  {
577  node->idx = (term - m_stringtab); /* Already in the string table! */
578  node->mode = Node_Term;
579  }
580  node->parent = lastidx;
581  node->valset = oldvalset;
582  if (node->valset)
583  {
584  new (&node->value) K(oldvalue);
585  }
586  }
587  else
588  {
589  /* Finally, we have to create two new nodes instead of just one. */
590  node->mode = Node_Arc;
591 
592  /* Get the new base and apply re-basing */
593  q = x_check2(*keyptr, *term);
594  node = &m_base[curidx];
595 
596  node->idx = q;
597  lastidx = curidx;
598 
599  /* Re-create the old terminated node */
600  curidx = q + charval(*term);
601  node = &m_base[curidx];
602  term++;
603  node->valset = oldvalset;
604  if (node->valset)
605  {
606  new (&node->value) K(oldvalue);
607  }
608  node->parent = lastidx;
609  if (*term == '\0')
610  {
611  node->mode = Node_Arc;
612  }
613  else
614  {
615  node->mode = Node_Term;
616  node->idx = (term - m_stringtab); /* Already in the string table! */
617  }
618 
619  /* Create the new keyed input node */
620  curidx = q + charval(*keyptr);
621  node = &m_base[curidx];
622  keyptr++;
623  node->valset = true;
624  new (&node->value) K(obj);
625  node->parent = lastidx;
626  if (*keyptr == '\0')
627  {
628  node->mode = Node_Arc;
629  }
630  else
631  {
632  node->mode = Node_Term;
633  node->idx = x_addstring(keyptr);
634  }
635  }
636 
637  m_numElements++;
638 
639  /* Phew! */
640  return true;
641  }
642  else
643  {
644  assert(node->mode == Node_Arc);
645  }
646  }
647  lastidx = curidx;
648  } while (*keyptr != '\0');
649 
650  assert(node);
651 
652  /* If we've exhausted the string and we have a valid reached node,
653  * the production rule already existed. Make sure it's valid to set first.
654  */
655 
656  /* We have to be an Arc. If the last result was anything else, we would have returned a new
657  * production earlier.
658  */
659  assert(node->mode == Node_Arc);
660 
661  if (!node->valset)
662  {
663  node->valset = true;
664  new (&node->value) K(obj);
665  m_numElements++;
666  return true;
667  }
668 
669  return false;
670  }
671 
690  void bad_iterator(char *buffer,
691  size_t maxlength,
692  void *data,
693  void (*func)(KTrie *, const char *, K & obj, void *data))
694  {
695  bad_iterator_r(buffer, maxlength, 0, data, func, 1);
696  }
697 
698 private:
699  void bad_iterator_r(char *buffer,
700  size_t maxlength,
701  size_t buf_pos,
702  void *data,
703  void (*func)(KTrie *, const char *, K & obj, void *data),
704  unsigned int root)
705  {
706  char *term;
707  unsigned int idx, limit, start;
708 
709  limit = 255;
710  start = m_base[root].idx;
711 
712  /* Bound our limits */
713  if (start + limit > m_baseSize)
714  {
715  limit = m_baseSize - start;
716  }
717 
718  /* Search for strings */
719  for (unsigned int i = 1; i <= limit; i++)
720  {
721  idx = start + i;
722  if (m_base[idx].mode == Node_Unused
723  || m_base[idx].parent != root)
724  {
725  continue;
726  }
727 
728  if (m_base[idx].mode == Node_Arc)
729  {
730  if (buf_pos < maxlength - 1)
731  {
732  buffer[buf_pos++] = (char)i;
733  }
734 
735  if (m_base[idx].valset)
736  {
737  buffer[buf_pos] = '\0';
738  func(this, buffer, m_base[idx].value, data);
739  }
740 
741  bad_iterator_r(buffer,
742  maxlength,
743  buf_pos,
744  data,
745  func,
746  idx);
747 
748  buf_pos--;
749  }
750  else if (m_base[idx].mode == Node_Term
751  && m_base[idx].valset == true)
752  {
753  size_t save_buf_pos;
754 
755  save_buf_pos = buf_pos;
756  if (buf_pos < maxlength - 1)
757  {
758  buffer[buf_pos++] = (char)i;
759  }
760  if (buf_pos < maxlength - 1)
761  {
762  size_t destlen, j;
763 
764  term = &m_stringtab[m_base[idx].idx];
765  destlen = strlen(term);
766  for (j = 0;
767  j < destlen && j + buf_pos < maxlength - 1;
768  j++)
769  {
770  buffer[buf_pos + j] = term[j];
771  }
772  buf_pos += j;
773  }
774  buffer[buf_pos] = '\0';
775 
776  func(this, buffer, m_base[idx].value, data);
777 
778  buf_pos = save_buf_pos;
779  }
780  }
781  }
782 public:
783  KTrie()
784  {
785  m_base = (KTrieNode *)malloc(sizeof(KTrieNode) * (256 + 1));
786  m_stringtab = (char *)malloc(sizeof(char) * 256);
787  m_baseSize = 256;
788  m_stSize = 256;
789  m_empty = NULL;
790  m_numElements = 0;
791 
792  internal_clear();
793  }
794  ~KTrie()
795  {
796  if (m_empty != NULL && m_empty->valset)
797  {
798  m_empty->value.~K();
799  m_empty->valset = false;
800  }
801  free(m_empty);
802  run_destructors();
803  free(m_base);
804  free(m_stringtab);
805  }
806  void run_destructor(void (*dtor)(K * ptr))
807  {
808  for (size_t i = 0; i <= m_baseSize; i++)
809  {
810  if (m_base[i].valset)
811  {
812  dtor(&m_base[i].value);
813  m_base[i].valset = false;
814  }
815  }
816  }
817 private:
818  class KTrieNode
819  {
820  friend class KTrie;
821  private:
827  unsigned int idx;
828 
834  unsigned int parent;
835  K value; /* Value associated with this node */
836  NodeType mode; /* Current usage type of the node */
837  bool valset; /* Whether or not a value is set */
838  };
839 private:
840  KTrieNode *internal_retrieve(const char *key)
841  {
842  unsigned int lastidx = 1; /* the last node index */
843  unsigned int curidx; /* current node index */
844  const char *keyptr = key; /* input stream at current token */
845  KTrieNode *node = NULL; /* current node being processed */
846 
847  if (!*key)
848  {
849  return m_empty;
850  }
851 
852  /* Start traversing at the root node */
853  do
854  {
855  /* Find where the next character is, then advance */
856  curidx = m_base[lastidx].idx;
857  node = &m_base[curidx];
858  curidx += charval(*keyptr);
859  node = &m_base[curidx];
860  keyptr++;
861 
862  /* Check if this slot is supposed to be empty or is a collision */
863  if ((curidx > m_baseSize) || node->mode == Node_Unused || node->parent != lastidx)
864  {
865  return NULL;
866  }
867  else if (node->mode == Node_Term)
868  {
869  char *term = &m_stringtab[node->idx];
870  if (strcmp(keyptr, term) == 0)
871  {
872  break;
873  }
874  else
875  {
876  return NULL;
877  }
878  }
879  lastidx = curidx;
880  } while (*keyptr != '\0');
881 
882  return node;
883  }
884  bool grow()
885  {
886  /* The current # of nodes in the tree is trie->baseSize + 1 */
887  unsigned int cur_size = m_baseSize;
888  unsigned int new_size = cur_size * 2;
889 
890  KTrieNode *new_base = (KTrieNode *)malloc((new_size + 1) * sizeof(KTrieNode));
891  if (!new_base)
892  {
893  return false;
894  }
895 
896  memcpy(new_base, m_base, sizeof(KTrieNode) * (m_baseSize + 1));
897  memset(&new_base[cur_size + 1], 0, (new_size - cur_size) * sizeof(KTrieNode));
898 
899  for (size_t i = 0; i <= m_baseSize; i++)
900  {
901  if (m_base[i].valset)
902  {
903  /* Placement construct+copy the object, then placement destroy the old. */
904  new (&new_base[i].value) K(m_base[i].value);
905  m_base[i].value.~K();
906  }
907  }
908 
909  free(m_base);
910  m_base = new_base;
911  m_baseSize = new_size;
912 
913  return true;
914  }
915  inline unsigned char charval(char c)
916  {
917  return (unsigned char)c;
918  }
919  void internal_clear()
920  {
921  m_tail = 0;
922  m_numElements = 0;
923 
924  memset(m_base, 0, sizeof(KTrieNode) * (m_baseSize + 1));
925  memset(m_stringtab, 0, sizeof(char) * m_stSize);
926 
927  /* Sentinel root node */
928  m_base[1].idx = 1;
929  m_base[1].mode = Node_Arc;
930  m_base[1].parent = 1;
931  }
932  void run_destructors()
933  {
934  for (size_t i = 0; i <= m_baseSize; i++)
935  {
936  if (m_base[i].valset)
937  {
938  m_base[i].value.~K();
939  }
940  }
941  }
942  unsigned int x_addstring(const char *ptr)
943  {
944  size_t len = strlen(ptr) + 1;
945 
946  if (m_tail + len >= m_stSize)
947  {
948  while (m_tail + len >= m_stSize)
949  {
950  m_stSize *= 2;
951  }
952  m_stringtab = (char *)realloc(m_stringtab,m_stSize);
953  }
954 
955  unsigned int tail = m_tail;
956  strcpy(&m_stringtab[tail], ptr);
957  m_tail += len;
958 
959  return tail;
960  }
961  unsigned int x_check(char c, unsigned int start=1)
962  {
963  unsigned char _c = charval(c);
964  unsigned int to_check = m_baseSize - _c;
965  for (unsigned int i=start; i<=to_check; i++)
966  {
967  if (m_base[i+_c].mode == Node_Unused)
968  {
969  return i;
970  }
971  }
972 
973  grow();
974 
975  return x_check(c, to_check+1);
976  }
977  unsigned int x_check2(char c1, char c2, unsigned int start=1)
978  {
979  unsigned char _c1 = charval(c1);
980  unsigned char _c2 = charval(c2);
981  unsigned int to_check = m_baseSize - (_c1 > _c2 ? _c1 : _c2);
982  for (unsigned int i=start; i<=to_check; i++)
983  {
984  if (m_base[i+_c1].mode == Node_Unused
985  && m_base[i+_c2].mode == Node_Unused)
986  {
987  return i;
988  }
989  }
990 
991  grow();
992 
993  return x_check2(c1, c2, to_check+1);
994  }
995  unsigned int x_check_multi(
996  unsigned int offsets[],
997  unsigned int count,
998  unsigned int start=1)
999  {
1000  KTrieNode *cur;
1001  unsigned int to_check = m_baseSize;
1002  unsigned int highest = 0;
1003 
1004  for (unsigned int i=0; i<count; i++)
1005  {
1006  if (offsets[i] > highest)
1007  {
1008  highest = offsets[i];
1009  }
1010  }
1011 
1012  to_check -= highest;
1013 
1014  for (unsigned int i=start; i<=to_check; i++)
1015  {
1016  bool okay = true;
1017  for (unsigned int j=0; j<count; j++)
1018  {
1019  cur = &m_base[i+offsets[j]];
1020  if (cur->mode != Node_Unused)
1021  {
1022  okay = false;
1023  break;
1024  }
1025  }
1026  if (okay)
1027  {
1028  return i;
1029  }
1030  }
1031 
1032  grow();
1033 
1034  return x_check_multi(offsets, count, to_check+1);
1035  }
1036 public:
1037  size_t mem_usage()
1038  {
1039  return (sizeof(KTrieNode) * (m_baseSize))
1040  + m_stSize
1041  + sizeof(KTrieNode);
1042  }
1043  size_t size()
1044  {
1045  return m_numElements;
1046  }
1047 private:
1048  KTrieNode *m_base; /* Base array for the sparse tables */
1049  KTrieNode *m_empty; /* Special case for empty strings */
1050  char *m_stringtab; /* String table pointer */
1051  unsigned int m_baseSize; /* Size of the base array, in members */
1052  unsigned int m_stSize; /* Size of the string table, in bytes */
1053  unsigned int m_tail; /* Current unused offset into the string table */
1054  size_t m_numElements; /* Number of elements in use */
1055 };
1056 
1122 #endif //_INCLUDE_SOURCEMOD_TEMPLATED_TRIE_H_
bool insert(const char *key, const K &obj)
Inserts an object at a key.
Definition: sm_trie_tpl.h:160
bool replace(const char *key, const K &obj)
Inserts or updates the object stored at a key.
Definition: sm_trie_tpl.h:134
bool retrieve(const char *key, K *result)
Wrapper around retrieve(key) for a cleaner API.
Definition: sm_trie_tpl.h:118
void bad_iterator(char *buffer, size_t maxlength, void *data, void(*func)(KTrie *, const char *, K &obj, void *data))
Iterates over the trie returning all known values.
Definition: sm_trie_tpl.h:690
NodeType
Definition: sm_trie_tpl.h:40
K * retrieve(const char *key)
Retrieves a pointer to the object stored at a given key.
Definition: sm_trie_tpl.h:101
Definition: sm_trie_tpl.h:60
void clear()
Clears all set objects in the trie.
Definition: sm_trie_tpl.h:67