diff --git a/CMakeLists.txt b/CMakeLists.txt index 2927b2c..d810162 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/include/json2cpp/json2cpp.hpp b/include/json2cpp/json2cpp.hpp index a32d6c4..88aa289 100644 --- a/include/json2cpp/json2cpp.hpp +++ b/include/json2cpp/json2cpp.hpp @@ -85,16 +85,24 @@ template struct basic_json { return { data_.object_value, size() }; } - constexpr const basic_json* find_value(std::basic_string_view 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 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"); } @@ -111,14 +119,15 @@ template 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 v) noexcept { + + inline constexpr basic_json(std::basic_string_view 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; } @@ -138,7 +147,7 @@ template struct basic_json { constexpr const basic_json &operator[](size_t index) const { return at(index); } template - 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()); } @@ -148,7 +157,7 @@ template struct basic_json { return is_array() ? array_data()[index] : object_data()[index].second; } - constexpr const basic_json &at(std::basic_string_view key) const { + inline constexpr const basic_json &at(std::basic_string_view key) const { const auto* result = find_value(key); if (!result) throw std::out_of_range("Key not found"); return *result; @@ -164,14 +173,15 @@ template struct basic_json { constexpr std::basic_string_view getString() const { return { string_data(), size() }; } - constexpr double getNumber() const { + inline constexpr double getNumber() const { return type() == Type::UInteger ? static_cast(data_.uint_value) : type() == Type::Integer ? static_cast(data_.int_value) : type() == Type::Float ? data_.float_value : throw std::runtime_error("Not a number"); } - template constexpr T get() const { + template + inline constexpr T get() const { if constexpr (std::is_same_v>) { return getString(); } else if constexpr (std::is_same_v) { @@ -197,4 +207,4 @@ using value_pair_t = basic_value_pair_t; } -#endif +#endi diff --git a/src/json2cpp.cpp b/src/json2cpp.cpp index 33a6f22..82bc87b 100644 --- a/src/json2cpp.cpp +++ b/src/json2cpp.cpp @@ -25,10 +25,10 @@ SOFTWARE. #include "json2cpp.hpp" #include #include -#include -#include #include +#include #include +#include std::string format_json_string(const std::string &str) { @@ -84,55 +84,46 @@ struct StringDuplicateTracker } }; -struct DuplicateTracker { - std::unordered_map counts; - std::unordered_map signature_to_var; - std::set processed_signatures; - std::size_t counter = 0; - const std::string prefix; +struct DuplicateTracker +{ + std::unordered_map counts; + std::unordered_map signature_to_var; + std::set 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); @@ -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()); } @@ -172,42 +165,47 @@ std::string generate_node_body(const nlohmann::ordered_json &value, if (value.is_object()) { std::vector 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 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 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 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 ""; } @@ -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()); } else if (value.is_number_unsigned()) { @@ -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(); @@ -303,29 +299,36 @@ namespace compiled_json::{}::impl {{ using array_t = json2cpp::basic_array_t; using object_t = json2cpp::basic_object_t; using value_pair_t = json2cpp::basic_value_pair_t; - )", 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; } @@ -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,