12 #ifndef __UT_ARTMAP_H__
13 #define __UT_ARTMAP_H__
32 #if defined(__i386__) || defined(__amd64__)
33 #include <emmintrin.h>
34 #include <immintrin.h>
35 #elif defined (_WIN32)
40 #if defined(USE_MEMORY_RESOURCE)
41 #include <memory_resource>
42 namespace UT_PMR = std::pmr;
45 #if defined(LINUX) || defined(MBSD)
46 #if defined(__i386) || defined(__amd64)
47 #define SUPPORT_FAST_STR
49 #elif !defined(__clang__) && defined(_M_AMD64)
50 #define SUPPORT_FAST_STR
78 #
if defined(USE_MEMORY_RESOURCE)
86 #
if defined(USE_MEMORY_RESOURCE)
101 template <
typename... Args>
118 virtual bool isFull()
const = 0;
144 if (child !=
nullptr)
146 child->
debug(wbuf, indent + 1);
148 }
while (child !=
nullptr);
167 myKey = stolen->myKey;
169 stolen->myPrefix.begin() - prefix_length,
170 stolen->myPrefix.end());
172 myValue = std::move(stolen->myValue);
174 stolen->moveChildren(ref);
195 const char*
start = child->myPrefix.begin() - node_prefix_length;
196 UT_ASSERT(start >= child->myKey.buffer());
197 child->myPrefix =
UT_StringView(start, child->myPrefix.end());
199 (*ref)->insertChild(ref, std::move(child));
218 const char* k = key.
data();
219 const char* p = node_prefix.
data();
222 #if defined(SUPPORT_FAST_STR) && !defined(ASAN_ENABLED)
223 static constexpr
exint theMaxSIMDLen = 16;
224 const int mode = _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_EACH
225 | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY;
226 while (n > theMaxSIMDLen)
228 const __m128i
data = _mm_loadu_si128(
229 reinterpret_cast<const __m128i*>(k));
230 const __m128i
prefix = _mm_loadu_si128(
231 reinterpret_cast<const __m128i*>(p));
233 int check_len =
std::min(n, theMaxSIMDLen);
234 int res = _mm_cmpestri(data, check_len, prefix, check_len, mode);
239 if (n < 16 && res == 16)
278 #if defined(USE_MEMORY_RESOURCE)
279 UT_PMR::polymorphic_allocator<UT_ARTNode<T>> getAllocator()
285 template <
typename NodeT>
288 #if defined(USE_MEMORY_RESOURCE)
289 UT_PMR::polymorphic_allocator<NodeT> alloc(myAllocator);
290 NodeT* node = alloc.allocate(1);
308 #if defined(USE_MEMORY_RESOURCE)
309 UT_PMR::polymorphic_allocator<UT_ARTNode<T>> myAllocator;
321 template <
typename T>
325 #if defined(USE_MEMORY_RESOURCE)
326 auto alloc = ptr->getAllocator();
328 alloc.deallocate(ptr, 1);
334 template <
typename T>
350 return myCurrent == it.myCurrent;
354 return !(*
this == it);
378 bool hasValue()
const {
return myCurrent && myCurrent->hasValue(); }
380 const T&
value()
const {
return *myCurrent->value(); }
381 T&
value() {
return *myCurrent->value(); }
392 if (myCurrent && !myCurrent->hasValue())
396 void findNextValue_()
407 myNodeStack.
emplace_back(std::make_pair(myCurrent, myIndex));
411 else if (!myNodeStack.
isEmpty())
413 auto&& [node,
index] = myNodeStack.
last();
424 }
while (myCurrent !=
nullptr && !myCurrent->hasValue());
434 template <
typename T>
441 #if defined(USE_MEMORY_RESOURCE)
467 return theType.asHolder();
469 bool isFull()
const override;
486 template <
typename T>
493 #if defined(USE_MEMORY_RESOURCE)
509 for (
int i = 16; i-->0; )
516 return theType.asHolder();
518 bool isFull()
const override;
535 unsigned bitfield = 0;
536 #if defined(__i386) || defined(__amd64)
538 __m128i key_vec = _mm_set1_epi8(c);
539 __m128i results = _mm_cmpeq_epi8(
541 bitfield = _mm_movemask_epi8(results) &
mask;
543 for (
int i = 0; i < 16; ++i)
546 bitfield |= (1 << i);
555 return __builtin_ctz(bitfield);
557 return _tzcnt_u32(bitfield);
567 template <
typename T>
574 #if defined(USE_MEMORY_RESOURCE)
590 for (
int i = 48; i-->0; )
596 return theType.asHolder();
598 bool isFull()
const override;
616 template <
typename T>
623 #if defined(USE_MEMORY_RESOURCE)
638 for (
int i = 256; i-->0; )
645 return theType.asHolder();
647 bool isFull()
const override;
665 template <
typename T>
677 #if defined(USE_MEMORY_RESOURCE)
679 UT_ARTMap(UT_PMR::get_default_resource())
682 explicit UT_ARTMap(UT_PMR::memory_resource* resource) :
683 myAllocator(resource)
688 myRoot = createBaseNode(
695 #if defined(USE_MEMORY_RESOURCE)
696 UT_PMR::polymorphic_allocator<Node> alloc(myAllocator);
697 alloc.destroy(myRoot);
698 alloc.deallocate(myRoot, 1);
708 bool allow_partial =
false)
710 return insert_(name, allow_partial, data);
716 bool allow_partial =
false)
718 return insert_(name, allow_partial,
value);
721 template <
typename... Args>
727 name,
false, std::forward<Args>(
args)...);
730 typename std::enable_if_t<std::is_default_constructible_v<T>,
T&>
733 auto&& [it, inserted] = insert_(key,
false);
736 if (it.myCurrent && inserted)
738 it.myCurrent->emplace();
744 typename std::enable_if_t<std::is_default_constructible_v<T>,
T&>
747 auto&& [it, inserted] = insert_(key,
false);
750 if (it.myCurrent && inserted)
752 it.myCurrent->emplace();
763 Node* last_partial =
nullptr;
765 while (!prefix.
isEmpty() && child !=
nullptr)
772 prefix, found_prefix);
773 if (common_length == found_prefix.length())
780 && common_length != found_prefix.length())
790 last_partial = child;
812 else if (last_partial !=
nullptr && last_partial->
hasValue())
828 myRoot->
debug(wbuf, 0);
832 return doErase_(prefix);
838 #if defined(USE_MEMORY_RESOURCE)
839 UT_PMR::polymorphic_allocator<Node> alloc(myAllocator);
840 alloc.destroy(myRoot);
841 alloc.deallocate(myRoot, 1);
846 myRoot = createBaseNode(
852 return myRoot->childCount();
871 template <
typename... Args>
872 std::pair<iterator, bool> insert_(
878 return std::make_pair(
end(),
false);
880 bool inserted =
false;
881 Node* node = doInsert_(name, inserted);
883 UT_ASSERT((inserted && node !=
nullptr) || !inserted);
884 if (node && inserted)
886 node->emplace(std::forward<Args>(
args)...);
887 node->setAllowsPartial(allows_partial);
889 return std::make_pair(
iterator(node), inserted);
897 Node** current = &myRoot;
899 bool removed =
false;
901 while (!removed && current && prefix)
903 Node** node = (*current)->findPartialPrefixChild(prefix);
911 exint common_length = (*current)->getCommonPrefixLength(
912 prefix, (*node)->myPrefix);
914 if (common_length == prefix.length())
918 if (!(*node)->hasValue())
923 if ((*node)->isLeaf())
925 auto stolen = (*current)->stealChild(current, *node);
931 removed_node->moveChildren(current);
934 (*current)->compress(current);
938 prefix.removePrefix(common_length);
950 #if defined(USE_MEMORY_RESOURCE)
951 UT_PMR::polymorphic_allocator<UT_ARTNode4<T>> alloc(myAllocator);
953 alloc.construct(node, key, prefix, alloc);
968 Node** current = &myRoot;
971 while (current !=
nullptr && prefix.
length() > 0)
978 Node** inserted_node = (*current)->insertChild(
979 current, createBaseNode(key, prefix));
981 return (*inserted_node);
985 const exint prefix_length = (*current)->getCommonPrefixLength(
986 prefix, (*node)->myPrefix);
989 if (prefix.
length() == (*node)->myPrefix.length()
990 && prefix.
length() == prefix_length)
992 inserted = !(*node)->hasValue();
997 else if (prefix_length == (*node)->myPrefix.length())
1004 else if (prefix_length == prefix.
length())
1006 auto new_child = createBaseNode(key, prefix);
1012 stolen->myPrefix.removePrefix(prefix_length);
1015 new_child->insertChild(&ptr, std::move(stolen));
1018 Node** inserted_node = (*current)->insertChild(
1019 current, std::move(new_child));
1022 return (*inserted_node);
1035 ->stealChild(current, *node);
1038 auto parent = createBaseNode(key, item_prefix);
1040 erased_child->myPrefix.removePrefix(prefix_length);
1042 Node* ptr = parent.get();
1043 parent->insertChild(
1044 &ptr, std::move(erased_child));
1048 Node** inserted_node = parent->insertChild(
1049 &ptr, createBaseNode(key, prefix));
1053 (*current)->insertChild(current, std::move(parent));
1056 return *inserted_node;
1064 Node* myRoot =
nullptr;
1065 #if defined(USE_MEMORY_RESOURCE)
1066 UT_PMR::polymorphic_allocator<std::byte> myAllocator;
1071 template <std::
size_t N,
class T, std::enable_if_t<N == 0,
int> = 0>
1078 template <std::
size_t N,
class T, std::enable_if_t<N == 1,
int> = 0>
1087 template <
typename T>
1088 struct tuple_size<UT_ARTIterator<
T>>
1089 :
public std::integral_constant<std::size_t, 2>
1093 template <std::
size_t N,
typename T>
1094 struct tuple_element<
N, UT_ARTIterator<
T>>
1096 using type = decltype(get<N>(
1103 #endif // __UT_ARTMAP_H__
bool erase(const UT_StringRef &prefix)
bool operator!=(const UT_ARTIterator &it) const
SYS_NO_DISCARD_RESULT bool isEmpty() const
UT_StringHolder key() const
UT_ARTNodePtr< parent_t > parent_ptr_t
parent_t * nextChild(int &index) override
SYS_NO_DISCARD_RESULT iterator begin()
parent_t * nextChild(int &index) override
SYS_FORCE_INLINE void removeLast()
virtual ~UT_ARTNode()=default
parent_ptr_t stealChild(parent_t **ref, parent_t *child) override
UT_ARTNodePtr< parent_t > parent_ptr_t
std::ptrdiff_t difference_type
SYS_NO_DISCARD_RESULT iterator end()
SYS_FORCE_INLINE T * SYSconst_cast(const T *foo)
bool isEmpty() const
Same as !isstring()
void emplace(Args &&...args)
void moveHeader(UT_ARTNode< T > &move_to)
void debug(UT_WorkBuffer &wbuf, unsigned indent=0) const
UT_ARTNode16(const UT_StringHolder &key, UT_StringView prefix)
SYS_NO_DISCARD_RESULT bool contains(const UT_StringHolder &prefix) const
Check if the tree contains the provided string.
parent_t * myChildren[256]
GLuint GLsizei GLsizei * length
UT_NON_COPYABLE(UT_ARTNode)
UT_ARTNode4(const UT_StringHolder &key, UT_StringView prefix)
parent_ptr_t stealChild(parent_t **ref, parent_t *child) override
ImageBuf OIIO_API min(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
bool operator==(const UT_ARTIterator &it) const
const UT_StringHolder & type() const override
const UT_StringHolder & type() const override
void moveChildren(UT_ARTNode< T > **ref)
SYS_FORCE_INLINE const value_type * value() const
int prefixToIndex_(char c) const
parent_t ** findPartialPrefixChild(const UT_StringView &prefix) override
void removePrefix(exint n)
exint getCommonPrefixLength(const UT_StringView &key, const UT_StringView &node_prefix)
decltype(get< N >(std::declval< UT_ARTIterator< T >>())) type
std::optional< T > UT_Optional
virtual UT_ARTNode< T > * nextChild(int &idx)=0
std::unique_ptr< T, Deleter > UT_UniquePtr
A smart pointer for unique ownership of dynamically allocated objects.
parent_t * myChildren[16]
SYS_NO_DISCARD_RESULT SYS_FORCE_INLINE const char * data() const noexcept
Returns a pointer to the first character of a view.
void debug(UT_WorkBuffer &wbuf) const
Write out the tree into the work buffer. This is pretty printed.
A utility class to do read-only operations on a subset of an existing string.
size_t appendFormat(const char *fmt, const Args &...args)
SYS_NO_DISCARD_RESULT SYS_FORCE_INLINE bool isEmpty() const
Returns true if the string is empty.
A simple radix tree implementation.
UT_ARTNodePtr< parent_t > parent_ptr_t
SYS_NO_DISCARD_RESULT SYS_FORCE_INLINE exint length() const
Returns the length of the string in bytes.
std::forward_iterator_tag iterator_category
exint emplace_back(S &&...s)
std::pair< iterator, bool > insert(const UT_StringHolder &name, const T &data, bool allow_partial=false)
Insert a node.
unsigned char myChildKeys[16]
bool isFull() const override
SYS_NO_DISCARD_RESULT const_iterator end() const
virtual const UT_StringHolder & type() const =0
static const UT_StringHolder theEmptyString
SYS_NO_DISCARD_RESULT const_iterator begin() const
const UT_StringHolder & type() const override
void clear()
Clear out the tree.
bool isFull() const override
UT_ARTNode(const UT_StringHolder &key, const UT_StringView &prefix)
UT_ARTIterator< T > iterator
SYS_NO_DISCARD_RESULT iterator find(UT_StringView prefix) const
Find the node based on the provided prefix.
UT_ARTIterator & operator++()
UT_NON_COPYABLE(UT_ARTNode48)
SYS_FORCE_INLINE bool isLeaf() const
#define SYS_NO_DISCARD_RESULT
UT_Optional< value_type > myValue
parent_t ** insertChild(parent_t **ref, parent_ptr_t child) override
SYS_NO_DISCARD_RESULT const_iterator cend() const
GLuint const GLchar * name
bool isFull() const override
std::enable_if_t< std::is_default_constructible_v< T >, T & > operator[](UT_StringHolder &&key)
const UT_StringHolder & type() const override
parent_t ** findPartialPrefixChild(const UT_StringView &prefix) override
UT_ARTNodePtr< parent_t > parent_ptr_t
parent_t ** insertChild(parent_t **ref, parent_ptr_t child) override
parent_t ** findPartialPrefixChild(const UT_StringView &prefix) override
parent_t ** insertChild(parent_t **ref, parent_ptr_t child) override
parent_ptr_t stealChild(parent_t **ref, parent_t *child) override
~UT_ARTNode256() override
virtual UT_ARTNodePtr< UT_ARTNode< T > > stealChild(UT_ARTNode **ref, UT_ARTNode *child)=0
const_reference operator*() const
virtual UT_ARTNode< T > ** findPartialPrefixChild(const UT_StringView &prefix)=0
parent_t ** insertChild(parent_t **ref, parent_ptr_t child) override
UT_ARTIterator operator++(int)
unsigned char myChildKeys[256]
std::pair< iterator, bool > emplace(const UT_StringHolder &name, Args &&...args)
std::pair< iterator, bool > insert(const UT_StringHolder &name, T &&value, bool allow_partial=false)
unsigned char myChildKeys[4]
SYS_FORCE_INLINE bool hasValue() const
UT_ARTNodePtr< NodeT > newNode()
virtual bool isFull() const =0
parent_t * nextChild(int &index) override
SYS_FORCE_INLINE void append(char character)
SYS_NO_DISCARD_RESULT const_iterator cbegin() const
GA_API const UT_StringHolder N
parent_t * nextChild(int &index) override
**If you just want to fire and args
SYS_NO_DISCARD_RESULT SYS_FORCE_INLINE const_iterator begin() const
Returns a constant iterator pointing to the beginning of the string.
UT_ARTNode256(const UT_StringHolder &key, UT_StringView prefix)
SYS_FORCE_INLINE value_type * value()
static void destroy(UT_ARTNode< T > *node)
UT_NON_COPYABLE(UT_ARTMap)
SYS_FORCE_INLINE void clear()
parent_t ** findPartialPrefixChild(const UT_StringView &prefix) override
UT_NON_COPYABLE(UT_ARTNode16)
void setAllowsPartial(bool allow)
std::enable_if_t< std::is_default_constructible_v< T >, T & > operator[](const UT_StringHolder &key)
parent_ptr_t stealChild(parent_t **ref, parent_t *child) override
virtual UT_ARTNode< T > ** insertChild(UT_ARTNode **ref, UT_ARTNodePtr< UT_ARTNode< T >> node)=0
void compress(UT_ARTNode< T > **ref)
parent_t * myChildren[48]
UT_UniquePtr< T, UT_ARTNodeDelete > UT_ARTNodePtr
SYS_FORCE_INLINE const UT_StringView & prefix() const
UT_NON_COPYABLE(UT_ARTNode256)
UT_NON_COPYABLE(UT_ARTNode4)
bool isFull() const override
UT_StringHolder key() const
const_pointer operator->() const
UT_ARTNode48(const UT_StringHolder &key, UT_StringView prefix)
bool isEmpty() const
Returns true iff there are no occupied elements in the array.
SYS_FORCE_INLINE bool allowsPartial() const