The Battle for Wesnoth  1.13.4+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 - 2016 by David White <[email protected]>
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 "global.hpp"
19 
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>
25 
26 #include "simple_wml.hpp"
27 
28 #include "log.hpp"
29 
30 static lg::log_domain log_config("config");
31 #define ERR_SWML LOG_STREAM(err, log_config)
32 
33 namespace simple_wml {
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 > 40000000) {
66  throw error("WML document exceeds 40MB limit");
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_.push_back(node_pos(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_.push_back(attribute(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  char buf[64];
442  sprintf(buf, "%d", value);
443  return set_attr_dup(key, buf);
444 }
445 
446 node& node::add_child_at(const char* name, size_t index)
447 {
448  set_dirty();
449 
450  const int list_index = get_children(name);
451  child_list& list = children_[list_index].second;
452  if(index > list.size()) {
453  index = list.size();
454  }
455 
457  list.insert(list.begin() + index, new node(*doc_, this));
458  insert_ordered_child(list_index, index);
459 
461  return *list[index];
462 }
463 
464 
465 node& node::add_child(const char* name)
466 {
467  set_dirty();
468 
469  const int list_index = get_children(name);
471  child_list& list = children_[list_index].second;
472  list.push_back(new node(*doc_, this));
473  ordered_children_.push_back(node_pos(list_index, list.size() - 1));
475  return *list.back();
476 }
477 
479 {
480  set_dirty();
481 
482  //if we don't already have a vector for this item we don't want to add one.
484  if(itor == children_.end()) {
485  return;
486  }
487 
488  child_list& list = itor->second;
489  if(index >= list.size()) {
490  return;
491  }
492 
493  remove_ordered_child(itor - children_.begin(), index);
494 
495  debug_delete(list[index]);
496  list.erase(list.begin() + index);
497 
498  if(list.empty()) {
499  remove_ordered_child_list(itor - children_.begin());
500  children_.erase(itor);
501  }
502 }
503 
504 void node::insert_ordered_child(int child_map_index, int child_list_index)
505 {
506  bool inserted = false;
508  while(i != ordered_children_.end()) {
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) {
512  inserted = true;
513  i->child_list_index++;
514  i = ordered_children_.insert(i, node_pos(child_map_index, child_list_index));
515  ++i;
516  }
517 
518  ++i;
519  }
520 
521  if(!inserted) {
522  ordered_children_.push_back(node_pos(child_map_index, child_list_index));
523  }
524 }
525 
526 void node::remove_ordered_child(int child_map_index, int child_list_index)
527 {
528  int erase_count = 0;
530  while(i != ordered_children_.end()) {
531  if(i->child_map_index == child_map_index && i->child_list_index == child_list_index) {
532  i = ordered_children_.erase(i);
533  ++erase_count;
534  } else {
535  if(i->child_map_index == child_map_index && i->child_list_index > child_list_index) {
536  i->child_list_index--;
537  }
538  ++i;
539  }
540  }
541 
542  assert(erase_count == 1);
543 }
544 
545 void node::insert_ordered_child_list(int child_map_index)
546 {
548  while(i != ordered_children_.end()) {
549  if(i->child_map_index >= child_map_index) {
550  i->child_map_index++;
551  }
552  }
553 }
554 
555 void node::remove_ordered_child_list(int child_map_index)
556 {
558  while(i != ordered_children_.end()) {
559  if(i->child_map_index == child_map_index) {
560  assert(false);
561  i = ordered_children_.erase(i);
562  } else {
563  if(i->child_map_index > child_map_index) {
564  i->child_map_index--;
565  }
566 
567  ++i;
568  }
569  }
570 }
571 
573 {
574 // only define this symbol in debug mode to work out child ordering.
575 #ifdef CHECK_ORDERED_CHILDREN
576  std::vector<node_pos>::const_iterator i = ordered_children_.begin();
577  while(i != ordered_children_.end()) {
578  assert(i->child_map_index < children_.size());
579  assert(i->child_list_index < children_[i->child_map_index].second.size());
580  ++i;
581  }
582 
583  for(child_map::const_iterator j = children_.begin(); j != children_.end(); ++j) {
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();
587  bool found = false;
588  for(int n = 0; n != ordered_children_.size(); ++n) {
589  if(ordered_children_[n].child_map_index == child_map_index &&
590  ordered_children_[n].child_list_index == child_list_index) {
591  found = true;
592  break;
593  }
594  }
595 
596  assert(found);
597  }
598  }
599 #endif // CHECK_ORDERED_CHILDREN
600 }
601 
602 void node::remove_child(const char* name, size_t index)
603 {
604  remove_child(string_span(name), index);
605 }
606 
607 node* node::child(const char* name)
608 {
609  for(child_map::iterator i = children_.begin(); i != children_.end(); ++i) {
610  if(i->first == name) {
611  assert(i->second.empty() == false);
612  return i->second.front();
613  }
614  }
615 
616  return nullptr;
617 }
618 
619 const node* node::child(const char* name) const
620 {
621  for(child_map::const_iterator i = children_.begin(); i != children_.end(); ++i) {
622  if(i->first == name) {
623  if(i->second.empty()) {
624  return nullptr;
625  } else {
626  return i->second.front();
627  }
628  }
629  }
630 
631  return nullptr;
632 }
633 
634 const node::child_list& node::children(const char* name) const
635 {
636  for(child_map::const_iterator i = children_.begin(); i != children_.end(); ++i) {
637  if(i->first == name) {
638  return i->second;
639  }
640  }
641 
642  static const node::child_list empty;
643  return empty;
644 }
645 
646 int node::get_children(const char* name)
647 {
648  return get_children(string_span(name));
649 }
650 
652 {
653  for(child_map::iterator i = children_.begin(); i != children_.end(); ++i) {
654  if(i->first == name) {
655  return i - children_.begin();
656  }
657  }
658 
659  children_.push_back(child_pair(string_span(name), child_list()));
660  return children_.size() - 1;
661 }
662 
663 node::child_map::const_iterator node::find_in_map(const child_map& m, const string_span& attr)
664 {
665  child_map::const_iterator i = m.begin();
666  for(; i != m.end(); ++i) {
667  if(i->first == attr) {
668  break;
669  }
670  }
671 
672  return i;
673 }
674 
676 {
677  child_map::iterator i = m.begin();
678  for(; i != m.end(); ++i) {
679  if(i->first == attr) {
680  break;
681  }
682  }
683 
684  return i;
685 }
686 
688 {
689  if(children_.empty()) {
690  static const string_span empty;
691  return empty;
692  }
693 
694  return children_.begin()->first;
695 }
696 
697 int node::output_size() const
698 {
700  if(output_cache_.empty() == false) {
701  return output_cache_.size();
702  }
703 
704  int res = 0;
705  for(attribute_list::const_iterator i = attr_.begin(); i != attr_.end(); ++i) {
706  res += i->first.size() + i->second.size() + 4;
707  }
708 
709  size_t count_children = 0;
710  for(child_map::const_iterator i = children_.begin(); i != children_.end(); ++i) {
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();
714  ++count_children;
715  }
716  }
717 
718  assert(count_children == ordered_children_.size());
719 
720  return res;
721 }
722 
724 {
725  if(!output_cache_.empty()) {
727  }
728 
729  for(std::vector<attribute>::iterator i = attr_.begin(); i != attr_.end(); ++i) {
730  i->first = string_span(i->first.begin() + offset, i->first.size());
731  i->second = string_span(i->second.begin() + offset, i->second.size());
732  }
733 
734  for(child_map::iterator i = children_.begin(); i != children_.end(); ++i) {
735  string_span& key = i->first;
736  key = string_span(key.begin() + offset, key.size());
737  for(child_list::iterator j = i->second.begin(); j != i->second.end(); ++j) {
738  (*j)->shift_buffers(offset);
739  }
740  }
741 }
742 
743 void node::output(char*& buf, CACHE_STATUS cache_status)
744 {
745  if(output_cache_.empty() == false) {
746  memcpy(buf, output_cache_.begin(), output_cache_.size());
747  if(cache_status == REFRESH_CACHE) {
749  }
750  buf += output_cache_.size();
751  return;
752  }
753 
754  char* begin = buf;
755 
756  for(std::vector<attribute>::iterator i = attr_.begin(); i != attr_.end(); ++i) {
757  memcpy(buf, i->first.begin(), i->first.size());
758  i->first = string_span(buf, i->first.size());
759  buf += i->first.size();
760  *buf++ = '=';
761  *buf++ = '"';
762  memcpy(buf, i->second.begin(), i->second.size());
763  i->second = string_span(buf, i->second.size());
764  buf += i->second.size();
765  *buf++ = '"';
766  *buf++ = '\n';
767  }
768 
769  for(std::vector<node_pos>::const_iterator i = ordered_children_.begin();
770  i != ordered_children_.end(); ++i) {
771  assert(i->child_map_index < children_.size());
772  assert(i->child_list_index < children_[i->child_map_index].second.size());
773  string_span& attr = children_[i->child_map_index].first;
774  *buf++ = '[';
775  memcpy(buf, attr.begin(), attr.size());
776  attr = string_span(buf, attr.size());
777  buf += attr.size();
778  *buf++ = ']';
779  *buf++ = '\n';
780  children_[i->child_map_index].second[i->child_list_index]->output(buf, cache_status);
781  *buf++ = '[';
782  *buf++ = '/';
783  memcpy(buf, attr.begin(), attr.size());
784  buf += attr.size();
785  *buf++ = ']';
786  *buf++ = '\n';
787  }
788 
789  if(cache_status == REFRESH_CACHE) {
790  output_cache_ = string_span(begin, buf - begin);
791  }
792 }
793 
795 {
796  //calling output with status=DO_NOT_MODIFY_CACHE really doesn't modify the
797  //node, so we can do it safely
798  node& mutable_node = const_cast<node&>(n);
799  std::vector<char> v(mutable_node.output_size());
800  char* ptr = &v[0];
801  mutable_node.output(ptr, node::DO_NOT_MODIFY_CACHE);
802  assert(ptr == &v[0] + v.size());
803  return std::string(v.begin(), v.end());
804 }
805 
806 void node::copy_into(node& n) const
807 {
808  n.set_dirty();
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();
813  n.doc_->take_ownership_of_buffer(value);
814  n.set_attr(key, value);
815  }
816 
817  for(std::vector<node_pos>::const_iterator i = ordered_children_.begin();
818  i != ordered_children_.end(); ++i) {
819  assert(i->child_map_index < children_.size());
820  assert(i->child_list_index < children_[i->child_map_index].second.size());
821  char* buf = children_[i->child_map_index].first.duplicate();
823  children_[i->child_map_index].second[i->child_list_index]->copy_into(n.add_child(buf));
824  }
825 }
826 
827 void node::apply_diff(const node& diff)
828 {
829  set_dirty();
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();
835  set_attr(name, value);
838  }
839  }
840 
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) {
844  std::pair<attribute_list::iterator,
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);
848  }
849  }
850  }
851 
852  const child_list& child_changes = diff.children("change_child");
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) {
856  const string_span& name = j->first;
857  for(child_list::const_iterator k = j->second.begin(); k != j->second.end(); ++k) {
859  if(itor != children_.end()) {
860  if(index < itor->second.size()) {
861  itor->second[index]->apply_diff(**k);
862  }
863  }
864  }
865  }
866  }
867 
868  const child_list& child_inserts = diff.children("insert_child");
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) {
872  const string_span& name = j->first;
873  for(child_list::const_iterator k = j->second.begin(); k != j->second.end(); ++k) {
874  char* buf = name.duplicate();
876  (*k)->copy_into(add_child_at(buf, index));
877  }
878  }
879  }
880 
881  const child_list& child_deletes = diff.children("delete_child");
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()) {
886  continue;
887  }
888 
889  const string_span& name = j->first;
890  remove_child(name, index);
891  }
892  }
893 }
894 
896 {
897  doc_ = doc;
898 
899  for(child_map::iterator i = children_.begin(); i != children_.end(); ++i) {
900  for(child_list::iterator j = i->second.begin(); j != i->second.end(); ++j) {
901  (*j)->set_doc(doc);
902  }
903  }
904 }
905 
906 int node::nchildren() const
907 {
908  int res = 0;
909  for(child_map::const_iterator i = children_.begin(); i != children_.end(); ++i) {
910  for(child_list::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
911  ++res;
912  res += (*j)->nchildren();
913  }
914  }
915 
916  return res;
917 }
918 
920 {
921  int res = attr_.capacity();
922  for(child_map::const_iterator i = children_.begin(); i != children_.end(); ++i) {
923  for(child_list::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
924  res += (*j)->nattributes_recursive();
925  }
926  }
927 
928  return res;
929 }
930 
932 {
933  for(node* n = this; n != nullptr && n->output_cache_.is_null() == false; n = n->parent_) {
934  n->output_cache_ = string_span();
935  }
936 }
937 
939  compressed_buf_(),
940  output_(nullptr),
941  buffers_(),
942  root_(new node(*this, nullptr)),
943  prev_(nullptr),
944  next_(nullptr)
945 {
946  attach_list();
947 }
948 
950  compressed_buf_(),
951  output_(buf),
952  buffers_(),
953  root_(nullptr),
954  prev_(nullptr),
955  next_(nullptr)
956 {
957  if(control == INIT_TAKE_OWNERSHIP) {
958  buffers_.push_back(buf);
959  }
960  const char* cbuf = buf;
961  root_ = new node(*this, nullptr, &cbuf);
962 
963  attach_list();
964 }
965 
966 document::document(const char* buf, INIT_STATE state) :
967  compressed_buf_(),
968  output_(buf),
969  buffers_(),
970  root_(nullptr),
971  prev_(nullptr),
972  next_(nullptr)
973 {
974  if(state == INIT_COMPRESSED) {
976  output_ = nullptr;
977  } else {
978  root_ = new node(*this, nullptr, &buf);
979  }
980 
981  attach_list();
982 }
983 
985  compressed_buf_(compressed_buf),
986  output_(nullptr),
987  buffers_(),
988  root_(nullptr),
989  prev_(nullptr),
990  next_(nullptr)
991 {
992  string_span uncompressed_buf;
993  buffers_.push_back(uncompress_buffer(compressed_buf, &uncompressed_buf));
994  output_ = uncompressed_buf.begin();
995  const char* cbuf = output_;
996  try {
997  root_ = new node(*this, nullptr, &cbuf);
998  } catch(...) {
999  delete [] buffers_.front();
1000  buffers_.clear();
1001  throw;
1002  }
1003 
1004  attach_list();
1005 }
1006 
1008 {
1009  for(std::vector<char*>::iterator i = buffers_.begin(); i != buffers_.end(); ++i) {
1010  delete [] *i;
1011  }
1012 
1013  buffers_.clear();
1014  debug_delete(root_);
1015 
1016  detach_list();
1017 }
1018 
1019 const char* document::dup_string(const char* str)
1020 {
1021  const int len = strlen(str);
1022  char* res = new char[len+1];
1023  memcpy(res, str, len + 1);
1024  buffers_.push_back(res);
1025  return res;
1026 }
1027 
1028 const char* document::output()
1029 {
1030  if(output_ && (!root_ || root_->is_dirty() == false)) {
1031  return output_;
1032  }
1033  if(!root_) {
1034  assert(compressed_buf_.empty() == false);
1035  string_span uncompressed_buf;
1036  buffers_.push_back(uncompress_buffer(compressed_buf_, &uncompressed_buf));
1037  output_ = uncompressed_buf.begin();
1038  return output_;
1039  }
1040 
1041  //we're dirty, so the compressed buf must also be dirty; clear it.
1043 
1044  std::vector<char*> bufs;
1045  bufs.swap(buffers_);
1046 
1047  const int buf_size = root_->output_size() + 1;
1048  char* buf;
1049  try {
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().");
1055  }
1056  buffers_.push_back(buf);
1057  output_ = buf;
1058 
1060  *buf++ = 0;
1061  assert(buf == output_ + buf_size);
1062 
1063  for(std::vector<char*>::iterator i = bufs.begin(); i != bufs.end(); ++i) {
1064  delete [] *i;
1065  }
1066 
1067  bufs.clear();
1068 
1069  return output_;
1070 }
1071 
1073 {
1074  if(compressed_buf_.empty() == false &&
1075  (root_ == nullptr || root_->is_dirty() == false)) {
1076  assert(*compressed_buf_.begin() == (bzip2 ? 'B' : 31));
1077  return compressed_buf_;
1078  }
1079 
1080  buffers_.push_back(compress_buffer(output(), &compressed_buf_, bzip2));
1081  assert(*compressed_buf_.begin() == (bzip2 ? 'B' : 31));
1082 
1083  return compressed_buf_;
1084 }
1085 
1087 {
1089  debug_delete(root_);
1090  root_ = nullptr;
1091  output_ = nullptr;
1092  std::vector<char*> new_buffers;
1093  for(std::vector<char*>::iterator i = buffers_.begin(); i != buffers_.end(); ++i) {
1094  if(*i != compressed_buf_.begin()) {
1095  delete [] *i;
1096  } else {
1097  new_buffers.push_back(*i);
1098  }
1099  }
1100 
1101  buffers_.swap(new_buffers);
1102  assert(buffers_.size() == 1);
1103 }
1104 
1106 {
1107  if(output_ == nullptr) {
1108  assert(compressed_buf_.empty() == false);
1109  string_span uncompressed_buf;
1110  buffers_.push_back(uncompress_buffer(compressed_buf_, &uncompressed_buf));
1111  output_ = uncompressed_buf.begin();
1112  }
1113 
1114  assert(root_ == nullptr);
1115  const char* cbuf = output_;
1116  root_ = new node(*this, nullptr, &cbuf);
1117 }
1118 
1120 {
1121  char* buf = new char[strlen(output())+1];
1122  strcpy(buf, output());
1123  return new document(buf);
1124 }
1125 
1127 {
1130  buffers_.swap(o.buffers_);
1131  std::swap(root_, o.root_);
1132 
1133  root_->set_doc(this);
1134  o.root_->set_doc(&o);
1135 }
1136 
1138 {
1140  output_ = nullptr;
1141  debug_delete(root_);
1142  root_ = new node(*this, nullptr);
1143  for(std::vector<char*>::iterator i = buffers_.begin(); i != buffers_.end(); ++i) {
1144  delete [] *i;
1145  }
1146 
1147  buffers_.clear();
1148 }
1149 
1150 namespace {
1151 document* head_doc = nullptr;
1152 }
1153 
1155 {
1156  prev_ = nullptr;
1157  next_ = head_doc;
1158 
1159  if(next_) {
1160  next_->prev_ = this;
1161  }
1162  head_doc = this;
1163 }
1164 
1166 {
1167  if(head_doc == this) {
1168  head_doc = next_;
1169  }
1170 
1171  if(next_) {
1172  next_->prev_ = prev_;
1173  }
1174 
1175  if(prev_) {
1176  prev_->next_ = next_;
1177  }
1178  next_ = prev_ = nullptr;
1179 }
1180 
1182 {
1183  std::ostringstream s;
1184  int ndocs = 0;
1185  int ncompressed = 0;
1186  int compressed_size = 0;
1187  int ntext = 0;
1188  int text_size = 0;
1189  int nbuffers = 0;
1190  int nnodes = 0;
1191  int ndirty = 0;
1192  int nattributes = 0;
1193  for(document* d = head_doc; d != nullptr; d = d->next_) {
1194  ndocs++;
1195  nbuffers += d->buffers_.size();
1196 
1197  if(d->compressed_buf_.is_null() == false) {
1198  ++ncompressed;
1199  compressed_size += d->compressed_buf_.size();
1200  }
1201 
1202  if(d->output_) {
1203  ++ntext;
1204  text_size += strlen(d->output_);
1205  }
1206 
1207  if(d->root_) {
1208  nnodes += 1 + d->root_->nchildren();
1209  nattributes += d->root_->nattributes_recursive();
1210  }
1211 
1212  if(d->root_ && d->root_->is_dirty()) {
1213  ++ndirty;
1214  }
1215  }
1216 
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;
1220 
1221  s << "WML documents: " << ndocs << "\n"
1222  << "Dirty: " << ndirty << "\n"
1223  << "With compression: " << ncompressed << " (" << compressed_size
1224  << " bytes)\n"
1225  << "With text: " << ntext << " (" << text_size
1226  << " bytes)\n"
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";
1231 
1232  return s.str();
1233 }
1234 
1235 }
1236 
1237 #ifdef UNIT_TEST_SIMPLE_WML
1238 
1239 int main(int argc, char** argv)
1240 {
1241  char* doctext = strdup(
1242 "[test]\n"
1243 "a=\"blah\"\n"
1244 "b=\"blah\"\n"
1245 "c=\"\\\\\"\n"
1246 "d=\"\\\"\"\n"
1247 "[/test]");
1248  std::cerr << doctext << "\n";
1249  simple_wml::document doc(doctext);
1250 
1251  simple_wml::node& node = doc.root();
1252  simple_wml::node* test_node = node.child("test");
1253  assert(test_node);
1254  assert((*test_node)["a"] == "blah");
1255  assert((*test_node)["b"] == "blah");
1256  assert((*test_node)["c"] == "\\\\");
1257  assert((*test_node)["d"] == "\\\"");
1258 
1259  node.set_attr("blah", "blah");
1260  test_node->set_attr("e", "f");
1261  std::cerr << doc.output();
1262 }
1263 
1264 #endif
void remove_ordered_child(int child_map_index, int child_list_index)
Definition: simple_wml.cpp:526
node & add_child(const char *name)
Definition: simple_wml.cpp:465
string_span compressed_buf_
Definition: simple_wml.hpp:291
string_span output_compressed(bool bzip2=false)
GLuint counter
Definition: glew.h:2584
std::ostream & operator<<(std::ostream &o, const string_span &s)
Definition: simple_wml.cpp:200
GLint GLint GLsizei GLsizei GLsizei depth
Definition: glew.h:1222
void apply_diff(const node &diff)
Definition: simple_wml.cpp:827
std::string to_string() const
Definition: simple_wml.cpp:181
GLenum GLenum GLvoid GLvoid GLvoid * span
Definition: glew.h:3805
void shift_buffers(ptrdiff_t offset)
Definition: simple_wml.cpp:723
void output(char *&buf, CACHE_STATUS status=DO_NOT_MODIFY_CACHE)
Definition: simple_wml.cpp:743
GLenum GLint * range
Definition: glew.h:3025
int pos
Definition: formula.cpp:800
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:504
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:29
string_span output_cache_
Definition: simple_wml.hpp:221
static child_map::const_iterator find_in_map(const child_map &m, const string_span &attr)
Definition: simple_wml.cpp:663
std::vector< char * > buffers_
Definition: simple_wml.hpp:293
std::pair< string_span, child_list > child_pair
Definition: simple_wml.hpp:193
GLenum GLenum GLenum input
Definition: glew.h:10668
int nattributes_recursive() const
Definition: simple_wml.cpp:919
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:446
int get_children(const string_span &name)
Definition: simple_wml.cpp:651
attribute_list attr_
Definition: simple_wml.hpp:189
char * duplicate() const
Definition: simple_wml.cpp:186
#define d
const string_span & first_child() const
Definition: simple_wml.cpp:687
#define ERR_SWML
Definition: simple_wml.cpp:31
document * doc_
Definition: simple_wml.hpp:186
int nchildren() const
Definition: simple_wml.cpp:906
std::vector< child_pair > child_map
Definition: simple_wml.hpp:194
GLuint in
Definition: glew.h:9261
void insert_ordered_child_list(int child_map_index)
Definition: simple_wml.cpp:545
GLdouble GLdouble GLdouble b
Definition: glew.h:6966
GLintptr offset
Definition: glew.h:1650
GLuint GLuint stream
Definition: glew.h:5239
const char * output()
node * child(const char *name)
Definition: simple_wml.cpp:607
GLuint GLuint end
Definition: glew.h:1221
const GLdouble * v
Definition: glew.h:1359
void remove_child(const char *name, size_t index)
Definition: simple_wml.cpp:602
GLsizei const GLfloat * value
Definition: glew.h:1817
GLenum GLsizei len
Definition: glew.h:5662
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:7319
bool has_attr(const char *key) const
Definition: simple_wml.cpp:403
GLenum GLuint GLsizei const char * buf
Definition: glew.h:2498
void remove_ordered_child_list(int child_map_index)
Definition: simple_wml.cpp:555
const char * end() const
Definition: simple_wml.hpp:91
child_map children_
Definition: simple_wml.hpp:198
void swap(document &o)
bool is_dirty() const
Definition: simple_wml.hpp:157
const string_span & operator[](const char *key) const
Definition: simple_wml.cpp:390
const char * output_
Definition: simple_wml.hpp:292
GLuint res
Definition: glew.h:9258
static std::string stats()
std::map< std::string, tfilter >::iterator itor
Definition: filter.cpp:199
node & set_attr_dup(const char *key, const char *value)
Definition: simple_wml.cpp:427
GLuint index
Definition: glew.h:1782
std::vector< node * > child_list
Definition: simple_wml.hpp:121
size_t i
Definition: function.cpp:1057
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:112
void copy_into(node &n) const
Definition: simple_wml.cpp:806
const child_list & children(const char *name) const
Definition: simple_wml.cpp:634
bool to_bool(bool default_value=false) const
Definition: simple_wml.cpp:157
const GLenum * bufs
Definition: glew.h:1791
int main(int argc, char **argv)
const char * begin() const
Definition: simple_wml.hpp:90
GLuint const GLchar * name
Definition: glew.h:1782
void check_ordered_children() const
Definition: simple_wml.cpp:572
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition: glew.h:3448
GLclampd n
Definition: glew.h:5903
std::pair< string_span, string_span > attribute
Definition: simple_wml.hpp:120
const GLdouble * m
Definition: glew.h:6968
void swap(game_board &one, game_board &other)
Definition: game_board.cpp:56
Standard logging facilities (interface).
void set_doc(document *doc)
Definition: simple_wml.cpp:895
#define e
bool is_null() const
Definition: simple_wml.hpp:95
std::vector< node_pos > ordered_children_
Definition: simple_wml.hpp:212
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
GLdouble s
Definition: glew.h:1358
int output_size() const
Definition: simple_wml.cpp:697
GLsizei const GLcharARB ** string
Definition: glew.h:4503
void take_ownership_of_buffer(char *buffer)
Definition: simple_wml.hpp:277
const string_span & attr(const char *key) const
Definition: simple_wml.hpp:124
std::string node_to_string(const node &n)
Definition: simple_wml.cpp:794
static lg::log_domain log_config("config")