jak-project/common/util/Trie.h
Hat Kid 93afb02cf4
decomp3: spawn target, add merc and particle buckets and some temporary hacks (#3445)
This includes all the collision stuff needed to spawn `target`,
decompiles the sparticle code and adds some of the PC hacks needed for
merc to run (it doesn't work quite right and looks bad, likely due to a
combination of code copied from Jak 2 and the time of day hacks).

There are a bunch of temporary hacks (see commits) in place to prevent
the game from crashing quite as much, but it is still extremely prone to
doing so due to lots of missing functions/potentially bad decomp.

---------

Co-authored-by: water <awaterford111445@gmail.com>
2024-04-05 00:07:39 -04:00

190 lines
4.4 KiB
C++

#pragma once
#include <string>
#include <vector>
#include "common/util/Assert.h"
/*!
* A simple prefix tree. It works similarly to a map, but also supports fast lookups by prefix with
* the ability to lookup all entries with keys that begin with a given prefix.
*
* It owns the memory for the objects it stores.
* Doing an insert will create a copy of your object.
*
* Other than deleting the whole thing, there is no support for removing a node.
*/
template <typename T>
class Trie {
public:
Trie() = default;
Trie(const Trie&) = delete;
Trie& operator=(const Trie&) = delete;
// Insert an object, replacing an existing one if it exists
void insert(const std::string& str, const T& obj);
// Get the object at the string. Default construct a new one if none exists.
T* operator[](const std::string& str);
// Lookup an existing object. If none exists, return nullptr.
T* lookup(const std::string& str) const;
// return the number of entries.
int size() const { return m_size; }
// Get all objects starting with the given prefix.
std::vector<T*> lookup_prefix(const std::string& str) const;
// Get all nodes in the tree.
std::vector<T*> get_all_nodes() const;
~Trie();
private:
static constexpr int CHAR_SIZE = 256;
static int idx(char c) { return (u8)c; }
struct Node {
T* value = nullptr;
Node* children[CHAR_SIZE] = {0};
void delete_children() {
if (value) {
delete value;
value = nullptr;
}
for (auto child : children) {
if (child) {
child->delete_children();
delete child;
child = nullptr;
}
}
}
/*!
* Return true if a new object was inserted.
*/
bool insert(const char* str, const T* obj) {
if (!*str) {
// we are the child!
if (value) {
delete value;
value = new T(*obj);
return false; // didn't change the count.
} else {
value = new T(*obj);
return true; // did change the count
}
} else {
// still more to go
char first = *str;
if (!children[idx(first)]) {
children[idx(first)] = new Node();
}
return children[idx(first)]->insert(str + 1, obj);
}
}
T* bracket_operator(const char* str, bool* inserted) {
if (!*str) {
// we are the child!
if (!value) {
value = new T();
*inserted = true;
} else {
*inserted = false;
}
return value;
} else {
// still more to go
char first = *str;
if (!children[idx(first)]) {
children[idx(first)] = new Node();
}
return children[idx(first)]->bracket_operator(str + 1, inserted);
}
}
T* lookup(const char* str) const {
if (!*str) {
return value;
}
if (children[idx(*str)]) {
return children[idx(*str)]->lookup(str + 1);
}
return nullptr;
}
void get_all_children(std::vector<T*>& result) const {
if (value) {
result.push_back(value);
}
for (auto child : children) {
if (child) {
child->get_all_children(result);
}
}
}
std::vector<T*> lookup_prefix(const char* str) const {
if (!*str) {
std::vector<T*> result;
get_all_children(result);
return result;
} else {
if (children[idx(*str)]) {
return children[idx(*str)]->lookup_prefix(str + 1);
} else {
return {};
}
}
}
};
Node m_root;
int m_size = 0;
};
template <typename T>
Trie<T>::~Trie() {
m_root.delete_children();
m_size = 0;
}
template <typename T>
void Trie<T>::insert(const std::string& str, const T& obj) {
if (m_root.insert(str.c_str(), &obj)) {
m_size++;
}
}
template <typename T>
T* Trie<T>::lookup(const std::string& str) const {
return m_root.lookup(str.c_str());
}
template <typename T>
T* Trie<T>::operator[](const std::string& str) {
bool added = false;
auto result = m_root.bracket_operator(str.c_str(), &added);
if (added) {
m_size++;
}
return result;
}
template <typename T>
std::vector<T*> Trie<T>::lookup_prefix(const std::string& str) const {
return m_root.lookup_prefix(str.c_str());
}
template <typename T>
std::vector<T*> Trie<T>::get_all_nodes() const {
std::vector<T*> result;
m_root.get_all_children(result);
return result;
}