simple_wml.cpp

Go to the documentation of this file.
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 }  // namespace
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     //if we don't already have a vector for this item we don't want to add one.
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     //we're dirty, so the compressed buf must also be dirty; clear it.
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

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs