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
116 changes: 116 additions & 0 deletions benchmark/json.cc
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,121 @@ static void JSON_Parse_Real(benchmark::State &state) {
}
}

static void JSON_Parse_Decimal(benchmark::State &state) {
const auto document{R"JSON([
123456789012345678901234567890,
987654321098765432109876543210,
111111111111111111111111111111,
999999999999999999999999999999,
555555555555555555555555555555,
1e309,
1e310,
1e320,
1e350,
1e400,
9.99e308,
9.99e309,
9.99e310,
9.99e320,
9.99e350,
123456789012345678901234567890,
987654321098765432109876543210,
111111111111111111111111111111,
999999999999999999999999999999,
555555555555555555555555555555,
1e309,
1e310,
1e320,
1e350,
1e400,
9.99e308,
9.99e309,
9.99e310,
9.99e320,
9.99e350,
123456789012345678901234567890,
987654321098765432109876543210,
111111111111111111111111111111,
999999999999999999999999999999,
555555555555555555555555555555,
1e309,
1e310,
1e320,
1e350,
1e400,
9.99e308,
9.99e309,
9.99e310,
9.99e320,
9.99e350,
123456789012345678901234567890,
987654321098765432109876543210,
111111111111111111111111111111,
999999999999999999999999999999,
555555555555555555555555555555,
1e309,
1e310,
1e320,
1e350,
1e400,
9.99e308,
9.99e309,
9.99e310,
9.99e320,
9.99e350,
123456789012345678901234567890,
987654321098765432109876543210,
111111111111111111111111111111,
999999999999999999999999999999,
555555555555555555555555555555,
1e309,
1e310,
1e320,
1e350,
1e400,
9.99e308,
9.99e309,
9.99e310,
9.99e320,
9.99e350,
123456789012345678901234567890,
987654321098765432109876543210,
111111111111111111111111111111,
999999999999999999999999999999,
555555555555555555555555555555,
1e309,
1e310,
1e320,
1e350,
1e400,
9.99e308,
9.99e309,
9.99e310,
9.99e320,
9.99e350,
123456789012345678901234567890,
987654321098765432109876543210,
111111111111111111111111111111,
999999999999999999999999999999,
555555555555555555555555555555,
1e309,
1e310,
1e320,
1e350,
1e400
])JSON"};

assert(
std::ranges::all_of(sourcemeta::core::parse_json(document).as_array(),
[](const auto &item) { return item.is_decimal(); }));

for (auto _ : state) {
auto result{sourcemeta::core::parse_json(document)};
assert(result.is_array());
benchmark::DoNotOptimize(result);
}
}

