4#include "Resources/ResourceStore.h"
6#include "Foundation/Logging/Logger.h"
7#include "Foundation/Platform/FileContents.h"
9#define _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING
10#include "rapidjson/reader.h"
11#include "rapidjson/error/en.h"
12#undef _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING
25 size_t countLines(
const StringView & source,
size_t )
29 const char* data = source.data();
31 while (data != source.end()) {
32 if (*data ==
'\n') count++;
39 const char * findLineOffset(
const StringView & source,
size_t offset,
int direction)
41 int skip = direction > 0 ? 1 : -1;
43 int target = direction > 0 ? direction - 1 : -direction;
45 auto term = direction > 0 ? source.end() : source.begin();
46 auto cur = source.data() + offset;
48 if (direction == 1 && (*cur ==
'\n'))
return cur + 1;
49 else if (direction == 1 && (*cur ==
'\r'))
return cur + 2;
53 if (++lines > target) {
64 StringView findLine(
const StringView & source,
size_t offset,
int direction)
66 if (source.empty())
return {};
67 if (offset >= source.size())
return {};
69 const char* begin = findLineOffset(source, offset, direction);
70 const char* end = findLineOffset(source, offset, direction + 1);
71 size_t len = std::distance(begin, end);
73 assert(len >= 0 &&
"JSON string offsets invalid.");
75 return StringView(begin, len > 0 ? len - 2 : 0);
79Document Cogs::Core::parseJson(
const StringView & content, JsonParseFlags flags)
83 StringView jsonContent = content;
85 bool zstdMagic = (4 < content.size()) && (uint8_t(content[0])== 0x28 && uint8_t(content[1]) == 0xb5 && uint8_t(content[2]) == 0x2f && uint8_t(content[3]) == 0xfd);
87 if (zstdMagic ||((flags & JsonParseFlags::Compressed) == JsonParseFlags::Compressed))
89 LOG_TRACE(logger,
"ZSTD detected");
91 ZSTD_DCtx* zstd_dctx = ZSTD_createDCtx();
94 const auto size = ZSTD_getFrameContentSize(content.data(), content.size());
95 if (size == ZSTD_CONTENTSIZE_UNKNOWN) {
96 LOG_ERROR(logger,
"ZSTD_CONTENTSIZE_UNKNOWN");
99 else if (size == ZSTD_CONTENTSIZE_ERROR) {
100 LOG_ERROR(logger,
"ZSTD_CONTENTSIZE_ERROR");
103#if defined( EMSCRIPTEN )
106 else if (std::numeric_limits<size_t>::max() < size) {
107 LOG_ERROR(logger,
"File too large: %s", std::to_string(size).c_str());
112 decompressed.resize(
static_cast<size_t>(size),
false);
113 const auto dataSize = ZSTD_decompressDCtx(zstd_dctx, decompressed.data(), decompressed.size(), content.data(), content.size());
114 if (ZSTD_isError(dataSize)) {
115 LOG_ERROR(logger,
"ZSTD decompression failed: %s", ZSTD_getErrorName(dataSize));
119 ZSTD_freeDCtx(zstd_dctx);
120 LOG_TRACE(logger,
"ZSTD decoding done");
122 const void* dataPtr = decompressed.data();
123 jsonContent = StringView(
static_cast<const char*
>(dataPtr), dataSize);
127 document.Parse<ParseFlag::kParseCommentsFlag | kParseTrailingCommasFlag>(jsonContent.data(), jsonContent.size());
128 if (document.HasParseError()) {
129 const ParseErrorCode errorCode = document.GetParseError();
130 const size_t offset = document.GetErrorOffset();
132 auto prevLine = findLine(jsonContent, offset, -1);
133 auto currLine = findLine(jsonContent, offset, 0);
134 auto nextLine = findLine(jsonContent, offset, 1);
136 if (currLine.size()) {
137 LOG_ERROR(logger,
"Error parsing JSON: %s", GetParseError_En(errorCode));
138 auto lineNo = countLines(jsonContent, offset);
140 if (prevLine.size()) LOG_ERROR(logger,
"%3zd: %.*s", lineNo - 1, StringViewFormat(prevLine));
141 if (currLine.size()) LOG_ERROR(logger,
"%3zd: %.*s", lineNo, StringViewFormat(currLine));
142 auto len = (jsonContent.data() + offset) - currLine.data();
143 auto lineOffset = std::max((
size_t)0, (
size_t)len);
145 std::string spaces(lineOffset,
' ');
148 if (jsonContent[offset] ==
'\n') spaces +=
" (\\n)";
149 else if (jsonContent[offset] ==
'\r') spaces +=
" (\\r)";
151 LOG_ERROR(logger,
" %s", spaces.c_str());
153 if (nextLine.size()) LOG_ERROR(logger,
"%3zd: %.*s", lineNo + 1, StringViewFormat(nextLine));
155 LOG_ERROR(logger,
"Error parsing JSON: %s", GetParseError_En(errorCode));
162Document Cogs::Core::parseJson(Context * context,
const StringView & fileName, JsonParseFlags flags)
166 if ((flags & JsonParseFlags::NoCachedContent) == JsonParseFlags::NoCachedContent) {
167 resourceFlags |= ResourceStoreFlags::NoCachedContent;
169 else if ((flags & JsonParseFlags::PreferUncachedContent) == JsonParseFlags::PreferUncachedContent) {
170 resourceFlags |= ResourceStoreFlags::PreferUncachedContent;
174 ResourceBuffer content = context->resourceStore->getResourceContents(fileName, resourceFlags);
175 if (!content.size()) {
176 LOG_ERROR(logger,
"Could not open JSON file %.*s.", StringViewFormat(fileName));
180 return parseJson(content.toStringView(), flags);
183Document Cogs::Core::parseJson(
class Context* , std::unique_ptr<FileContents> contents, JsonParseFlags flags)
185 return parseJson(StringView(
static_cast<const char*
>(contents->data()), contents->size), flags);
Log implementation class.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Contains all Cogs related functionality.