-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsmallfolk.h
More file actions
382 lines (322 loc) · 13.9 KB
/
Copy pathsmallfolk.h
File metadata and controls
382 lines (322 loc) · 13.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
#ifndef SMALLFOLK_H
#define SMALLFOLK_H
#include <string>
#include <unordered_map>
#include <memory>
#include <stdexcept>
#include <cstddef>
#include <cstdint>
#include <utility>
#include <initializer_list>
#include <forward_list>
#include <deque>
#include <map>
class smallfolk_exception : public std::logic_error
{
public:
static size_t const buffer_size = 2048;
smallfolk_exception(const char * format, ...);
const char* what() const noexcept override;
std::string errmsg;
};
enum LuaTypeTag
{
TNIL,
TSTRING,
TNUMBER,
TTABLE,
TBOOL,
};
struct LoadLimits
{
size_t max_input_size = 16 * 1024 * 1024;
size_t max_string_length = 1024 * 1024;
unsigned max_nesting_depth = 256;
size_t max_value_count = 100000;
// Maximum key/value pairs in a single table. 0 disables this check.
size_t max_table_entries = 100000;
bool require_consumed_input = true;
// When true, reject I/i/N/Q non-finite number encodings during loads().
bool reject_non_finite_numbers = false;
};
class LuaVal;
size_t LuaValHash(LuaVal const & v);
namespace std {
template <>
struct hash<LuaVal> {
public:
size_t operator()(LuaVal const & v) const
{
return LuaValHash(v);
};
};
}
class LuaVal
{
public:
static LuaVal const nil;
// Immutable library defaults (never changes at runtime).
static LoadLimits const & default_load_limits();
// Tighter defaults suitable for untrusted user input in servers.
static LoadLimits untrusted_load_limits();
// Thread-safe read of the process-wide default used by loads() without an explicit limits argument.
static LoadLimits get_load_limits();
// Thread-safe write of the process-wide default. Prefer passing LoadLimits per call in multi-threaded code.
static void set_load_limits(LoadLimits limits);
// Static nil value, same as LuaVal(TNIL). Useful as a default const reference.
// Returns the string representation of the value, similar to lua tostring.
std::string tostring() const;
// Use as the hasher for containers, for example std::unordered_map<LuaVal, int, LuaVal::LuaValHasher>.
struct LuaValHasher
{
size_t operator()(LuaVal const & v) const;
};
typedef std::unordered_map<LuaVal, LuaVal> LuaTable;
// Circular reference memleak if insert self to self (deep copy on assign avoids sharing).
typedef std::unique_ptr<LuaTable> TblPtr;
LuaVal(const LuaTypeTag tag) : tag(tag), tbl_ptr(tag == TTABLE ? new LuaTable() : nullptr), d(0), b(false) {}
LuaVal() : tag(TTABLE), tbl_ptr(new LuaTable()), d(0), b(false) {}
LuaVal(const int d) : tag(TNUMBER), tbl_ptr(nullptr), d(d), b(false) {}
LuaVal(const unsigned int d) : tag(TNUMBER), tbl_ptr(nullptr), d(d), b(false) {}
LuaVal(const int64_t d) : tag(TNUMBER), tbl_ptr(nullptr), d(static_cast<double>(d)), b(false) {}
LuaVal(const float d) : tag(TNUMBER), tbl_ptr(nullptr), d(d), b(false) {}
LuaVal(const double d) : tag(TNUMBER), tbl_ptr(nullptr), d(d), b(false) {}
LuaVal(const std::string & s) : tag(TSTRING), tbl_ptr(nullptr), s(s), d(0), b(false) {}
LuaVal(const char * s) : tag(TSTRING), tbl_ptr(nullptr), s(s), d(0), b(false) {}
LuaVal(const bool b) : tag(TBOOL), tbl_ptr(nullptr), d(0), b(b) {}
LuaVal(LuaVal const & val) : tag(val.tag), tbl_ptr(val.tag == TTABLE ? val.tbl_ptr ? new LuaTable(*val.tbl_ptr) : new LuaTable() : nullptr), s(val.s), d(val.d), b(val.b) {}
LuaVal(LuaVal && val) noexcept : tag(std::move(val.tag)), tbl_ptr(std::move(val.tbl_ptr)), s(std::move(val.s)), d(std::move(val.d)), b(std::move(val.b))
{
if (val.tag == TTABLE)
val.tbl_ptr.reset(new LuaTable());
}
LuaVal(std::initializer_list<LuaVal> const & l);
template<typename T>
LuaVal(std::initializer_list<T> const & l) : tag(TTABLE), tbl_ptr(new LuaTable()), d(0), b(false)
{
InitializeSequence(l);
}
LuaVal(LuaTable const & l) : tag(TTABLE), tbl_ptr(new LuaTable(l)), d(0), b(false) {}
template<typename T>
LuaVal(std::forward_list<T> const & l) : tag(TTABLE), tbl_ptr(new LuaTable()), d(0), b(false)
{
InitializeSequence(l);
}
template<typename T>
LuaVal(std::deque<T> const & l) : tag(TTABLE), tbl_ptr(new LuaTable()), d(0), b(false)
{
InitializeSequence(l);
}
template<typename K, typename V>
LuaVal(std::unordered_map<K, V> const & l) : tag(TTABLE), tbl_ptr(new LuaTable()), d(0), b(false)
{
InitializeMap(l);
}
static LuaVal table() { return LuaVal(TTABLE); }
static LuaVal merge(LuaVal const & l, LuaVal const & r);
static LuaVal merge(LuaVal && l, LuaVal && r);
static LuaVal merge(LuaVal && l, LuaVal const & r);
static LuaVal merge(LuaVal const & l, LuaVal && r);
static LuaVal mrg(LuaVal const & l, LuaVal const & r) { return merge(l, r); }
static LuaVal mrg(LuaVal && l, LuaVal && r) { return merge(std::move(l), std::move(r)); }
static LuaVal mrg(LuaVal && l, LuaVal const & r) { return merge(std::move(l), r); }
static LuaVal mrg(LuaVal const & l, LuaVal && r) { return merge(l, std::move(r)); }
~LuaVal() = default;
bool isstring() const { return tag == TSTRING; }
bool isnumber() const { return tag == TNUMBER; }
bool istable() const { return tag == TTABLE; }
bool isbool() const { return tag == TBOOL; }
bool isnil() const { return tag == TNIL; }
// gettable; adds key-nil pair if not existing. nil key throws error.
// Inserts an empty table when the key is missing.
LuaVal & operator[](LuaVal const & k);
LuaVal const & operator[](LuaVal const & k) const;
// gettable; returns LuaVal::nil when the key is missing (same reference as static nil).
LuaVal const & get(LuaVal const & k) const;
LuaVal const & get(std::string const & k) const;
LuaVal const & get(int k) const;
LuaVal const & get(char const * k) const { return get(std::string(k)); }
LuaVal const & get(double k) const { return get(LuaVal(k)); }
// nullptr when the key is missing; otherwise points at the stored value (including nil).
LuaVal const * try_get(LuaVal const & k) const;
LuaVal const * try_get(std::string const & k) const;
LuaVal const * try_get(int k) const;
LuaVal const * find(LuaVal const & k) const { return try_get(k); }
// Throws when the key is missing; does not insert.
LuaVal & at(LuaVal const & k);
LuaVal const & at(LuaVal const & k) const;
LuaVal & at(std::string const & k);
LuaVal const & at(std::string const & k) const;
LuaVal & at(int k);
LuaVal const & at(int k) const;
// returns true if value was found with key
bool has(LuaVal const & k) const;
bool has(std::string const & k) const;
bool has(int k) const;
// Read-only nested lookup; never auto-vivifies. Empty path refers to this value.
LuaVal const * try_get_path(std::initializer_list<LuaVal> keys) const;
LuaVal const & get_path(std::initializer_list<LuaVal> keys) const;
LuaVal & at_path(std::initializer_list<LuaVal> keys);
LuaVal const & at_path(std::initializer_list<LuaVal> keys) const;
bool has_path(std::initializer_list<LuaVal> keys) const;
template<typename... Keys>
LuaVal const * try_get_path(Keys const &... keys) const
{
return try_get_path(std::initializer_list<LuaVal>{ LuaVal(keys)... });
}
template<typename... Keys>
LuaVal const & get_path(Keys const &... keys) const
{
return get_path(std::initializer_list<LuaVal>{ LuaVal(keys)... });
}
template<typename... Keys>
LuaVal & at_path(Keys const &... keys)
{
return at_path(std::initializer_list<LuaVal>{ LuaVal(keys)... });
}
template<typename... Keys>
LuaVal const & at_path(Keys const &... keys) const
{
return at_path(std::initializer_list<LuaVal>{ LuaVal(keys)... });
}
template<typename... Keys>
bool has_path(Keys const &... keys) const
{
return has_path(std::initializer_list<LuaVal>{ LuaVal(keys)... });
}
// Nested set/erase. set_path auto-vivifies missing intermediate tables; erase_path is a no-op when the path is missing.
LuaVal & set_path(std::initializer_list<LuaVal> keys, LuaVal const & v);
LuaVal & set_path(std::initializer_list<LuaVal> keys, LuaVal && v);
LuaVal & erase_path(std::initializer_list<LuaVal> keys);
template<typename... Keys>
LuaVal & erase_path(Keys const &... keys)
{
return erase_path(std::initializer_list<LuaVal>{ LuaVal(keys)... });
}
// settable; return self
LuaVal & set(LuaVal const & k, LuaVal const & v);
LuaVal & set(LuaVal const & k, LuaVal && v);
LuaVal & set(std::string const & k, LuaVal const & v);
LuaVal & set(std::string const & k, LuaVal && v);
LuaVal & set(std::string const & k, std::string const & v) { return set(k, LuaVal(v)); }
LuaVal & set(std::string const & k, char const * v) { return set(k, LuaVal(v)); }
LuaVal & set(char const * k, LuaVal const & v) { return set(std::string(k), v); }
LuaVal & set(char const * k, LuaVal && v) { return set(std::string(k), std::move(v)); }
LuaVal & set(char const * k, int v) { return set(std::string(k), LuaVal(v)); }
LuaVal & set(char const * k, double v) { return set(std::string(k), LuaVal(v)); }
LuaVal & set(char const * k, char const * v) { return set(std::string(k), LuaVal(v)); }
LuaVal & set(int k, LuaVal const & v);
LuaVal & set(int k, LuaVal && v);
LuaVal & set(int k, std::string const & v) { return set(k, LuaVal(v)); }
LuaVal & set(int k, char const * v) { return set(k, LuaVal(v)); }
LuaVal & set(double k, LuaVal const & v) { return set(LuaVal(k), v); }
LuaVal & set(double k, LuaVal && v) { return set(LuaVal(k), std::move(v)); }
// settable ignore if exists; return self
LuaVal & setignore(LuaVal const & k, LuaVal const & v);
LuaVal & setignore(LuaVal const & k, LuaVal && v);
// erase; return self
LuaVal & erase(LuaVal const & k);
LuaVal & rem(LuaVal const & k) { return erase(k); }
// table array size, not actual element count
unsigned int len() const;
// table.insert; return self
LuaVal & insert(LuaVal const & v, LuaVal const & pos = nil);
LuaVal & insert(LuaVal && v, LuaVal const & pos = nil);
LuaVal & insert(char const * v) { return insert(LuaVal(v)); }
// table.remove; return self
LuaVal & remove(LuaVal const & pos = nil);
// get a number value
double num() const;
// get a boolean value
bool boolean() const;
// get a string value
std::string const & str() const;
// get a table value
LuaTable const & tbl() const;
bool try_as_number(double & out) const;
bool try_as_string(std::string const *& out) const;
bool try_as_bool(bool & out) const;
// Returns a typetag, the internal identifier for each type.
LuaTypeTag typetag() const { return tag; }
// Returns the LuaVal's type as a string.
std::string type() const { return type(typetag()); }
// Returns the type tag's type as a string.
static std::string type(LuaTypeTag tag);
// Serializes the value into string.
// errmsg is optional; on failure an empty string is returned and errmsg is assigned (not appended).
std::string dumps(std::string* errmsg = nullptr) const;
std::string dumps_or_throw() const;
// Deserialize a string into a LuaVal.
// errmsg is optional; on failure nil is returned and errmsg is assigned (not appended).
static LuaVal loads(std::string const & string, std::string* errmsg = nullptr);
static LuaVal loads(std::string const & string, LoadLimits const & limits, std::string* errmsg = nullptr);
static LuaVal loads_or_throw(std::string const & string);
static LuaVal loads_or_throw(std::string const & string, LoadLimits const & limits);
bool operator==(LuaVal const& rhs) const;
bool operator!=(LuaVal const& rhs) const { return !(*this == rhs); }
// You can use !val to check for nil or false.
explicit operator bool() const;
LuaVal& operator=(LuaVal const& val);
LuaVal& operator=(LuaVal && val)
{
tag = std::move(val.tag);
tbl_ptr = std::move(val.tbl_ptr);
s = std::move(val.s);
d = std::move(val.d);
b = std::move(val.b);
if (val.tag == TTABLE)
val.tbl_ptr.reset(new LuaTable());
else
val.tbl_ptr = nullptr;
return *this;
}
private:
void InitializeSequence(std::initializer_list<LuaVal> const & l);
template<typename T>
void InitializeSequence(T const & l)
{
LuaTable & tbl = *tbl_ptr;
unsigned int i = 0;
for (auto const & v : l)
{
LuaVal vv(v);
if (vv.isnil())
++i;
else
tbl[++i] = std::move(vv);
}
}
template<typename T>
void InitializeMap(T const & l)
{
LuaTable & tbl = *tbl_ptr;
for (auto const & e : l)
{
LuaVal k(e.first);
LuaVal v(e.second);
if (!k.isnil() && !v.isnil())
tbl[std::move(k)] = std::move(v);
}
}
friend size_t LuaValHash(LuaVal const & v);
LuaTypeTag tag;
TblPtr tbl_ptr;
std::string s;
// int64_t i; // lua 5.3 support? Numbers are stored as double today.
double d;
bool b;
};
namespace lua_val {
inline LuaVal nil() { return LuaVal(TNIL); }
inline LuaVal number(double v) { return LuaVal(v); }
inline LuaVal number(int v) { return LuaVal(v); }
inline LuaVal number(int64_t v) { return LuaVal(v); }
inline LuaVal number(float v) { return LuaVal(v); }
inline LuaVal string(std::string const & v) { return LuaVal(v); }
inline LuaVal string(char const * v) { return LuaVal(v); }
inline LuaVal boolean(bool v) { return LuaVal(v); }
inline LuaVal table() { return LuaVal::table(); }
inline LuaVal array(std::initializer_list<LuaVal> const & items) { return LuaVal(items); }
LuaVal map(std::initializer_list<std::pair<LuaVal, LuaVal>> const & entries);
} // namespace lua_val
#endif