1/**
2 * Copyright (c) 2006-2023 LOVE Development Team
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty. In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 *
8 * Permission is granted to anyone to use this software for any purpose,
9 * including commercial applications, and to alter it and redistribute it
10 * freely, subject to the following restrictions:
11 *
12 * 1. The origin of this software must not be misrepresented; you must not
13 * claim that you wrote the original software. If you use this software
14 * in a product, an acknowledgment in the product documentation would be
15 * appreciated but is not required.
16 * 2. Altered source versions must be plainly marked as such, and must not be
17 * misrepresented as being the original software.
18 * 3. This notice may not be removed or altered from any source distribution.
19 **/
20
21#ifndef LOVE_STRING_MAP_H
22#define LOVE_STRING_MAP_H
23
24#include "Exception.h"
25
26#include <string>
27#include <vector>
28
29// As StringMap instantiates std::vector<std::string> for instances that use
30// getNames(), we end up with multiple copies in the object files. This
31// declaration means we only emit it once (in StringMap.cpp).
32extern template class std::vector<std::string>;
33
34namespace love
35{
36
37template<typename T, unsigned int SIZE>
38class StringMap
39{
40public:
41
42 struct Entry
43 {
44 const char *key;
45 T value;
46 };
47
48 StringMap(const Entry *entries, unsigned int num)
49 {
50
51 for (unsigned int i = 0; i < SIZE; ++i)
52 reverse[i] = nullptr;
53
54 unsigned int n = num / sizeof(Entry);
55
56 for (unsigned int i = 0; i < n; ++i)
57 add(entries[i].key, entries[i].value);
58 }
59
60 bool streq(const char *a, const char *b)
61 {
62 while (*a != 0 && *b != 0)
63 {
64 if (*a != *b)
65 return false;
66
67 ++a;
68 ++b;
69 }
70
71 return (*a == 0 && *b == 0);
72 }
73
74 bool find(const char *key, T &t)
75 {
76 unsigned int str_hash = djb2(key);
77
78 for (unsigned int i = 0; i < MAX; ++i)
79 {
80 unsigned int str_i = (str_hash + i) % MAX;
81
82 if (!records[str_i].set)
83 return false;
84
85 if (streq(records[str_i].key, key))
86 {
87 t = records[str_i].value;
88 return true;
89 }
90 }
91
92 return false;
93 }
94
95 bool find(T key, const char *&str)
96 {
97 unsigned int index = (unsigned int) key;
98
99 if (index >= SIZE)
100 return false;
101
102 if (reverse[index] != nullptr)
103 {
104 str = reverse[index];
105 return true;
106 }
107 else
108 {
109 return false;
110 }
111 }
112
113 bool add(const char *key, T value)
114 {
115 unsigned int str_hash = djb2(key);
116 bool inserted = false;
117
118 for (unsigned int i = 0; i < MAX; ++i)
119 {
120 unsigned int str_i = (str_hash + i) % MAX;
121
122 if (!records[str_i].set)
123 {
124 inserted = true;
125 records[str_i].set = true;
126 records[str_i].key = key;
127 records[str_i].value = value;
128 break;
129 }
130 }
131
132 unsigned int index = (unsigned int) value;
133
134 if (index >= SIZE)
135 {
136 printf("Constant %s out of bounds with %u!\n", key, index);
137 return false;
138 }
139
140 reverse[index] = key;
141
142 return inserted;
143 }
144
145 unsigned int djb2(const char *key)
146 {
147 unsigned int hash = 5381;
148 int c;
149
150 while ((c = *key++))
151 hash = ((hash << 5) + hash) + c;
152
153 return hash;
154 }
155
156 std::vector<std::string> getNames() const
157 {
158 std::vector<std::string> names;
159 names.reserve(SIZE);
160
161 for (unsigned int i = 0; i < SIZE; ++i)
162 if (reverse[i] != nullptr)
163 names.emplace_back(reverse[i]);
164
165 return names;
166 }
167
168private:
169
170 struct Record
171 {
172 const char *key;
173 T value;
174 bool set;
175 Record() : set(false) {}
176 };
177
178 static const unsigned int MAX = SIZE * 2;
179
180 Record records[MAX];
181 const char *reverse[SIZE];
182
183}; // StringMap
184
185} // love
186
187#endif // LOVE_STRING_MAP_H
188