-
-
Notifications
You must be signed in to change notification settings - Fork 988
/
simple_wml.hpp
310 lines (237 loc) · 8.03 KB
/
simple_wml.hpp
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
/*
Copyright (C) 2008 - 2018 by David White <dave@whitevine.net>
Part of the Battle for Wesnoth Project https://www.wesnoth.org
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License 2
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#pragma once
#include <cstring>
#include <cstddef>
#include <iosfwd>
#include <map>
#include <string>
#include <vector>
#include "exceptions.hpp"
namespace simple_wml {
struct error : public game::error {
error(const char* msg);
};
class string_span
{
public:
string_span() : str_(nullptr), size_(0)
{}
string_span(const char* str, int size) : str_(str), size_(size)
{}
string_span(const char* str) : str_(str), size_(strlen(str))
{}
string_span(const char* begin, const char* end) : str_(begin), size_(end - begin)
{}
typedef const char* const_iterator;
typedef const char* iterator;
typedef const char value_type;
bool operator==(const char* o) const {
const char* i1 = str_;
const char* i2 = str_ + size_;
while(i1 != i2 && *o && *i1 == *o) {
++i1;
++o;
}
return i1 == i2 && *o == 0;
}
bool operator!=(const char* o) const {
return !operator==(o);
}
bool operator==(const std::string& o) const {
return size_ == o.size() && memcmp(str_, o.data(), size_) == 0;
}
bool operator!=(const std::string& o) const {
return !operator==(o);
}
bool operator==(const string_span& o) const {
return size_ == o.size_ && memcmp(str_, o.str_, size_) == 0;
}
bool operator!=(const string_span& o) const {
return !operator==(o);
}
bool operator<(const string_span& o) const {
const int len = size_ < o.size_ ? size_ : o.size_;
for(int n = 0; n < len; ++n) {
if(str_[n] != o.str_[n]) {
if(str_[n] < o.str_[n]) {
return true;
} else {
return false;
}
}
}
return size_ < o.size_;
}
const char* begin() const { return str_; }
const char* end() const { return str_ + size_; }
int size() const { return size_; }
bool empty() const { return size_ == 0; }
bool is_null() const { return str_ == nullptr; }
bool to_bool(bool default_value=false) const;
int to_int() const;
std::string to_string() const;
//returns a duplicate of the string span in a new[] allocated buffer
char* duplicate() const;
private:
const char* str_;
unsigned int size_;
};
std::ostream& operator<<(std::ostream& o, const string_span& s);
class document;
class node
{
public:
node(document& doc, node* parent);
node(document& doc, node* parent, const char** str, int depth=0);
~node();
struct attribute
{
attribute(const string_span& k, const string_span& v) : key(k), value(v) {}
string_span key;
string_span value;
};
typedef std::vector<node*> child_list;
const string_span& operator[](const char* key) const;
const string_span& attr(const char* key) const {
return (*this)[key];
}
bool has_attr(const char* key) const;
//sets an attribute in the WML node. The node will keep a direct reference
//to key and value which it will maintain for its lifetime. The caller
//MUST guarantee that the key and value buffers remain valid for the
//lifetime of the node.
node& set_attr(const char* key, const char* value);
//functions which are identical to set_attr() except that the buffer
//referred to by 'value' will be duplicated and the new buffer managed by
//the node. The caller may destroy the value buffer as soon as the function
//returns. The key buffer must remain valid for the lifetime of the node.
node& set_attr_dup(const char* key, const char* value);
node& set_attr_dup(const char* key, const string_span& value);
node& set_attr_int(const char* key, int value);
node& add_child(const char* name);
node& add_child_at(const char* name, std::size_t index);
void remove_child(const char* name, std::size_t index);
void remove_child(const string_span& name, std::size_t index);
node* child(const char* name);
const node* child(const char* name) const;
const child_list& children(const char* name) const;
const string_span& first_child() const;
bool is_dirty() const { return output_cache_.is_null(); }
enum CACHE_STATUS { REFRESH_CACHE, DO_NOT_MODIFY_CACHE };
int output_size() const;
void output(char*& buf, CACHE_STATUS status=DO_NOT_MODIFY_CACHE);
void copy_into(node& n) const;
bool no_children() const { return children_.empty(); }
bool one_child() const { return children_.size() == 1 && children_.begin()->second.size() == 1; }
void apply_diff(const node& diff);
void set_doc(document* doc);
int nchildren() const;
int nattributes_recursive() const;
private:
node(const node&);
void operator=(const node&);
int get_children(const string_span& name);
int get_children(const char* name);
void set_dirty();
void shift_buffers(ptrdiff_t offset);
document* doc_;
typedef std::vector<attribute> attribute_list;
attribute_list attr_;
node* parent_;
typedef std::pair<string_span, child_list> child_pair;
typedef std::vector<child_pair> child_map;
static child_map::const_iterator find_in_map(const child_map& m, const string_span& attr);
static child_map::iterator find_in_map(child_map& m, const string_span& attr);
child_map children_;
//a node position indicates the index into the child map where the node
//is, and then the index into the child list within where the node is.
struct node_pos {
node_pos(int child_map_index, int child_list_index)
: child_map_index(static_cast<unsigned short>(child_map_index)),
child_list_index(static_cast<unsigned short>(child_list_index))
{}
unsigned short child_map_index;
unsigned short child_list_index;
};
//a list of all the children in order.
std::vector<node_pos> ordered_children_;
void insert_ordered_child(int child_map_index, int child_list_index);
void remove_ordered_child(int child_map_index, int child_list_index);
void insert_ordered_child_list(int child_map_index);
void remove_ordered_child_list(int child_map_index);
void check_ordered_children() const;
string_span output_cache_;
};
std::string node_to_string(const node& n);
enum INIT_BUFFER_CONTROL { INIT_TAKE_OWNERSHIP };
enum INIT_STATE { INIT_COMPRESSED, INIT_STATIC };
class document
{
public:
document();
explicit document(char* buf, INIT_BUFFER_CONTROL control=INIT_TAKE_OWNERSHIP);
document(const char* buf, INIT_STATE state);
explicit document(string_span compressed_buf);
~document();
const char* dup_string(const char* str);
node& root() { if(!root_) { generate_root(); } return *root_; }
const node& root() const { if(!root_) { const_cast<document*>(this)->generate_root(); } return *root_; }
const char* output();
string_span output_compressed(bool bzip2 = false);
void compress();
document* clone();
const string_span& operator[](const char* key) const {
return root()[key];
}
const string_span& attr(const char* key) const {
return root()[key];
}
node* child(const char* name) {
return root().child(name);
}
const node* child(const char* name) const {
return root().child(name);
}
const node::child_list& children(const char* name) const {
return root().children(name);
}
node& set_attr(const char* key, const char* value) {
return root().set_attr(key, value);
}
node& set_attr_dup(const char* key, const char* value) {
return root().set_attr_dup(key, value);
}
void take_ownership_of_buffer(char* buffer) {
buffers_.push_back(buffer);
}
void swap(document& o);
void clear();
static std::string stats();
static std::size_t document_size_limit;
private:
void generate_root();
document(const document&);
void operator=(const document&);
string_span compressed_buf_;
const char* output_;
std::vector<char*> buffers_;
node* root_;
//linked list of documents for accounting purposes
void attach_list();
void detach_list();
document* prev_;
document* next_;
};
/** Implement non-member swap function for std::swap (calls @ref document::swap). */
void swap(document& lhs, document& rhs);
}