static void JSON_Fast_Hash_Helm_Chart_Lock(benchmark::State &state) {
// From `helm-chart-lock`
const auto document{sourcemeta::core::parse_json(R"JSON({
Expand Down Expand Up @@ -427,6 +542,7 @@ static void JSON_Object_Defines_Miss_Too_Large(benchmark::State &state) {
BENCHMARK(JSON_Array_Of_Objects_Unique);
BENCHMARK(JSON_Parse_1);
BENCHMARK(JSON_Parse_Real);
BENCHMARK(JSON_Parse_Decimal);
BENCHMARK(JSON_Fast_Hash_Helm_Chart_Lock);
BENCHMARK(JSON_Equality_Helm_Chart_Lock);
BENCHMARK(JSON_String_Equal)->Args({10})->Args({100});
Expand Down
24 changes: 23 additions & 1 deletion config.cmake.in
Original file line number Diff line number Diff line change
Expand Up @@ -50,41 +50,63 @@ foreach(component ${SOURCEMETA_CORE_COMPONENTS})
elseif(component STREQUAL "uri")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_uri.cmake")
elseif(component STREQUAL "json")
find_dependency(mpdecimal CONFIG)
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_numeric.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_io.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_json.cmake")
elseif(component STREQUAL "jsonl")
find_dependency(mpdecimal CONFIG)
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_numeric.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_io.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_json.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_jsonl.cmake")
elseif(component STREQUAL "jsonpointer")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_regex.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_uri.cmake")
find_dependency(mpdecimal CONFIG)
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_numeric.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_io.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_json.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_jsonpointer.cmake")
elseif(component STREQUAL "jsonschema")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_uri.cmake")
find_dependency(mpdecimal CONFIG)
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_numeric.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_io.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_json.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_jsonpointer.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_jsonschema.cmake")
elseif(component STREQUAL "yaml")
find_dependency(mpdecimal CONFIG)
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_numeric.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_io.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_json.cmake")
find_dependency(yaml CONFIG)
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_io.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_yaml.cmake")
elseif(component STREQUAL "alterschema")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_uri.cmake")
find_dependency(mpdecimal CONFIG)
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_numeric.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_io.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_json.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_jsonpointer.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_jsonschema.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_alterschema.cmake")
elseif(component STREQUAL "editorschema")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_uri.cmake")
find_dependency(mpdecimal CONFIG)
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_numeric.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_io.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_json.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_jsonpointer.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_jsonschema.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_editorschema.cmake")
elseif(component STREQUAL "schemaconfig")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_io.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_uri.cmake")
find_dependency(mpdecimal CONFIG)
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_numeric.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_io.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_json.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_jsonpointer.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_schemaconfig.cmake")
Expand Down
11 changes: 11 additions & 0 deletions src/core/json/include/sourcemeta/core/json_auto.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,17 @@ auto from_json(const JSON &value) -> std::optional<T> {
}
}

/// @ingroup json
template <typename T>
requires std::is_same_v<T, Decimal>
auto from_json(const JSON &value) -> std::optional<T> {
if (value.is_decimal()) {
return value.to_decimal();
} else {
return std::nullopt;
}
}

// TODO: How can we keep this in the hash header that does not yet know about
// JSON?
/// @ingroup json
Expand Down
14 changes: 0 additions & 14 deletions src/core/json/include/sourcemeta/core/json_error.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,6 @@ class SOURCEMETA_CORE_JSON_EXPORT JSONParseError : public std::exception {
std::string message_{"Failed to parse the JSON document"};
};

/// @ingroup json
/// This class represents a numeric integer limit parsing error
class SOURCEMETA_CORE_JSON_EXPORT JSONParseIntegerLimitError
: public JSONParseError {
public:
/// Create a parsing error
JSONParseIntegerLimitError(const std::uint64_t line,
const std::uint64_t column)
: JSONParseError{
line, column,
"The JSON value is not representable by the IETF RFC 8259 "
"interoperable signed integer range"} {}
};

/// @ingroup json
/// This class represents a parsing error occurring from parsing a file
class SOURCEMETA_CORE_JSON_EXPORT JSONFileParseError : public JSONParseError {
Expand Down
39 changes: 38 additions & 1 deletion src/core/json/include/sourcemeta/core/json_value.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include <sourcemeta/core/json_hash.h>
#include <sourcemeta/core/json_object.h>

#include <sourcemeta/core/numeric.h>

#include <algorithm> // std::any_of
#include <cassert> // assert
#include <cstddef> // std::size_t
Expand Down Expand Up @@ -55,7 +57,8 @@ class SOURCEMETA_CORE_JSON_EXPORT JSON {
Real = 3,
String = 4,
Array = 5,
Object = 6
Object = 6,
Decimal = 7
};

/// An optional callback that can be passed to parsing functions to obtain
Expand Down Expand Up @@ -214,6 +217,12 @@ class SOURCEMETA_CORE_JSON_EXPORT JSON {
/// A copy constructor for the object type.
explicit JSON(const Object &value);

/// A copy constructor for the decimal type.
explicit JSON(const Decimal &value);

/// A move constructor for the decimal type.
explicit JSON(Decimal &&value);

/// Misc constructors
JSON(const JSON &);
JSON(JSON &&) noexcept;
Expand Down Expand Up @@ -463,6 +472,19 @@ class SOURCEMETA_CORE_JSON_EXPORT JSON {
/// ```
[[nodiscard]] auto is_object() const noexcept -> bool;

/// Check if the input JSON document is an arbitrary precision decimal value.
/// For example:
///
/// ```cpp
/// #include <sourcemeta/core/json.h>
/// #include <cassert>
///
/// const sourcemeta::core::Decimal value{1234567890};
/// const sourcemeta::core::JSON document{value};
/// assert(document.is_decimal());
/// ```
[[nodiscard]] auto is_decimal() const noexcept -> bool;

/// Get the type of the JSON document. For example:
///
/// ```cpp
Expand Down Expand Up @@ -519,6 +541,20 @@ class SOURCEMETA_CORE_JSON_EXPORT JSON {
/// ```
[[nodiscard]] auto to_real() const noexcept -> Real;

/// Convert a JSON instance into a decimal value. The result of this method
/// is undefined unless the JSON instance holds a decimal value. For example:
///
/// ```cpp
/// #include <sourcemeta/core/json.h>
/// #include <cassert>
///
/// const sourcemeta::core::Decimal value{1234567890};
/// const sourcemeta::core::JSON document{value};
/// assert(document.is_decimal());
/// assert(document.to_decimal().to_int64() == 1234567890);
/// ```
[[nodiscard]] auto to_decimal() const noexcept -> const Decimal &;

/// Convert a JSON instance into a standard string value. The result of this
/// method is undefined unless the JSON instance holds a string value. For
/// example:
Expand Down Expand Up @@ -1713,6 +1749,7 @@ class SOURCEMETA_CORE_JSON_EXPORT JSON {
String data_string;
Array data_array;
Object data_object;
Decimal data_decimal;
};
#if defined(_MSC_VER)
#pragma warning(default : 4251)
Expand Down
5 changes: 2 additions & 3 deletions src/core/json/json.cc
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,6 @@ auto read_json(const std::filesystem::path &path,
auto stream{read_file<JSON::Char, JSON::CharTraits>(path)};
try {
return parse_json(stream, callback);
} catch (const JSONParseIntegerLimitError &error) {
// For producing better error messages
throw JSONFileParseError(path, error);
} catch (const JSONParseError &error) {
// For producing better error messages
throw JSONFileParseError(path, error);
Expand Down Expand Up @@ -94,6 +91,8 @@ auto operator<<(std::basic_ostream<JSON::Char, JSON::CharTraits> &stream,
return stream << "integer";
case sourcemeta::core::JSON::Type::Real:
return stream << "real";
case sourcemeta::core::JSON::Type::Decimal:
return stream << "decimal";
case sourcemeta::core::JSON::Type::String:
return stream << "string";
case sourcemeta::core::JSON::Type::Array:
Expand Down
Loading