00001 #include <assert.h>
00002 #include <algorithm>
00003 #include <iostream>
00004 #include <sstream>
00005 #include <string>
00006 #include <zlib.h>
00007
00008 #include <boost/iostreams/filtering_stream.hpp>
00009 #include <boost/iostreams/filter/gzip.hpp>
00010
00011 #include "../serialization/binary_wml.hpp"
00012 #include "simple_wml.hpp"
00013
00014 namespace simple_wml {
00015
00016 namespace {
00017
00018 void debug_delete(node* n) {
00019 if(n) {
00020 delete n;
00021 }
00022 }
00023
00024 char* uncompress_buffer(const string_span& input, string_span* span)
00025 {
00026 std::istringstream stream(std::string(input.begin(), input.end()));
00027 boost::iostreams::filtering_stream<boost::iostreams::input> filter;
00028 filter.push(boost::iostreams::gzip_decompressor());
00029 filter.push(stream);
00030
00031 const int chunk_size = input.size() * 10;
00032 std::vector<char> buf(chunk_size);
00033 int len = 0;
00034 int pos = 0;
00035 while(filter.good() && (len = filter.read(&buf[pos], chunk_size).gcount()) == chunk_size) {
00036 pos += len;
00037 buf.resize(pos + chunk_size);
00038 len = 0;
00039 }
00040
00041 if(!filter.eof() && !filter.good()) {
00042 throw error("failed to uncompress");
00043 }
00044
00045 pos += len;
00046 buf.resize(pos);
00047
00048 char* small_out = new char[pos+1];
00049 memcpy(small_out, &buf[0], pos);
00050 small_out[pos] = 0;
00051
00052 *span = string_span(small_out, pos);
00053 return small_out;
00054 }
00055
00056 char* compress_buffer(const char* input, string_span* span)
00057 {
00058 std::string in(input);
00059 std::istringstream stream(in);
00060 boost::iostreams::filtering_stream<boost::iostreams::input> filter;
00061 filter.push(boost::iostreams::gzip_compressor());
00062 filter.push(stream);
00063
00064 std::vector<char> buf(in.size()*2 + 80);
00065 const int len = filter.read(&buf[0], buf.size()).gcount();
00066 if((!filter.eof() && !filter.good()) || len == buf.size()) {
00067 throw error("failed to compress");
00068 }
00069
00070 buf.resize(len);
00071
00072 char* small_out = new char[len];
00073 memcpy(small_out, &buf[0], len);
00074
00075 *span = string_span(small_out, len);
00076 assert(*small_out == 31);
00077 return small_out;
00078 }
00079
00080 }
00081
00082 bool string_span::to_bool(bool default_value) const
00083 {
00084 if(empty()) {
00085 return default_value;
00086 }
00087
00088 return operator==("yes") || operator==("on") || operator==("true") || operator==("1");
00089 }
00090
00091 int string_span::to_int() const
00092 {
00093 const int buf_size = 64;
00094 if(size() >= buf_size) {
00095 return 0;
00096 }
00097 char buf[64];
00098 memcpy(buf, begin(), size());
00099 buf[size()] = 0;
00100 return atoi(buf);
00101 }
00102
00103 std::string string_span::to_string() const
00104 {
00105 return std::string(begin(), end());
00106 }
00107
00108 char* string_span::duplicate() const
00109 {
00110 char* buf = new char[size() + 1];
00111 memcpy(buf, begin(), size());
00112 buf[size()] = 0;
00113 return buf;
00114 }
00115
00116 error::error(const char* msg)
00117 {
00118 std::cerr << "ERROR: '" << msg << "'\n";
00119 }
00120
00121 std::ostream& operator<<(std::ostream& o, const string_span& s)
00122 {
00123 o << std::string(s.begin(), s.end());
00124 return o;
00125 }
00126
00127 node::node(document& doc, node* parent)
00128 : doc_(&doc), parent_(parent)
00129 {
00130 }
00131
00132 node::node(document& doc, node* parent, const char** str, int depth)
00133 : doc_(&doc), parent_(parent)
00134 {
00135 if(depth >= 30) {
00136 throw error("elements nested too deep");
00137 }
00138
00139 const char*& s = *str;
00140
00141 const char* const begin = s;
00142 while(*s) {
00143 switch(*s) {
00144 case '[': {
00145 if(s[1] == '/') {
00146 output_cache_ = string_span(begin, s - begin);
00147 s = strchr(s, ']');
00148 if(s == NULL) {
00149 throw error("end element unterminated");
00150 }
00151
00152 ++s;
00153 return;
00154 }
00155
00156 ++s;
00157 const char* end = strchr(s, ']');
00158 if(end == NULL) {
00159 throw error("unterminated element");
00160 }
00161
00162 child_list& list = get_children(string_span(s, end - s));
00163
00164 s = end + 1;
00165
00166 list.push_back(new node(doc, this, str, depth+1));
00167
00168 break;
00169 }
00170 case ' ':
00171 case '\t':
00172 case '\n':
00173 ++s;
00174 break;
00175 case '#':
00176 s = strchr(s, '\n');
00177 if(s == NULL) {
00178 throw error("could not find newline after #");
00179 }
00180 break;
00181 default: {
00182 const char* end = strchr(s, '=');
00183 if(end == NULL) {
00184 std::cerr << "attribute: " << s << "\n";
00185 throw error("could not find '=' after attribute");
00186 }
00187
00188 string_span name(s, end - s);
00189 s = end + 1;
00190 if(*s == '_') {
00191 s = strchr(s, '"');
00192 if(s == NULL) {
00193 throw error("could not find '\"' after _");
00194 }
00195 }
00196
00197 if(*s != '"') {
00198 std::cerr << "no quotes for attribute '" << name << "'\n";
00199 throw error("did not find quotes around attribute");
00200 }
00201
00202 end = s;
00203
00204 for(;;) {
00205 while((end = strchr(end+1, '"')) && end[1] == '"') {
00206 ++end;
00207 }
00208
00209 if(end == NULL) {
00210 std::cerr << "ATTR: '" << name << "' (((" << s << ")))\n";
00211 throw error("did not find end of attribute");
00212 }
00213
00214 const char* endline = end;
00215 while(*endline && *endline != '\n' && *endline != '+') {
00216 ++endline;
00217 }
00218
00219 if(*endline != '+') {
00220 break;
00221 }
00222
00223 end = strchr(endline, '"');
00224 if(end == NULL) {
00225 throw error("did not find quotes after +");
00226 }
00227 }
00228
00229 ++s;
00230
00231 string_span value(s, end - s);
00232 if(attr_.empty() == false && !(attr_.back().first < name)) {
00233 std::cerr << "attributes: '" << attr_.back().first << "' < '" << name << "'\n";
00234 throw error("attributes not in order");
00235 }
00236
00237 s = end + 1;
00238
00239 attr_.push_back(attribute(name, value));
00240 }
00241 }
00242 }
00243
00244 output_cache_ = string_span(begin, s - begin);
00245 }
00246
00247 node::~node()
00248 {
00249 for(child_map::iterator i = children_.begin(); i != children_.end(); ++i) {
00250 for(child_list::iterator j = i->second.begin(); j != i->second.end(); ++j) {
00251 debug_delete(*j);
00252 }
00253 }
00254 }
00255
00256 namespace {
00257 struct string_span_pair_comparer
00258 {
00259 bool operator()(const string_span& a, const node::attribute& b) const {
00260 return a < b.first;
00261 }
00262
00263 bool operator()(const node::attribute& a, const string_span& b) const {
00264 return a.first < b;
00265 }
00266
00267 bool operator()(const node::attribute& a,
00268 const node::attribute& b) const {
00269 return a.first < b.first;
00270 }
00271 };
00272 }
00273
00274 const string_span& node::operator[](const char* key) const
00275 {
00276 static string_span empty("");
00277 string_span span(key);
00278 std::pair<attribute_list::const_iterator,
00279 attribute_list::const_iterator> range = std::equal_range(attr_.begin(), attr_.end(), span, string_span_pair_comparer());
00280 if(range.first != range.second) {
00281 return range.first->second;
00282 }
00283
00284 return empty;
00285 }
00286
00287 bool node::has_attr(const char* key) const
00288 {
00289 string_span span(key);
00290 std::pair<attribute_list::const_iterator,
00291 attribute_list::const_iterator> range = std::equal_range(attr_.begin(), attr_.end(), span, string_span_pair_comparer());
00292 return range.first != range.second;
00293 }
00294
00295 node& node::set_attr(const char* key, const char* value)
00296 {
00297 set_dirty();
00298
00299 string_span span(key);
00300 std::pair<attribute_list::iterator,
00301 attribute_list::iterator> range = std::equal_range(attr_.begin(), attr_.end(), span, string_span_pair_comparer());
00302 if(range.first != range.second) {
00303 range.first->second = string_span(value);
00304 } else {
00305 attr_.insert(range.first, attribute(span, string_span(value)));
00306 }
00307
00308 return *this;
00309 }
00310
00311 node& node::set_attr_dup(const char* key, const char* value)
00312 {
00313 return set_attr(key, doc_->dup_string(value));
00314 }
00315
00316 node& node::set_attr_dup(const char* key, const string_span& value)
00317 {
00318 char* buf = value.duplicate();
00319 doc_->take_ownership_of_buffer(buf);
00320 return set_attr(key, buf);
00321 }
00322
00323 node& node::set_attr_dup_key_and_value(const char* key, const char* value)
00324 {
00325 return set_attr(doc_->dup_string(key), doc_->dup_string(value));
00326 }
00327
00328 node& node::set_attr_int(const char* key, int value)
00329 {
00330 char buf[64];
00331 sprintf(buf, "%d", value);
00332 return set_attr_dup(key, buf);
00333 }
00334
00335 node& node::add_child_at(const char* name, size_t index)
00336 {
00337 set_dirty();
00338
00339 child_list& list = get_children(name);
00340 if(index > list.size()) {
00341 index = list.size();
00342 }
00343
00344 list.insert(list.begin() + index, new node(*doc_, this));
00345 return *list[index];
00346 }
00347
00348
00349 node& node::add_child(const char* name)
00350 {
00351 set_dirty();
00352
00353 child_list& list = get_children(name);
00354 list.push_back(new node(*doc_, this));
00355 return *list.back();
00356 }
00357
00358 void node::remove_child(const string_span& name, size_t index)
00359 {
00360 set_dirty();
00361
00362
00363 child_map::iterator itor = find_in_map(children_, name);
00364 if(itor == children_.end()) {
00365 return;
00366 }
00367
00368 child_list& list = itor->second;
00369 if(index >= list.size()) {
00370 return;
00371 }
00372
00373 debug_delete(list[index]);
00374 list.erase(list.begin() + index);
00375
00376 if(list.empty()) {
00377 children_.erase(itor);
00378 }
00379 }
00380
00381 void node::remove_child(const char* name, size_t index)
00382 {
00383 remove_child(string_span(name), index);
00384 }
00385
00386 node* node::child(const char* name)
00387 {
00388 for(child_map::iterator i = children_.begin(); i != children_.end(); ++i) {
00389 if(i->first == name) {
00390 assert(i->second.empty() == false);
00391 return i->second.front();
00392 }
00393 }
00394
00395 return NULL;
00396 }
00397
00398 const node* node::child(const char* name) const
00399 {
00400 for(child_map::const_iterator i = children_.begin(); i != children_.end(); ++i) {
00401 if(i->first == name) {
00402 if(i->second.empty()) {
00403 return NULL;
00404 } else {
00405 return i->second.front();
00406 }
00407 }
00408 }
00409
00410 return NULL;
00411 }
00412
00413 const node::child_list& node::children(const char* name) const
00414 {
00415 for(child_map::const_iterator i = children_.begin(); i != children_.end(); ++i) {
00416 if(i->first == name) {
00417 return i->second;
00418 }
00419 }
00420
00421 static const node::child_list empty;
00422 return empty;
00423 }
00424
00425 node::child_list& node::get_children(const char* name)
00426 {
00427 return get_children(string_span(name));
00428 }
00429
00430 node::child_list& node::get_children(const string_span& name)
00431 {
00432 for(child_map::iterator i = children_.begin(); i != children_.end(); ++i) {
00433 if(i->first == name) {
00434 return i->second;
00435 }
00436 }
00437
00438 children_.push_back(child_pair(string_span(name), child_list()));
00439 return children_.back().second;
00440 }
00441
00442 node::child_map::const_iterator node::find_in_map(const child_map& m, const string_span& attr)
00443 {
00444 child_map::const_iterator i = m.begin();
00445 for(; i != m.end(); ++i) {
00446 if(i->first == attr) {
00447 break;
00448 }
00449 }
00450
00451 return i;
00452 }
00453
00454 node::child_map::iterator node::find_in_map(child_map& m, const string_span& attr)
00455 {
00456 child_map::iterator i = m.begin();
00457 for(; i != m.end(); ++i) {
00458 if(i->first == attr) {
00459 break;
00460 }
00461 }
00462
00463 return i;
00464 }
00465
00466 const string_span& node::first_child() const
00467 {
00468 if(children_.empty()) {
00469 static const string_span empty;
00470 return empty;
00471 }
00472
00473 return children_.begin()->first;
00474 }
00475
00476 int node::output_size() const
00477 {
00478 if(output_cache_.empty() == false) {
00479 return output_cache_.size();
00480 }
00481
00482 int res = 0;
00483 for(attribute_list::const_iterator i = attr_.begin(); i != attr_.end(); ++i) {
00484 res += i->first.size() + i->second.size() + 4;
00485 }
00486
00487 for(child_map::const_iterator i = children_.begin(); i != children_.end(); ++i) {
00488 for(child_list::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
00489 res += i->first.size()*2 + 7;
00490 res += (*j)->output_size();
00491 }
00492 }
00493
00494 return res;
00495 }
00496
00497 void node::shift_buffers(ptrdiff_t offset)
00498 {
00499 if(!output_cache_.empty()) {
00500 output_cache_ = string_span(output_cache_.begin() + offset, output_cache_.size());
00501 }
00502
00503 for(std::vector<attribute>::iterator i = attr_.begin(); i != attr_.end(); ++i) {
00504 i->first = string_span(i->first.begin() + offset, i->first.size());
00505 i->second = string_span(i->second.begin() + offset, i->second.size());
00506 }
00507
00508 for(child_map::iterator i = children_.begin(); i != children_.end(); ++i) {
00509 string_span& key = i->first;
00510 key = string_span(key.begin() + offset, key.size());
00511 for(child_list::iterator j = i->second.begin(); j != i->second.end(); ++j) {
00512 (*j)->shift_buffers(offset);
00513 }
00514 }
00515 }
00516
00517 void node::output(char*& buf)
00518 {
00519 if(output_cache_.empty() == false) {
00520 memcpy(buf, output_cache_.begin(), output_cache_.size());
00521 shift_buffers(buf - output_cache_.begin());
00522 buf += output_cache_.size();
00523 return;
00524 }
00525
00526 char* begin = buf;
00527
00528 for(std::vector<attribute>::iterator i = attr_.begin(); i != attr_.end(); ++i) {
00529 memcpy(buf, i->first.begin(), i->first.size());
00530 i->first = string_span(buf, i->first.size());
00531 buf += i->first.size();
00532 *buf++ = '=';
00533 *buf++ = '"';
00534 memcpy(buf, i->second.begin(), i->second.size());
00535 i->second = string_span(buf, i->second.size());
00536 buf += i->second.size();
00537 *buf++ = '"';
00538 *buf++ = '\n';
00539 }
00540
00541 for(child_map::iterator i = children_.begin(); i != children_.end(); ++i) {
00542 assert(i->second.empty() == false);
00543 for(child_list::iterator j = i->second.begin(); j != i->second.end(); ++j) {
00544 *buf++ = '[';
00545 memcpy(buf, i->first.begin(), i->first.size());
00546 i->first = string_span(buf, i->first.size());
00547 buf += i->first.size();
00548 *buf++ = ']';
00549 *buf++ = '\n';
00550 (*j)->output(buf);
00551 *buf++ = '[';
00552 *buf++ = '/';
00553 memcpy(buf, i->first.begin(), i->first.size());
00554 buf += i->first.size();
00555 *buf++ = ']';
00556 *buf++ = '\n';
00557 }
00558 }
00559
00560 output_cache_ = string_span(begin, buf - begin);
00561 }
00562
00563 void node::copy_into(node& n) const
00564 {
00565 n.set_dirty();
00566 for(attribute_list::const_iterator i = attr_.begin(); i != attr_.end(); ++i) {
00567 char* key = i->first.duplicate();
00568 char* value = i->second.duplicate();
00569 n.doc_->take_ownership_of_buffer(key);
00570 n.doc_->take_ownership_of_buffer(value);
00571 n.set_attr(key, value);
00572 }
00573
00574 for(child_map::const_iterator i = children_.begin(); i != children_.end(); ++i) {
00575 if(i->second.empty()) {
00576 continue;
00577 }
00578
00579 char* buf = i->first.duplicate();
00580 n.doc_->take_ownership_of_buffer(buf);
00581
00582 for(child_list::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
00583 (*j)->copy_into(n.add_child(buf));
00584 }
00585 }
00586 }
00587
00588 void node::apply_diff(const node& diff)
00589 {
00590 set_dirty();
00591 const node* inserts = diff.child("insert");
00592 if(inserts != NULL) {
00593 for(attribute_list::const_iterator i = inserts->attr_.begin(); i != inserts->attr_.end(); ++i) {
00594 char* name = i->first.duplicate();
00595 char* value = i->second.duplicate();
00596 set_attr(name, value);
00597 doc_->take_ownership_of_buffer(name);
00598 doc_->take_ownership_of_buffer(value);
00599 }
00600 }
00601
00602 const node* deletes = diff.child("delete");
00603 if(deletes != NULL) {
00604 for(attribute_list::const_iterator i = deletes->attr_.begin(); i != deletes->attr_.end(); ++i) {
00605 std::pair<attribute_list::iterator,
00606 attribute_list::iterator> range = std::equal_range(attr_.begin(), attr_.end(), i->first, string_span_pair_comparer());
00607 if(range.first != range.second) {
00608 attr_.erase(range.first);
00609 }
00610 }
00611 }
00612
00613 const child_list& child_changes = diff.children("change_child");
00614 for(child_list::const_iterator i = child_changes.begin(); i != child_changes.end(); ++i) {
00615 const size_t index = (**i)["index"].to_int();
00616 for(child_map::const_iterator j = (*i)->children_.begin(); j != (*i)->children_.end(); ++j) {
00617 const string_span& name = j->first;
00618 for(child_list::const_iterator k = j->second.begin(); k != j->second.end(); ++k) {
00619 child_map::iterator itor = find_in_map(children_, name);
00620 if(itor != children_.end()) {
00621 if(index < itor->second.size()) {
00622 itor->second[index]->apply_diff(**k);
00623 }
00624 }
00625 }
00626 }
00627 }
00628
00629 const child_list& child_inserts = diff.children("insert_child");
00630 for(child_list::const_iterator i = child_inserts.begin(); i != child_inserts.end(); ++i) {
00631 const size_t index = (**i)["index"].to_int();
00632 for(child_map::const_iterator j = (*i)->children_.begin(); j != (*i)->children_.end(); ++j) {
00633 const string_span& name = j->first;
00634 for(child_list::const_iterator k = j->second.begin(); k != j->second.end(); ++k) {
00635 char* buf = name.duplicate();
00636 doc_->take_ownership_of_buffer(buf);
00637 (*k)->copy_into(add_child_at(buf, index));
00638 }
00639 }
00640 }
00641
00642 const child_list& child_deletes = diff.children("delete_child");
00643 for(child_list::const_iterator i = child_deletes.begin(); i != child_deletes.end(); ++i) {
00644 const size_t index = (**i)["index"].to_int();
00645 for(child_map::const_iterator j = (*i)->children_.begin(); j != (*i)->children_.end(); ++j) {
00646 if(j->second.empty()) {
00647 continue;
00648 }
00649
00650 const string_span& name = j->first;
00651 remove_child(name, index);
00652 }
00653 }
00654 }
00655
00656 void node::set_doc(document* doc)
00657 {
00658 doc_ = doc;
00659
00660 for(child_map::iterator i = children_.begin(); i != children_.end(); ++i) {
00661 for(child_list::iterator j = i->second.begin(); j != i->second.end(); ++j) {
00662 (*j)->set_doc(doc);
00663 }
00664 }
00665 }
00666
00667 int node::nchildren() const
00668 {
00669 int res = 0;
00670 for(child_map::const_iterator i = children_.begin(); i != children_.end(); ++i) {
00671 for(child_list::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
00672 ++res;
00673 res += (*j)->nchildren();
00674 }
00675 }
00676
00677 return res;
00678 }
00679
00680 int node::nattributes_recursive() const
00681 {
00682 int res = attr_.capacity();
00683 for(child_map::const_iterator i = children_.begin(); i != children_.end(); ++i) {
00684 for(child_list::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
00685 res += (*j)->nattributes_recursive();
00686 }
00687 }
00688
00689 return res;
00690 }
00691
00692 void node::set_dirty()
00693 {
00694 for(node* n = this; n != NULL && n->output_cache_.is_null() == false; n = n->parent_) {
00695 n->output_cache_ = string_span();
00696 }
00697 }
00698
00699 document::document() : output_(NULL),
00700 root_(new node(*this, NULL))
00701 {
00702 attach_list();
00703 }
00704
00705 document::document(char* buf, INIT_BUFFER_CONTROL control) : output_(buf), root_(NULL)
00706 {
00707 if(control == INIT_TAKE_OWNERSHIP) {
00708 buffers_.push_back(buf);
00709 }
00710 const char* cbuf = buf;
00711 root_ = new node(*this, NULL, &cbuf);
00712
00713 attach_list();
00714 }
00715
00716 document::document(const char* buf, INIT_STATE state) : output_(NULL),
00717 root_(NULL)
00718 {
00719 output_ = buf;
00720 if(state == INIT_COMPRESSED) {
00721 output_compressed();
00722 output_ = NULL;
00723 } else {
00724 root_ = new node(*this, NULL, &buf);
00725 }
00726
00727 attach_list();
00728 }
00729
00730 document::document(string_span compressed_buf)
00731 : compressed_buf_(compressed_buf),
00732 output_(NULL),
00733 root_(NULL)
00734 {
00735 string_span uncompressed_buf;
00736 buffers_.push_back(uncompress_buffer(compressed_buf, &uncompressed_buf));
00737 output_ = uncompressed_buf.begin();
00738 const char* cbuf = output_;
00739 try {
00740 root_ = new node(*this, NULL, &cbuf);
00741 } catch(...) {
00742 delete [] buffers_.front();
00743 buffers_.clear();
00744 throw;
00745 }
00746
00747 attach_list();
00748 }
00749
00750 document::~document()
00751 {
00752 for(std::vector<char*>::iterator i = buffers_.begin(); i != buffers_.end(); ++i) {
00753 delete [] *i;
00754 }
00755
00756 buffers_.clear();
00757 debug_delete(root_);
00758
00759 detach_list();
00760 }
00761
00762 const char* document::dup_string(const char* str)
00763 {
00764 const int len = strlen(str);
00765 char* res = new char[len+1];
00766 memcpy(res, str, len + 1);
00767 buffers_.push_back(res);
00768 return res;
00769 }
00770
00771 const char* document::output()
00772 {
00773 if(output_ && (!root_ || root_->is_dirty() == false)) {
00774 return output_;
00775 }
00776 if(!root_) {
00777 assert(compressed_buf_.empty() == false);
00778 string_span uncompressed_buf;
00779 buffers_.push_back(uncompress_buffer(compressed_buf_, &uncompressed_buf));
00780 output_ = uncompressed_buf.begin();
00781 return output_;
00782 }
00783
00784
00785 compressed_buf_ = string_span();
00786
00787 std::vector<char*> bufs;
00788 bufs.swap(buffers_);
00789
00790 const int buf_size = root_->output_size() + 1;
00791 char* buf = new char[buf_size];
00792 buffers_.push_back(buf);
00793 output_ = buf;
00794
00795 root_->output(buf);
00796 *buf++ = 0;
00797 assert(buf == output_ + buf_size);
00798
00799 for(std::vector<char*>::iterator i = bufs.begin(); i != bufs.end(); ++i) {
00800 delete [] *i;
00801 }
00802
00803 bufs.clear();
00804
00805 return output_;
00806 }
00807
00808 string_span document::output_compressed()
00809 {
00810 if(compressed_buf_.empty() == false &&
00811 (root_ == NULL || root_->is_dirty() == false)) {
00812 assert(*compressed_buf_.begin() == 31);
00813 return compressed_buf_;
00814 }
00815
00816 buffers_.push_back(compress_buffer(output(), &compressed_buf_));
00817 assert(*compressed_buf_.begin() == 31);
00818
00819 return compressed_buf_;
00820 }
00821
00822 void document::compress()
00823 {
00824 output_compressed();
00825 debug_delete(root_);
00826 root_ = NULL;
00827 output_ = NULL;
00828 std::vector<char*> new_buffers;
00829 for(std::vector<char*>::iterator i = buffers_.begin(); i != buffers_.end(); ++i) {
00830 if(*i != compressed_buf_.begin()) {
00831 delete [] *i;
00832 } else {
00833 new_buffers.push_back(*i);
00834 }
00835 }
00836
00837 buffers_.swap(new_buffers);
00838 assert(buffers_.size() == 1);
00839 }
00840
00841 void document::generate_root()
00842 {
00843 if(output_ == NULL) {
00844 assert(compressed_buf_.empty() == false);
00845 string_span uncompressed_buf;
00846 buffers_.push_back(uncompress_buffer(compressed_buf_, &uncompressed_buf));
00847 output_ = uncompressed_buf.begin();
00848 }
00849
00850 assert(root_ == NULL);
00851 const char* cbuf = output_;
00852 root_ = new node(*this, NULL, &cbuf);
00853 }
00854
00855 document* document::clone()
00856 {
00857 char* buf = new char[strlen(output())+1];
00858 strcpy(buf, output());
00859 return new document(buf);
00860 }
00861
00862 void document::swap(document& o)
00863 {
00864 std::swap(compressed_buf_, o.compressed_buf_);
00865 std::swap(output_, o.output_);
00866 buffers_.swap(o.buffers_);
00867 std::swap(root_, o.root_);
00868
00869 root_->set_doc(this);
00870 o.root_->set_doc(&o);
00871 }
00872
00873 void document::clear()
00874 {
00875 compressed_buf_ = string_span();
00876 output_ = NULL;
00877 debug_delete(root_);
00878 root_ = new node(*this, NULL);
00879 for(std::vector<char*>::iterator i = buffers_.begin(); i != buffers_.end(); ++i) {
00880 delete [] *i;
00881 }
00882
00883 buffers_.clear();
00884 }
00885
00886 namespace {
00887 document* head_doc = NULL;
00888 }
00889
00890 void document::attach_list()
00891 {
00892 prev_ = NULL;
00893 next_ = head_doc;
00894
00895 if(next_) {
00896 next_->prev_ = this;
00897 }
00898 head_doc = this;
00899 }
00900
00901 void document::detach_list()
00902 {
00903 if(head_doc == this) {
00904 head_doc = next_;
00905 }
00906
00907 if(next_) {
00908 next_->prev_ = prev_;
00909 }
00910
00911 if(prev_) {
00912 prev_->next_ = next_;
00913 }
00914 next_ = prev_ = NULL;
00915 }
00916
00917 std::string document::stats()
00918 {
00919 std::ostringstream s;
00920 int ndocs = 0;
00921 int ncompressed = 0;
00922 int compressed_size = 0;
00923 int ntext = 0;
00924 int text_size = 0;
00925 int nbuffers = 0;
00926 int nnodes = 0;
00927 int nhas_nodes = 0;
00928 int ndirty = 0;
00929 int nattributes = 0;
00930 for(document* d = head_doc; d != NULL; d = d->next_) {
00931 ndocs++;
00932 nbuffers += d->buffers_.size();
00933
00934 if(d->compressed_buf_.is_null() == false) {
00935 ++ncompressed;
00936 compressed_size += d->compressed_buf_.size();
00937 }
00938
00939 if(d->output_) {
00940 ++ntext;
00941 text_size += strlen(d->output_);
00942 }
00943
00944 if(d->root_) {
00945 ++nhas_nodes;
00946 nnodes += 1 + d->root_->nchildren();
00947 nattributes += d->root_->nattributes_recursive();
00948 }
00949
00950 if(d->root_ && d->root_->is_dirty()) {
00951 ++ndirty;
00952 }
00953 }
00954
00955 const int nodes_alloc = nnodes*(sizeof(node) + 12);
00956 const int attr_alloc = nattributes*(sizeof(string_span)*2);
00957 const int total_alloc = compressed_size + text_size + nodes_alloc + attr_alloc;
00958
00959 s << "WML documents: " << ndocs << "\n"
00960 << "Dirty: " << ndirty << "\n"
00961 << "With compression: " << ncompressed << " (" << compressed_size
00962 << " bytes)\n"
00963 << "With text: " << ntext << " (" << text_size
00964 << " bytes)\n"
00965 << "Nodes: " << nnodes << " (" << nodes_alloc << " bytes)\n"
00966 << "Attr: " << nattributes << " (" << attr_alloc << " bytes)\n"
00967 << "Buffers: " << nbuffers << "\n"
00968 << "Total allocation: " << total_alloc << " bytes\n";
00969
00970 return s.str();
00971 }
00972
00973 }
00974
00975 #ifdef UNIT_TEST_SIMPLE_WML
00976
00977 int main(int argc, char** argv)
00978 {
00979 char* doctext = strdup(
00980 "[test]\n"
00981 "a=\"blah\"\n"
00982 "b=\"blah\"\n"
00983 "c=\"\\\\\"\n"
00984 "d=\"\\\"\"\n"
00985 "[/test]");
00986 std::cerr << doctext << "\n";
00987 simple_wml::document doc(doctext);
00988
00989 simple_wml::node& node = doc.root();
00990 simple_wml::node* test_node = node.child("test");
00991 assert(test_node);
00992 assert((*test_node)["a"] == "blah");
00993 assert((*test_node)["b"] == "blah");
00994 assert((*test_node)["c"] == "\\\\");
00995 assert((*test_node)["d"] == "\\\"");
00996
00997 node.set_attr("blah", "blah");
00998 test_node->set_attr("e", "f");
00999 std::cerr << doc.output();
01000 }
01001
01002 #endif