guix/gnu/packages/patches/sajson-for-gemmi-numbers-as-strings.patch
David Elsing 3cbb634a89
gnu: Add sajson-for-gemmi.
* gnu/packages/cpp.scm (sajson-for-gemmi): New variable.

Signed-off-by: Liliana Marie Prikler <liliana.prikler@gmail.com>
2022-10-15 12:01:34 +02:00

195 lines
6.5 KiB
Diff

Patch for gemmi: Keep numbers in JSON file as strings.
Adapted from this commit of the bundled fork of sajson in gemmi:
https://github.com/project-gemmi/gemmi/commit/fccbca4f6040364ba708613e1429c2251872240d
diff -ur a/include/sajson.h b/include/sajson.h
--- a/include/sajson.h
+++ b/include/sajson.h
@@ -411,43 +411,6 @@
};
} // namespace internal
-namespace integer_storage {
-enum { word_length = 1 };
-
-inline int load(const size_t* location) {
- int value;
- memcpy(&value, location, sizeof(value));
- return value;
-}
-
-inline void store(size_t* location, int value) {
- // NOTE: Most modern compilers optimize away this constant-size
- // memcpy into a single instruction. If any don't, and treat
- // punning through a union as legal, they can be special-cased.
- static_assert(
- sizeof(value) <= sizeof(*location),
- "size_t must not be smaller than int");
- memcpy(location, &value, sizeof(value));
-}
-} // namespace integer_storage
-
-namespace double_storage {
-enum { word_length = sizeof(double) / sizeof(size_t) };
-
-inline double load(const size_t* location) {
- double value;
- memcpy(&value, location, sizeof(double));
- return value;
-}
-
-inline void store(size_t* location, double value) {
- // NOTE: Most modern compilers optimize away this constant-size
- // memcpy into a single instruction. If any don't, and treat
- // punning through a union as legal, they can be special-cased.
- memcpy(location, &value, sizeof(double));
-}
-} // namespace double_storage
-
/// Represents a JSON value. First, call get_type() to check its type,
/// which determines which methods are available.
///
@@ -585,70 +548,10 @@
return length;
}
- /// If a numeric value was parsed as a 32-bit integer, returns it.
- /// Only legal if get_type() is TYPE_INTEGER.
- int get_integer_value() const {
- assert_tag(tag::integer);
- return integer_storage::load(payload);
- }
-
- /// If a numeric value was parsed as a double, returns it.
- /// Only legal if get_type() is TYPE_DOUBLE.
- double get_double_value() const {
- assert_tag(tag::double_);
- return double_storage::load(payload);
- }
-
- /// Returns a numeric value as a double-precision float.
- /// Only legal if get_type() is TYPE_INTEGER or TYPE_DOUBLE.
- double get_number_value() const {
- assert_tag_2(tag::integer, tag::double_);
- if (value_tag == tag::integer) {
- return get_integer_value();
- } else {
- return get_double_value();
- }
- }
-
- /// Returns true and writes to the output argument if the numeric value
- /// fits in a 53-bit integer. This is useful for timestamps and other
- /// situations where integral values with greater than 32-bit precision
- /// are used, as 64-bit values are not understood by all JSON
- /// implementations or languages.
- /// Returns false if the value is not an integer or not in range.
- /// Only legal if get_type() is TYPE_INTEGER or TYPE_DOUBLE.
- bool get_int53_value(int64_t* out) const {
- // Make sure the output variable is always defined to avoid any
- // possible situation like
- // https://gist.github.com/chadaustin/2c249cb850619ddec05b23ca42cf7a18
- *out = 0;
-
- assert_tag_2(tag::integer, tag::double_);
- switch (value_tag) {
- case tag::integer:
- *out = get_integer_value();
- return true;
- case tag::double_: {
- double v = get_double_value();
- if (v < -(1LL << 53) || v > (1LL << 53)) {
- return false;
- }
- int64_t as_int = static_cast<int64_t>(v);
- if (as_int != v) {
- return false;
- }
- *out = as_int;
- return true;
- }
- default:
- return false;
- }
- }
-
/// Returns the length of the string.
/// Only legal if get_type() is TYPE_STRING.
size_t get_string_length() const {
- assert_tag(tag::string);
+ assert_tag_3(tag::string, tag::integer, tag::double_);
return payload[1] - payload[0];
}
@@ -659,7 +562,7 @@
/// embedded NULs.
/// Only legal if get_type() is TYPE_STRING.
const char* as_cstring() const {
- assert_tag(tag::string);
+ assert_tag_3(tag::string, tag::integer, tag::double_);
return text + payload[0];
}
@@ -667,7 +570,7 @@
/// Returns a string's value as a std::string.
/// Only legal if get_type() is TYPE_STRING.
std::string as_string() const {
- assert_tag(tag::string);
+ assert_tag_3(tag::string, tag::integer, tag::double_);
return std::string(text + payload[0], text + payload[1]);
}
#endif
@@ -690,6 +593,10 @@
assert(e1 == value_tag || e2 == value_tag);
}
+ void assert_tag_3(tag e1, tag e2, tag e3) const {
+ assert(e1 == value_tag || e2 == value_tag || e3 == value_tag);
+ }
+
void assert_in_bounds(size_t i) const { assert(i < get_length()); }
const tag value_tag;
@@ -2059,6 +1966,8 @@
std::pair<char*, internal::tag> parse_number(char* p) {
using internal::tag;
+ size_t start = p - input.get_data();
+
// Assume 32-bit, two's complement integers.
static constexpr unsigned RISKY = INT_MAX / 10u;
unsigned max_digit_after_risky = INT_MAX % 10u;
@@ -2235,23 +2144,18 @@
u = 0u - u;
}
}
+
+ bool success;
+ size_t* out = allocator.reserve(2, &success);
+ if (SAJSON_UNLIKELY(!success)) {
+ return std::make_pair(oom(p, "number"), tag::null);
+ }
+ out[0] = start;
+ out[1] = p - input.get_data();
+
if (try_double) {
- bool success;
- size_t* out
- = allocator.reserve(double_storage::word_length, &success);
- if (SAJSON_UNLIKELY(!success)) {
- return std::make_pair(oom(p, "double"), tag::null);
- }
- double_storage::store(out, d);
return std::make_pair(p, tag::double_);
} else {
- bool success;
- size_t* out
- = allocator.reserve(integer_storage::word_length, &success);
- if (SAJSON_UNLIKELY(!success)) {
- return std::make_pair(oom(p, "integer"), tag::null);
- }
- integer_storage::store(out, static_cast<int>(u));
return std::make_pair(p, tag::integer);
}
}