This is a Trie data structure for mapping strings to integers or pointers in O(n) time. Based on the idea that we never delete anything from the container, we can perform concurrent read/write operations without any locks.
Note that TCMap is initialized separately, its role is to map relevant characters to indices. (We only need a subset of the ASCII table; letters, numbers, and a few symbols.)
static const int TrieNChild = 40;
class TrieCharMap {
int m[256];
public:
TrieCharMap();
TrieCharMap(const TrieCharMap& o) = delete;
void operator = (const TrieCharMap& o) = delete;
inline int operator [] (char x) { return m[(unsigned char)(x)]; }
};
extern TrieCharMap TCMap;
template <class T>
class Trie {
struct node {
std::atomic<node*> p[TrieNChild] {};
std::atomic<T> z{};
void clear() {
for (int i = 0; i < TrieNChild; i++)
if (p[i]) ((node*)(p[i]))->clear();
}
};
node root;
std::mutex mtx;
public:
Trie() { }
~Trie() { root.clear(); }
private:
Trie(const Trie& o) {}
void operator = (const Trie& o) {}
public:
T add(const char* s, T value) { //return old value
node* x = &root;
for (; *s; s++) {
int i = TCMap[*s];
node* next = x->p[i];
if (!next) {
next = new node;
node* val = 0;
if (!x->p[i].compare_exchange_strong(val, next, std::memory_order_relaxed, std::memory_order_relaxed)) {
//someone else wrote the pointer, just use that
delete next;
next = val;
}
}
x = next;
}
return x->z.exchange(value, std::memory_order_relaxed);
}
T search(const char* s) {
node* x = &root;
for (; x && *s; s++)
x = x->p[TCMap[*s]];
if (x) return x->z;
return T{};
}
};