#include "caching/xml_format.h" #include "base/base64.h" #include "base/indented_stream.h" #include "base/xml_utils.h" #include using namespace caching; using namespace persistent_data; using namespace std; using namespace xml_utils; //---------------------------------------------------------------- namespace { //-------------------------------- // Emitter //-------------------------------- class xml_emitter : public emitter { public: xml_emitter(ostream &out) : out_(out) { } void begin_superblock(std::string const &uuid, block_address block_size, block_address nr_cache_blocks, std::string const &policy, size_t hint_width) { out_.indent(); out_ << "" << endl; out_.inc(); } virtual void end_superblock() { out_.dec(); out_.indent(); out_ << "" << endl; } virtual void begin_mappings() { out_.indent(); out_ << "" << endl; out_.inc(); } virtual void end_mappings() { out_.dec(); out_.indent(); out_ << "" << endl; } virtual void mapping(block_address cblock, block_address oblock, bool dirty) { out_.indent(); out_ << "" << endl; } virtual void begin_hints() { out_.indent(); out_ << "" << endl; out_.inc(); } virtual void end_hints() { out_.dec(); out_.indent(); out_ << "" << endl; } virtual void hint(block_address cblock, vector const &data) { using namespace base; out_.indent(); out_ << "" << endl; } virtual void begin_discards() { out_.indent(); out_ << "" << endl; out_.inc(); } virtual void end_discards() { out_.dec(); out_.indent(); out_ << "" << endl; } virtual void discard(block_address dblock_b, block_address dblock_e) { out_.indent(); out_ << "" << endl; } private: string as_truth(bool v) const { return v ? "true" : "false"; } indented_stream out_; }; //-------------------------------- // Parser //-------------------------------- void parse_superblock(emitter *e, attributes const &attr) { e->begin_superblock(get_attr(attr, "uuid"), get_attr(attr, "block_size"), get_attr(attr, "nr_cache_blocks"), get_attr(attr, "policy"), get_attr(attr, "hint_width")); } bool to_bool(string const &str) { if (str == "true") return true; else if (str == "false") return false; throw runtime_error("bad boolean value"); } void parse_mapping(emitter *e, attributes const &attr) { e->mapping(get_attr(attr, "cache_block"), get_attr(attr, "origin_block"), to_bool(get_attr(attr, "dirty"))); } void parse_hint(emitter *e, attributes const &attr) { using namespace base; block_address cblock = get_attr(attr, "cache_block"); decoded_or_error doe = base64_decode(get_attr(attr, "data")); if (!boost::get >(&doe)) { ostringstream msg; msg << "invalid base64 encoding of hint for cache block " << cblock << ": " << boost::get(doe); throw runtime_error(msg.str()); } e->hint(cblock, boost::get >(doe)); } // FIXME: why passing e by ptr? void parse_discard(emitter *e, attributes const &attr) { e->discard(get_attr(attr, "dbegin"), get_attr(attr, "dend")); } void start_tag(void *data, char const *el, char const **attr) { emitter *e = static_cast(data); attributes a; build_attributes(a, attr); if (!strcmp(el, "superblock")) parse_superblock(e, a); else if (!strcmp(el, "mappings")) e->begin_mappings(); else if (!strcmp(el, "mapping")) parse_mapping(e, a); else if (!strcmp(el, "hints")) e->begin_hints(); else if (!strcmp(el, "hint")) parse_hint(e, a); else if (!strcmp(el, "discards")) e->begin_discards(); else if (!strcmp(el, "discard")) parse_discard(e, a); else throw runtime_error("unknown tag type"); } void end_tag(void *data, const char *el) { emitter *e = static_cast(data); if (!strcmp(el, "superblock")) e->end_superblock(); else if (!strcmp(el, "mappings")) e->end_mappings(); else if (!strcmp(el, "mapping")) // do nothing ; else if (!strcmp(el, "hints")) e->end_hints(); else if (!strcmp(el, "hint")) // do nothing ; else if (!strcmp(el, "discards")) e->end_discards(); else if (!strcmp(el, "discard")) // do nothing ; else throw runtime_error("unknown tag close"); } } //---------------------------------------------------------------- caching::emitter::ptr caching::create_xml_emitter(ostream &out) { return emitter::ptr(new xml_emitter(out)); } void caching::parse_xml(istream &in, emitter::ptr e, size_t input_length, base::progress_monitor &monitor) { xml_parser p; XML_SetUserData(p.get_parser(), e.get()); XML_SetElementHandler(p.get_parser(), start_tag, end_tag); size_t total = 0; while (!in.eof()) { char buffer[4096]; in.read(buffer, sizeof(buffer)); size_t len = in.gcount(); int done = in.eof(); if (!XML_Parse(p.get_parser(), buffer, len, done)) { ostringstream out; out << "Parse error at line " << XML_GetCurrentLineNumber(p.get_parser()) << ":\n" << XML_ErrorString(XML_GetErrorCode(p.get_parser())) << endl; throw runtime_error(out.str()); } total += len; monitor.update_percent(total * 100 / input_length); } } //----------------------------------------------------------------