20 #include <boost/iostreams/copy.hpp>
21 #include <boost/iostreams/filtering_stream.hpp>
22 #include <boost/iostreams/filter/bzip2.hpp>
23 #include <boost/iostreams/filter/counter.hpp>
24 #include <boost/iostreams/filter/gzip.hpp>
31 #define ERR_SWML LOG_STREAM(err, log_config)
37 void debug_delete(
node*
n) {
43 int nalloc = input.
size();
48 boost::iostreams::filtering_stream<boost::iostreams::input>
filter;
51 filter.push(boost::iostreams::bzip2_decompressor());
53 filter.push(boost::iostreams::gzip_decompressor());
58 const size_t chunk_size = input.
size() * 10;
60 std::vector<char>
buf(chunk_size);
64 while(filter.good() && (len = filter.read(&buf[pos], chunk_size).gcount()) == chunk_size) {
65 if(pos + chunk_size > 40000000) {
66 throw error(
"WML document exceeds 40MB limit");
70 buf.resize(pos + chunk_size);
74 if(!filter.eof() && !filter.good()) {
75 throw error(
"failed to uncompress");
85 char* small_out =
new char[pos+1];
86 memcpy(small_out, &buf[0], pos);
94 }
catch (std::bad_alloc&
e) {
95 ERR_SWML <<
"ERROR: bad_alloc caught in uncompress_buffer() state "
96 << state <<
" alloc bytes " << nalloc <<
" with input: '"
97 << input <<
"' " << e.what() << std::endl;
98 throw error(
"Bad allocation request in uncompress_buffer().");
102 char* compress_buffer(
const char* input,
string_span* span,
bool bzip2)
104 int nalloc = strlen(input);
109 std::istringstream istream(in);
111 boost::iostreams::filtering_stream<boost::iostreams::output>
filter;
114 filter.push(boost::iostreams::bzip2_compressor());
116 filter.push(boost::iostreams::gzip_compressor());
119 nalloc = in.size()*2 + 80;
120 std::vector<char>
buf(nalloc);
121 boost::iostreams::array_sink out(&buf[0], buf.size());
127 boost::iostreams::copy(istream, filter, buf.size());
129 assert(len < 128*1024*1024);
130 if((!filter.eof() && !filter.good()) || len == static_cast<int>(buf.size())) {
131 throw error(
"failed to compress");
139 char* small_out =
new char[
len];
140 memcpy(small_out, &buf[0], len);
144 assert(*small_out == (bzip2 ?
'B' : 31));
147 }
catch (std::bad_alloc&
e) {
148 ERR_SWML <<
"ERROR: bad_alloc caught in compress_buffer() state "
149 << state <<
" alloc bytes " << nalloc <<
" with input: '"
150 << input <<
"' " << e.what() << std::endl;
151 throw error(
"Bad allocation request in compress_buffer().");
160 return default_value;
163 if (
operator==(
"no") ||
operator==(
"off") ||
operator==(
"false") ||
operator==(
"0") ||
operator==(
"0.0"))
171 const int buf_size = 64;
172 if(
size() >= buf_size) {
188 char*
buf =
new char[
size() + 1];
197 ERR_SWML <<
"ERROR: '" << msg <<
"'" << std::endl;
217 #pragma warning (push)
218 #pragma warning (disable: 4706)
229 throw error(
"elements nested too deep");
232 const char*&
s = *str;
234 const char*
const begin =
s;
242 throw error(
"end element unterminated");
250 const char*
end = strchr(s,
']');
252 throw error(
"unterminated element");
260 children_[list_index].second.push_back(
new node(doc,
this, str, depth+1));
274 throw error(
"did not find newline after '#'");
278 const char*
end = strchr(s,
'=');
280 ERR_SWML <<
"attribute: " << s << std::endl;
281 throw error(
"did not find '=' after attribute");
289 throw error(
"did not find '\"' after '_'");
294 end = strchr(s,
'\n');
296 ERR_SWML <<
"ATTR: '" << name <<
"' (((" << s <<
")))" << std::endl;
297 throw error(
"did not find end of attribute");
299 if (memchr(s,
'"', end - s))
300 throw error(
"found stray quotes in unquoted value");
307 while((end = strchr(end+1,
'"')) && end[1] ==
'"') {
309 #pragma warning (pop)
314 throw error(
"did not find end of attribute");
317 const char *endline = end + 1;
318 while (*endline ==
' ') ++endline;
319 if (*endline ==
'\n')
break;
322 if (*(endline++) !=
'+')
323 throw error(
"did not find newline after end of attribute");
324 if (*(endline++) !=
'\n')
325 throw error(
"did not find newline after '+'");
328 if (*endline ==
'#') {
329 endline = strchr(endline + 1,
'\n');
331 throw error(
"did not find newline after '#'");
336 while (*endline ==
'\t') ++endline;
337 if (*endline ==
'_') ++endline;
339 throw error(
"did not find quotes after '+'");
348 ERR_SWML <<
"attributes: '" <<
attr_.back().first <<
"' < '" << name <<
"'" << std::endl;
349 throw error(
"attributes not in order");
373 struct string_span_pair_comparer
385 return a.first < b.first;
394 std::pair<attribute_list::const_iterator,
395 attribute_list::const_iterator>
range = std::equal_range(
attr_.begin(),
attr_.end(),
span, string_span_pair_comparer());
396 if(range.first != range.second) {
397 return range.first->second;
406 std::pair<attribute_list::const_iterator,
407 attribute_list::const_iterator>
range = std::equal_range(
attr_.begin(),
attr_.end(),
span, string_span_pair_comparer());
408 return range.first != range.second;
417 attribute_list::iterator>
range = std::equal_range(
attr_.begin(),
attr_.end(),
span, string_span_pair_comparer());
418 if(range.first != range.second) {
442 sprintf(buf,
"%d", value);
452 if(index > list.size()) {
472 list.push_back(
new node(*
doc_,
this));
489 if(index >= list.size()) {
495 debug_delete(list[index]);
496 list.erase(list.begin() +
index);
506 bool inserted =
false;
509 if(i->child_map_index == child_map_index && i->child_list_index > child_list_index) {
510 i->child_list_index++;
511 }
else if(i->child_map_index == child_map_index && i->child_list_index == child_list_index) {
513 i->child_list_index++;
531 if(i->child_map_index == child_map_index && i->child_list_index == child_list_index) {
535 if(i->child_map_index == child_map_index && i->child_list_index > child_list_index) {
536 i->child_list_index--;
542 assert(erase_count == 1);
549 if(i->child_map_index >= child_map_index) {
550 i->child_map_index++;
559 if(i->child_map_index == child_map_index) {
563 if(i->child_map_index > child_map_index) {
564 i->child_map_index--;
575 #ifdef CHECK_ORDERED_CHILDREN
578 assert(i->child_map_index <
children_.size());
579 assert(i->child_list_index <
children_[i->child_map_index].second.size());
584 const unsigned short child_map_index = j -
children_.begin();
585 for(child_list::const_iterator k = j->second.begin(); k != j->second.end(); ++k) {
586 const unsigned short child_list_index = k - j->second.begin();
599 #endif // CHECK_ORDERED_CHILDREN
610 if(
i->first == name) {
611 assert(
i->second.empty() ==
false);
612 return i->second.front();
622 if(
i->first == name) {
623 if(
i->second.empty()) {
626 return i->second.front();
637 if(
i->first == name) {
654 if(
i->first == name) {
665 child_map::const_iterator
i = m.begin();
666 for(; i != m.end(); ++
i) {
667 if(i->first == attr) {
678 for(; i != m.end(); ++
i) {
679 if(i->first == attr) {
705 for(attribute_list::const_iterator
i =
attr_.begin();
i !=
attr_.end(); ++
i) {
706 res +=
i->first.size() +
i->second.size() + 4;
709 size_t count_children = 0;
711 for(child_list::const_iterator j =
i->second.begin(); j !=
i->second.end(); ++j) {
712 res +=
i->first.size()*2 + 7;
713 res += (*j)->output_size();
738 (*j)->shift_buffers(offset);
757 memcpy(buf,
i->first.begin(),
i->first.size());
759 buf +=
i->first.size();
762 memcpy(buf,
i->second.begin(),
i->second.size());
764 buf +=
i->second.size();
771 assert(
i->child_map_index <
children_.size());
772 assert(
i->child_list_index <
children_[
i->child_map_index].second.size());
775 memcpy(buf, attr.begin(), attr.size());
780 children_[
i->child_map_index].second[
i->child_list_index]->output(buf, cache_status);
783 memcpy(buf, attr.
begin(), attr.size());
798 node& mutable_node =
const_cast<node&
>(
n);
802 assert(ptr == &
v[0] +
v.size());
809 for(attribute_list::const_iterator
i =
attr_.begin();
i !=
attr_.end(); ++
i) {
810 char* key =
i->first.duplicate();
811 char*
value =
i->second.duplicate();
819 assert(
i->child_map_index <
children_.size());
820 assert(
i->child_list_index <
children_[
i->child_map_index].second.size());
830 const node* inserts = diff.
child(
"insert");
831 if(inserts !=
nullptr) {
832 for(attribute_list::const_iterator
i = inserts->
attr_.begin();
i != inserts->
attr_.end(); ++
i) {
833 char*
name =
i->first.duplicate();
834 char*
value =
i->second.duplicate();
841 const node* deletes = diff.
child(
"delete");
842 if(deletes !=
nullptr) {
843 for(attribute_list::const_iterator
i = deletes->
attr_.begin();
i != deletes->
attr_.end(); ++
i) {
845 attribute_list::iterator>
range = std::equal_range(
attr_.begin(),
attr_.end(),
i->first, string_span_pair_comparer());
846 if(range.first != range.second) {
847 attr_.erase(range.first);
853 for(child_list::const_iterator
i = child_changes.begin();
i != child_changes.end(); ++
i) {
854 const size_t index = (**i)[
"index"].to_int();
855 for(child_map::const_iterator j = (*i)->children_.begin(); j != (*i)->children_.end(); ++j) {
857 for(child_list::const_iterator k = j->second.begin(); k != j->second.end(); ++k) {
861 itor->second[
index]->apply_diff(**k);
869 for(child_list::const_iterator
i = child_inserts.begin();
i != child_inserts.end(); ++
i) {
870 const size_t index = (**i)[
"index"].to_int();
871 for(child_map::const_iterator j = (*i)->children_.begin(); j != (*i)->children_.end(); ++j) {
873 for(child_list::const_iterator k = j->second.begin(); k != j->second.end(); ++k) {
882 for(child_list::const_iterator
i = child_deletes.begin();
i != child_deletes.end(); ++
i) {
883 const size_t index = (**i)[
"index"].to_int();
884 for(child_map::const_iterator j = (*i)->children_.begin(); j != (*i)->children_.end(); ++j) {
885 if(j->second.empty()) {
910 for(child_list::const_iterator j =
i->second.begin(); j !=
i->second.end(); ++j) {
912 res += (*j)->nchildren();
923 for(child_list::const_iterator j =
i->second.begin(); j !=
i->second.end(); ++j) {
924 res += (*j)->nattributes_recursive();
942 root_(new
node(*this, nullptr)),
960 const char* cbuf =
buf;
985 compressed_buf_(compressed_buf),
993 buffers_.push_back(uncompress_buffer(compressed_buf, &uncompressed_buf));
1014 debug_delete(
root_);
1021 const int len = strlen(str);
1022 char*
res =
new char[len+1];
1023 memcpy(res, str, len + 1);
1037 output_ = uncompressed_buf.begin();
1044 std::vector<char*>
bufs;
1050 buf =
new char[buf_size];
1051 }
catch (std::bad_alloc&
e) {
1052 ERR_SWML <<
"ERROR: Trying to allocate " << buf_size <<
" bytes. "
1053 << e.what() << std::endl;
1054 throw error(
"Bad allocation request in output().");
1061 assert(buf ==
output_ + buf_size);
1089 debug_delete(
root_);
1092 std::vector<char*> new_buffers;
1097 new_buffers.push_back(*
i);
1111 output_ = uncompressed_buf.begin();
1114 assert(
root_ ==
nullptr);
1116 root_ =
new node(*
this,
nullptr, &cbuf);
1121 char*
buf =
new char[strlen(
output())+1];
1141 debug_delete(
root_);
1167 if(head_doc ==
this) {
1183 std::ostringstream
s;
1185 int ncompressed = 0;
1186 int compressed_size = 0;
1192 int nattributes = 0;
1193 for(
document*
d = head_doc;
d !=
nullptr;
d =
d->next_) {
1195 nbuffers +=
d->buffers_.size();
1197 if(
d->compressed_buf_.is_null() ==
false) {
1199 compressed_size +=
d->compressed_buf_.size();
1204 text_size += strlen(
d->output_);
1208 nnodes += 1 +
d->root_->nchildren();
1209 nattributes +=
d->root_->nattributes_recursive();
1212 if(
d->root_ &&
d->root_->is_dirty()) {
1217 const int nodes_alloc = nnodes*(
sizeof(
node) + 12);
1218 const int attr_alloc = nattributes*(
sizeof(
string_span)*2);
1219 const int total_alloc = compressed_size + text_size + nodes_alloc + attr_alloc;
1221 s <<
"WML documents: " << ndocs <<
"\n"
1222 <<
"Dirty: " << ndirty <<
"\n"
1223 <<
"With compression: " << ncompressed <<
" (" << compressed_size
1225 <<
"With text: " << ntext <<
" (" << text_size
1227 <<
"Nodes: " << nnodes <<
" (" << nodes_alloc <<
" bytes)\n"
1228 <<
"Attr: " << nattributes <<
" (" << attr_alloc <<
" bytes)\n"
1229 <<
"Buffers: " << nbuffers <<
"\n"
1230 <<
"Total allocation: " << total_alloc <<
" bytes\n";
1237 #ifdef UNIT_TEST_SIMPLE_WML
1239 int main(
int argc,
char** argv)
1241 char* doctext = strdup(
1248 std::cerr << doctext <<
"\n";
1254 assert((*test_node)[
"a"] ==
"blah");
1255 assert((*test_node)[
"b"] ==
"blah");
1256 assert((*test_node)[
"c"] ==
"\\\\");
1257 assert((*test_node)[
"d"] ==
"\\\"");
1261 std::cerr << doc.
output();
void remove_ordered_child(int child_map_index, int child_list_index)
node & add_child(const char *name)
string_span compressed_buf_
string_span output_compressed(bool bzip2=false)
std::ostream & operator<<(std::ostream &o, const string_span &s)
GLint GLint GLsizei GLsizei GLsizei depth
void apply_diff(const node &diff)
std::string to_string() const
GLenum GLenum GLvoid GLvoid GLvoid * span
void shift_buffers(ptrdiff_t offset)
void output(char *&buf, CACHE_STATUS status=DO_NOT_MODIFY_CACHE)
node(document &doc, node *parent)
void insert_ordered_child(int child_map_index, int child_list_index)
static l_noret error(LoadState *S, const char *why)
string_span output_cache_
static child_map::const_iterator find_in_map(const child_map &m, const string_span &attr)
std::vector< char * > buffers_
std::pair< string_span, child_list > child_pair
GLenum GLenum GLenum input
int nattributes_recursive() const
node & set_attr_int(const char *key, int value)
const char * dup_string(const char *str)
node & set_attr(const char *key, const char *value)
node & add_child_at(const char *name, size_t index)
int get_children(const string_span &name)
const string_span & first_child() const
std::vector< child_pair > child_map
void insert_ordered_child_list(int child_map_index)
GLdouble GLdouble GLdouble b
node * child(const char *name)
void remove_child(const char *name, size_t index)
GLsizei const GLfloat * value
GLboolean GLboolean GLboolean GLboolean a
bool has_attr(const char *key) const
GLenum GLuint GLsizei const char * buf
void remove_ordered_child_list(int child_map_index)
const string_span & operator[](const char *key) const
static std::string stats()
std::map< std::string, tfilter >::iterator itor
node & set_attr_dup(const char *key, const char *value)
std::vector< node * > child_list
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
void copy_into(node &n) const
const child_list & children(const char *name) const
bool to_bool(bool default_value=false) const
int main(int argc, char **argv)
const char * begin() const
GLuint const GLchar * name
void check_ordered_children() const
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
std::pair< string_span, string_span > attribute
void swap(game_board &one, game_board &other)
Standard logging facilities (interface).
void set_doc(document *doc)
std::vector< node_pos > ordered_children_
GLsizei const GLcharARB ** string
void take_ownership_of_buffer(char *buffer)
const string_span & attr(const char *key) const
std::string node_to_string(const node &n)
static lg::log_domain log_config("config")