The Battle for Wesnoth  1.13.11+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
simple_wml.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2018 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License 2
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #include <iostream>
16 #include <sstream>
17 
18 #include <boost/iostreams/copy.hpp>
19 #include <boost/iostreams/filtering_stream.hpp>
20 #include <boost/iostreams/filter/bzip2.hpp>
21 #include <boost/iostreams/filter/counter.hpp>
22 #include <boost/iostreams/filter/gzip.hpp>
23 
24 #include "server/simple_wml.hpp"
25 
26 #include "log.hpp"
27 
28 static lg::log_domain log_config("config");
29 #define ERR_SWML LOG_STREAM(err, log_config)
30 
31 namespace simple_wml {
32 
33 size_t document::document_size_limit = 40000000;
34 
35 namespace {
36 
37 void debug_delete(node* n) {
38  delete n;
39 }
40 
41 char* uncompress_buffer(const string_span& input, string_span* span)
42 {
43  int nalloc = input.size();
44  int state = 0;
45  try {
46  std::istringstream stream(std::string(input.begin(), input.end()));
47  state = 1;
48  boost::iostreams::filtering_stream<boost::iostreams::input> filter;
49  state = 2;
50  if (!span->empty() && *span->begin() == 'B') {
51  filter.push(boost::iostreams::bzip2_decompressor());
52  } else {
53  filter.push(boost::iostreams::gzip_decompressor());
54  }
55  filter.push(stream);
56  state = 3;
57 
58  const size_t chunk_size = input.size() * 10;
59  nalloc = chunk_size;
60  std::vector<char> buf(chunk_size);
61  state = 4;
62  size_t len = 0;
63  size_t pos = 0;
64  while(filter.good() && (len = filter.read(&buf[pos], chunk_size).gcount()) == chunk_size) {
65  if(pos + chunk_size > document::document_size_limit) {
66  throw error("WML document exceeded size limit during decompression");
67  }
68 
69  pos += len;
70  buf.resize(pos + chunk_size);
71  len = 0;
72  }
73 
74  if(!filter.eof() && !filter.good()) {
75  throw error("failed to uncompress");
76  }
77 
78  pos += len;
79  state = 5;
80  nalloc = pos;
81 
82  buf.resize(pos);
83  state = 6;
84 
85  char* small_out = new char[pos+1];
86  memcpy(small_out, &buf[0], pos);
87  state = 7;
88 
89  small_out[pos] = 0;
90 
91  *span = string_span(small_out, pos);
92  state = 8;
93  return small_out;
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().");
99  }
100 }
101 
102 char* compress_buffer(const char* input, string_span* span, bool bzip2)
103 {
104  int nalloc = strlen(input);
105  int state = 0;
106  try {
107  std::string in(input);
108  state = 1;
109  std::istringstream istream(in);
110  state = 2;
111  boost::iostreams::filtering_stream<boost::iostreams::output> filter;
112  state = 3;
113  if (bzip2) {
114  filter.push(boost::iostreams::bzip2_compressor());
115  } else {
116  filter.push(boost::iostreams::gzip_compressor());
117  }
118  state = 4;
119  nalloc = in.size()*2 + 80;
120  std::vector<char> buf(nalloc);
121  boost::iostreams::array_sink out(&buf[0], buf.size());
122  filter.push(boost::iostreams::counter());
123  filter.push(out);
124 
125  state = 5;
126 
127  boost::iostreams::copy(istream, filter, buf.size());
128  const int len = filter.component<boost::iostreams::counter>(1)->characters();
129  assert(len < 128*1024*1024);
130  if((!filter.eof() && !filter.good()) || len == static_cast<int>(buf.size())) {
131  throw error("failed to compress");
132  }
133  state = 6;
134  nalloc = len;
135 
136  buf.resize(len);
137  state = 7;
138 
139  char* small_out = new char[len];
140  memcpy(small_out, &buf[0], len);
141  state = 8;
142 
143  *span = string_span(small_out, len);
144  assert(*small_out == (bzip2 ? 'B' : 31));
145  state = 9;
146  return small_out;
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().");
152  }
153 }
154 
155 } // namespace
156 
157 bool string_span::to_bool(bool default_value) const
158 {
159  if(empty()) {
160  return default_value;
161  }
162 
163  if (operator==("no") || operator==("off") || operator==("false") || operator==("0") || operator==("0.0"))
164  return false;
165 
166  return true;
167 }
168 
170 {
171  const int buf_size = 64;
172  if(size() >= buf_size) {
173  return 0;
174  }
175  char buf[64];
176  memcpy(buf, begin(), size());
177  buf[size()] = 0;
178  return atoi(buf);
179 }
180 
182 {
183  return std::string(begin(), end());
184 }
185 
187 {
188  char* buf = new char[size() + 1];
189  memcpy(buf, begin(), size());
190  buf[size()] = 0;
191  return buf;
192 }
193 
194 error::error(const char* msg)
195  : game::error(msg)
196 {
197  ERR_SWML << "ERROR: '" << msg << "'" << std::endl;
198 }
199 
200 std::ostream& operator<<(std::ostream& o, const string_span& s)
201 {
202  o << std::string(s.begin(), s.end());
203  return o;
204 }
205 
206 node::node(document& doc, node* parent) :
207  doc_(&doc),
208  attr_(),
209  parent_(parent),
210  children_(),
211  ordered_children_(),
212  output_cache_()
213 {
214 }
215 
216 #ifdef _MSC_VER
217 #pragma warning (push)
218 #pragma warning (disable: 4706)
219 #endif
220 node::node(document& doc, node* parent, const char** str, int depth) :
221  doc_(&doc),
222  attr_(),
223  parent_(parent),
224  children_(),
225  ordered_children_(),
226  output_cache_()
227 {
228  if(depth >= 1000) {
229  throw error("elements nested too deep");
230  }
231 
232  const char*& s = *str;
233 
234  const char* const begin = s;
235  while(*s) {
236  switch(*s) {
237  case '[': {
238  if(s[1] == '/') {
239  output_cache_ = string_span(begin, s - begin);
240  s = strchr(s, ']');
241  if(s == nullptr) {
242  throw error("end element unterminated");
243  }
244 
245  ++s;
246  return;
247  }
248 
249  ++s;
250  const char* end = strchr(s, ']');
251  if(end == nullptr) {
252  throw error("unterminated element");
253  }
254 
255  const int list_index = get_children(string_span(s, end - s));
257 
258  s = end + 1;
259 
260  children_[list_index].second.push_back(new node(doc, this, str, depth+1));
261  ordered_children_.emplace_back(list_index, children_[list_index].second.size() - 1);
263 
264  break;
265  }
266  case ' ':
267  case '\t':
268  case '\n':
269  ++s;
270  break;
271  case '#':
272  s = strchr(s, '\n');
273  if(s == nullptr) {
274  throw error("did not find newline after '#'");
275  }
276  break;
277  default: {
278  const char* end = strchr(s, '=');
279  if(end == nullptr) {
280  ERR_SWML << "attribute: " << s << std::endl;
281  throw error("did not find '=' after attribute");
282  }
283 
284  string_span name(s, end - s);
285  s = end + 1;
286  if(*s == '_') {
287  s = strchr(s, '"');
288  if(s == nullptr) {
289  throw error("did not find '\"' after '_'");
290  }
291  }
292 
293  if (*s != '"') {
294  end = strchr(s, '\n');
295  if (!end) {
296  ERR_SWML << "ATTR: '" << name << "' (((" << s << ")))" << std::endl;
297  throw error("did not find end of attribute");
298  }
299  if (memchr(s, '"', end - s))
300  throw error("found stray quotes in unquoted value");
301  goto read_attribute;
302  }
303  end = s;
304  for(;;)
305  {
306  // Read until the first single double quote.
307  while((end = strchr(end+1, '"')) && end[1] == '"') {
308 #ifdef _MSC_VER
309 #pragma warning (pop)
310 #endif
311  ++end;
312  }
313  if(end == nullptr)
314  throw error("did not find end of attribute");
315 
316  // Stop if newline.
317  const char *endline = end + 1;
318  while (*endline == ' ') ++endline;
319  if (*endline == '\n') break;
320 
321  // Read concatenation marker.
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 '+'");
326 
327  // Read textdomain marker.
328  if (*endline == '#') {
329  endline = strchr(endline + 1, '\n');
330  if (!endline)
331  throw error("did not find newline after '#'");
332  ++endline;
333  }
334 
335  // Read indentation and start of string.
336  while (*endline == '\t') ++endline;
337  if (*endline == '_') ++endline;
338  if (*endline != '"')
339  throw error("did not find quotes after '+'");
340  end = endline;
341  }
342 
343  ++s;
344 
345  read_attribute:
346  string_span value(s, end - s);
347  if(attr_.empty() == false && !(attr_.back().first < name)) {
348  ERR_SWML << "attributes: '" << attr_.back().first << "' < '" << name << "'" << std::endl;
349  throw error("attributes not in order");
350  }
351 
352  s = end + 1;
353 
354  attr_.emplace_back(name, value);
355  }
356  }
357  }
358 
359  output_cache_ = string_span(begin, s - begin);
361 }
362 
364 {
365  for(child_map::iterator i = children_.begin(); i != children_.end(); ++i) {
366  for(child_list::iterator j = i->second.begin(); j != i->second.end(); ++j) {
367  debug_delete(*j);
368  }
369  }
370 }
371 
372 namespace {
373 struct string_span_pair_comparer
374 {
375  bool operator()(const string_span& a, const node::attribute& b) const {
376  return a < b.first;
377  }
378 
379  bool operator()(const node::attribute& a, const string_span& b) const {
380  return a.first < b;
381  }
382 
383  bool operator()(const node::attribute& a,
384  const node::attribute& b) const {
385  return a.first < b.first;
386  }
387 };
388 }
389 
390 const string_span& node::operator[](const char* key) const
391 {
392  static string_span empty("");
393  string_span span(key);
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;
398  }
399 
400  return empty;
401 }
402 
403 bool node::has_attr(const char* key) const
404 {
405  string_span span(key);
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;
409 }
410 
411 node& node::set_attr(const char* key, const char* value)
412 {
413  set_dirty();
414 
415  string_span span(key);
416  std::pair<attribute_list::iterator,
417  attribute_list::iterator> range = std::equal_range(attr_.begin(), attr_.end(), span, string_span_pair_comparer());
418  if(range.first != range.second) {
419  range.first->second = string_span(value);
420  } else {
421  attr_.insert(range.first, attribute(span, string_span(value)));
422  }
423 
424  return *this;
425 }
426 
427 node& node::set_attr_dup(const char* key, const char* value)
428 {
429  return set_attr(key, doc_->dup_string(value));
430 }
431 
432 node& node::set_attr_dup(const char* key, const string_span& value)
433 {
434  char* buf = value.duplicate();
436  return set_attr(key, buf);
437 }
438 
439 node& node::set_attr_int(const char* key, int value)
440 {
441  std::string temp = std::to_string(value);
442  return set_attr_dup(key, temp.c_str());
443 }
444 
445 node& node::add_child_at(const char* name, size_t index)
446 {
447  set_dirty();
448 
449  const int list_index = get_children(name);
450  child_list& list = children_[list_index].second;
451  if(index > list.size()) {
452  index = list.size();
453  }
454 
456  list.insert(list.begin() + index, new node(*doc_, this));
457  insert_ordered_child(list_index, index);
458 
460  return *list[index];
461 }
462 
463 
464 node& node::add_child(const char* name)
465 {
466  set_dirty();
467 
468  const int list_index = get_children(name);
470  child_list& list = children_[list_index].second;
471  list.push_back(new node(*doc_, this));
472  ordered_children_.emplace_back(list_index, list.size() - 1);
474  return *list.back();
475 }
476 
478 {
479  set_dirty();
480 
481  //if we don't already have a vector for this item we don't want to add one.
483  if(itor == children_.end()) {
484  return;
485  }
486 
487  child_list& list = itor->second;
488  if(index >= list.size()) {
489  return;
490  }
491 
492  remove_ordered_child(std::distance(children_.begin(), itor), index);
493 
494  debug_delete(list[index]);
495  list.erase(list.begin() + index);
496 
497  if(list.empty()) {
498  remove_ordered_child_list(std::distance(children_.begin(), itor));
499  children_.erase(itor);
500  }
501 }
502 
503 void node::insert_ordered_child(int child_map_index, int child_list_index)
504 {
505  bool inserted = false;
507  while(i != ordered_children_.end()) {
508  if(i->child_map_index == child_map_index && i->child_list_index > child_list_index) {
509  i->child_list_index++;
510  } else if(i->child_map_index == child_map_index && i->child_list_index == child_list_index) {
511  inserted = true;
512  i->child_list_index++;
513  i = ordered_children_.insert(i, node_pos(child_map_index, child_list_index));
514  ++i;
515  }
516 
517  ++i;
518  }
519 
520  if(!inserted) {
521  ordered_children_.emplace_back(child_map_index, child_list_index);
522  }
523 }
524 
525 void node::remove_ordered_child(int child_map_index, int child_list_index)
526 {
527  int erase_count = 0;
529  while(i != ordered_children_.end()) {
530  if(i->child_map_index == child_map_index && i->child_list_index == child_list_index) {
531  i = ordered_children_.erase(i);
532  ++erase_count;
533  } else {
534  if(i->child_map_index == child_map_index && i->child_list_index > child_list_index) {
535  i->child_list_index--;
536  }
537  ++i;
538  }
539  }
540 
541  assert(erase_count == 1);
542 }
543 
544 void node::insert_ordered_child_list(int child_map_index)
545 {
547  while(i != ordered_children_.end()) {
548  if(i->child_map_index >= child_map_index) {
549  i->child_map_index++;
550  }
551  }
552 }
553 
554 void node::remove_ordered_child_list(int child_map_index)
555 {
557  while(i != ordered_children_.end()) {
558  if(i->child_map_index == child_map_index) {
559  assert(false);
560  i = ordered_children_.erase(i);
561  } else {
562  if(i->child_map_index > child_map_index) {
563  i->child_map_index--;
564  }
565 
566  ++i;
567  }
568  }
569 }
570 
572 {
573 // only define this symbol in debug mode to work out child ordering.
574 #ifdef CHECK_ORDERED_CHILDREN
575  std::vector<node_pos>::const_iterator i = ordered_children_.begin();
576  while(i != ordered_children_.end()) {
577  assert(i->child_map_index < children_.size());
578  assert(i->child_list_index < children_[i->child_map_index].second.size());
579  ++i;
580  }
581 
582  for(child_map::const_iterator j = children_.begin(); j != children_.end(); ++j) {
583  const unsigned short child_map_index = j - children_.begin();
584  for(child_list::const_iterator k = j->second.begin(); k != j->second.end(); ++k) {
585  const unsigned short child_list_index = k - j->second.begin();
586  bool found = false;
587  for(int n = 0; n != ordered_children_.size(); ++n) {
588  if(ordered_children_[n].child_map_index == child_map_index &&
589  ordered_children_[n].child_list_index == child_list_index) {
590  found = true;
591  break;
592  }
593  }
594 
595  assert(found);
596  }
597  }
598 #endif // CHECK_ORDERED_CHILDREN
599 }
600 
601 void node::remove_child(const char* name, size_t index)
602 {
603  remove_child(string_span(name), index);
604 }
605 
606 node* node::child(const char* name)
607 {
608  for(child_map::iterator i = children_.begin(); i != children_.end(); ++i) {
609  if(i->first == name) {
610  assert(i->second.empty() == false);
611  return i->second.front();
612  }
613  }
614 
615  return nullptr;
616 }
617 
618 const node* node::child(const char* name) const
619 {
620  for(child_map::const_iterator i = children_.begin(); i != children_.end(); ++i) {
621  if(i->first == name) {
622  if(i->second.empty()) {
623  return nullptr;
624  } else {
625  return i->second.front();
626  }
627  }
628  }
629 
630  return nullptr;
631 }
632 
633 const node::child_list& node::children(const char* name) const
634 {
635  for(child_map::const_iterator i = children_.begin(); i != children_.end(); ++i) {
636  if(i->first == name) {
637  return i->second;
638  }
639  }
640 
641  static const node::child_list empty;
642  return empty;
643 }
644 
645 int node::get_children(const char* name)
646 {
647  return get_children(string_span(name));
648 }
649 
651 {
652  for(child_map::iterator i = children_.begin(); i != children_.end(); ++i) {
653  if(i->first == name) {
654  return std::distance(children_.begin(), i);
655  }
656  }
657 
658  children_.emplace_back(string_span(name), child_list());
659  return children_.size() - 1;
660 }
661 
662 node::child_map::const_iterator node::find_in_map(const child_map& m, const string_span& attr)
663 {
664  child_map::const_iterator i = m.begin();
665  for(; i != m.end(); ++i) {
666  if(i->first == attr) {
667  break;
668  }
669  }
670 
671  return i;
672 }
673 
675 {
676  child_map::iterator i = m.begin();
677  for(; i != m.end(); ++i) {
678  if(i->first == attr) {
679  break;
680  }
681  }
682 
683  return i;
684 }
685 
687 {
688  if(children_.empty()) {
689  static const string_span empty;
690  return empty;
691  }
692 
693  return children_.begin()->first;
694 }
695 
696 int node::output_size() const
697 {
699  if(output_cache_.empty() == false) {
700  return output_cache_.size();
701  }
702 
703  int res = 0;
704  for(attribute_list::const_iterator i = attr_.begin(); i != attr_.end(); ++i) {
705  res += i->first.size() + i->second.size() + 4;
706  }
707 
708  size_t count_children = 0;
709  for(child_map::const_iterator i = children_.begin(); i != children_.end(); ++i) {
710  for(child_list::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
711  res += i->first.size()*2 + 7;
712  res += (*j)->output_size();
713  ++count_children;
714  }
715  }
716 
717  assert(count_children == ordered_children_.size());
718 
719  return res;
720 }
721 
722 void node::shift_buffers(ptrdiff_t offset)
723 {
724  if(!output_cache_.empty()) {
726  }
727 
728  for(std::vector<attribute>::iterator i = attr_.begin(); i != attr_.end(); ++i) {
729  i->first = string_span(i->first.begin() + offset, i->first.size());
730  i->second = string_span(i->second.begin() + offset, i->second.size());
731  }
732 
733  for(child_map::iterator i = children_.begin(); i != children_.end(); ++i) {
734  string_span& key = i->first;
735  key = string_span(key.begin() + offset, key.size());
736  for(child_list::iterator j = i->second.begin(); j != i->second.end(); ++j) {
737  (*j)->shift_buffers(offset);
738  }
739  }
740 }
741 
742 void node::output(char*& buf, CACHE_STATUS cache_status)
743 {
744  if(output_cache_.empty() == false) {
745  memcpy(buf, output_cache_.begin(), output_cache_.size());
746  if(cache_status == REFRESH_CACHE) {
748  }
749  buf += output_cache_.size();
750  return;
751  }
752 
753  char* begin = buf;
754 
755  for(std::vector<attribute>::iterator i = attr_.begin(); i != attr_.end(); ++i) {
756  memcpy(buf, i->first.begin(), i->first.size());
757  i->first = string_span(buf, i->first.size());
758  buf += i->first.size();
759  *buf++ = '=';
760  *buf++ = '"';
761  memcpy(buf, i->second.begin(), i->second.size());
762  i->second = string_span(buf, i->second.size());
763  buf += i->second.size();
764  *buf++ = '"';
765  *buf++ = '\n';
766  }
767 
768  for(std::vector<node_pos>::const_iterator i = ordered_children_.begin();
769  i != ordered_children_.end(); ++i) {
770  assert(i->child_map_index < children_.size());
771  assert(i->child_list_index < children_[i->child_map_index].second.size());
772  string_span& attr = children_[i->child_map_index].first;
773  *buf++ = '[';
774  memcpy(buf, attr.begin(), attr.size());
775  attr = string_span(buf, attr.size());
776  buf += attr.size();
777  *buf++ = ']';
778  *buf++ = '\n';
779  children_[i->child_map_index].second[i->child_list_index]->output(buf, cache_status);
780  *buf++ = '[';
781  *buf++ = '/';
782  memcpy(buf, attr.begin(), attr.size());
783  buf += attr.size();
784  *buf++ = ']';
785  *buf++ = '\n';
786  }
787 
788  if(cache_status == REFRESH_CACHE) {
789  output_cache_ = string_span(begin, buf - begin);
790  }
791 }
792 
794 {
795  //calling output with status=DO_NOT_MODIFY_CACHE really doesn't modify the
796  //node, so we can do it safely
797  node& mutable_node = const_cast<node&>(n);
798  std::vector<char> v(mutable_node.output_size());
799  char* ptr = &v[0];
800  mutable_node.output(ptr, node::DO_NOT_MODIFY_CACHE);
801  assert(ptr == &v[0] + v.size());
802  return std::string(v.begin(), v.end());
803 }
804 
805 void node::copy_into(node& n) const
806 {
807  n.set_dirty();
808  for(attribute_list::const_iterator i = attr_.begin(); i != attr_.end(); ++i) {
809  char* key = i->first.duplicate();
810  char* value = i->second.duplicate();
812  n.doc_->take_ownership_of_buffer(value);
813  n.set_attr(key, value);
814  }
815 
816  for(std::vector<node_pos>::const_iterator i = ordered_children_.begin();
817  i != ordered_children_.end(); ++i) {
818  assert(i->child_map_index < children_.size());
819  assert(i->child_list_index < children_[i->child_map_index].second.size());
820  char* buf = children_[i->child_map_index].first.duplicate();
822  children_[i->child_map_index].second[i->child_list_index]->copy_into(n.add_child(buf));
823  }
824 }
825 
826 void node::apply_diff(const node& diff)
827 {
828  set_dirty();
829  const node* inserts = diff.child("insert");
830  if(inserts != nullptr) {
831  for(attribute_list::const_iterator i = inserts->attr_.begin(); i != inserts->attr_.end(); ++i) {
832  char* name = i->first.duplicate();
833  char* value = i->second.duplicate();
834  set_attr(name, value);
837  }
838  }
839 
840  const node* deletes = diff.child("delete");
841  if(deletes != nullptr) {
842  for(attribute_list::const_iterator i = deletes->attr_.begin(); i != deletes->attr_.end(); ++i) {
843  std::pair<attribute_list::iterator,
844  attribute_list::iterator> range = std::equal_range(attr_.begin(), attr_.end(), i->first, string_span_pair_comparer());
845  if(range.first != range.second) {
846  attr_.erase(range.first);
847  }
848  }
849  }
850 
851  const child_list& child_changes = diff.children("change_child");
852  for(child_list::const_iterator i = child_changes.begin(); i != child_changes.end(); ++i) {
853  const size_t index = (**i)["index"].to_int();
854  for(child_map::const_iterator j = (*i)->children_.begin(); j != (*i)->children_.end(); ++j) {
855  const string_span& name = j->first;
856  for(child_list::const_iterator k = j->second.begin(); k != j->second.end(); ++k) {
858  if(itor != children_.end()) {
859  if(index < itor->second.size()) {
860  itor->second[index]->apply_diff(**k);
861  }
862  }
863  }
864  }
865  }
866 
867  const child_list& child_inserts = diff.children("insert_child");
868  for(child_list::const_iterator i = child_inserts.begin(); i != child_inserts.end(); ++i) {
869  const size_t index = (**i)["index"].to_int();
870  for(child_map::const_iterator j = (*i)->children_.begin(); j != (*i)->children_.end(); ++j) {
871  const string_span& name = j->first;
872  for(child_list::const_iterator k = j->second.begin(); k != j->second.end(); ++k) {
873  char* buf = name.duplicate();
875  (*k)->copy_into(add_child_at(buf, index));
876  }
877  }
878  }
879 
880  const child_list& child_deletes = diff.children("delete_child");
881  for(child_list::const_iterator i = child_deletes.begin(); i != child_deletes.end(); ++i) {
882  const size_t index = (**i)["index"].to_int();
883  for(child_map::const_iterator j = (*i)->children_.begin(); j != (*i)->children_.end(); ++j) {
884  if(j->second.empty()) {
885  continue;
886  }
887 
888  const string_span& name = j->first;
889  remove_child(name, index);
890  }
891  }
892 }
893 
895 {
896  doc_ = doc;
897 
898  for(child_map::iterator i = children_.begin(); i != children_.end(); ++i) {
899  for(child_list::iterator j = i->second.begin(); j != i->second.end(); ++j) {
900  (*j)->set_doc(doc);
901  }
902  }
903 }
904 
905 int node::nchildren() const
906 {
907  int res = 0;
908  for(child_map::const_iterator i = children_.begin(); i != children_.end(); ++i) {
909  for(child_list::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
910  ++res;
911  res += (*j)->nchildren();
912  }
913  }
914 
915  return res;
916 }
917 
919 {
920  int res = attr_.capacity();
921  for(child_map::const_iterator i = children_.begin(); i != children_.end(); ++i) {
922  for(child_list::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
923  res += (*j)->nattributes_recursive();
924  }
925  }
926 
927  return res;
928 }
929 
931 {
932  for(node* n = this; n != nullptr && n->output_cache_.is_null() == false; n = n->parent_) {
933  n->output_cache_ = string_span();
934  }
935 }
936 
938  compressed_buf_(),
939  output_(nullptr),
940  buffers_(),
941  root_(new node(*this, nullptr)),
942  prev_(nullptr),
943  next_(nullptr)
944 {
945  attach_list();
946 }
947 
949  compressed_buf_(),
950  output_(buf),
951  buffers_(),
952  root_(nullptr),
953  prev_(nullptr),
954  next_(nullptr)
955 {
956  if(control == INIT_TAKE_OWNERSHIP) {
957  buffers_.push_back(buf);
958  }
959  const char* cbuf = buf;
960  root_ = new node(*this, nullptr, &cbuf);
961 
962  attach_list();
963 }
964 
965 document::document(const char* buf, INIT_STATE state) :
966  compressed_buf_(),
967  output_(buf),
968  buffers_(),
969  root_(nullptr),
970  prev_(nullptr),
971  next_(nullptr)
972 {
973  if(state == INIT_COMPRESSED) {
975  output_ = nullptr;
976  } else {
977  root_ = new node(*this, nullptr, &buf);
978  }
979 
980  attach_list();
981 }
982 
984  compressed_buf_(compressed_buf),
985  output_(nullptr),
986  buffers_(),
987  root_(nullptr),
988  prev_(nullptr),
989  next_(nullptr)
990 {
991  string_span uncompressed_buf;
992  buffers_.push_back(uncompress_buffer(compressed_buf, &uncompressed_buf));
993  output_ = uncompressed_buf.begin();
994  const char* cbuf = output_;
995  try {
996  root_ = new node(*this, nullptr, &cbuf);
997  } catch(...) {
998  delete [] buffers_.front();
999  buffers_.clear();
1000  throw;
1001  }
1002 
1003  attach_list();
1004 }
1005 
1007 {
1008  for(std::vector<char*>::iterator i = buffers_.begin(); i != buffers_.end(); ++i) {
1009  delete [] *i;
1010  }
1011 
1012  buffers_.clear();
1013  debug_delete(root_);
1014 
1015  detach_list();
1016 }
1017 
1018 const char* document::dup_string(const char* str)
1019 {
1020  const int len = strlen(str);
1021  char* res = new char[len+1];
1022  memcpy(res, str, len + 1);
1023  buffers_.push_back(res);
1024  return res;
1025 }
1026 
1027 const char* document::output()
1028 {
1029  if(output_ && (!root_ || root_->is_dirty() == false)) {
1030  return output_;
1031  }
1032  if(!root_) {
1033  assert(compressed_buf_.empty() == false);
1034  string_span uncompressed_buf;
1035  buffers_.push_back(uncompress_buffer(compressed_buf_, &uncompressed_buf));
1036  output_ = uncompressed_buf.begin();
1037  return output_;
1038  }
1039 
1040  //we're dirty, so the compressed buf must also be dirty; clear it.
1042 
1043  std::vector<char*> bufs;
1044  bufs.swap(buffers_);
1045 
1046  const int buf_size = root_->output_size() + 1;
1047  char* buf;
1048  try {
1049  buf = new char[buf_size];
1050  } catch (std::bad_alloc& e) {
1051  ERR_SWML << "ERROR: Trying to allocate " << buf_size << " bytes. "
1052  << e.what() << std::endl;
1053  throw error("Bad allocation request in output().");
1054  }
1055  buffers_.push_back(buf);
1056  output_ = buf;
1057 
1059  *buf++ = 0;
1060  assert(buf == output_ + buf_size);
1061 
1062  for(std::vector<char*>::iterator i = bufs.begin(); i != bufs.end(); ++i) {
1063  delete [] *i;
1064  }
1065 
1066  bufs.clear();
1067 
1068  return output_;
1069 }
1070 
1072 {
1073  if(compressed_buf_.empty() == false &&
1074  (root_ == nullptr || root_->is_dirty() == false)) {
1075  assert(*compressed_buf_.begin() == (bzip2 ? 'B' : 31));
1076  return compressed_buf_;
1077  }
1078 
1079  buffers_.push_back(compress_buffer(output(), &compressed_buf_, bzip2));
1080  assert(*compressed_buf_.begin() == (bzip2 ? 'B' : 31));
1081 
1082  return compressed_buf_;
1083 }
1084 
1086 {
1088  debug_delete(root_);
1089  root_ = nullptr;
1090  output_ = nullptr;
1091  std::vector<char*> new_buffers;
1092  for(std::vector<char*>::iterator i = buffers_.begin(); i != buffers_.end(); ++i) {
1093  if(*i != compressed_buf_.begin()) {
1094  delete [] *i;
1095  } else {
1096  new_buffers.push_back(*i);
1097  }
1098  }
1099 
1100  buffers_.swap(new_buffers);
1101  assert(buffers_.size() == 1);
1102 }
1103 
1105 {
1106  if(output_ == nullptr) {
1107  assert(compressed_buf_.empty() == false);
1108  string_span uncompressed_buf;
1109  buffers_.push_back(uncompress_buffer(compressed_buf_, &uncompressed_buf));
1110  output_ = uncompressed_buf.begin();
1111  }
1112 
1113  assert(root_ == nullptr);
1114  const char* cbuf = output_;
1115  root_ = new node(*this, nullptr, &cbuf);
1116 }
1117 
1119 {
1120  char* buf = new char[strlen(output())+1];
1121  strcpy(buf, output());
1122  return new document(buf);
1123 }
1124 
1126 {
1129  buffers_.swap(o.buffers_);
1130  std::swap(root_, o.root_);
1131 
1132  root_->set_doc(this);
1133  o.root_->set_doc(&o);
1134 }
1135 
1137 {
1139  output_ = nullptr;
1140  debug_delete(root_);
1141  root_ = new node(*this, nullptr);
1142  for(std::vector<char*>::iterator i = buffers_.begin(); i != buffers_.end(); ++i) {
1143  delete [] *i;
1144  }
1145 
1146  buffers_.clear();
1147 }
1148 
1149 namespace {
1150 document* head_doc = nullptr;
1151 }
1152 
1154 {
1155  prev_ = nullptr;
1156  next_ = head_doc;
1157 
1158  if(next_) {
1159  next_->prev_ = this;
1160  }
1161  head_doc = this;
1162 }
1163 
1165 {
1166  if(head_doc == this) {
1167  head_doc = next_;
1168  }
1169 
1170  if(next_) {
1171  next_->prev_ = prev_;
1172  }
1173 
1174  if(prev_) {
1175  prev_->next_ = next_;
1176  }
1177  next_ = prev_ = nullptr;
1178 }
1179 
1181 {
1182  std::ostringstream s;
1183  int ndocs = 0;
1184  int ncompressed = 0;
1185  int compressed_size = 0;
1186  int ntext = 0;
1187  int text_size = 0;
1188  int nbuffers = 0;
1189  int nnodes = 0;
1190  int ndirty = 0;
1191  int nattributes = 0;
1192  for(document* d = head_doc; d != nullptr; d = d->next_) {
1193  ndocs++;
1194  nbuffers += d->buffers_.size();
1195 
1196  if(d->compressed_buf_.is_null() == false) {
1197  ++ncompressed;
1198  compressed_size += d->compressed_buf_.size();
1199  }
1200 
1201  if(d->output_) {
1202  ++ntext;
1203  text_size += strlen(d->output_);
1204  }
1205 
1206  if(d->root_) {
1207  nnodes += 1 + d->root_->nchildren();
1208  nattributes += d->root_->nattributes_recursive();
1209  }
1210 
1211  if(d->root_ && d->root_->is_dirty()) {
1212  ++ndirty;
1213  }
1214  }
1215 
1216  const int nodes_alloc = nnodes*(sizeof(node) + 12);
1217  const int attr_alloc = nattributes*(sizeof(string_span)*2);
1218  const int total_alloc = compressed_size + text_size + nodes_alloc + attr_alloc;
1219 
1220  s << "WML documents: " << ndocs << "\n"
1221  << "Dirty: " << ndirty << "\n"
1222  << "With compression: " << ncompressed << " (" << compressed_size
1223  << " bytes)\n"
1224  << "With text: " << ntext << " (" << text_size
1225  << " bytes)\n"
1226  << "Nodes: " << nnodes << " (" << nodes_alloc << " bytes)\n"
1227  << "Attr: " << nattributes << " (" << attr_alloc << " bytes)\n"
1228  << "Buffers: " << nbuffers << "\n"
1229  << "Total allocation: " << total_alloc << " bytes\n";
1230 
1231  return s.str();
1232 }
1233 
1234 void swap(document& lhs, document& rhs)
1235 {
1236  lhs.swap(rhs);
1237 }
1238 
1239 }
1240 
1241 #ifdef UNIT_TEST_SIMPLE_WML
1242 
1243 int main(int argc, char** argv)
1244 {
1245  char* doctext = strdup(
1246 "[test]\n"
1247 "a=\"blah\"\n"
1248 "b=\"blah\"\n"
1249 "c=\"\\\\\"\n"
1250 "d=\"\\\"\"\n"
1251 "[/test]");
1252  std::cerr << doctext << "\n";
1253  simple_wml::document doc(doctext);
1254 
1255  simple_wml::node& node = doc.root();
1256  simple_wml::node* test_node = node.child("test");
1257  assert(test_node);
1258  assert((*test_node)["a"] == "blah");
1259  assert((*test_node)["b"] == "blah");
1260  assert((*test_node)["c"] == "\\\\");
1261  assert((*test_node)["d"] == "\\\"");
1262 
1263  node.set_attr("blah", "blah");
1264  test_node->set_attr("e", "f");
1265  std::cerr << doc.output();
1266 }
1267 
1268 #endif
void remove_ordered_child(int child_map_index, int child_list_index)
Definition: simple_wml.cpp:525
static size_t document_size_limit
Definition: simple_wml.hpp:285
node & add_child(const char *name)
Definition: simple_wml.cpp:464
string_span compressed_buf_
Definition: simple_wml.hpp:291
string_span output_compressed(bool bzip2=false)
std::vector< char_t > string
size_t index(const utf8::string &str, const size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:71
std::ostream & operator<<(std::ostream &o, const string_span &s)
Definition: simple_wml.cpp:200
void apply_diff(const node &diff)
Definition: simple_wml.cpp:826
std::string to_string() const
Definition: simple_wml.cpp:181
void shift_buffers(ptrdiff_t offset)
Definition: simple_wml.cpp:722
void output(char *&buf, CACHE_STATUS status=DO_NOT_MODIFY_CACHE)
Definition: simple_wml.cpp:742
node(document &doc, node *parent)
Definition: simple_wml.cpp:206
void insert_ordered_child(int child_map_index, int child_list_index)
Definition: simple_wml.cpp:503
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:39
#define a
string_span output_cache_
Definition: simple_wml.hpp:220
static child_map::const_iterator find_in_map(const child_map &m, const string_span &attr)
Definition: simple_wml.cpp:662
std::vector< char * > buffers_
Definition: simple_wml.hpp:293
int nattributes_recursive() const
Definition: simple_wml.cpp:918
node & set_attr_int(const char *key, int value)
Definition: simple_wml.cpp:439
const char * dup_string(const char *str)
node & set_attr(const char *key, const char *value)
Definition: simple_wml.cpp:411
node & add_child_at(const char *name, size_t index)
Definition: simple_wml.cpp:445
int get_children(const string_span &name)
Definition: simple_wml.cpp:650
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
attribute_list attr_
Definition: simple_wml.hpp:188
char * duplicate() const
Definition: simple_wml.cpp:186
#define d
const string_span & first_child() const
Definition: simple_wml.cpp:686
#define ERR_SWML
Definition: simple_wml.cpp:29
document * doc_
Definition: simple_wml.hpp:185
int nchildren() const
Definition: simple_wml.cpp:905
std::vector< child_pair > child_map
Definition: simple_wml.hpp:193
void insert_ordered_child_list(int child_map_index)
Definition: simple_wml.cpp:544
#define b
unsigned in
If equal to search_counter, the node is off the list.
const char * output()
node * child(const char *name)
Definition: simple_wml.cpp:606
void remove_child(const char *name, size_t index)
Definition: simple_wml.cpp:601
bool has_attr(const char *key) const
Definition: simple_wml.cpp:403
void remove_ordered_child_list(int child_map_index)
Definition: simple_wml.cpp:554
const char * end() const
Definition: simple_wml.hpp:90
child_map children_
Definition: simple_wml.hpp:197
void swap(document &o)
bool is_dirty() const
Definition: simple_wml.hpp:156
const string_span & operator[](const char *key) const
Definition: simple_wml.cpp:390
const char * output_
Definition: simple_wml.hpp:292
static std::string stats()
node & set_attr_dup(const char *key, const char *value)
Definition: simple_wml.cpp:427
int main()
static map_location::DIRECTION s
std::vector< node * > child_list
Definition: simple_wml.hpp:120
size_t i
Definition: function.cpp:933
void copy_into(node &n) const
Definition: simple_wml.cpp:805
const child_list & children(const char *name) const
Definition: simple_wml.cpp:633
bool to_bool(bool default_value=false) const
Definition: simple_wml.cpp:157
const char * begin() const
Definition: simple_wml.hpp:89
void check_ordered_children() const
Definition: simple_wml.cpp:571
std::pair< string_span, string_span > attribute
Definition: simple_wml.hpp:119
Standard logging facilities (interface).
static const char * name(const std::vector< SDL_Joystick * > &joysticks, const size_t index)
Definition: joystick.cpp:48
void set_doc(document *doc)
Definition: simple_wml.cpp:894
#define e
std::vector< node_pos > ordered_children_
Definition: simple_wml.hpp:211
void swap(document &lhs, document &rhs)
Implement non-member swap function for std::swap (calls document::swap).
int output_size() const
Definition: simple_wml.cpp:696
static map_location::DIRECTION n
void take_ownership_of_buffer(char *buffer)
Definition: simple_wml.hpp:276
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
const string_span & attr(const char *key) const
Definition: simple_wml.hpp:123
std::string node_to_string(const node &n)
Definition: simple_wml.cpp:793
static lg::log_domain log_config("config")