HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
NET_RadixTree.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: NET_RadixTree.h
7  *
8  * COMMENTS:
9  *
10  */
11 
12 #ifndef __NET_RADIXTREE_H__
13 #define __NET_RADIXTREE_H__
14 
15 #include "NET_API.h"
16 
17 #include <UT/UT_Assert.h>
18 #include <UT/UT_Debug.h>
19 #include <UT/UT_Optional.h>
20 #include <UT/UT_Set.h>
21 #include <UT/UT_SharedPtr.h>
22 #include <UT/UT_StringHolder.h>
23 #include <UT/UT_UniquePtr.h>
24 #include <UT/UT_WorkBuffer.h>
25 #include <UT/UT_IntrusivePtr.h>
26 
27 #include <SYS/SYS_Compiler.h>
28 
29 #include <cctype>
30 
31 /// A simple radix tree implementation
32 template <typename T>
34 {
35 public:
36  class Node;
37 
39 
40  class Node : public UT_IntrusiveRefCounter<Node>
41  {
42  public:
43  using Type = T;
45 
46  struct NodeCompare
47  {
48  bool operator()(const NodeT &lhs, const NodeT &rhs) const
49  {
50  return std::toupper(lhs->myPrefix[0]) ==
51  std::toupper(rhs->myPrefix[0]);
52  }
53  };
54 
56 
57  Node() : myPrefix(), myValue(), myChildren(), myAllowsPartial(false) {}
58 
59  Node(const UT_StringHolder &prefix, bool allows_partial = false)
60  : myPrefix(prefix),
61  myValue(),
62  myChildren(),
63  myAllowsPartial(allows_partial)
64  {
65  }
67  const T &data,
68  bool allows_partial = false)
69  : myPrefix(prefix),
70  myValue(UTmakeUnique<T>(data)),
71  myChildren(),
72  myAllowsPartial(allows_partial)
73  {
74  }
75 
77  {
78  return myValue.get();
79  }
80  SYS_FORCE_INLINE bool hasValue() const { return myValue != nullptr; }
82  {
83  return myPrefix;
84  }
85  SYS_FORCE_INLINE bool isLeaf() const { return myChildren.size() == 0; }
86  SYS_FORCE_INLINE bool allowsPartial() const { return myAllowsPartial; }
87 
88  void insert(const UT_StringHolder &key,
89  const T &data,
90  bool allow_partial = false)
91  {
92  UT_ASSERT(key.length() > 0);
93 
94  auto it = findPartialPrefixChild(key);
95  if (it == myChildren.end())
96  {
97  // No child partially matched the prefix so we will create
98  // one
99  auto child = UTmakeIntrusive<Node>(key, data, allow_partial);
100  myChildren.insert(child);
101  }
102  else
103  {
104  auto child = *it;
105  UT_ASSERT(child);
106 
107  const exint prefix_length = getPrefixLength(
108  key, child->myPrefix);
109 
110  // Check if this is a duplicate key
111  if (key.length() == child->myPrefix.length() &&
112  key.length() == prefix_length)
113  {
114  if (child->hasValue())
115  return;
116 
117  child->myValue = UTmakeUnique<Type>(data);
118  child->myAllowsPartial = allow_partial;
119  return;
120  }
121  // The childs key is the prefix of the insert item
122  // abc -> abc - d [ after inserting "abcd" ]
123  else if (prefix_length == child->myPrefix.length())
124  {
125  UT_StringHolder suffix(key.begin() + prefix_length);
126  child->insert(suffix, data, allow_partial);
127  return;
128  }
129  // The insert item is the prefix of the childs prefix
130  // abc -> ab - c [after inserting "abc"]
131  else if (prefix_length == key.length())
132  {
133  UT_StringHolder suffix(child->myPrefix.begin() +
134  prefix_length);
135 
136  auto new_child = UTmakeIntrusive<Node>(
137  key, data, allow_partial);
138 
139  // Remove the current child
140  myChildren.erase(child);
141  // Update the child prefix
142  child->myPrefix = suffix;
143  // Add old child to the new child
144  new_child->myChildren.insert(child);
145 
146  // Attach the new child to the current children
147  myChildren.insert(new_child);
148  }
149  // Inserting item and childs key have the same common prefix
150  else
151  {
152  // We need to split the current child and the suffix
153  // into
154  // two children whos parent is the prefix of the two
155  // children
156  UT_StringHolder item_prefix(
157  child->myPrefix.begin(), prefix_length);
158  UT_StringHolder item_suffix(child->myPrefix.begin() +
159  prefix_length);
160 
161  // remove the existing child as this will be move to the
162  // parent of the two children.
163  UT_VERIFY(myChildren.erase(child) == 1);
164 
165  auto parent = UTmakeIntrusive<Node>(item_prefix);
166  // Add the prefix of the old child and the new key into
167  // this children set.
168  myChildren.insert(parent);
169 
170  // update the new prefix
171  child->myPrefix = item_suffix;
172  // Add the suffix of the old key into the new branch
173  parent->myChildren.insert(child);
174 
175  // Add the suffix of the key into the new split branch
176  UT_StringHolder suffix(key.begin() + prefix_length);
177  auto new_child = UTmakeIntrusive<Node>(
178  suffix, data, allow_partial);
179  parent->myChildren.insert(new_child);
180  }
181  }
182  }
183 
184  // Find a child based on the prefix
186  {
187  UT_ASSERT(prefix.length() > 0);
188 
189  auto it = findPartialPrefixChild(prefix);
190  if (it == myChildren.end() || !prefix.startsWith((*it)->myPrefix))
191  return nullptr;
192 
193  return *it;
194  }
195 
196  void debug(UT_WorkBuffer &wbuf, unsigned indent = 0) const
197  {
198  wbuf.append(indent, ' ');
199  wbuf.append(myPrefix.length() > 0
200  ? static_cast<const char *>(myPrefix) : "<empty>");
201  wbuf.append(' ');
202  if (hasValue())
203  wbuf.appendFormat("{}", *myValue);
204  else
205  wbuf.append("<None>");
206  wbuf.append('\n');
207 
208  for (auto &&child : myChildren)
209  child->debug(wbuf, indent + 1);
210  }
211 
212  private:
213  // Find the max length that both key and node prefix share
214  exint getPrefixLength(const UT_StringHolder &key,
215  const UT_StringHolder &node_prefix)
216  {
217  exint length = 0;
218 
219  for (auto k_it = key.begin(), np_it = node_prefix.begin();
220  k_it != key.end() && np_it != node_prefix.end();
221  ++length, ++k_it, ++np_it)
222  {
223  if (*k_it != *np_it)
224  break;
225  }
226 
227  return length;
228  }
229 
230  // Find a child that is a partial match to the prefix.
231  typename ChildrenT::iterator findPartialPrefixChild(
232  const UT_StringHolder &prefix)
233  {
234  if (prefix.length() <= 0)
235  return myChildren.end();
236 
237  char item = prefix[0];
238  for (auto it = myChildren.begin(); it != myChildren.end(); it++)
239  {
240  if ((*it)->myPrefix[0] == item)
241  return it;
242  }
243  return myChildren.end();
244  }
245 
246  UT_StringHolder myPrefix;
247  ValueT myValue;
248  ChildrenT myChildren;
249  // If the node allows for partial matches
250  bool myAllowsPartial;
251  };
252 
253  /// Insert a node
255  const T &data,
256  bool allow_partial = false)
257  {
258  if (!myRoot)
259  myRoot = UTmakeIntrusive<Node>();
260  myRoot->insert(name, data, allow_partial);
261  }
262 
263  /// Find the node based on the provided prefix.
264  NodeT find(const UT_StringHolder &prefix) const
265  {
266  if (myRoot == nullptr)
267  return nullptr;
268 
269  if (myRoot->prefix() == prefix)
270  return myRoot;
271 
272  auto child = myRoot;
273  NodeT last_partial = nullptr;
274  UT_WorkBuffer wbuf(prefix);
275 
276  exint offset = 0;
277  while (offset < wbuf.length() && child != nullptr)
278  {
279  UT_ASSERT(wbuf.begin() + offset <= wbuf.end());
280  UT_StringView view(wbuf.begin() + offset, wbuf.end());
281  child = child->findChild(view);
282 
283  if (child)
284  {
285  offset += child->prefix().length();
286  // If this child allows for partial matches then update the
287  // last partial node we found.
288  if (child->allowsPartial())
289  last_partial = child;
290  }
291  // If we didnt find a child that matches check if our traversal came
292  // across a node that allows for partial matches. If we found one
293  // then return this node instead of reporting nothing found.
294  else if (last_partial != nullptr)
295  return last_partial;
296  }
297 
298  return child;
299  }
300 
301  /// Find the node that fully matches the prefix or partially matches based
302  /// on the tree node being setup for it. Basically this method only returns
303  /// a viable node if it "fully" matches the prefix. Use find() if you want
304  /// find any node that will either partially match or fully match the
305  /// prefix.
307  {
308  NodeT node = find(prefix);
309  // Check that the node we found actually has a value (is an actuall
310  // value in our tree).
311  if (node == nullptr || !node->hasValue())
312  return nullptr;
313  return node;
314  }
315 
316  /// Check if the tree contains the provided string
318  {
319  NodeT node = find(prefix);
320 
321  return node != nullptr && node->hasValue();
322  }
323 
324  /// Write out the tree into the work buffer. This is pretty printed.
325  void debug(UT_WorkBuffer &wbuf) const
326  {
327  wbuf.clear();
328 
329  if (myRoot)
330  myRoot->debug(wbuf, 0);
331  else
332  wbuf = "<empty>";
333  }
334 
335  /// Clear out the tree
336  void clear() { myRoot.reset(); }
337 
338 private:
339  NodeT myRoot;
340 };
341 
342 #endif // __NET_RADIXTREE_H__
343 
SYS_NO_DISCARD_RESULT bool startsWith(const char *prefix, bool case_sensitive=true, exint len=-1) const
Returns true if our string starts with the specified prefix.
SYS_FORCE_INLINE const_iterator begin() const
SYS_FORCE_INLINE exint length() const
SYS_NO_DISCARD_RESULT bool contains(const UT_StringHolder &prefix) const
Check if the tree contains the provided string.
SYS_FORCE_INLINE bool allowsPartial() const
Definition: NET_RadixTree.h:86
A simple radix tree implementation.
Definition: NET_RadixTree.h:33
UT_UniquePtr< Type > ValueT
Definition: NET_RadixTree.h:44
GLboolean * data
Definition: glcorearb.h:131
SYS_FORCE_INLINE const UT_StringHolder & prefix() const
Definition: NET_RadixTree.h:81
void insert(const UT_StringHolder &name, const T &data, bool allow_partial=false)
Insert a node.
int64 exint
Definition: SYS_Types.h:125
SYS_FORCE_INLINE bool isLeaf() const
Definition: NET_RadixTree.h:85
GLuint GLsizei GLsizei * length
Definition: glcorearb.h:795
void debug(UT_WorkBuffer &wbuf, unsigned indent=0) const
A reference counter base class for use with UT_IntrusivePtr.
NodeT find(const UT_StringHolder &prefix) const
Find the node based on the provided prefix.
std::unique_ptr< T, Deleter > UT_UniquePtr
A smart pointer for unique ownership of dynamically allocated objects.
Definition: UT_UniquePtr.h:39
Node(const UT_StringHolder &prefix, const T &data, bool allows_partial=false)
Definition: NET_RadixTree.h:66
SYS_FORCE_INLINE const_iterator end() const
A utility class to do read-only operations on a subset of an existing string.
Definition: UT_StringView.h:39
size_t appendFormat(const char *fmt, const Args &...args)
bool operator()(const NodeT &lhs, const NodeT &rhs) const
Definition: NET_RadixTree.h:48
SYS_NO_DISCARD_RESULT SYS_FORCE_INLINE exint length() const
Returns the length of the string in bytes.
GLintptr offset
Definition: glcorearb.h:665
exint length() const
SYS_FORCE_INLINE const char * end() const
SYS_FORCE_INLINE const char * begin() const
Iterator compatibility.
#define SYS_FORCE_INLINE
Definition: SYS_Inline.h:45
#define SYS_NO_DISCARD_RESULT
Definition: SYS_Compiler.h:93
std::enable_if< !std::is_array< T >::value, UT_UniquePtr< T >>::type UTmakeUnique(REST &&...args)
Definition: UT_UniquePtr.h:52
GLuint const GLchar * name
Definition: glcorearb.h:786
SYS_FORCE_INLINE bool hasValue() const
Definition: NET_RadixTree.h:80
SYS_FORCE_INLINE Type * value() const
Definition: NET_RadixTree.h:76
UT_Set< NodeT, hboost::hash< NodeT >, NodeCompare > ChildrenT
Definition: NET_RadixTree.h:55
#define UT_VERIFY(expr)
Definition: UT_Assert.h:202
UT_IntrusivePtr< Node > NodeT
Definition: NET_RadixTree.h:38
NodeT findChild(const UT_StringView prefix)
SYS_FORCE_INLINE void append(char character)
Definition: core.h:982
void clear()
Clear out the tree.
#define UT_ASSERT(ZZ)
Definition: UT_Assert.h:156
SYS_FORCE_INLINE void clear()
void debug(UT_WorkBuffer &wbuf) const
Write out the tree into the work buffer. This is pretty printed.
SYS_NO_DISCARD_RESULT NodeT match(const UT_StringHolder &prefix) const
Node(const UT_StringHolder &prefix, bool allows_partial=false)
Definition: NET_RadixTree.h:59
void insert(const UT_StringHolder &key, const T &data, bool allow_partial=false)
Definition: NET_RadixTree.h:88
Definition: format.h:895