Rewrite json_loader to use cata_path and contextually relevant cache paths.

This commit is contained in:
akrieger 2022-07-03 11:50:47 -07:00
parent d329307a51
commit e0027464e2
6 changed files with 154 additions and 27 deletions

1
.gitignore vendored
View File

@ -13,6 +13,7 @@
/bindist/
/build/
/build-start-time
/cache/
/cmake-build-debug/
/config/
/data/*.template

View File

@ -60,13 +60,9 @@ class cata_path
// Returns an fs::path constructed by concatenating the 'actual' path
// value for the logical root with the relative path stored in this.
// path with root_path::unknown are returned as-is.
// If the contained path is not relative, simply returns the root path.
inline fs::path get_unrelative_path() const {
fs::path result = get_logical_root_path();
// TODO check for '..' components?
if( relative_path_.is_relative() ) {
result /= relative_path_;
}
result /= relative_path_;
return result;
}

View File

@ -327,8 +327,7 @@ class flexbuffer_disk_cache
flexbuffer_cache::flexbuffer_cache( const fs::path &cache_directory,
const fs::path &root_directory )
{
disk_cache_ = flexbuffer_disk_cache::init_from_folder( cache_directory,
root_directory );
disk_cache_ = flexbuffer_disk_cache::init_from_folder( cache_directory, root_directory );
}
flexbuffer_cache::~flexbuffer_cache() = default;

View File

@ -55,10 +55,8 @@ class flexbuffer_cache
explicit flexbuffer_cache( const fs::path &cache_directory, const fs::path &root_directory );
~flexbuffer_cache();
size_t drop_cache() noexcept;
// Throw exceptions on IO and parse errors.
shared_flexbuffer parse( fs::path json_source_path, size_t offset = 0 ) noexcept( false );
static shared_flexbuffer parse( fs::path json_source_path, size_t offset = 0 ) noexcept( false );
shared_flexbuffer parse_and_cache( fs::path json_source_path,
size_t offset = 0 ) noexcept( false ) ;

View File

@ -5,24 +5,123 @@
#include <ghc/fs_std_fwd.hpp>
#include "filesystem.h"
#include "flexbuffer_cache.h"
#include "flexbuffer_json.h"
#include "path_info.h"
namespace
{
flexbuffer_cache &global_cache()
flexbuffer_cache &base_cache()
{
static flexbuffer_cache cache{ fs::u8path( PATH_INFO::cache_dir() ), fs::u8path( PATH_INFO::base_path() ) };
static flexbuffer_cache cache{ fs::u8path( PATH_INFO::base_path() ) / "cache", fs::u8path( PATH_INFO::base_path() ) };
return cache;
}
// The file pointed to by source_file must exist.
cata::optional<JsonValue> from_path_at_offset_opt_impl( fs::path source_file, size_t offset )
flexbuffer_cache &config_cache()
{
std::shared_ptr<parsed_flexbuffer> buffer = global_cache().parse_and_cache(
std::move( source_file ), offset );
static flexbuffer_cache cache{ fs::u8path( PATH_INFO::config_dir() ) / "cache", fs::u8path( PATH_INFO::config_dir() ) };
return cache;
}
flexbuffer_cache &data_cache()
{
static flexbuffer_cache cache{ fs::u8path( PATH_INFO::datadir() ) / "cache", fs::u8path( PATH_INFO::datadir() ) };
return cache;
}
flexbuffer_cache &memorial_cache()
{
static flexbuffer_cache cache{ fs::u8path( PATH_INFO::memorialdir() ) / "cache", fs::u8path( PATH_INFO::memorialdir() ) };
return cache;
}
flexbuffer_cache &user_cache()
{
static flexbuffer_cache cache{ fs::u8path( PATH_INFO::user_dir() ) / "cache", fs::u8path( PATH_INFO::user_dir() ) };
return cache;
}
std::unordered_map<std::string, std::unique_ptr<flexbuffer_cache>> save_caches;
std::unordered_map<std::string, std::unordered_map<std::string, std::unique_ptr<flexbuffer_cache>>>
world_to_character_caches;
flexbuffer_cache &cache_for_save( const cata_path &path )
{
// Assume lexically normal path
auto path_it = path.get_relative_path().begin();
// First path element is the world name
std::string worldname = path_it->u8string();
++path_it;
// Next element is either a file, a character folder, or the maps folder
std::string folder_or_file = path_it->u8string();
++path_it;
auto it = save_caches.find( worldname );
if( it == save_caches.end() ) {
it = save_caches.emplace( worldname,
std::make_unique<flexbuffer_cache>( fs::u8path( PATH_INFO::savedir() ) / worldname / "cache",
fs::u8path( PATH_INFO::savedir() ) / worldname ) ).first;
}
if( folder_or_file == "maps" ) {
// Generic per-save cache is fine.
return *it->second;
}
// Either a global save file or a per-character file.
fs::path test_path = fs::u8path( PATH_INFO::savedir() ) / worldname / folder_or_file;
if( fs::is_directory( test_path ) ) {
// Character file.
const std::string &character_name = folder_or_file;
std::unordered_map<std::string, std::unique_ptr<flexbuffer_cache>> &character_caches_for_world =
world_to_character_caches[worldname];
it = character_caches_for_world.find( character_name );
if( it == character_caches_for_world.end() ) {
it = character_caches_for_world.emplace( character_name,
std::make_unique<flexbuffer_cache>( test_path / "cache", test_path ) ).first;
}
}
return *it->second;
}
flexbuffer_cache &cache_for_lexically_normal_path( const cata_path &path )
{
switch( path.get_logical_root() ) {
case cata_path::root_path::base:
return base_cache();
case cata_path::root_path::config:
return config_cache();
case cata_path::root_path::data:
return data_cache();
case cata_path::root_path::memorial:
return memorial_cache();
case cata_path::root_path::save:
// Saves we store data one per save.
// But also... per character per save.
return cache_for_save( path );
case cata_path::root_path::user:
return user_cache();
case cata_path::root_path::unknown:
default:
cata_fatal( "Cannot create cache for unknown path %s",
path.generic_u8string() );
}
}
// The file pointed to by source_file must exist.
cata::optional<JsonValue> from_path_at_offset_opt_impl( const cata_path &source_file,
size_t offset )
{
cata_path lexically_normal_path = source_file.lexically_normal();
std::shared_ptr<parsed_flexbuffer> buffer;
if( lexically_normal_path.get_logical_root() != cata_path::root_path::unknown ) {
flexbuffer_cache &cache = cache_for_lexically_normal_path( lexically_normal_path );
buffer = cache.parse_and_cache(
lexically_normal_path.get_unrelative_path(), offset );
} else {
buffer = flexbuffer_cache::parse( lexically_normal_path.get_unrelative_path(), offset );
}
if( !buffer ) {
return cata::nullopt;
}
@ -33,35 +132,38 @@ cata::optional<JsonValue> from_path_at_offset_opt_impl( fs::path source_file, si
} // namespace
cata::optional<JsonValue> json_loader::from_path_at_offset_opt( fs::path source_file,
cata::optional<JsonValue> json_loader::from_path_at_offset_opt( const cata_path &source_file,
size_t offset ) noexcept( false )
{
if( !file_exist( source_file ) ) {
if( !file_exist( source_file.get_unrelative_path() ) ) {
return cata::nullopt;
}
return from_path_at_offset_opt_impl( std::move( source_file ), offset );
return from_path_at_offset_opt_impl( source_file, offset );
}
cata::optional<JsonValue> json_loader::from_path_opt( fs::path source_file ) noexcept( false )
cata::optional<JsonValue> json_loader::from_path_opt( const cata_path &source_file ) noexcept(
false )
{
return from_path_at_offset_opt( std::move( source_file ), 0 );
return from_path_at_offset_opt( source_file, 0 );
}
JsonValue json_loader::from_path_at_offset( const fs::path &source_file,
JsonValue json_loader::from_path_at_offset( const cata_path &source_file,
size_t offset ) noexcept( false )
{
if( !file_exist( source_file ) ) {
throw JsonError( source_file.generic_u8string() + " does not exist." );
fs::path unrelative_path = source_file.get_unrelative_path();
if( !file_exist( unrelative_path ) ) {
throw JsonError( unrelative_path.generic_u8string() + " does not exist." );
}
auto obj = from_path_at_offset_opt_impl( source_file, offset );
if( !obj ) {
throw JsonError( "Json file " + source_file.generic_u8string() + " did not contain valid json" );
throw JsonError( "Json file " + unrelative_path.generic_u8string() +
" did not contain valid json" );
}
return std::move( *obj );
}
JsonValue json_loader::from_path( const fs::path &source_file ) noexcept( false )
JsonValue json_loader::from_path( const cata_path &source_file ) noexcept( false )
{
return from_path_at_offset( source_file, 0 );
}
@ -76,3 +178,26 @@ JsonValue json_loader::from_string( std::string const &data ) noexcept( false )
flexbuffers::Reference buffer_root = flexbuffer_root_from_storage( buffer->get_storage() );
return JsonValue( std::move( buffer ), buffer_root, nullptr, 0 );
}
cata::optional<JsonValue> json_loader::from_path_at_offset_opt( fs::path source_file,
size_t offset ) noexcept( false )
{
return from_path_at_offset_opt( cata_path{ cata_path::root_path::unknown, std::move( source_file ) },
offset );
}
cata::optional<JsonValue> json_loader::from_path_opt( fs::path source_file ) noexcept( false )
{
return from_path_opt( cata_path{ cata_path::root_path::unknown, std::move( source_file ) } );
}
JsonValue json_loader::from_path_at_offset( const fs::path &source_file,
size_t offset ) noexcept( false )
{
return from_path_at_offset( cata_path{ cata_path::root_path::unknown, source_file }, offset );
}
JsonValue json_loader::from_path( const fs::path &source_file ) noexcept( false )
{
return from_path( cata_path{ cata_path::root_path::unknown, source_file } );
}

View File

@ -4,6 +4,7 @@
#include <ghc/fs_std_fwd.hpp>
#include "path_info.h"
#include "flexbuffer_json.h"
class json_loader
@ -16,10 +17,17 @@ class json_loader
static JsonValue from_path_at_offset( const fs::path &source_file,
size_t offset = 0 ) noexcept( false );
static JsonValue from_path( const cata_path &source_file ) noexcept( false );
static JsonValue from_path_at_offset( const cata_path &source_file,
size_t offset = 0 ) noexcept( false );
// Like json_loader::from_path, except does not throw if the file does not exist. It will still throw if the json cannot be parsed.
static cata::optional<JsonValue> from_path_opt( fs::path source_file ) noexcept( false );
static cata::optional<JsonValue> from_path_at_offset_opt( fs::path source_file,
size_t offset = 0 ) noexcept( false );
static cata::optional<JsonValue> from_path_opt( const cata_path &source_file ) noexcept( false );
static cata::optional<JsonValue> from_path_at_offset_opt( const cata_path &source_file,
size_t offset = 0 ) noexcept( false );
// Like json_loader::from_path, except instead of parsing data from a file, will parse data from a string in memory.
static JsonValue from_string( std::string const &data ) noexcept( false );