Cogs.Core
DiskCache.cpp
1#include "DiskCache.h"
2#include "JsonSerialization.h"
3
4#include "Foundation/Logging/Logger.h"
5#include "Foundation/Platform/IO.h"
6
7#include <fstream>
8
9namespace fs = std::filesystem;
10
11namespace {
12 using namespace Cogs::Core;
13 using namespace Cogs::Core::TerrainProvider;
14 Cogs::Logging::Log logger = Cogs::Logging::getLogger("DiskCache");
15
16#if 0
17 uint64_t combine(uint32_t a, uint32_t b)
18 {
19 return (uint64_t(a) << 32) | uint64_t(b);
20 }
21#endif
22
23 std::string buildPath(const std::string& pathPrefix, unsigned i, unsigned j, unsigned level, TerrainProvider::MimeType& kind)
24 {
25 std::string suffix;
26 switch (kind) {
27 case TerrainProvider::MimeType::None: suffix = ".none"; break;
28 case TerrainProvider::MimeType::Png: suffix = ".png"; break;
29 case TerrainProvider::MimeType::Gif: suffix = ".gif"; break;
30 case TerrainProvider::MimeType::Jpeg: suffix = ".jpg"; break;
31 case TerrainProvider::MimeType::Tiff: suffix = ".tif"; break;
32 case TerrainProvider::MimeType::F32: suffix = ".f32"; break;
33 case TerrainProvider::MimeType::RGBA8:suffix = ".rgba"; break;
34 default:
35 assert(false);
36 break;
37 }
38
39 std::string filename = std::to_string(j) + suffix;
40
41 return Cogs::IO::combine(pathPrefix, Cogs::IO::combine(std::to_string(level), Cogs::IO::combine(std::to_string(i), filename)));
42 }
43
44}
45
46bool Cogs::Core::TerrainProvider::DiskCache::init(Context* context, const BaseConfig* providerConf, StringView cachesPath)
47{
48 assert(providerConf);
49 assert(providerConf->cacheKey != NoString);
50
51 mimeType = providerConf->mimeType;
52 if (mimeType == MimeType::None) {
53 LOG_ERROR(logger, "No mimetype set.");
54 return false;
55 }
56
57 std::error_code ec;
58
59 rootPath = IO::combine(cachesPath.to_string(), Strings::getC(providerConf->cacheKey));
60 offline = providerConf->offline;
61
62 // Make sure cache directory exists
63 if (!fs::exists(rootPath, ec) && !ec) {
64 if (offline) {
65 LOG_ERROR(logger, "Path '%s' does not exist.", rootPath.c_str());
66 return false;
67 }
68 else {
69 if (!fs::create_directories(rootPath, ec) && !ec) {
70 LOG_ERROR(logger, "Failed to create path '%s'", rootPath.c_str());
71 return false;
72 }
73 else if (ec) {
74 LOG_ERROR(logger, "Failure when creating path %s: %s", rootPath.c_str(), ec.message().c_str());
75 return false;
76 }
77 }
78 }
79 else if (ec) {
80 LOG_ERROR(logger, "Failure when checking path %s: %s", rootPath.c_str(), ec.message().c_str());
81 return false;
82 }
83
84 // Check if there is a config file in the cache directory
85
86 auto paramPath = IO::combine(rootPath, "RasterSourceParameters.json");
87 auto paramFile = IO::readFileSync(paramPath);
88 if (paramFile) {
89
90 auto cacheConf = deserializeRastersourceConfig(context, StringView((const char*)paramFile->ptr, paramFile->size));
91
92 if (!cacheConf) return false;
93
94 if (!providerConf->compatible(cacheConf.get())) {
95 LOG_ERROR(logger, "Incompatible configurations");
96 return false;
97 }
98
99 }
100 else {
101 if (offline) {
102 LOG_ERROR(logger, "Failed to read %s", paramPath.c_str());
103 return false;
104 }
105 else {
106
107 if (!fs::is_empty(rootPath)) {
108 LOG_ERROR(logger, "%s is not empty and does not contain a rastersource config file", rootPath.c_str());
109 return false;
110 }
111
113 if (!serializeConfig(buf, providerConf)) {
114 LOG_ERROR(logger, "Failed to serialize rastersource");
115 return false;
116 }
117 if (!IO::writeBinaryFile(paramPath, buf.data(), buf.size())) {
118 LOG_ERROR(logger, "Error writing %s", paramPath.c_str());
119 return false;
120 }
121 }
122 }
123
124
125
126 LOG_DEBUG(logger, "Using disk cache at %s%s", rootPath.c_str(), offline ? " (offline)" : "");
127
128 initialized = true;
129 return true;
130}
131
132bool Cogs::Core::TerrainProvider::DiskCache::getTile(Memory::MemoryBuffer& contents, MimeType& kind, const TileId& id)
133{
134 if (!initialized) return false;
135
136 kind = mimeType;
137 auto tilePath = buildPath(rootPath, id.i, id.j, id.level, kind);
138 std::ifstream in(tilePath, std::ios::binary | std::ios::in | std::ios::ate);
139 if (!in.is_open()) {
140 return false;
141 }
142
143 in.seekg(0, std::ios::end);
144 size_t file_size = in.tellg();
145 contents.resize(file_size, false);
146
147 in.seekg(0, std::ios::beg);
148 in.read((char*)contents.data(), contents.size());
149 in.close();
150
151 return contents.size() != 0;
152}
153
154bool Cogs::Core::TerrainProvider::DiskCache::storeTile(const Memory::MemoryBuffer& contents, MimeType kind, const TileId& id, StringView debugLog)
155{
156 if (contents.size() == 0 && debugLog.size() == 0) return true;
157
158 std::error_code ec;
159
160 auto tilePath = buildPath(rootPath, id.i, id.j, id.level, kind);
161 auto parentPath = fs::path(tilePath).parent_path();
162
163 if (!fs::exists(parentPath, ec) && !ec) {
164
165 if (!fs::create_directories(parentPath, ec) && !ec) {
166 LOG_ERROR(logger, "Failed to create path: %s", parentPath.generic_string().c_str());
167 return false;
168 }
169 else if (ec) {
170 LOG_ERROR(logger, "Failure when creating path %s: %s", parentPath.generic_string().c_str(), ec.message().c_str());
171 return false;
172 }
173 }
174 else if (ec) {
175 LOG_ERROR(logger, "Failure checking tile path: %s", ec.message().c_str());
176 return false;
177 }
178
179
180 if (contents.size() != 0 && kind != MimeType::None) {
181
182 if (kind != mimeType) {
183 LOG_ERROR(logger, "Mismatched mime-type, cache mime-type=%s, tile mime-type=%s",
184 Strings::getC(mimeTypeString(mimeType)),
185 Strings::getC(mimeTypeString(kind)));
186 return false;
187 }
188
189 if (!IO::writeBinaryFile(tilePath, contents.data(), contents.size())) {
190 LOG_ERROR(logger, "Error writing %s", tilePath.c_str());
191 return false;
192 }
193 }
194
195 if (!debugLog.empty()) {
196 auto logPath = tilePath + ".txt";
197 if (!IO::writeBinaryFile(logPath, debugLog.data(), debugLog.size())) {
198 LOG_ERROR(logger, "Error writing debug log %s", logPath.c_str());
199 return false;
200 }
201 }
202
203 return true;
204}
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
Log implementation class.
Definition: LogManager.h:139
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:24
constexpr const char * data() const noexcept
Get the sequence of characters referenced by the string view.
Definition: StringView.h:171
constexpr size_t size() const noexcept
Get the size of the string.
Definition: StringView.h:178
constexpr bool empty() const noexcept
Check if the string is empty.
Definition: StringView.h:122
std::string to_string() const
String conversion method.
Definition: StringView.cpp:9
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180