00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "global.hpp"
00020
00021 #include "config.hpp"
00022 #include "loadscreen.hpp"
00023 #include "log.hpp"
00024 #include "serialization/binary_wml.hpp"
00025
00026 #include <algorithm>
00027 #include <iostream>
00028 #include <sstream>
00029
00030 #define ERR_CF LOG_STREAM(err, config)
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056 static const unsigned int
00057 compress_open_element = 0, compress_close_element = 1,
00058 compress_schema_item = 2, compress_literal_word = 3,
00059 compress_first_word = 4, compress_end_words = 256;
00060 static const size_t compress_max_words = compress_end_words - compress_first_word;
00061 static const size_t max_schema_item_length = 20;
00062 static const int max_recursion_levels = 1000;
00063
00064 static void compress_output_literal_word(std::ostream &out, std::string const &word)
00065 {
00066 out.write(word.c_str(), word.length() + 1);
00067 }
00068
00069 static compression_schema::word_char_map::const_iterator
00070 add_word_to_schema(std::string const &word, compression_schema &schema)
00071 {
00072 if (word.size() > max_schema_item_length)
00073 throw config::error("Schema item is too long");
00074
00075 unsigned int c = compress_first_word + schema.word_to_char.size();
00076
00077 schema.char_to_word.insert(std::make_pair(c, word));
00078 return schema.word_to_char.insert(std::make_pair(word, c)).first;
00079 }
00080
00081 static compression_schema::word_char_map::const_iterator
00082 get_word_in_schema(std::string const &word, compression_schema &schema, std::ostream &out)
00083 {
00084 if (word.size() > max_schema_item_length)
00085 return schema.word_to_char.end();
00086
00087
00088 const compression_schema::word_char_map::const_iterator w = schema.word_to_char.find(word);
00089 if (w != schema.word_to_char.end()) {
00090
00091 return w;
00092 } else if (schema.word_to_char.size() < compress_max_words) {
00093
00094
00095
00096 out.put(compress_schema_item);
00097 compress_output_literal_word(out, word);
00098
00099 return add_word_to_schema(word, schema);
00100 } else {
00101
00102 return schema.word_to_char.end();
00103 }
00104 }
00105
00106 static void compress_emit_word(std::ostream &out, std::string const &word, compression_schema &schema)
00107 {
00108
00109 const compression_schema::word_char_map::const_iterator w = get_word_in_schema(word, schema, out);
00110 if (w != schema.word_to_char.end()) {
00111
00112 out.put(w->second);
00113 } else {
00114
00115 out.put(compress_literal_word);
00116 compress_output_literal_word(out, word);
00117 }
00118 }
00119
00120 static std::string compress_read_literal_word(std::istream &in)
00121 {
00122 std::string buffer;
00123 std::getline(in, buffer, '\0');
00124 if (!in.good())
00125 throw config::error("Unexpected end of data in compressed config read");
00126 return buffer;
00127 }
00128
00129 static void write_compressed_internal(std::ostream &out, config const &cfg, compression_schema &schema, int level)
00130 {
00131 if (level > max_recursion_levels)
00132 throw config::error("Too many recursion levels in compressed config write");
00133
00134 for (string_map::const_iterator i = cfg.values.begin(), i_end = cfg.values.end(); i != i_end; ++i) {
00135 if (i->second.empty() == false) {
00136
00137 compress_emit_word(out, i->first, schema);
00138
00139
00140 compress_output_literal_word(out, i->second.to_serialized());
00141 }
00142 }
00143
00144 for (config::all_children_iterator j = cfg.ordered_begin(), j_end = cfg.ordered_end(); j != j_end; ++j) {
00145 std::pair< std::string const *, config const * > const &item = *j;
00146 std::string const &name = *item.first;
00147 config const &cfg2 = *item.second;
00148
00149 out.put(compress_open_element);
00150 compress_emit_word(out, name, schema);
00151 write_compressed_internal(out, cfg2, schema, level + 1);
00152 out.put(compress_close_element);
00153 }
00154 }
00155
00156 void write_compressed(std::ostream &out, config const &cfg, compression_schema &schema)
00157 {
00158 write_compressed_internal(out, cfg, schema, 0);
00159 }
00160
00161 static void read_compressed_internal(config &cfg, std::istream &in, compression_schema &schema, int level)
00162 {
00163 increment_binary_wml_progress();
00164 if (level >= max_recursion_levels)
00165 throw config::error("Too many recursion levels in compressed config read");
00166
00167 bool in_open_element = false;
00168 for(;;) {
00169 unsigned char const c = in.get();
00170 if (!in.good())
00171 return;
00172 switch (c) {
00173 case compress_open_element:
00174 in_open_element = true;
00175 break;
00176 case compress_close_element:
00177 return;
00178 case compress_schema_item:
00179 add_word_to_schema(compress_read_literal_word(in), schema);
00180 break;
00181
00182 default: {
00183 std::string word;
00184 if (c == compress_literal_word) {
00185 word = compress_read_literal_word(in);
00186 } else {
00187 unsigned int code = c;
00188
00189 const compression_schema::char_word_map::const_iterator itor
00190 = schema.char_to_word.find(code);
00191 if (itor == schema.char_to_word.end()) {
00192 ERR_CF << "illegal word code: " << code << "\n";
00193 throw config::error("Illegal character in compression input");
00194 }
00195
00196 word = itor->second;
00197 }
00198
00199 if (in_open_element) {
00200 in_open_element = false;
00201 config &cfg2 = cfg.add_child(word);
00202 read_compressed_internal(cfg2, in, schema, level + 1);
00203 } else {
00204
00205 std::string value = compress_read_literal_word(in);
00206 t_string t_value = t_string::from_serialized(value);
00207 cfg.values.insert(std::make_pair(word, t_value));
00208 }
00209 }
00210
00211 }
00212 }
00213 }
00214
00215 void read_compressed(config &cfg, std::istream &in, compression_schema &schema)
00216 {
00217 cfg.clear();
00218 read_compressed_internal(cfg, in, schema, 0);
00219 }
00220
00221 void write_compressed(std::ostream &out, config const &cfg) {
00222 compression_schema schema;
00223 write_compressed(out, cfg, schema);
00224 }
00225
00226 void read_compressed(config &cfg, std::istream &in) {
00227 compression_schema schema;
00228 read_compressed(cfg, in, schema);
00229 }
00230