Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,7 @@ set(CMAKE_CXX_EXTENSIONS ON)
# Set the project name and language
project(
json2cpp
VERSION 3.2
VERSION 3.3
DESCRIPTION ""
HOMEPAGE_URL "%%myurl%%"
LANGUAGES CXX C)
Expand Down
48 changes: 29 additions & 19 deletions include/json2cpp/json2cpp.hpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -85,16 +85,24 @@ template<typename CharType> struct basic_json{
return{data_.object_value, size() };
}

constexpr const basic_json* find_value(std::basic_string_view<CharType> key) const noexcept{
if (!is_object()) return nullptr;
const auto obj = object_data();
for (const auto& pair : obj){
if (pair.first == key) return &pair.second;
}
return nullptr;
}

constexpr void check_bounds(size_t index) const{
inline constexpr const basic_json* find_value(std::basic_string_view<CharType> key) const noexcept{
if (is_object()){
const auto obj = object_data();
const auto it = std::ranges::find_if(obj, [key](const auto& pair){
return pair.first.getString() == key;
});
return (it != obj.end()) ? &it->second : nullptr;
} else if (is_array()){
const auto arr = array_data();
const auto it = std::ranges::find_if(arr, [key](const auto& element){
return element.is_string() && element.getString() == key;
});
return (it != arr.end()) ? &(*it) : nullptr;
}
return nullptr;
}

inline constexpr void check_bounds(size_t index) const{
if (index >= size()) throw std::runtime_error("Index out of range");
}

Expand All@@ -111,14 +119,15 @@ template<typename CharType> struct basic_json{
constexpr basic_json(int64_t v) noexcept : data_{.int_value = v }{set_type_and_length(Type::Integer, 0)}
constexpr basic_json(uint64_t v) noexcept : data_{.uint_value = v }{set_type_and_length(Type::UInteger, 0)}
constexpr basic_json(double v) noexcept : data_{.float_value = v }{set_type_and_length(Type::Float, 0)}
constexpr basic_json(std::basic_string_view<CharType> v) noexcept{

inline constexpr basic_json(std::basic_string_view<CharType> v) noexcept{
const size_t len = v.size();
set_type_and_length(Type::String, len);
if (len <= capacity){

if (len <= capacity)
std::ranges::copy(v, data_.short_data.begin());
} else{
else
data_.long_data = v.data();
}
}

constexpr size_t size() const noexcept{return type_and_length_ & LENGTH_MASK}
Expand All@@ -138,7 +147,7 @@ template<typename CharType> struct basic_json{
constexpr const basic_json &operator[](size_t index) const{return at(index)}

template<typename ValueType>
constexpr bool operator==(const ValueType &other) const{
inline constexpr bool operator==(const ValueType &other) const{
return is_string() && size() == other.length() &&
std::equal(string_data(), string_data() + size(), other.data());
}
Expand All@@ -148,7 +157,7 @@ template<typename CharType> struct basic_json{
return is_array() ? array_data()[index] : object_data()[index].second;
}

constexpr const basic_json &at(std::basic_string_view<CharType> key) const{
inline constexpr const basic_json &at(std::basic_string_view<CharType> key) const{
const auto* result = find_value(key);
if (!result) throw std::out_of_range("Key not found");
return *result;
Expand All@@ -164,14 +173,15 @@ template<typename CharType> struct basic_json{

constexpr std::basic_string_view<CharType> getString() const{return{string_data(), size() }}

constexpr double getNumber() const{
inline constexpr double getNumber() const{
return type() == Type::UInteger ? static_cast<double>(data_.uint_value) :
type() == Type::Integer ? static_cast<double>(data_.int_value) :
type() == Type::Float ? data_.float_value :
throw std::runtime_error("Not a number");
}

template<typename T> constexpr T get() const{
template<typename T>
inline constexpr T get() const{
if constexpr (std::is_same_v<T, std::basic_string_view<CharType>>){
return getString();
} else if constexpr (std::is_same_v<T, bool>){
Expand All@@ -197,4 +207,4 @@ using value_pair_t = basic_value_pair_t<basicType>

}

#endif
#endi
174 changes: 89 additions & 85 deletions src/json2cpp.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,10 +25,10 @@ SOFTWARE.
#include "json2cpp.hpp"
#include <algorithm>
#include <fstream>
#include <unordered_map>
#include <vector>
#include <set>
#include <unordered_map>
#include <utility>
#include <vector>

std::string format_json_string(const std::string &str)
{
Expand DownExpand Up@@ -84,55 +84,46 @@ struct StringDuplicateTracker
}
};

struct DuplicateTracker{
std::unordered_map<std::string, int> counts;
std::unordered_map<std::string, std::string> signature_to_var;
std::set<std::string> processed_signatures;
std::size_t counter = 0;
const std::string prefix;
struct DuplicateTracker
{
std::unordered_map<std::string, int> counts;
std::unordered_map<std::string, std::string> signature_to_var;
std::set<std::string> processed_signatures;
std::size_t counter = 0;
const std::string prefix;

DuplicateTracker(std::string p) : prefix(std::move(p)){}
DuplicateTracker(std::string p) : prefix(std::move(p)){}

void track(const nlohmann::ordered_json& value){
counts[value.dump()]++;
}
void track(const nlohmann::ordered_json &value){counts[value.dump()]++}

void prepare_variables(){
for(const auto& [signature, count] : counts){
if (count > 1){
signature_to_var[signature] = fmt::format("shared_{}_{}", prefix, counter++);
}
}
void prepare_variables()
{
for (const auto &[signature, count] : counts){
if (count > 1){signature_to_var[signature] = fmt::format("shared_{}_{}", prefix, counter++)}
}
}

bool is_shared(const std::string& signature) const{
return signature_to_var.count(signature);
}
bool is_shared(const std::string &signature) const{return signature_to_var.count(signature)}

bool is_processed(const std::string& signature) const{
return processed_signatures.count(signature);
}

void mark_as_processed(const std::string& signature){
processed_signatures.insert(signature);
}

std::size_t get_reused_count() const{return signature_to_var.size()}
bool is_processed(const std::string &signature) const{return processed_signatures.count(signature)}

int32_t get_total_references_saved() const{
int32_t total = 0;
for (const auto& [sig, var] : signature_to_var){
total += counts.at(sig) - 1;
}
return total;
}
void mark_as_processed(const std::string &signature){processed_signatures.insert(signature)}

std::size_t get_reused_count() const{return signature_to_var.size()}

int32_t get_total_references_saved() const
{
int32_t total = 0;
for (const auto &[sig, var] : signature_to_var){total += counts.at(sig) - 1}
return total;
}
};

void analyze_for_duplicates(const nlohmann::ordered_json &value,
StringDuplicateTracker &string_tracker,
DuplicateTracker &object_tracker,
DuplicateTracker &array_tracker,
DuplicateTracker &pair_tracker)
StringDuplicateTracker &string_tracker,
DuplicateTracker &object_tracker,
DuplicateTracker &array_tracker,
DuplicateTracker &pair_tracker)
{
if (value.is_object()){
object_tracker.track(value);
Expand All@@ -145,7 +136,9 @@ void analyze_for_duplicates(const nlohmann::ordered_json &value,
}
} else if (value.is_array()){
array_tracker.track(value);
for (const auto &item : value){analyze_for_duplicates(item, string_tracker, object_tracker, array_tracker, pair_tracker)}
for (const auto &item : value){
analyze_for_duplicates(item, string_tracker, object_tracker, array_tracker, pair_tracker);
}
} else if (value.is_string()){
string_tracker.count_string(value.get<std::string>());
}
Expand All@@ -172,42 +165,47 @@ std::string generate_node_body(const nlohmann::ordered_json &value,
if (value.is_object()){
std::vector<std::string> pairs;
for (auto itr = value.begin(); itr != value.end(); ++itr){
nlohmann::ordered_json pair_rep;
pair_rep[itr.key()] = *itr;
std::string pair_signature = pair_rep.dump();

if (pair_tracker.is_shared(pair_signature)){
auto var_name = pair_tracker.signature_to_var.at(pair_signature);
if (!pair_tracker.is_processed(pair_signature)){
pair_tracker.mark_as_processed(pair_signature);
const auto key_repr = string_tracker.get_string_representation(itr.key());
const auto val_repr = compile_dispatch(*itr, obj_count, lines, string_tracker, object_tracker, array_tracker, pair_tracker);
lines.emplace_back(fmt::format("inline constexpr auto{} = value_pair_t{{{},{}}};", var_name, key_repr, val_repr));
}
pairs.emplace_back(fmt::format("{},", var_name));
} else{
const auto key_repr = string_tracker.get_string_representation(itr.key());
pairs.emplace_back(fmt::format("value_pair_t{{{},{}}},", key_repr, compile_dispatch(*itr, obj_count, lines, string_tracker, object_tracker, array_tracker, pair_tracker)));
nlohmann::ordered_json pair_rep;
pair_rep[itr.key()] = *itr;
std::string pair_signature = pair_rep.dump();

if (pair_tracker.is_shared(pair_signature)){
auto var_name = pair_tracker.signature_to_var.at(pair_signature);
if (!pair_tracker.is_processed(pair_signature)){
pair_tracker.mark_as_processed(pair_signature);
const auto key_repr = string_tracker.get_string_representation(itr.key());
const auto val_repr =
compile_dispatch(*itr, obj_count, lines, string_tracker, object_tracker, array_tracker, pair_tracker);
lines.emplace_back(
fmt::format("inline constexpr auto{} = value_pair_t{{{},{}}};", var_name, key_repr, val_repr));
}
pairs.emplace_back(fmt::format("{},", var_name));
} else{
const auto key_repr = string_tracker.get_string_representation(itr.key());
pairs.emplace_back(fmt::format("value_pair_t{{{},{}}},",
key_repr,
compile_dispatch(*itr, obj_count, lines, string_tracker, object_tracker, array_tracker, pair_tracker)));
}
}
lines.emplace_back(fmt::format(
"inline constexpr std::array<value_pair_t,{}> object_data_{} ={{{{", pairs.size(), current_object_number));
for (const auto& pair : pairs){lines.emplace_back(fmt::format("{}", pair))}
for (const auto &pair : pairs){lines.emplace_back(fmt::format("{}", pair))}
lines.emplace_back("}};");
return fmt::format("object_t{{object_data_{}}}", current_object_number);

} else if (value.is_array()){
std::vector<std::string> entries;
for (const auto& child : value){
entries.emplace_back(fmt::format("{{{}}},", compile_dispatch(child, obj_count, lines, string_tracker, object_tracker, array_tracker, pair_tracker)));
for (const auto &child : value){
entries.emplace_back(fmt::format("{{{}}},",
compile_dispatch(child, obj_count, lines, string_tracker, object_tracker, array_tracker, pair_tracker)));
}
lines.emplace_back(fmt::format(
"inline constexpr std::array<json,{}> object_data_{} ={{{{", entries.size(), current_object_number));
for (const auto& entry : entries){lines.emplace_back(fmt::format("{}", entry))}
for (const auto &entry : entries){lines.emplace_back(fmt::format("{}", entry))}
lines.emplace_back("}};");
return fmt::format("array_t{{object_data_{}}}", current_object_number);
}

return ""
}

Expand All@@ -221,31 +219,29 @@ std::string compile_dispatch(const nlohmann::ordered_json &value,
DuplicateTracker &pair_tracker)
{
std::string signature = value.dump();

if (value.is_object() && object_tracker.is_shared(signature)){
auto var_name = object_tracker.signature_to_var.at(signature);
if (object_tracker.is_processed(signature)){
return var_name;
}
if (object_tracker.is_processed(signature)){return var_name}
object_tracker.mark_as_processed(signature);
auto body = generate_node_body(value, obj_count, lines, string_tracker, object_tracker, array_tracker, pair_tracker);
auto body =
generate_node_body(value, obj_count, lines, string_tracker, object_tracker, array_tracker, pair_tracker);
lines.emplace_back(fmt::format("inline constexpr auto{} = json{{{{{}}}}};", var_name, body));
return var_name;
}

if (value.is_array() && array_tracker.is_shared(signature)){
auto var_name = array_tracker.signature_to_var.at(signature);
if (array_tracker.is_processed(signature)){
return var_name;
}
if (array_tracker.is_processed(signature)){return var_name}
array_tracker.mark_as_processed(signature);
auto body = generate_node_body(value, obj_count, lines, string_tracker, object_tracker, array_tracker, pair_tracker);
auto body =
generate_node_body(value, obj_count, lines, string_tracker, object_tracker, array_tracker, pair_tracker);
lines.emplace_back(fmt::format("inline constexpr auto{} = json{{{{{}}}}};", var_name, body));
return var_name;
}

if (value.is_object() || value.is_array()){
return generate_node_body(value, obj_count, lines, string_tracker, object_tracker, array_tracker, pair_tracker);
return generate_node_body(value, obj_count, lines, string_tracker, object_tracker, array_tracker, pair_tracker);
} else if (value.is_number_float()){
return fmt::format("double{{{}}}", value.get<double>());
} else if (value.is_number_unsigned()){
Expand All@@ -270,7 +266,7 @@ compile_results compile(const std::string_view document_name, const nlohmann::or
DuplicateTracker array_tracker("arr");
DuplicateTracker pair_tracker("pair");
compile_results results;

analyze_for_duplicates(json, string_tracker, object_tracker, array_tracker, pair_tracker);
string_tracker.generate_definitions();
object_tracker.prepare_variables();
Expand DownExpand Up@@ -303,29 +299,36 @@ namespace compiled_json::{}::impl{{
using array_t = json2cpp::basic_array_t<basicType>
using object_t = json2cpp::basic_object_t<basicType>
using value_pair_t = json2cpp::basic_value_pair_t<basicType>
)", document_name));
)",
document_name));

const auto &string_defs = string_tracker.get_definitions();
results.impl.insert(results.impl.end(), string_defs.begin(), string_defs.end());

std::size_t obj_count = 0;
const auto last_obj_name = compile_dispatch(json, obj_count, results.impl, string_tracker, object_tracker, array_tracker, pair_tracker);
const auto last_obj_name =
compile_dispatch(json, obj_count, results.impl, string_tracker, object_tracker, array_tracker, pair_tracker);

results.impl.emplace_back(fmt::format(R"(
inline constexpr auto document = json{{{{{}}}}};
}}
#endif)", last_obj_name));
#endif)",
last_obj_name));

spdlog::info("{} JSON objects processed.", obj_count);
spdlog::info("{} duplicate strings reused, saving{} string definitions.",
string_tracker.get_reused_count(), string_tracker.get_total_references_saved());
string_tracker.get_reused_count(),
string_tracker.get_total_references_saved());
spdlog::info("{} duplicate arrays reused, saving{} references.",
array_tracker.get_reused_count(), array_tracker.get_total_references_saved());
array_tracker.get_reused_count(),
array_tracker.get_total_references_saved());
spdlog::info("{} duplicate objects reused, saving{} references.",
object_tracker.get_reused_count(), object_tracker.get_total_references_saved());
object_tracker.get_reused_count(),
object_tracker.get_total_references_saved());
spdlog::info("{} duplicate key-value pairs reused, saving{} references.",
pair_tracker.get_reused_count(), pair_tracker.get_total_references_saved());

pair_tracker.get_reused_count(),
pair_tracker.get_total_references_saved());

return results;
}

Expand DownExpand Up@@ -358,7 +361,8 @@ void write_compilation([[maybe_unused]] std::string_view document_name,
cpp << fmt::format("#include \"{}\"\n", impl_name.filename().string());
cpp << fmt::format(
"namespace compiled_json::{}{{\nconst json2cpp::json &get(){{return compiled_json::{}::impl::document}}\n}}\n",
document_name, document_name);
document_name,
document_name);
}

void compile_to(const std::string_view document_name,
Expand Down