1#pragma once
2
3#include "basis/seadTypes.h"
4#include "container/seadFreeList.h"
5#include "container/seadTreeMap.h"
6#include "prim/seadDelegate.h"
7#include "prim/seadSafeString.h"
8
9namespace sead
10{
11/// Sorted associative container with fixed-length string keys.
12/// This is essentially std::map<char[MaxKeyLength], Value>
13template <s32 MaxKeyLength, typename Value>
14class StrTreeMap : public TreeMapImpl<SafeString>
15{
16public:
17 using MapImpl = TreeMapImpl<SafeString>;
18 class Node : public MapImpl::Node
19 {
20 public:
21 Node(StrTreeMap* map, const SafeString& key, const Value& value) : mValue(value), mMap(map)
22 {
23 BufferedSafeString buffer(mKeyData, MaxKeyLength + 1);
24 buffer.copy(src: key);
25 this->mKey = buffer;
26 }
27
28 void erase_() override;
29
30 Value& value() { return mValue; }
31 const Value& value() const { return mValue; }
32
33 private:
34 friend class StrTreeMap;
35
36 Value mValue;
37 StrTreeMap* mMap;
38 char mKeyData[MaxKeyLength + 1];
39 };
40
41 void allocBuffer(s32 node_max, Heap* heap, s32 alignment = sizeof(void*));
42 void setBuffer(s32 node_max, void* buffer);
43 void freeBuffer();
44
45 bool isBufferReady() const { return mFreeList.work() != nullptr; }
46
47 Value* insert(const SafeString& key, const Value& value);
48 void clear();
49
50 Node* find(const SafeString& key) const;
51
52 // Callable must have the signature Key&, Value&
53 template <typename Callable>
54 void forEach(const Callable& delegate) const;
55
56private:
57 void eraseNodeForClear_(typename MapImpl::Node* node);
58
59 FreeList mFreeList;
60 s32 mSize = 0;
61 s32 mCapacity = 0;
62};
63
64template <s32 N, typename Value>
65inline void StrTreeMap<N, Value>::Node::erase_()
66{
67 StrTreeMap* const map = mMap;
68 void* const this_ = this;
69 // Note: Nintendo does not call the destructor, which is dangerous...
70 map->mFreeList.free(ptr: this_);
71 --map->mSize;
72}
73
74template <s32 N, typename Value>
75inline void StrTreeMap<N, Value>::allocBuffer(s32 node_max, Heap* heap, s32 alignment)
76{
77 s32 node_size = sizeof(Node);
78
79 SEAD_ASSERT(mFreeList.work() == nullptr);
80 if (node_max <= 0)
81 {
82 SEAD_ASSERT_MSG(false, "node_max[%d] must be larger than zero", node_max);
83 AllocFailAssert(heap, node_max * node_size, alignment);
84 }
85
86 void* work = AllocBuffer(size: node_max * node_size, heap, alignment);
87 if (work)
88 setBuffer(node_max, buffer: work);
89}
90
91template <s32 N, typename Value>
92inline void StrTreeMap<N, Value>::setBuffer(s32 node_max, void* buffer)
93{
94 mCapacity = node_max;
95 mFreeList.setWork(work: buffer, elem_size: sizeof(Node), num: node_max);
96}
97
98template <s32 N, typename Value>
99inline void StrTreeMap<N, Value>::freeBuffer()
100{
101 void* buffer = mFreeList.work();
102 if (!buffer)
103 return;
104
105 ::operator delete[](ptr: buffer);
106 mCapacity = 0;
107 mFreeList.reset();
108}
109
110template <s32 N, typename Value>
111inline Value* StrTreeMap<N, Value>::insert(const SafeString& key, const Value& value)
112{
113 Value* ptr = nullptr;
114
115 if (mSize < mCapacity)
116 {
117 Node* node = new (mFreeList.alloc()) Node(this, key, value);
118 ptr = &node->value();
119 ++mSize;
120 MapImpl::insert(node);
121 }
122 else if (Node* node = find(key))
123 {
124 ptr = &node->value();
125 new (ptr) Value(value);
126 }
127 else
128 {
129 SEAD_ASSERT_MSG(false, "map is full.");
130 }
131
132 return ptr;
133}
134
135template <s32 N, typename Value>
136inline void StrTreeMap<N, Value>::clear()
137{
138 Delegate1<StrTreeMap<N, Value>, typename MapImpl::Node*> delegate(
139 this, &StrTreeMap::eraseNodeForClear_);
140 MapImpl::forEach(delegate);
141 mSize = 0;
142 MapImpl::clear();
143}
144
145template <s32 N, typename Value>
146inline typename StrTreeMap<N, Value>::Node* StrTreeMap<N, Value>::find(const SafeString& key) const
147{
148 return static_cast<Node*>(MapImpl::find(key));
149}
150
151template <s32 N, typename Value>
152template <typename Callable>
153inline void StrTreeMap<N, Value>::forEach(const Callable& delegate) const
154{
155 MapImpl::forEach([&delegate](auto* base_node) {
156 auto* node = static_cast<Node*>(base_node);
157 delegate(node->key(), node->value());
158 });
159}
160
161template <s32 N, typename Value>
162inline void StrTreeMap<N, Value>::eraseNodeForClear_(typename MapImpl::Node* node)
163{
164 // Note: Nintendo does not call the destructor, which is dangerous...
165 mFreeList.free(ptr: node);
166}
167} // namespace sead
168