Fixed UMC loading with new caching code

This commit is contained in:
Pauli Nieminen 2008-08-29 08:14:36 +00:00
parent dedfe5146d
commit 30a7dadec5
12 changed files with 363 additions and 126 deletions

View file

@ -7,9 +7,23 @@
[test_key]
define=test
[/test_key]
#define TEST_TRANSACTION
[test_key3]
define=transaction
[/test_key3]
#enddef
#define TEST_TRANSACTION_PARAM FIRST SECOND
[test_key4]
{FIRST}={SECOND}
[/test_key4]
#enddef
#endif
#ifdef TEST_DEFINE
[test_key2]
define=_ "testing translation reset."
[/test_key2]
#endif
#ifdef TEST_MACRO
{TEST_TRANSACTION}
{TEST_TRANSACTION_PARAM defined parameter}
#endif

7
data/test/test/umc.cfg Normal file
View file

@ -0,0 +1,7 @@
#textdomain wesnoth-test
[umc]
test="umc load"
[/umc]
{TEST_TRANSACTION}
{TEST_TRANSACTION_PARAM defined parameter}

View file

@ -26,6 +26,8 @@
#include "serialization/binary_or_text.hpp"
#include "serialization/parser.hpp"
#include <boost/bind.hpp>
#define ERR_CONFIG LOG_STREAM(err, config)
#define LOG_CONFIG LOG_STREAM(info, config)
#define DBG_CONFIG LOG_STREAM(debug, config)
@ -43,12 +45,19 @@ namespace game_config {
config_cache::config_cache() :
force_valid_cache_(false),
use_cache_(true),
defines_map_()
defines_map_(),
defines_active_map_()
{
// To settup initial defines map correctly
clear_defines();
}
struct output {
void operator()(const preproc_map::value_type& def)
{
std::cout << "key: " << def.first << " " << def.second << "\n";
}
};
const preproc_map& config_cache::get_preproc_map() const
{
return defines_map_;
@ -56,7 +65,9 @@ namespace game_config {
void config_cache::clear_defines()
{
LOG_CONFIG << "Clearing defines map!\n";
defines_map_.clear();
defines_active_map_.clear();
// settup default defines map
#ifdef USE_TINY_GUI
@ -89,43 +100,101 @@ namespace game_config {
return ret;
}
void config_cache::write_file(std::string path, const config& cfg)
{
preproc_map temp;
write_file(path,cfg, temp);
}
void config_cache::write_file(std::string path, const config& cfg, const preproc_map& defines_map)
{
scoped_ostream stream = ostream_file(path);
switch(game_config::cache_type)
const bool gzip = true;
config_writer writer(*stream,gzip,"",game_config::cache_compression_level);
writer.write(cfg);
if (!defines_map.empty())
{
case GZIP:
{
const bool gzip = true;
config_writer writer(*stream,gzip,"",game_config::cache_compression_level);
writer.write(cfg);
}
break;
case BWML:
write_compressed(*stream, cfg);
break;
writer.write(defines_map);
}
}
void config_cache::read_file(const std::string& path, config& cfg)
{
scoped_istream stream = istream_file(path);
read_gz(cfg, *stream);
}
void config_cache::read_file(const std::string& path, config& cfg, preproc_map& defines_map)
{
std::string error_log;
scoped_istream stream = istream_file(path);
switch(game_config::cache_type)
read_gz(cfg, *stream, &error_log);
if (!error_log.empty())
{
case BWML:
read_compressed(cfg, *stream);
throw config::error(error_log);
}
}
preproc_map& config_cache::make_copy_map()
{
if(config_cache_transaction::get_state() == config_cache_transaction::FREE)
{
defines_active_map_.clear();
std::copy(defines_map_.begin(),
defines_map_.end(),
std::insert_iterator<preproc_map>(defines_active_map_, defines_active_map_.begin()));
}
return defines_active_map_;
}
void config_cache::insert_to_active(const preproc_map::value_type& def)
{
defines_active_map_[def.first] = def.second;
}
bool compare_define(const preproc_map::value_type& a, const preproc_map::value_type& b)
{
if (a.first < b.first)
return true;
if (b.first < a.first)
return false;
if (a.second < b.second)
return true;
return false;
}
void config_cache::add_defines_map_diff(preproc_map& defines_map)
{
switch(config_cache_transaction::get_state())
{
case config_cache_transaction::FREE:
defines_active_map_.clear();
defines_map.clear();
break;
case GZIP:
read_gz(cfg, *stream);
case config_cache_transaction::START:
{
preproc_map temp;
std::set_difference(defines_map.begin(),
defines_map.end(),
defines_active_map_.begin(),
defines_active_map_.end(),
std::insert_iterator<preproc_map>(temp,temp.begin()),
&compare_define);
std::for_each(temp.begin(), temp.end(),boost::bind(&config_cache::insert_to_active,this, _1));
temp.swap(defines_map);
}
break;
case config_cache_transaction::LOCK:
defines_map.clear();
break;
}
}
void config_cache::read_configs(const std::string& path, config& cfg)
{
preproc_map defines_map(defines_map_);
void config_cache::read_configs(const std::string& path, config& cfg, preproc_map& defines_map)
{
std::string error_log;
//read the file and then write to the cache
scoped_istream stream = preprocess_file(path, &defines_map, &error_log);
@ -185,21 +254,26 @@ namespace game_config {
LOG_CONFIG << "found valid cache at '" << fname << "' with defines_map " << defines_string.str() << "\n";
log_scope("read cache");
try {
read_file(fname,cfg);
preproc_map defines_map;
read_file(fname,cfg, defines_map);
add_defines_map_diff(defines_map);
return;
} catch(config::error&) {
ERR_CONFIG << "cache is corrupt. Loading from files\n";
} catch(config::error& e) {
ERR_CONFIG << "cache " << fname << " is corrupt. Loading from files: "<< e.message<<"\n";
} catch(io_exception&) {
ERR_CONFIG << "error reading cache. Loading from files\n";
ERR_CONFIG << "error reading cache " << fname << ". Loading from files\n";
}
}
LOG_CONFIG << "no valid cache found. Writing cache to '" << fname << " with defines_map "<< defines_string.str() << "'\n";
preproc_map copy_map(make_copy_map());
read_configs(path, cfg);
read_configs(path, cfg, copy_map);
add_defines_map_diff(copy_map);
try {
write_file(fname, cfg);
write_file(fname, cfg,copy_map);
config checksum_cfg;
data_tree_checksum().write(checksum_cfg);
write_file(fname_checksum, checksum_cfg);
@ -210,7 +284,9 @@ namespace game_config {
}
}
LOG_CONFIG << "Loading plain config instead of cache\n";
load_configs(path, cfg);
preproc_map copy_map(make_copy_map());
read_configs(path, cfg, copy_map);
add_defines_map_diff(copy_map);
}
void config_cache::load_configs(const std::string& path, config& cfg)
@ -219,7 +295,9 @@ namespace game_config {
{
read_cache(path, cfg);
} else {
read_configs(path, cfg);
preproc_map copy_map(make_copy_map());
read_configs(path, cfg, copy_map);
add_defines_map_diff(copy_map);
}
return;
@ -251,4 +329,23 @@ namespace game_config {
DBG_CONFIG << "removing define: " << define << "\n";
defines_map_.erase(define);
}
config_cache_transaction::state config_cache_transaction::transaction_ = FREE;
config_cache_transaction::config_cache_transaction()
{
assert(transaction_ == FREE);
transaction_ = START;
}
config_cache_transaction::~config_cache_transaction()
{
transaction_ = FREE;
}
void config_cache_transaction::lock()
{
transaction_ = LOCK;
}
}

View file

@ -36,23 +36,27 @@ namespace game_config {
bool force_valid_cache_, use_cache_;
preproc_map defines_map_;
void read_configs(config&, std::string&);
preproc_map defines_active_map_;
void read_file(const std::string& file, config& cfg);
void read_file(const std::string& file, config& cfg, preproc_map&);
void write_file(std::string file, const config& cfg);
void write_file(std::string file, const config& cfg, const preproc_map&);
void read_cache(const std::string& path, config& cfg);
void read_configs(const std::string& path, config& cfg);
void read_configs(const std::string& path, config& cfg, preproc_map& defines);
void load_configs(const std::string& path, config& cfg);
preproc_map& make_copy_map();
// void add_defines_map(const preproc_map&);
void add_defines_map_diff(preproc_map&);
// Protected to let test code access
protected:
config_cache();
const preproc_map& get_preproc_map() const;
void load_configs(const std::string& path, config& cfg);
public:
/**
@ -66,6 +70,7 @@ namespace game_config {
* @param config object that is writen to, It should be empty
* because there is no quarentee how filled in config is handled
**/
const preproc_map& get_preproc_map() const;
void get_config(const std::string& path, config& cfg);
/**
* get config_ptr from given path
@ -99,6 +104,12 @@ namespace game_config {
* Force cache checksum validation.
**/
void recheck_filetree_checksum();
/**
* Used to let std::for_each insert defines from another
* map to active
**/
void insert_to_active(const preproc_map::value_type& def);
};
/**
@ -123,5 +134,26 @@ namespace game_config {
typedef scoped_preproc_define_internal<config_cache> scoped_preproc_define;
typedef boost::scoped_ptr<scoped_preproc_define> scoped_preproc_define_sptr;
/**
* Used to share macros between load operations
* It uses empty map if no transaction is started
**/
class config_cache_transaction : private boost::noncopyable {
public:
config_cache_transaction();
~config_cache_transaction();
void lock();
enum state { FREE,
START,
LOCK
};
private:
static state transaction_;
public:
static state get_state()
{return transaction_; }
};
}
#endif

View file

@ -1279,7 +1279,7 @@ bool game_controller::play_multiplayer()
/* do */ {
cache_.clear_defines();
cache_.add_define(state_.campaign_define);
game_config::scoped_preproc_define multiplayer(state_.campaign_define);
load_game_cfg();
events::discard(INPUT_MASK); // prevent the "keylogger" effect
cursor::set(cursor::NORMAL);
@ -1425,8 +1425,14 @@ void game_controller::load_game_cfg()
//reset the parse counter before reading the game files
loadscreen::global_loadscreen->parser_counter = 0;
// start transaction so macros are shared
game_config::config_cache_transaction main_transaction;
cache_.get_config(game_config::path +"/data", game_config_);
main_transaction.lock();
// clone and put the gfx rules aside so that we can prepend the add-on
// rules to them.
config core_terrain_rules;

View file

@ -53,13 +53,8 @@ namespace game_config
std::string wesnothd_name;
bool debug = false, editor = false, ignore_replay_errors = false, mp_debug = false, exit_at_end = false, no_delay = false, small_gui = false, disable_autosave = false;
#ifdef USE_DUMMYLOCALES
bool use_dummylocales = true;
#else
bool use_dummylocales = false;
#endif
cache_types cache_type = BWML;
int cache_compression_level = 5;
std::string game_icon = "wesnoth-icon.png", game_title, game_logo, title_music, lobby_music;

View file

@ -50,11 +50,6 @@ namespace game_config
extern bool use_dummylocales;
extern enum cache_types {
BWML,
GZIP
} cache_type;
extern int cache_compression_level;
extern std::string path;

View file

@ -23,9 +23,11 @@
#include "filesystem.hpp"
#include "serialization/binary_wml.hpp"
#include "serialization/parser.hpp"
#include "serialization/preprocessor.hpp"
#include <sstream>
#include <boost/bind.hpp>
#include <boost/iostreams/filter/gzip.hpp>
bool detect_format_and_read(config &cfg, std::istream &in, std::string* error_log)
@ -67,6 +69,29 @@ config_writer::config_writer(
}
}
void config_writer::write_define(const preproc_map::value_type& def)
{
out_ << "#line " << def.second.linenum << " " << def.second.location << "\n";
out_ << "#textdomain " << def.second.textdomain << "\n";
out_ << "#define " << def.first;
for(std::vector<std::string>::const_iterator it = def.second.arguments.begin();
it != def.second.arguments.end();
++it)
{
out_ << *it << " ";
}
out_ << "\n";
out_ << def.second.value;
out_ << "#enddef\n";
}
void config_writer::write(const preproc_map& defines_map)
{
std::for_each(defines_map.begin(), defines_map.end(), boost::bind(&config_writer::write_define,this,_1));
}
void config_writer::write(const config &cfg)
{
::write(out_, cfg, level_);

View file

@ -19,13 +19,14 @@
#ifndef SERIALIZATION_BINARY_OR_TEXT_HPP_INCLUDED
#define SERIALIZATION_BINARY_OR_TEXT_HPP_INCLUDED
#include "preprocessor.hpp"
#include <iosfwd>
#include <string>
#include <boost/iostreams/filtering_stream.hpp>
class config;
//! Reads a file, and detects it is compressed before reading it.
//! If it is not a valid file at all, it will throw an error
//! as if it was trying to read it as text WML.
@ -42,6 +43,10 @@ public:
config_writer(std::ostream &out, bool compress, const std::string &textdomain, int level = -1);
void write(const config &cfg);
void write(const preproc_map& defines_map);
void write_define(const preproc_map::value_type&);
void write_child(const std::string &key, const config &cfg);
void write_key_val(const std::string &key, const std::string &value);
void open_child(const std::string &key);

View file

@ -42,6 +42,14 @@ bool preproc_define::operator==(preproc_define const &v) const {
return value == v.value && arguments == v.arguments;
}
bool preproc_define::operator<(preproc_define const &v) const {
if (location < v.location)
return true;
if (v.location < location)
return false;
return linenum < v.linenum;
}
std::ostream& operator<<(std::ostream& stream, const preproc_define& def)
{

View file

@ -38,6 +38,7 @@ struct preproc_define
int linenum;
std::string location;
bool operator==(preproc_define const &) const;
bool operator<(preproc_define const &) const;
bool operator!=(preproc_define const &v) const { return !operator==(v); }
};

View file

@ -25,54 +25,8 @@
#include "serialization/preprocessor.hpp"
/**
* Used to make distinct singleton for testing it
* because other tests will need original one to load data
**/
class test_config_cache : public game_config::config_cache {
test_config_cache() : game_config::config_cache() {}
#include <boost/bind.hpp>
static test_config_cache cache_;
public:
static test_config_cache& instance() {
return cache_;
}
const preproc_map& get_preproc_map() const {
return game_config::config_cache::get_preproc_map();
}
};
/**
* Used to redirect defines settings to test cache
**/
typedef game_config::scoped_preproc_define_internal<test_config_cache> test_scoped_define;
test_config_cache test_config_cache::cache_;
struct config_cache_fixture {
config_cache_fixture() : cache(test_config_cache::instance()), old_locale(get_language())
{
const language_list& langs = get_languages();
language_list::const_iterator German = std::find_if(langs.begin(), langs.end(), *this);
set_language(*German);
}
~config_cache_fixture()
{
set_language(old_locale);
}
test_config_cache& cache;
language_def old_locale;
bool operator()(const language_def& def)
{
return def.localename == "de_DE";
}
};
BOOST_FIXTURE_TEST_SUITE( config_cache, config_cache_fixture )
const std::string test_data_path("data/test/test/_main.cfg");
static preproc_map setup_test_preproc_map()
{
@ -97,48 +51,52 @@ static preproc_map setup_test_preproc_map()
}
static config setup_test_config()
{
config test_config;
config* child = &test_config.add_child("textdomain");
(*child)["name"] = GETTEXT_DOMAIN;
child = &test_config.add_child("test_key");
(*child)["define"] = "test";
return test_config;
}
/**
* Used to make distinct singleton for testing it
* because other tests will need original one to load data
**/
class test_config_cache : public game_config::config_cache {
test_config_cache() : game_config::config_cache() {}
BOOST_AUTO_TEST_CASE( test_config_cache_defaults )
{
preproc_map defines_map(setup_test_preproc_map());
static test_config_cache cache_;
const preproc_map& test_defines = cache.get_preproc_map();
BOOST_CHECK_EQUAL_COLLECTIONS(test_defines.begin(),test_defines.end(),
defines_map.begin() ,defines_map.end());
}
public:
static test_config_cache& instance() {
return cache_;
}
};
/**
* Used to redirect defines settings to test cache
**/
typedef game_config::scoped_preproc_define_internal<test_config_cache> test_scoped_define;
BOOST_AUTO_TEST_CASE( test_load_config )
{
test_scoped_define test_def("TEST");
test_config_cache test_config_cache::cache_;
config test_config = setup_test_config();
BOOST_CHECK_EQUAL(test_config, *cache.get_config(test_data_path));
test_scoped_define test_define_def("TEST_DEFINE");
config* child = &test_config.add_child("test_key2");
(*child)["define"] = t_string("testing translation reset.", GETTEXT_DOMAIN);
BOOST_CHECK_EQUAL(test_config, *cache.get_config(test_data_path));
BOOST_CHECK_EQUAL((*test_config.child("test_key2"))["define"].str(), (*cache.get_config(test_data_path)->child("test_key2"))["define"].str());
}
struct config_cache_fixture {
config_cache_fixture() : cache(test_config_cache::instance()), old_locale(get_language()), test_def("TEST")
{
const language_list& langs = get_languages();
language_list::const_iterator German = std::find_if(langs.begin(), langs.end(), boost::bind(&config_cache_fixture::match_de, this, _1));
set_language(*German);
}
~config_cache_fixture()
{
set_language(old_locale);
}
test_config_cache& cache;
language_def old_locale;
test_scoped_define test_def;
bool match_de(const language_def& def)
{
return def.localename == "de_DE";
}
};
BOOST_AUTO_TEST_CASE( test_preproc_defines )
{
test_config_cache& cache = test_config_cache::instance();
const preproc_map& test_defines = cache.get_preproc_map();
preproc_map defines_map(setup_test_preproc_map());
@ -173,10 +131,54 @@ BOOST_AUTO_TEST_CASE( test_preproc_defines )
defines_map.begin() ,defines_map.end());
}
BOOST_AUTO_TEST_CASE( test_config_cache_defaults )
{
test_config_cache& cache = test_config_cache::instance();
preproc_map defines_map(setup_test_preproc_map());
const preproc_map& test_defines = cache.get_preproc_map();
BOOST_CHECK_EQUAL_COLLECTIONS(test_defines.begin(),test_defines.end(),
defines_map.begin() ,defines_map.end());
}
BOOST_FIXTURE_TEST_SUITE( config_cache, config_cache_fixture )
const std::string test_data_path("data/test/test/_main.cfg");
static config setup_test_config()
{
config test_config;
config* child = &test_config.add_child("textdomain");
(*child)["name"] = GETTEXT_DOMAIN;
child = &test_config.add_child("test_key");
(*child)["define"] = "test";
return test_config;
}
BOOST_AUTO_TEST_CASE( test_load_config )
{
config test_config = setup_test_config();
BOOST_CHECK_EQUAL(test_config, *cache.get_config(test_data_path));
test_scoped_define test_define_def("TEST_DEFINE");
config* child = &test_config.add_child("test_key2");
(*child)["define"] = t_string("testing translation reset.", GETTEXT_DOMAIN);
BOOST_CHECK_EQUAL(test_config, *cache.get_config(test_data_path));
BOOST_CHECK_EQUAL((*test_config.child("test_key2"))["define"].str(), (*cache.get_config(test_data_path)->child("test_key2"))["define"].str());
}
BOOST_AUTO_TEST_CASE( test_non_clean_config_loading )
{
test_scoped_define test_def("TEST");
config test_config = setup_test_config();
// Test clean load first
@ -195,6 +197,56 @@ BOOST_AUTO_TEST_CASE( test_non_clean_config_loading )
BOOST_CHECK_EQUAL(test_config, cfg);
}
}
BOOST_AUTO_TEST_CASE( test_macrosubstitution )
{
config test_config = setup_test_config();
config* child = &test_config.add_child("test_key3");
(*child)["define"] = "transaction";
child = &test_config.add_child("test_key4");
(*child)["defined"] = "parameter";
// test first that macro loading works
test_scoped_define macro("TEST_MACRO");
// Without cache
BOOST_CHECK_EQUAL(test_config, *cache.get_config(test_data_path));
// With cache
BOOST_CHECK_EQUAL(test_config, *cache.get_config(test_data_path));
}
BOOST_AUTO_TEST_CASE( test_transaction )
{
config test_config = setup_test_config();
config* child = &test_config.add_child("test_key3");
(*child)["define"] = "transaction";
child = &test_config.add_child("test_key4");
(*child)["defined"] = "parameter";
// test first that macro loading works
test_scoped_define macro("TEST_MACRO");
//Start transaction
game_config::config_cache_transaction transaction;
BOOST_CHECK_EQUAL(test_config, *cache.get_config(test_data_path));
transaction.lock();
config umc_config;
child = &umc_config.add_child("umc");
(*child)["test"] = "umc load";
child = &test_config.add_child("test_key3");
(*child)["define"] = "transaction";
child = &test_config.add_child("test_key4");
(*child)["defined"] = "parameter";
BOOST_CHECK_EQUAL(umc_config, *cache.get_config("data/test/test/umc.cfg"));
}
/* vim: set ts=4 sw=4: */
BOOST_AUTO_TEST_SUITE_END()