Initial tested version
This commit is contained in:
commit
5b6723f86f
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
# Never use CMAKE in production
|
||||
CMakeLists.txt
|
||||
cmake-build-debug/
|
||||
# Output of build system
|
||||
built/
|
||||
# This is a compilated build system script
|
||||
building/main
|
||||
building/*.png
|
||||
building/*.svg
|
||||
|
||||
.idea/
|
||||
|
60
README.txt
Normal file
60
README.txt
Normal file
@ -0,0 +1,60 @@
|
||||
### libjsonincpp
|
||||
|
||||
Suckless C++ library for storing JSON and generating and parsing JSON strings
|
||||
|
||||
### Examples
|
||||
|
||||
|
||||
#include <libjsonincpp/jsonobj.h>
|
||||
#include <libjsonincpp/string_representation.h>
|
||||
|
||||
void func(){
|
||||
json::JSON obj = json::JSON("Hello, World\n");
|
||||
assert(obj.isString());
|
||||
std::cout << obj.asString() << std::endl;
|
||||
}
|
||||
|
||||
void foo(){
|
||||
json::JSON obj = json::JSON(std::vector<JSON>(JSON(12l), JSON(true)));
|
||||
for (json::JSON& el: obj.asArray()){
|
||||
std::cout << json::generate_str(el, json::print_pretty) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
### Pros
|
||||
|
||||
libjsonincpp is non-recurrsive, out-of-memory-safe C++ library.
|
||||
|
||||
Accessing a member of json object that does not exist (for example taking second element of true value, or taking element, corresponding to key "dict-key" of an array value)
|
||||
does not result in error. libjsonincpp defines a special structure json::JSON_reference. It can be undefined. It is returned by operator[].
|
||||
|
||||
Also, this is a correct code in libjsonincpp:
|
||||
|
||||
|
||||
json::JSON obj; // Initial value is null symbol
|
||||
obj["aaaa"]["foo"][12][0] = JSON("value");
|
||||
|
||||
This code will automatically make obj a dictionary, make obj["aaaa"] a dictionary, make obj["aaaa"]["foo"] an array, e.t.c. And at the end, it will perform copy of "value"
|
||||
|
||||
### Cons
|
||||
|
||||
It has no cons, but more like "limitations":
|
||||
|
||||
JSON specification allows some crazy precise float values as JOSN number (imagine unironically handling float with decimal exponent equal to 99....999 with as
|
||||
many nines as your internet connection can handle). Handling floats and big integers was not my goal. Thus, it can correctly interpret obly numbers with less then 19 digits.
|
||||
Other number values are stored as unparsed strings.
|
||||
|
||||
Also, libregexis024 imposes a restriction on JSON string encoding: All JSON string values MUST be correct UTF-8 strings, otherwise generating string representation of
|
||||
your JSON object will result in json::misuse exception.
|
||||
|
||||
### Dependencies
|
||||
|
||||
C++, regexis024_build_system
|
||||
|
||||
What is regexis024_build_system? It's nothing. Don't think about it. Just download it and forget about it.
|
||||
|
||||
Also, don't forget to disable overcommitment on your system in order for _your system_ to be usable.
|
||||
Bruh, why don't we already rename OOM-killer to just regular killer and charge it for all the crimes it did?
|
||||
Why we keep executing OOM-killer on our servers when we can take the guy who thought that overcommitment was a good idea and execute him on an electric chair?
|
||||
|
10
building/build_build_system.sh
Executable file
10
building/build_build_system.sh
Executable file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
BUILDING_DIR="./building"
|
||||
[ -d "$BUILDING_DIR" ] || exit 1
|
||||
MAIN_FILE="$BUILDING_DIR/main.cpp"
|
||||
[ -f "$MAIN_FILE" ] || exit 1
|
||||
|
||||
COOL_FLAGS="$(pkg-config --cflags regexis024-build-system)"
|
||||
|
||||
g++ $COOL_FLAGS -o "$BUILDING_DIR/main" "$MAIN_FILE" || exit 1
|
102
building/main.cpp
Normal file
102
building/main.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
#include "regexis024_build_system.h"
|
||||
|
||||
struct TestWebsiteBuildScript {
|
||||
/* Building runlevel */
|
||||
BuildUnitsArray runlevel_1;
|
||||
/* Installation runlevel */
|
||||
BuildUnitsArray runlevel_2;
|
||||
|
||||
/* "debug" or "release" */
|
||||
std::string build_type;
|
||||
bool make_tests = false;
|
||||
|
||||
std::vector<std::string> warning_flags = {"-Wall", "-Wno-unused-variable", "-Werror=return-type","-pedantic",
|
||||
"-Wno-unused-but-set-variable", "-Wno-reorder"};
|
||||
std::vector<std::string> version_flags = {"--std", "c++14", "-D", "_POSIX_C_SOURCE=200809L"};
|
||||
|
||||
std::vector<std::string> debug_defines_release = {"_GLIBCXX_DEBUG"};
|
||||
std::vector<std::string> debug_defines_debug = {"_GLIBCXX_DEBUG", "DEBUG_ALLOW_LOUD"};
|
||||
std::vector<std::string> opt_flags_release = {"-g", "-O2"};
|
||||
std::vector<std::string> opt_flags_debug = {"-g", "-ggdb", "-O0"};
|
||||
|
||||
|
||||
std::vector<std::string> getSomeRadFlags() {
|
||||
std::vector<std::string> my_flag_collection;
|
||||
gxx_add_cli_options(my_flag_collection, warning_flags);
|
||||
gxx_add_cli_options(my_flag_collection, version_flags);
|
||||
if (build_type == "release") {
|
||||
gxx_add_cli_defines(my_flag_collection, debug_defines_release);
|
||||
gxx_add_cli_options(my_flag_collection, opt_flags_release);
|
||||
} else if (build_type == "debug") {
|
||||
gxx_add_cli_defines(my_flag_collection, debug_defines_debug);
|
||||
gxx_add_cli_options(my_flag_collection, opt_flags_debug);
|
||||
}
|
||||
return my_flag_collection;
|
||||
}
|
||||
|
||||
TestWebsiteBuildScript(const std::string& _build_type, bool make_tests,
|
||||
const NormalCBuildSystemCommandMeaning& cmd)
|
||||
: build_type(_build_type), make_tests(make_tests)
|
||||
{
|
||||
ASSERT(build_type == "release" || build_type == "debug", "Unknown build type");
|
||||
|
||||
std::vector<ExternalLibraryTarget> ext_targets;
|
||||
|
||||
std::vector<CTarget> my_targets;
|
||||
{ CTarget T("libjsonincpp", "shared_library");
|
||||
T.additional_compilation_flags = getSomeRadFlags();
|
||||
T.units_dir = "library";
|
||||
T.units = {
|
||||
"libjsonincpp/utf8.cpp",
|
||||
"libjsonincpp/jsonobj.cpp",
|
||||
"libjsonincpp/quality_of_life.cpp",
|
||||
"libjsonincpp/quality_of_life_2.cpp",
|
||||
"libjsonincpp/integer.cpp",
|
||||
"libjsonincpp/inner_storage.cpp",
|
||||
"libjsonincpp/generator.cpp",
|
||||
"libjsonincpp/parser.cpp",
|
||||
"libjsonincpp/parser_context.cpp",
|
||||
"libjsonincpp/container_parsing.cpp",
|
||||
};
|
||||
T.include_pr = "library";
|
||||
T.include_ir = "";
|
||||
T.exported_headers = {
|
||||
"libjsonincpp/jsonobj.h",
|
||||
"libjsonincpp/string_representation.h",
|
||||
"libjsonincpp/utf8.h"
|
||||
};
|
||||
T.installation_dir = "";
|
||||
T.description = "C++ JSON object structure + parser and generator";
|
||||
my_targets.push_back(T);
|
||||
}
|
||||
if (make_tests) { CTarget T("test0", "executable");
|
||||
T.additional_compilation_flags = getSomeRadFlags();
|
||||
T.proj_deps = {CTargetDependenceOnProjectsLibrary("libjsonincpp")};
|
||||
T.units_dir = "tests";
|
||||
T.units = {"test0.cpp"};
|
||||
T.include_pr = "tests";
|
||||
my_targets.push_back(T);
|
||||
}
|
||||
regular_ctargets_to_2bus_conversion(ext_targets, my_targets, runlevel_1, runlevel_2,
|
||||
cmd.project_root, cmd.installation_root);
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
try {
|
||||
assert(argc > 0);
|
||||
std::vector<std::string> args(argc - 1);
|
||||
for (int i = 0; i + 1 < argc; i++) {
|
||||
args[i] = argv[i + 1];
|
||||
}
|
||||
NormalCBuildSystemCommandMeaning cmd;
|
||||
regular_bs_cli_cmd_interpret(args, cmd);
|
||||
TestWebsiteBuildScript bs("debug", true, cmd);
|
||||
if (cmd.need_to_build)
|
||||
complete_tasks_of_build_units(bs.runlevel_1);
|
||||
if (cmd.need_to_install)
|
||||
complete_tasks_of_build_units(bs.runlevel_2);
|
||||
} catch (const buildSystemFailure& e) {
|
||||
printf("Build system failure\n""%s\n", e.toString().c_str());
|
||||
}
|
||||
}
|
39
src/library/libjsonincpp/container_parsing.cpp
Normal file
39
src/library/libjsonincpp/container_parsing.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
#include "parser.h"
|
||||
|
||||
namespace json {
|
||||
ArrayParseCall::ArrayParseCall(std::vector<JSON> &result): result(result) {}
|
||||
|
||||
std::unique_ptr<ParsingCall> ArrayParseCall::here(ParserContext& pctx) {
|
||||
skipWhitespaces(pctx);
|
||||
if (peep(pctx) == ']') {
|
||||
skip(pctx);
|
||||
return NULL;
|
||||
}
|
||||
if (got_one) {
|
||||
demandSkip(pctx, ',');
|
||||
}
|
||||
result.emplace_back();
|
||||
got_one = true;
|
||||
return std::make_unique<ValueParseCall>(result.back());
|
||||
}
|
||||
|
||||
DictionaryParseCall::DictionaryParseCall(std::map<std::string, JSON>& result): result(result) {}
|
||||
|
||||
std::unique_ptr<ParsingCall> DictionaryParseCall::here(ParserContext& pctx) {
|
||||
skipWhitespaces(pctx);
|
||||
if (peep(pctx) == '}') {
|
||||
skip(pctx);
|
||||
return NULL;
|
||||
}
|
||||
if (got_one) {
|
||||
demandSkip(pctx, ',');
|
||||
}
|
||||
std::string key = demandStringJson(pctx);
|
||||
skipWhitespaces(pctx);
|
||||
demandSkip(pctx, ':');
|
||||
if (result.count(key) > 0)
|
||||
throw bad_syntax();
|
||||
got_one = true;
|
||||
return std::make_unique<ValueParseCall>(result[key]);
|
||||
}
|
||||
}
|
134
src/library/libjsonincpp/generator.cpp
Normal file
134
src/library/libjsonincpp/generator.cpp
Normal file
@ -0,0 +1,134 @@
|
||||
#include "string_representation.h"
|
||||
#include "inner_storage.h"
|
||||
#include <assert.h>
|
||||
#include "utf8.h"
|
||||
|
||||
namespace json {
|
||||
void escape_string(std::string& dest, const std::string& src) {
|
||||
if (!isUtf8String(src)) {
|
||||
throw misuse("Can't stringify json string value that is not a utf-8 string");
|
||||
}
|
||||
dest += '"';
|
||||
for (char ch: src) {
|
||||
if (ch == '"' || ch == '\\' || (0 <= ch && ch <= 0x1f)) {
|
||||
char buf[10];
|
||||
snprintf(buf, 10, "\\u%04x", ch);
|
||||
dest += buf;
|
||||
} else {
|
||||
dest += ch;
|
||||
}
|
||||
}
|
||||
dest += '"';
|
||||
}
|
||||
|
||||
int try_append_rep_childless(const JSON& obj, uint32_t mode, std::string& ret_ans) {
|
||||
#define azaza(sym) case sym ## _symbol: ret_ans += #sym; return 0;
|
||||
switch (obj.type) {
|
||||
azaza(null)
|
||||
azaza(false)
|
||||
azaza(true)
|
||||
case integer:
|
||||
ret_ans += obj.asInteger().to_string();
|
||||
return 0;
|
||||
case string:
|
||||
escape_string(ret_ans, obj.asString());
|
||||
return 0;
|
||||
case array:
|
||||
if (obj.asArray().empty()) {
|
||||
ret_ans += "[]";
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
case dictionary:
|
||||
if (obj.asDictionary().empty()) {
|
||||
ret_ans += "{}";
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void indent(int depth, std::string& res) {
|
||||
for (int i = 0; i < depth; i++)
|
||||
res += " ";
|
||||
}
|
||||
|
||||
/* Returns node.it == node.data.end(). If true is returned, cur is switched to parent */
|
||||
template<typename ObjData>
|
||||
bool print_iter_pref(ObjData& node, std::string& result, int& cur_depth, const JSON*& cur,
|
||||
char container_begin, char container_end, uint32_t mode) {
|
||||
if (node.it == node.data.begin()) {
|
||||
cur_depth++;
|
||||
result += container_begin;
|
||||
if (mode == print_pretty) {
|
||||
result += "\n";
|
||||
indent(cur_depth, result);
|
||||
}
|
||||
} else if (node.it == node.data.end()) {
|
||||
cur_depth--;
|
||||
if (mode == print_pretty) {
|
||||
result += '\n';
|
||||
indent(cur_depth, result);
|
||||
}
|
||||
result += container_end;
|
||||
cur = (const JSON*)node.wayback;
|
||||
return true;
|
||||
} else {
|
||||
result += ',';
|
||||
if (mode == print_pretty) {
|
||||
result += '\n';
|
||||
indent(cur_depth, result);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string generate_str(const JSON &obj, uint32_t mode) {
|
||||
int ret;
|
||||
std::string result;
|
||||
ret = try_append_rep_childless(obj, mode, result);
|
||||
if (ret < 0) {
|
||||
int cur_depth = 0;
|
||||
const JSON* cur = &obj;
|
||||
setup_nr_iterators(obj, NULL);
|
||||
while (cur) {
|
||||
if (cur->isArray()) {
|
||||
ArrayData& node = *static_cast<ArrayData*>(cur->value);
|
||||
proc_child_1:
|
||||
if (print_iter_pref(node, result, cur_depth, cur, '[', ']', mode))
|
||||
continue;
|
||||
JSON& child = *node.it;
|
||||
++node.it;
|
||||
ret = try_append_rep_childless(child, mode, result);
|
||||
if (ret == 0) {
|
||||
goto proc_child_1;
|
||||
}
|
||||
setup_nr_iterators(child, (void*)cur);
|
||||
cur = &child;
|
||||
} else if (cur->isDictionary()) {
|
||||
DictionaryData& node = *static_cast<DictionaryData*>(cur->value);
|
||||
proc_child_2:
|
||||
if (print_iter_pref(node, result, cur_depth, cur, '{', '}', mode))
|
||||
continue;
|
||||
escape_string(result, node.it->first);
|
||||
result += ':';
|
||||
if (mode == print_pretty)
|
||||
result += ' ';
|
||||
JSON& child = node.it->second;
|
||||
++node.it;
|
||||
ret = try_append_rep_childless(child, mode, result);
|
||||
if (ret == 0)
|
||||
goto proc_child_2;
|
||||
setup_nr_iterators(child, (void*)cur);
|
||||
cur = &child;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mode == print_pretty) {
|
||||
result += "\n";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
183
src/library/libjsonincpp/inner_storage.cpp
Normal file
183
src/library/libjsonincpp/inner_storage.cpp
Normal file
@ -0,0 +1,183 @@
|
||||
#include "jsonobj.h"
|
||||
#include "inner_storage.h"
|
||||
#include <assert.h>
|
||||
|
||||
namespace json {
|
||||
ArrayData::~ArrayData() {
|
||||
for (auto& p: data)
|
||||
assert(p.isNull());
|
||||
}
|
||||
|
||||
DictionaryData::~DictionaryData() {
|
||||
for (auto& p: data) {
|
||||
assert(p.second.isNull());
|
||||
}
|
||||
}
|
||||
|
||||
void stomp(JSON& finished) noexcept {
|
||||
finished.value = NULL;
|
||||
finished.type = null_symbol;
|
||||
}
|
||||
|
||||
void nullify_childless(JSON& obj) noexcept {
|
||||
if (obj.type == integer) {
|
||||
delete static_cast<Integer*>(obj.value);
|
||||
} else if (obj.type == string) {
|
||||
delete static_cast<std::string*>(obj.value);
|
||||
}
|
||||
stomp(obj);
|
||||
}
|
||||
|
||||
void setup_nr_iterators(const JSON& obj_child, void* wayback) noexcept {
|
||||
if (obj_child.type == array) {
|
||||
ArrayData& ad = *static_cast<ArrayData*>(obj_child.value);
|
||||
ad.wayback = wayback;
|
||||
ad.it = ad.data.begin();
|
||||
} else if (obj_child.type == dictionary) {
|
||||
DictionaryData& dd = *static_cast<DictionaryData*>(obj_child.value);
|
||||
dd.wayback = wayback;
|
||||
dd.it = dd.data.begin();
|
||||
} else
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void nullify(JSON &obj) noexcept {
|
||||
if (!obj.isNatalistic()) {
|
||||
nullify_childless(obj);
|
||||
return;
|
||||
}
|
||||
JSON* current = &obj;
|
||||
setup_nr_iterators(obj, NULL);
|
||||
while (current) {
|
||||
if (current->type == array) {
|
||||
ArrayData& ad = *static_cast<ArrayData*>(current->value);
|
||||
it_check:
|
||||
if (ad.it == ad.data.end()) {
|
||||
stomp(*current);
|
||||
current = static_cast<JSON*>(ad.wayback);
|
||||
delete &ad;
|
||||
} else {
|
||||
JSON& child = *ad.it;
|
||||
++ad.it;
|
||||
if (!child.isNatalistic()) {
|
||||
nullify_childless(child);
|
||||
goto it_check;
|
||||
}
|
||||
setup_nr_iterators(child, current);
|
||||
current = &child;
|
||||
}
|
||||
} else if (current->type == dictionary) {
|
||||
DictionaryData& dd = *static_cast<DictionaryData*>(current->value);
|
||||
it_check2:
|
||||
if (dd.it == dd.data.end()) {
|
||||
stomp(*current);
|
||||
current = static_cast<JSON*>(dd.wayback);
|
||||
delete ⅆ
|
||||
} else {
|
||||
JSON& child = dd.it->second;
|
||||
++dd.it;
|
||||
if (!child.isNatalistic()) {
|
||||
nullify_childless(child);
|
||||
goto it_check2;
|
||||
}
|
||||
setup_nr_iterators(child, current);
|
||||
current = &child;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Strong exception guarantee */
|
||||
void copy_childless(JSON& destination, const JSON& source) {
|
||||
assert(destination.type == null_symbol);
|
||||
if (source.type == integer) {
|
||||
destination.value = new Integer(source.asInteger());
|
||||
} else if (source.type == string) {
|
||||
destination.value = new std::string(source.asString());
|
||||
}
|
||||
destination.type = source.type;
|
||||
}
|
||||
|
||||
/* Basic exception guarantee */
|
||||
void setup_it_plus_double_wayback(JSON& dest_child, const JSON& src_child, JSON* dest_wayback, const JSON* src_wayback)
|
||||
{
|
||||
assert(dest_child.isNull());
|
||||
if (src_child.type == array) {
|
||||
ArrayData& SRC_DATA = *static_cast<ArrayData*>(src_child.value);
|
||||
dest_child.value = new ArrayData();
|
||||
dest_child.type = array;
|
||||
/* Destination is in correct state */
|
||||
ArrayData& DEST_DATA = *static_cast<ArrayData*>(dest_child.value);
|
||||
DEST_DATA.data.resize(SRC_DATA.data.size());
|
||||
DEST_DATA.it = DEST_DATA.data.begin();
|
||||
DEST_DATA.wayback = dest_wayback;
|
||||
SRC_DATA.it = SRC_DATA.data.begin();
|
||||
SRC_DATA.wayback = const_cast<void*>(static_cast<const void*>(src_wayback));
|
||||
} else if (src_child.type == dictionary) {
|
||||
DictionaryData& SRC_DATA = *static_cast<DictionaryData*>(src_child.value);
|
||||
dest_child.value = new DictionaryData();
|
||||
dest_child.type = dictionary;
|
||||
DictionaryData& DEST_DATA = *static_cast<DictionaryData*>(dest_child.value);
|
||||
// Here keeping destination data iterator is unnecessary
|
||||
DEST_DATA.wayback = dest_wayback;
|
||||
SRC_DATA.it = SRC_DATA.data.begin();
|
||||
SRC_DATA.wayback = const_cast<void*>(static_cast<const void*>(src_wayback));
|
||||
} else
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void copy_json(JSON& destination, const JSON& source) {
|
||||
assert(destination.type == null_symbol);
|
||||
if (!source.isNatalistic()) {
|
||||
copy_childless(destination, source);
|
||||
return;
|
||||
}
|
||||
setup_it_plus_double_wayback(destination, source, NULL, NULL);
|
||||
JSON* dest_cur = &destination;
|
||||
const JSON* src_cur = &source;
|
||||
while (dest_cur) {
|
||||
assert(src_cur);
|
||||
if (src_cur->type == array) {
|
||||
ArrayData& ad_dest = *static_cast<ArrayData*>(dest_cur->value);
|
||||
ArrayData& ad_src = *static_cast<ArrayData*>(src_cur->value);
|
||||
it_check:
|
||||
if (ad_src.it == ad_src.data.end()) {
|
||||
dest_cur = static_cast<JSON*>(ad_dest.wayback);
|
||||
src_cur = static_cast<const JSON*>(ad_src.wayback);
|
||||
} else {
|
||||
JSON& src_child = *ad_src.it;
|
||||
JSON& dest_child = *ad_dest.it;
|
||||
++ad_src.it;
|
||||
++ad_dest.it;
|
||||
if (!src_child.isNatalistic()) {
|
||||
copy_childless(dest_child, src_child);
|
||||
goto it_check;
|
||||
}
|
||||
setup_it_plus_double_wayback(dest_child, src_child, dest_cur, src_cur);
|
||||
dest_cur = &dest_child;
|
||||
src_cur = &src_child;
|
||||
}
|
||||
} else if (src_cur->type == dictionary) {
|
||||
DictionaryData& dd_dest = *static_cast<DictionaryData*>(dest_cur->value);
|
||||
DictionaryData& dd_src = *static_cast<DictionaryData*>(src_cur->value);
|
||||
it_check2:
|
||||
if (dd_src.it == dd_src.data.end()) {
|
||||
dest_cur = static_cast<JSON*>(dd_dest.wayback);
|
||||
src_cur = static_cast<const JSON*>(dd_src.wayback);
|
||||
} else {
|
||||
JSON& src_child = dd_src.it->second;
|
||||
JSON& dest_child = dd_dest.data[dd_src.it->first]; // This function blows. ... I mean throws
|
||||
++dd_src.it;
|
||||
if (!src_child.isNatalistic()) {
|
||||
copy_childless(dest_child, src_child);
|
||||
goto it_check2;
|
||||
}
|
||||
setup_it_plus_double_wayback(dest_child, src_child, dest_cur, src_cur);
|
||||
dest_cur = &dest_child;
|
||||
src_cur = &src_child;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(!src_cur);
|
||||
}
|
||||
}
|
40
src/library/libjsonincpp/inner_storage.h
Normal file
40
src/library/libjsonincpp/inner_storage.h
Normal file
@ -0,0 +1,40 @@
|
||||
#ifndef TEST_WEBSITE_SRC_MODULE_JSON_LIBJSONINCPP_INNER_STORAGE_H
|
||||
#define TEST_WEBSITE_SRC_MODULE_JSON_LIBJSONINCPP_INNER_STORAGE_H
|
||||
|
||||
/* DO NOT EXPORT THIS FILE */
|
||||
|
||||
#include "jsonobj.h"
|
||||
|
||||
namespace json {
|
||||
struct ArrayData {
|
||||
std::vector<JSON> data;
|
||||
/* This field will be used in destructor to iterat over children. Destructor must not be
|
||||
* recursive and must not allocate memory */
|
||||
std::vector<JSON>::iterator it;
|
||||
/* Parent (used only in destructor) */
|
||||
void* wayback = NULL; // Null means no parent (end of recursion)
|
||||
|
||||
~ArrayData();
|
||||
};
|
||||
|
||||
struct DictionaryData {
|
||||
std::map<std::string, JSON> data;
|
||||
/* Again, destructors can't allocate a heap stack by themselves (it would be too late), JSON has to take
|
||||
* care of it NOW */
|
||||
std::map<std::string, JSON>::iterator it;
|
||||
void* wayback = NULL; // Same as ArrayDaya::wayback
|
||||
|
||||
~DictionaryData();
|
||||
};
|
||||
|
||||
void setup_nr_iterators(const JSON& obj_child, void* wayback) noexcept;
|
||||
|
||||
void nullify(JSON& obj) noexcept;
|
||||
|
||||
void copy_json(JSON& destination, const JSON& source);
|
||||
}
|
||||
|
||||
// #define get_wayback(data) static_cast<JSON*&>
|
||||
|
||||
|
||||
#endif
|
49
src/library/libjsonincpp/integer.cpp
Normal file
49
src/library/libjsonincpp/integer.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
#include "integer.h"
|
||||
#include <string.h>
|
||||
|
||||
namespace json {
|
||||
Integer::Integer(int64_t v): value(v) {}
|
||||
|
||||
/* Throw bad_alloc. Very safe */
|
||||
void copy_horror(Integer& i, const char* other_horror) {
|
||||
if (!other_horror)
|
||||
return;
|
||||
size_t n = strlen(other_horror);
|
||||
i.uncomprehendable_horror = (char*)calloc(n + 1, 1);
|
||||
if (!i.uncomprehendable_horror)
|
||||
throw std::bad_alloc();
|
||||
memcpy(i.uncomprehendable_horror, other_horror, n);
|
||||
}
|
||||
|
||||
Integer::Integer(const char *terrifyingly_big_string) {
|
||||
copy_horror(*this, terrifyingly_big_string);
|
||||
}
|
||||
|
||||
Integer::Integer(const Integer &other): value(other.value) {
|
||||
copy_horror(*this, other.uncomprehendable_horror);
|
||||
}
|
||||
|
||||
Integer & Integer::operator=(const Integer &other) {
|
||||
value = other.value;
|
||||
free(uncomprehendable_horror); uncomprehendable_horror = NULL;
|
||||
copy_horror(*this, other.uncomprehendable_horror);
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string Integer::to_string() const {
|
||||
if (uncomprehendable_horror) {
|
||||
return std::string(uncomprehendable_horror);
|
||||
}
|
||||
return std::to_string(value);
|
||||
}
|
||||
|
||||
int64_t Integer::get_int() const {
|
||||
if (uncomprehendable_horror)
|
||||
return 9999999;
|
||||
return value;
|
||||
}
|
||||
|
||||
Integer::~Integer() {
|
||||
free(uncomprehendable_horror);
|
||||
}
|
||||
}
|
30
src/library/libjsonincpp/integer.h
Normal file
30
src/library/libjsonincpp/integer.h
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef TEST_WEBSITE_SRC_MODULE_JSON_LIBJSONINCPP_INTEGER_H
|
||||
#define TEST_WEBSITE_SRC_MODULE_JSON_LIBJSONINCPP_INTEGER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace json {
|
||||
struct Integer {
|
||||
int64_t value = 0;
|
||||
/* JSON specification allows enormously big enormously precise float values. I don't want to handle this
|
||||
* nonsense */
|
||||
char* uncomprehendable_horror = NULL;
|
||||
|
||||
/* Only these members should be accessed */
|
||||
Integer() = default;
|
||||
explicit Integer(int64_t v);
|
||||
explicit Integer(const char* terrifyingly_big_string);
|
||||
Integer(const Integer& other);
|
||||
Integer& operator=(const Integer& other);
|
||||
|
||||
std::string to_string() const;
|
||||
|
||||
int64_t get_int() const;
|
||||
|
||||
~Integer();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
86
src/library/libjsonincpp/jsonobj.cpp
Normal file
86
src/library/libjsonincpp/jsonobj.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
#include "jsonobj.h"
|
||||
#include "inner_storage.h"
|
||||
#include <memory>
|
||||
#include <assert.h>
|
||||
#include "utf8.h"
|
||||
|
||||
#define constr_begin try {
|
||||
#define constr_end } catch (const std::bad_alloc& ba) { nullify(*this); throw; }
|
||||
|
||||
namespace json {
|
||||
misuse::misuse(const char *string): runtime_error(string) {}
|
||||
|
||||
JSON::JSON(json_t type): type(type) {
|
||||
if (type == integer) {
|
||||
value = new Integer();
|
||||
} else if (type == string) {
|
||||
value = new std::string();
|
||||
} else if (type == array) {
|
||||
value = new ArrayData();
|
||||
} else if (type == dictionary) {
|
||||
value = new DictionaryData();
|
||||
}
|
||||
}
|
||||
|
||||
JSON::JSON(bool V) {
|
||||
type = V ? true_symbol : false_symbol;
|
||||
}
|
||||
|
||||
JSON::JSON(int64_t val): type(integer) {
|
||||
value = new Integer(val);
|
||||
}
|
||||
|
||||
JSON::JSON(const Integer &V): type(integer) {
|
||||
value = new Integer(V);
|
||||
}
|
||||
|
||||
JSON::JSON(const char *str): type(string) {
|
||||
value = new std::string(str);
|
||||
}
|
||||
|
||||
JSON::JSON(const std::string &V): type(string) {
|
||||
value = new std::string(V);
|
||||
}
|
||||
|
||||
JSON::JSON(const std::vector<JSON> &V): type(array) {
|
||||
ArrayData* dataPtr = new ArrayData();
|
||||
value = dataPtr;
|
||||
// Now the object is in correct state, with allocated memory
|
||||
constr_begin
|
||||
/* std::vector invokes one of those really serious JSON non-recursive copy operators */
|
||||
dataPtr->data = V;
|
||||
constr_end
|
||||
}
|
||||
|
||||
JSON::JSON(const std::map<std::string, JSON> &V): type(dictionary) {
|
||||
DictionaryData* dataPtr = new DictionaryData();
|
||||
value = dataPtr;
|
||||
constr_begin
|
||||
/* std::map will invoke non-recursive JSON copy */
|
||||
dataPtr->data = V;
|
||||
constr_end
|
||||
}
|
||||
|
||||
JSON::JSON(const JSON &other) {
|
||||
constr_begin
|
||||
/* This is a very serious function. It must not be implemented recursively */
|
||||
copy_json(*this, other);
|
||||
constr_end
|
||||
}
|
||||
|
||||
JSON & JSON::operator=(const JSON &other) {
|
||||
/* This is another one of those super serious functions that must not be recursive no matter what */
|
||||
nullify(*this);
|
||||
copy_json(*this, other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
JSON::~JSON() {
|
||||
/* This is by far the most serious function I have ever written */
|
||||
nullify(*this);
|
||||
}
|
||||
|
||||
JSON_reference JSON::r() noexcept {
|
||||
return {this, {}};
|
||||
}
|
||||
}
|
116
src/library/libjsonincpp/jsonobj.h
Normal file
116
src/library/libjsonincpp/jsonobj.h
Normal file
@ -0,0 +1,116 @@
|
||||
#ifndef TEST_WEBSITE_SRC_MODULE_JSON_LIBJSONINCPP_JSONOBJ_H
|
||||
#define TEST_WEBSITE_SRC_MODULE_JSON_LIBJSONINCPP_JSONOBJ_H
|
||||
|
||||
#include "integer.h"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace json {
|
||||
struct misuse : std::runtime_error {
|
||||
explicit misuse(const char *string);
|
||||
};
|
||||
|
||||
enum json_t {
|
||||
null_symbol,
|
||||
false_symbol,
|
||||
true_symbol,
|
||||
integer,
|
||||
string,
|
||||
array,
|
||||
dictionary,
|
||||
};
|
||||
|
||||
enum imaginary_key_t {
|
||||
undefined_array_element,
|
||||
undefined_dictionary_element,
|
||||
};
|
||||
|
||||
struct JSON_reference;
|
||||
|
||||
struct JSON {
|
||||
void* value = NULL;
|
||||
json_t type = null_symbol;
|
||||
|
||||
JSON() = default;
|
||||
explicit JSON(json_t type);
|
||||
explicit JSON(bool V);
|
||||
explicit JSON(int64_t val);
|
||||
explicit JSON(const Integer& V);
|
||||
explicit JSON(const char* str);
|
||||
explicit JSON(const std::string& V);
|
||||
explicit JSON(const std::vector<JSON>& V);
|
||||
explicit JSON(const std::map<std::string, JSON>& V);
|
||||
|
||||
JSON(const JSON& other);
|
||||
JSON& operator=(const JSON& other);
|
||||
~JSON();
|
||||
|
||||
JSON_reference r() noexcept;
|
||||
|
||||
json_t getType() const;
|
||||
|
||||
bool isNull() const;
|
||||
|
||||
bool isBool() const;
|
||||
|
||||
bool isInteger() const;
|
||||
|
||||
bool isFalse() const;
|
||||
|
||||
bool isTrue() const;
|
||||
|
||||
bool isString() const;
|
||||
|
||||
bool isArray() const;
|
||||
|
||||
bool isDictionary() const;
|
||||
|
||||
bool isNatalistic() const;
|
||||
|
||||
bool isSymbol() const;
|
||||
|
||||
bool toBool() const;
|
||||
|
||||
Integer& asInteger() const;
|
||||
|
||||
std::string& asString() const;
|
||||
|
||||
std::vector<JSON>& asArray() const;
|
||||
|
||||
std::map<std::string, JSON>& asDictionary() const;
|
||||
|
||||
JSON_reference operator[](size_t index);
|
||||
JSON_reference operator[](const std::string& key);
|
||||
|
||||
JSON& operator=(int64_t V);
|
||||
JSON& operator=(const Integer& V);
|
||||
JSON& operator=(const char* V);
|
||||
JSON& operator=(const std::string& V);
|
||||
};
|
||||
|
||||
struct ImaginaryKeyChainEValue {
|
||||
imaginary_key_t type;
|
||||
/* Why messing with RAII-ing (int|string) value behind some void pointer when I can just include both.
|
||||
* C'mon, bros, memory consumption issue does not exist */
|
||||
size_t when_array_index;
|
||||
std::string when_dictionary_key;
|
||||
};
|
||||
|
||||
/* These references get invalidated as soon as referenced object or any of its parents get changed */
|
||||
struct JSON_reference {
|
||||
JSON* last_real = NULL;
|
||||
std::vector<ImaginaryKeyChainEValue> imaginary_chain;
|
||||
|
||||
bool isDefined() const;
|
||||
|
||||
JSON& operator*() const;
|
||||
|
||||
void operator=(const JSON& obj);
|
||||
|
||||
JSON_reference operator[](size_t index);
|
||||
JSON_reference operator[](const std::string& key);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
244
src/library/libjsonincpp/parser.cpp
Normal file
244
src/library/libjsonincpp/parser.cpp
Normal file
@ -0,0 +1,244 @@
|
||||
#include "string_representation.h"
|
||||
#include "parser.h"
|
||||
#include <memory>
|
||||
#include <assert.h>
|
||||
|
||||
#include "utf8.h"
|
||||
|
||||
namespace json {
|
||||
std::unique_ptr<ParsingCall> ParsingCall::here(ParserContext &pctx) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ValueParseCall::ValueParseCall(JSON &result) : result(result) {
|
||||
assert(result.isNull());
|
||||
}
|
||||
|
||||
bool isDigit(int ch) {
|
||||
return ('0' <= ch && ch <= '9');
|
||||
}
|
||||
|
||||
bool isIntegerStart(int ch) {
|
||||
return isDigit(ch) || ch == '-';
|
||||
}
|
||||
|
||||
bool isSymbolConstituent(int ch) {
|
||||
return 'a' <= ch && ch <= 'z';
|
||||
}
|
||||
|
||||
void read_int_minus_part(ParserContext& pctx, bool& mantis_minus) {
|
||||
mantis_minus = false;
|
||||
if (peep(pctx) == '-') {
|
||||
skip(pctx);
|
||||
mantis_minus = true;
|
||||
}
|
||||
}
|
||||
|
||||
void read_int_int_part(ParserContext& pctx, int64_t& mantis_max18, bool& is_terrifying) {
|
||||
mantis_max18 = 0;
|
||||
int d = 0;
|
||||
while (true) {
|
||||
int ch = peep(pctx);
|
||||
if (!isDigit(ch))
|
||||
break;
|
||||
skip(pctx);
|
||||
if (ch == '0' && d == 0)
|
||||
break;
|
||||
if (d < 18) {
|
||||
mantis_max18 = mantis_max18 * 10 + (ch - '0');
|
||||
d++;
|
||||
} else {
|
||||
is_terrifying = true;
|
||||
}
|
||||
}
|
||||
if (d == 0)
|
||||
throw bad_syntax();
|
||||
}
|
||||
|
||||
void read_that_int_part_with_at_least_one_digit(ParserContext& pctx) {
|
||||
if (!isDigit(peep(pctx)))
|
||||
throw bad_syntax();
|
||||
skip(pctx);
|
||||
while (isDigit(peep(pctx)))
|
||||
skip(pctx);
|
||||
}
|
||||
|
||||
void read_int_frac_exp_part(ParserContext& pctx, bool& is_terrifying) {
|
||||
if (peep(pctx) == '.') {
|
||||
is_terrifying = true;
|
||||
skip(pctx);
|
||||
read_that_int_part_with_at_least_one_digit(pctx);
|
||||
}
|
||||
if (peep(pctx) == 'e' || peep(pctx) == 'E') {
|
||||
is_terrifying = true;
|
||||
skip(pctx);
|
||||
if (peep(pctx) == '+' || peep(pctx) == '-')
|
||||
skip(pctx);
|
||||
read_that_int_part_with_at_least_one_digit(pctx);
|
||||
}
|
||||
}
|
||||
|
||||
/* Starts with reading u. Throws json::bad_syntax on bad syntax */
|
||||
uint32_t read_4nibbles(ParserContext& pctx) {
|
||||
uint32_t result = 0;
|
||||
demandSkip(pctx, 'u');
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int ch = peep(pctx);
|
||||
result <<= 4;
|
||||
if (isDigit(ch)) {
|
||||
result += (ch - '0');
|
||||
} else if ('a' <= ch && ch <= 'f') {
|
||||
result += (ch - 'a') + 10;
|
||||
} else if ('A' <= ch && ch <= 'F') {
|
||||
result += (ch - 'A') + 10;
|
||||
} else
|
||||
throw bad_syntax();
|
||||
skip(pctx);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool is_utf16_2bp_high_surrogate(uint32_t v) {
|
||||
return 0xD800 <= v && v <= 0xDBFF;
|
||||
}
|
||||
|
||||
bool is_utf16_2bp_low_surrogate(uint32_t v) {
|
||||
return 0xDC00 <= v && v <= 0xE000;
|
||||
}
|
||||
|
||||
constexpr char escaping_rules[][2] = {{'"', '"'}, {'\\', '\\'}, {'/', '/'}, {'b', '\b'}, {'f', '\f',},
|
||||
{'n', '\n'}, {'r', '\r'}, {'t', '\t'}, {0, 0}};
|
||||
|
||||
void resert_to_one_char_escape(int leader, std::string& str) {
|
||||
for (int i = 0; escaping_rules[i][0] != 0; i++) {
|
||||
if (escaping_rules[i][0] == leader) {
|
||||
str += escaping_rules[i][1];
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw bad_syntax();
|
||||
}
|
||||
|
||||
std::string demandStringJson(ParserContext &pctx) {
|
||||
skipWhitespaces(pctx);
|
||||
std::string str;
|
||||
demandSkip(pctx, '"');
|
||||
int ch;
|
||||
while ((ch = peep(pctx)) != '"') {
|
||||
if ((0 <= ch && ch <= 0x1f) || ch == endOfFile)
|
||||
throw bad_syntax();
|
||||
skip(pctx);
|
||||
if (ch == '\\') {
|
||||
int leader = peep(pctx);
|
||||
if (leader == 'u') {
|
||||
uint32_t first_utf16 = read_4nibbles(pctx);
|
||||
if (is_utf16_2bp_low_surrogate(first_utf16))
|
||||
throw bad_syntax();
|
||||
if (!is_utf16_2bp_high_surrogate(first_utf16)) {
|
||||
codepoint_to_utf8(first_utf16, str);
|
||||
} else {
|
||||
demandSkip(pctx, '\\');
|
||||
uint32_t second_utf16 = read_4nibbles(pctx);
|
||||
if (!is_utf16_2bp_low_surrogate(second_utf16))
|
||||
throw bad_syntax();
|
||||
uint32_t cp = 0x10000 + ((first_utf16 - 0xD800) << 10) + (second_utf16 - 0xDC00);
|
||||
codepoint_to_utf8(cp, str);
|
||||
}
|
||||
} else {
|
||||
resert_to_one_char_escape(leader, str);
|
||||
skip(pctx); // Skipping leader
|
||||
}
|
||||
} else {
|
||||
str += ch;
|
||||
}
|
||||
}
|
||||
skip(pctx);
|
||||
if (!isUtf8String(str))
|
||||
throw bad_syntax();
|
||||
return str;
|
||||
}
|
||||
|
||||
std::unique_ptr<ParsingCall> ValueParseCall::here(ParserContext &pctx) {
|
||||
if (got_him)
|
||||
return NULL;
|
||||
got_him = true;
|
||||
skipWhitespaces(pctx);
|
||||
int herald = peep(pctx);
|
||||
if (herald == '"') {
|
||||
result = demandStringJson(pctx);
|
||||
} else if (isIntegerStart(herald)) {
|
||||
size_t pos_beg = pctx.pos;
|
||||
bool terrifying = false;
|
||||
bool mantis_minus;
|
||||
read_int_minus_part(pctx, mantis_minus);
|
||||
int64_t mantis_abs_max18;
|
||||
read_int_int_part(pctx, mantis_abs_max18, terrifying);
|
||||
read_int_frac_exp_part(pctx, terrifying);
|
||||
if (terrifying) {
|
||||
result = Integer(pctx.text.substr(pos_beg, pctx.pos).c_str());
|
||||
} else if (mantis_minus) {
|
||||
result = -mantis_abs_max18;
|
||||
} else {
|
||||
result = mantis_abs_max18;
|
||||
}
|
||||
} else if (isSymbolConstituent(herald)) {
|
||||
std::string sym;
|
||||
while (isSymbolConstituent(peep(pctx))) {
|
||||
sym += peep(pctx);
|
||||
skip(pctx);
|
||||
}
|
||||
if (sym == "null") {
|
||||
result = JSON(null_symbol);
|
||||
} else if (sym == "false") {
|
||||
result = JSON(false_symbol);
|
||||
} else if (sym == "true") {
|
||||
result = JSON(true_symbol);
|
||||
} else
|
||||
throw bad_syntax();
|
||||
} else if (herald == '[') {
|
||||
skip(pctx);
|
||||
result = JSON(array);
|
||||
return std::make_unique<ArrayParseCall>(result.asArray());
|
||||
} else if (herald == '{') {
|
||||
skip(pctx);
|
||||
result = JSON(dictionary);
|
||||
return std::make_unique<DictionaryParseCall>(result.asDictionary());
|
||||
} else
|
||||
throw bad_syntax();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int parse_str(const std::string& text, JSON& ret_ans, WrongSyntax& ret_error) {
|
||||
assert(ret_ans.isNull());
|
||||
ParserContext pctx(text);
|
||||
try {
|
||||
std::vector<std::unique_ptr<ParsingCall>> callStack;
|
||||
callStack.push_back(std::make_unique<ValueParseCall>(ret_ans));
|
||||
while (!callStack.empty()) {
|
||||
std::unique_ptr<ParsingCall> rt = callStack.back()->here(pctx);
|
||||
if (rt) {
|
||||
callStack.push_back(std::move(rt));
|
||||
} else {
|
||||
callStack.pop_back();
|
||||
}
|
||||
}
|
||||
skipWhitespaces(pctx);
|
||||
if (!isEof(pctx))
|
||||
throw bad_syntax();
|
||||
return 0;
|
||||
} catch (bad_syntax&) {
|
||||
ret_error.line = pctx.line;
|
||||
ret_error.column = pctx.column;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
JSON parse_str_flawless(const std::string &text) {
|
||||
WrongSyntax wsErr;
|
||||
JSON result;
|
||||
int ret = parse_str(text, result, wsErr);
|
||||
if (ret < 0)
|
||||
throw misuse("JSON parsing error");
|
||||
return result;
|
||||
}
|
||||
}
|
72
src/library/libjsonincpp/parser.h
Normal file
72
src/library/libjsonincpp/parser.h
Normal file
@ -0,0 +1,72 @@
|
||||
#ifndef TEST_WEBSITE_SRC_MODULE_JSON_LIBJSONINCPP_PARSER_H
|
||||
#define TEST_WEBSITE_SRC_MODULE_JSON_LIBJSONINCPP_PARSER_H
|
||||
|
||||
/* DO NOT EXPORT THIS FILE! User parser API is in string_representation.h */
|
||||
|
||||
#include "jsonobj.h"
|
||||
#include <memory>
|
||||
#include <assert.h>
|
||||
|
||||
namespace json {
|
||||
constexpr int endOfFile = 999999;
|
||||
|
||||
struct bad_syntax: public std::exception {
|
||||
inline bad_syntax() {
|
||||
assert(false);
|
||||
}
|
||||
};
|
||||
|
||||
struct ParserContext {
|
||||
const std::string& text;
|
||||
size_t pos = 0;
|
||||
size_t line = 0;
|
||||
size_t column = 0;
|
||||
|
||||
explicit ParserContext(const std::string& text);
|
||||
};
|
||||
|
||||
bool isEof(ParserContext& pctx);
|
||||
int peep(ParserContext& pctx);
|
||||
int skip(ParserContext& pctx);
|
||||
void demandSkip(ParserContext& pctx, char ch);
|
||||
bool isWhitespace(int vch);
|
||||
void skipWhitespaces(ParserContext& pctx);
|
||||
|
||||
/* Function that parses string value */
|
||||
std::string demandStringJson(ParserContext& pctx);
|
||||
|
||||
struct ParsingCall {
|
||||
virtual std::unique_ptr<ParsingCall> here(ParserContext& pctx);
|
||||
|
||||
virtual ~ParsingCall() = default;
|
||||
};
|
||||
|
||||
struct ValueParseCall: public ParsingCall {
|
||||
JSON& result;
|
||||
bool got_him = false;
|
||||
|
||||
std::unique_ptr<ParsingCall> here(ParserContext& pctx) override;
|
||||
|
||||
ValueParseCall(JSON& result);
|
||||
};
|
||||
|
||||
struct ArrayParseCall : public ParsingCall {
|
||||
std::vector<JSON>& result;
|
||||
bool got_one = false;
|
||||
|
||||
explicit ArrayParseCall(std::vector<JSON>& result);
|
||||
|
||||
std::unique_ptr<ParsingCall> here(ParserContext& pctx) override;
|
||||
};
|
||||
|
||||
struct DictionaryParseCall: public ParsingCall {
|
||||
std::map<std::string, JSON>& result;
|
||||
bool got_one = false;
|
||||
|
||||
explicit DictionaryParseCall(std::map<std::string, JSON>& result);
|
||||
|
||||
std::unique_ptr<ParsingCall> here(ParserContext& pctx) override;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
45
src/library/libjsonincpp/parser_context.cpp
Normal file
45
src/library/libjsonincpp/parser_context.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
#include "parser.h"
|
||||
#include <assert.h>
|
||||
|
||||
namespace json {
|
||||
ParserContext::ParserContext(const std::string& text): text(text) {}
|
||||
|
||||
bool isEof(ParserContext &pctx) {
|
||||
return pctx.pos >= pctx.text.size();
|
||||
}
|
||||
|
||||
int peep(ParserContext &pctx) {
|
||||
if (pctx.pos >= pctx.text.size())
|
||||
return endOfFile;
|
||||
return pctx.text[pctx.pos];
|
||||
}
|
||||
|
||||
int skip(ParserContext &pctx) {
|
||||
if (isEof(pctx))
|
||||
throw bad_syntax();
|
||||
int v = pctx.text[pctx.pos++];
|
||||
if (v == '\n') {
|
||||
pctx.line++;
|
||||
pctx.column = 0;
|
||||
} else
|
||||
pctx.column++;
|
||||
return v;
|
||||
|
||||
}
|
||||
|
||||
void demandSkip(ParserContext &pctx, char ch) {
|
||||
if (peep(pctx) != ch)
|
||||
throw bad_syntax();
|
||||
skip(pctx);
|
||||
}
|
||||
|
||||
bool isWhitespace(int vch) {
|
||||
return vch == ' ' || vch == '\t' || vch == '\n';
|
||||
}
|
||||
|
||||
void skipWhitespaces(ParserContext &pctx) {
|
||||
while (isWhitespace(peep(pctx))) {
|
||||
skip(pctx);
|
||||
}
|
||||
}
|
||||
}
|
120
src/library/libjsonincpp/quality_of_life.cpp
Normal file
120
src/library/libjsonincpp/quality_of_life.cpp
Normal file
@ -0,0 +1,120 @@
|
||||
#include "inner_storage.h"
|
||||
|
||||
#include "jsonobj.h"
|
||||
|
||||
namespace json {
|
||||
json_t JSON::getType() const {
|
||||
return type;
|
||||
}
|
||||
|
||||
bool JSON::isNull() const {
|
||||
return type == null_symbol;
|
||||
}
|
||||
|
||||
bool JSON::isBool() const {
|
||||
return type == false_symbol || type == true_symbol;
|
||||
}
|
||||
|
||||
bool JSON::isInteger() const {
|
||||
return type == integer;
|
||||
}
|
||||
|
||||
bool JSON::isFalse() const {
|
||||
return type == false_symbol;
|
||||
}
|
||||
|
||||
bool JSON::isTrue() const {
|
||||
return type == true_symbol;
|
||||
}
|
||||
|
||||
bool JSON::isString() const {
|
||||
return type == string;
|
||||
}
|
||||
|
||||
bool JSON::isArray() const {
|
||||
return type == array;
|
||||
}
|
||||
|
||||
bool JSON::isDictionary() const {
|
||||
return type == dictionary;
|
||||
}
|
||||
|
||||
bool JSON::isNatalistic() const {
|
||||
return type == array || type == dictionary;
|
||||
}
|
||||
|
||||
bool JSON::isSymbol() const {
|
||||
return type == null_symbol || type == false_symbol || type == true_symbol;
|
||||
}
|
||||
|
||||
bool JSON::toBool() const {
|
||||
switch(type) {
|
||||
case false_symbol:
|
||||
return false;
|
||||
case true_symbol:
|
||||
return true;
|
||||
default:
|
||||
throw misuse("json obj is not boolean");
|
||||
}
|
||||
}
|
||||
|
||||
Integer& JSON::asInteger() const {
|
||||
if (isInteger())
|
||||
return *static_cast<Integer*>(value);
|
||||
throw misuse("json obj is not integer");
|
||||
}
|
||||
|
||||
std::string& JSON::asString() const {
|
||||
if (isString())
|
||||
return *static_cast<std::string*>(value);
|
||||
throw misuse("json obj is not string");
|
||||
}
|
||||
|
||||
std::vector<JSON>& JSON::asArray() const {
|
||||
if (isArray())
|
||||
return static_cast<ArrayData*>(value)->data;
|
||||
throw misuse("json obj is not array");
|
||||
}
|
||||
|
||||
std::map<std::string, JSON>& JSON::asDictionary() const {
|
||||
if (isDictionary())
|
||||
return static_cast<DictionaryData*>(value)->data;
|
||||
throw misuse("json obj is not dictionary");
|
||||
}
|
||||
|
||||
JSON_reference JSON::operator[](size_t index) {
|
||||
return r()[index];
|
||||
}
|
||||
|
||||
JSON_reference JSON::operator[](const std::string &key) {
|
||||
return r()[key];
|
||||
}
|
||||
|
||||
JSON& JSON::operator=(int64_t V) {
|
||||
nullify(*this);
|
||||
value = new Integer(V);
|
||||
type = integer;
|
||||
return *this;
|
||||
}
|
||||
|
||||
JSON & JSON::operator=(const Integer &V) {
|
||||
nullify(*this);
|
||||
value = new Integer(V);
|
||||
type = integer;
|
||||
return *this;
|
||||
}
|
||||
|
||||
JSON & JSON::operator=(const char *V) {
|
||||
nullify(*this);
|
||||
value = new std::string(V);
|
||||
type = string;
|
||||
return *this;
|
||||
}
|
||||
|
||||
JSON & JSON::operator=(const std::string &V) {
|
||||
nullify(*this);
|
||||
value = new std::string(V);
|
||||
type = string;
|
||||
return *this;
|
||||
}
|
||||
}
|
58
src/library/libjsonincpp/quality_of_life_2.cpp
Normal file
58
src/library/libjsonincpp/quality_of_life_2.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
#include "jsonobj.h"
|
||||
|
||||
namespace json {
|
||||
bool JSON_reference::isDefined() const {
|
||||
return imaginary_chain.empty();
|
||||
}
|
||||
|
||||
JSON& JSON_reference::operator*() const {
|
||||
if (!isDefined())
|
||||
throw misuse("dereferencing json reference with non-empty imaginary part");
|
||||
return *last_real;
|
||||
}
|
||||
|
||||
void JSON_reference::operator=(const JSON &obj) {
|
||||
JSON* cur_last_real = last_real;
|
||||
for (const auto& ck: imaginary_chain) {
|
||||
if (ck.type == undefined_array_element) {
|
||||
if (cur_last_real->type == null_symbol)
|
||||
*cur_last_real = JSON(array);
|
||||
if (cur_last_real->type != array)
|
||||
throw misuse("Implicit array creation on top of neither non-null nor short-array json obj is not allowed");
|
||||
cur_last_real->asArray().resize(ck.when_array_index + 1);
|
||||
cur_last_real = &cur_last_real->asArray()[ck.when_array_index];
|
||||
} else {
|
||||
if (cur_last_real->type == null_symbol)
|
||||
*cur_last_real = JSON(dictionary);
|
||||
if (cur_last_real->type != dictionary)
|
||||
throw misuse("Implicit dictionary creation on top of neither non-null nor illiterate-dict json obj is not allowed");
|
||||
cur_last_real = &(cur_last_real->asDictionary()[ck.when_dictionary_key]);
|
||||
}
|
||||
}
|
||||
*cur_last_real = obj;
|
||||
}
|
||||
|
||||
JSON_reference JSON_reference::operator[](size_t index) {
|
||||
if (!imaginary_chain.empty()) {
|
||||
std::vector<ImaginaryKeyChainEValue> elongated = imaginary_chain;
|
||||
elongated.push_back({undefined_array_element, index, ""});
|
||||
return {last_real, elongated};
|
||||
}
|
||||
if (last_real->isArray() && last_real->asArray().size() > index) {
|
||||
return {&last_real->asArray()[index], {}};
|
||||
}
|
||||
return {last_real, {ImaginaryKeyChainEValue{undefined_array_element, index, ""}}};
|
||||
}
|
||||
|
||||
JSON_reference JSON_reference::operator[](const std::string &key) {
|
||||
if (!imaginary_chain.empty()) {
|
||||
std::vector<ImaginaryKeyChainEValue> elongated = imaginary_chain;
|
||||
elongated.push_back({undefined_dictionary_element, 0, key});
|
||||
return {last_real, elongated};
|
||||
}
|
||||
if (last_real->isDictionary() && last_real->asDictionary().count(key) > 0) {
|
||||
return {&last_real->asDictionary()[key], {}};
|
||||
}
|
||||
return {last_real, {ImaginaryKeyChainEValue{undefined_dictionary_element, 0, key}}};
|
||||
}
|
||||
}
|
27
src/library/libjsonincpp/string_representation.h
Normal file
27
src/library/libjsonincpp/string_representation.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef TEST_WEBSITE_SRC_MODULE_JSON_LIBJSONINCPP_STRING_REPRESENTATION_H
|
||||
#define TEST_WEBSITE_SRC_MODULE_JSON_LIBJSONINCPP_STRING_REPRESENTATION_H
|
||||
|
||||
#include "jsonobj.h"
|
||||
|
||||
namespace json {
|
||||
constexpr uint32_t print_compact = 1;
|
||||
constexpr uint32_t print_pretty = 0;
|
||||
|
||||
/* Throws json::misuse if obj contains non-utf8 strings. Throws std::bad_alloc */
|
||||
std::string generate_str(const JSON &obj, uint32_t mode);
|
||||
|
||||
struct WrongSyntax {
|
||||
size_t line;
|
||||
size_t column;
|
||||
};
|
||||
|
||||
/* Returns negative on error. Fills ret_error on syntax error. Still can throw std::bad_alloc
|
||||
* ret_ans should be passed as null symbol
|
||||
*/
|
||||
int parse_str(const std::string& text, JSON& ret_ans, WrongSyntax& ret_error);
|
||||
|
||||
/* Throws json::misuse when parsing fails */
|
||||
JSON parse_str_flawless(const std::string& text);
|
||||
}
|
||||
|
||||
#endif
|
82
src/library/libjsonincpp/utf8.cpp
Normal file
82
src/library/libjsonincpp/utf8.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
#include "utf8.h"
|
||||
#include <assert.h>
|
||||
|
||||
int _utf8_retrieve_size(uint8_t firstByte) {
|
||||
if (!(firstByte & 0b10000000))
|
||||
return 1;
|
||||
uint8_t a = 0b11000000;
|
||||
uint8_t b = 0b00100000;
|
||||
for (int i = 2; i <= 4; i++){
|
||||
if ((firstByte & (a | b)) == a)
|
||||
return i;
|
||||
a |= b;
|
||||
b >>= 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t _utf8_retrieve_character(int sz, size_t pos, const char *string) {
|
||||
if (sz == 1)
|
||||
return (int32_t)string[pos];
|
||||
uint32_t v = ((uint8_t)string[pos]) & (0b01111111 >> sz);
|
||||
pos++;
|
||||
for (int i = 1; i < sz; i++){
|
||||
uint32_t th = (uint8_t)string[pos];
|
||||
if ((th & 0b11000000) != 0b10000000)
|
||||
return -1;
|
||||
v <<= 6;
|
||||
v |= (th & 0b00111111);
|
||||
pos++;
|
||||
}
|
||||
assert(v <= INT32_MAX);
|
||||
return static_cast<int32_t>(v);
|
||||
}
|
||||
|
||||
void utf8_string_iterat(int32_t &cp, size_t &adj, size_t pos, const char *string, size_t string_size) {
|
||||
if (pos >= string_size) {cp = -1; return;}
|
||||
adj = _utf8_retrieve_size((uint8_t)string[pos]);
|
||||
if (adj < 0 || pos + adj > string_size) {cp = -1; return;}
|
||||
if ((cp = _utf8_retrieve_character(adj, pos, string)) < 0) {cp = -1; return;}
|
||||
}
|
||||
|
||||
bool isUtf8String(const std::string &str) {
|
||||
size_t N = str.size();
|
||||
size_t cpos = 0;
|
||||
while (cpos < N) {
|
||||
int32_t codepoint;
|
||||
size_t adj;
|
||||
utf8_string_iterat(codepoint, adj, cpos, str.data(), N);
|
||||
if (codepoint < 0)
|
||||
return false;
|
||||
cpos += adj;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int codepoint_to_utf8(uint32_t cp, std::string &out) {
|
||||
size_t N = out.size();
|
||||
auto make_compl = [cp](int imp) -> char {
|
||||
return (char)(((cp >> imp) & 0x3f) | 0x80);
|
||||
};
|
||||
if (cp > 0x10FFFF)
|
||||
return -1;
|
||||
if (cp <= 0x7F) {
|
||||
out += (char)cp;
|
||||
} else if (cp <= 0x7ff) {
|
||||
out.resize(N + 2);
|
||||
out[N] = (char)((cp >> 6) | 0xc0);
|
||||
out[N + 1] = make_compl(0);
|
||||
} else if (cp <= 0xffff) {
|
||||
out.resize(N + 3);
|
||||
out[N] = (char)((cp >> 12) | 0xe0);
|
||||
out[N + 1] = make_compl(6);
|
||||
out[N + 2] = make_compl(0);
|
||||
} else {
|
||||
out.resize(N + 4);
|
||||
out[N] = (char)((cp >> 18) | 0xf0);
|
||||
out[N + 1] = make_compl(12);
|
||||
out[N + 2] = make_compl(6);
|
||||
out[N + 3] = make_compl(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
18
src/library/libjsonincpp/utf8.h
Normal file
18
src/library/libjsonincpp/utf8.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef TEST_WEBSITE_SRC_MODULE_JSON_LIBJSONINCPP_UTF8_H
|
||||
#define TEST_WEBSITE_SRC_MODULE_JSON_LIBJSONINCPP_UTF8_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
int _utf8_retrieve_size(uint8_t firstByte);
|
||||
|
||||
int32_t _utf8_retrieve_character(int sz, size_t pos, const char *string);
|
||||
|
||||
void utf8_string_iterat(int32_t &cp, size_t &adj, size_t pos, const char *string, size_t string_size);
|
||||
|
||||
bool isUtf8String(const std::string& str);
|
||||
|
||||
/* Returns -1 if cp is not in 0-0x10FFFF range */
|
||||
int codepoint_to_utf8(uint32_t cp, std::string& out);
|
||||
|
||||
#endif
|
17
src/tests/test0.cpp
Normal file
17
src/tests/test0.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include <libjsonincpp/jsonobj.h>
|
||||
#include <libjsonincpp/string_representation.h>
|
||||
#include <assert.h>
|
||||
|
||||
using namespace json;
|
||||
|
||||
void prettyprint_json(const JSON& obj) {
|
||||
printf("%s", generate_str(obj, print_pretty).c_str());
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::string text = "{\"\":\"\", \"true\":true, \"asd\" : { \"aaa\" : [[[[[[[123, 123, 13123123]]]]]]]}} ";
|
||||
JSON j;
|
||||
j = parse_str_flawless(text);
|
||||
prettyprint_json(j);
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user