Skip to content

Commit

Permalink
Rewrite json_loader to use cata_path and contextually relevant cache …
Browse files Browse the repository at this point in the history
…paths.
  • Loading branch information
akrieger committed Sep 16, 2022
1 parent d329307 commit e002746
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 25 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
/bindist/
/build/
/build-start-time
/cache/
/cmake-build-debug/
/config/
/data/*.template
Expand Down
6 changes: 1 addition & 5 deletions src/cata_path.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
3 changes: 1 addition & 2 deletions src/flexbuffer_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 1 addition & 3 deletions src/flexbuffer_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 ) ;

Expand Down
155 changes: 140 additions & 15 deletions src/json_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 &base_cache()
{
static flexbuffer_cache cache{ fs::u8path( PATH_INFO::base_path() ) / "cache", fs::u8path( PATH_INFO::base_path() ) };
return cache;
}

flexbuffer_cache &config_cache()
{
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 &global_cache()
flexbuffer_cache &user_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::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( fs::path source_file, size_t offset )
cata::optional<JsonValue> from_path_at_offset_opt_impl( const cata_path &source_file,
size_t offset )
{
std::shared_ptr<parsed_flexbuffer> buffer = global_cache().parse_and_cache(
std::move( source_file ), 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;
}
Expand All @@ -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 );
}
Expand All @@ -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 } );
}
8 changes: 8 additions & 0 deletions src/json_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <ghc/fs_std_fwd.hpp>

#include "path_info.h"
#include "flexbuffer_json.h"

class json_loader
Expand All @@ -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 );
Expand Down

0 comments on commit e002746

Please sign in to comment.