-
Notifications
You must be signed in to change notification settings - Fork 0
/
nrs.h
169 lines (140 loc) · 4.62 KB
/
nrs.h
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
#ifndef NRS_H
#define NRS_H
#include <cstdint>
#include <vector>
#include <string>
#include <functional>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <stack>
#include <set>
//
// Based on savefile class courtesy of OneLoneCoder video:
//
// https://www.youtube.com/watch?v=jlS1Y2-yKV0
//
// Actual class is a part of his custom game engine:
//
// https://github.com/OneLoneCoder/olcPixelGameEngine/blob/master/utilities/olcUTIL_DataFile.h
//
// I modified the author's implementation a little bit to suit my personal code
// style by doing .h/.cpp splitting and removing of static methods. Also I made
// the format a little more JSON-like. Thus it can be made into oneliner
// and we can save space on whitespaces (lol) about 50% in the filesize
// department.
//
// The storage format looks like this:
//
// -----------------------------------------------------------------------------
// object1 : {
// name : "Dummy Actor",
// hitpoints : 40,
// skills : {
// sword : 1,
// armor : 2,
// athletics : 3,
// },
// inventory : item1/item2/item3/"it,e/m",
// },
// -----------------------------------------------------------------------------
//
// All items are key/value string pairs.
// It is possible to have a list of items in a value by separating items
// with '/'. If list item itself contains '/' and/or ',' then the whole item
// will be enclosed in quotes, as it is shown in the example above.
//
class NRS
{
public:
void SetString(const std::string& s, size_t index = 0);
const std::string& GetString(size_t index = 0) const;
void SetInt(int64_t value, size_t index = 0);
int64_t GetInt(size_t index = 0) const;
void SetUInt(uint64_t value, size_t index = 0);
uint64_t GetUInt(size_t index = 0) const;
void Clear();
size_t ValuesCount() const;
size_t ChildrenCount() const;
bool Has(const std::string& nodeName);
NRS& operator[](const std::string& nodeName);
//
// For more readable access to inner elements, e.g. "object.inventory"
// instead of ["object"]["inventory"]
//
NRS& GetNode(const std::string& path);
std::string ToStringObject();
void FromStringObject(const std::string& so);
bool CheckSyntax(const std::string& so);
enum class LoadResult
{
//
// Using just 'OK' will clash with ncurses '#define OK' constant.
// Talking about global namespace pollution...
//
LOAD_OK = 0,
INVALID_FORMAT,
ERROR
};
static const char* LoadResultToString(LoadResult res);
LoadResult Load(const std::string& fname);
std::string ToPrettyString();
std::string DumpObjectStructureToString();
private:
std::string MakeOneliner(const std::string& stringObject);
void WriteIntl(const NRS& d, std::stringstream& ss);
void DriveStateMachine(const char currentChar, bool debug = false);
//
// Contains value elements (after ':' symbol).
//
std::vector<std::string> _content;
//
// We need to preserve serial order of data nodes added to our save data
// instance, so we use vector. We can't use map since it's sorted by string
// and we cannot use unordered_map since it doesn't guarantee order at all.
// But we still need to access our nodes somehow. So the only way to do this
// while preserving serial order is by using indices in a vector.
// And since we're using indices there's no danger of "losing" access due to
// vector's relocation in memory because we don't rely on object addresses,
// just on their serial order.
// Also "humans like to group things", so we can put data in a file in
// whatever order that seems comfortable for us.
//
std::vector<std::pair<std::string, NRS>> _children;
//
// Mapping of children node names to their respective index
// in a vector of children.
//
std::unordered_map<std::string, size_t> _childIndexByName;
//
// For writing to file.
//
size_t _currentIndent = 0;
const std::string kEmptyString;
const std::string _unwantedCharacters = " \t\n\r\f\v";
enum class ParsingState
{
UNDEFINED = -1,
READING_KEY,
KEY_DONE,
READING_VALUE,
VALUE_DONE,
READING_VALUE_Q,
VALUE_Q_DONE,
READING_LIST,
READING_OBJECT,
OBJECT_DONE,
//
// Same shit with 'OK' here as in LoadingResult case.
//
PARSING_OK,
ERROR
};
//
// For checking syntax.
//
size_t _parsingScopeCount = 0;
ParsingState _parsingState = ParsingState::UNDEFINED;
const std::set<char> _transitionChars = { ':', '{', '}', ',', '"', '/' };
};
#endif // NRS_H