196 lines
6.5 KiB
Diff
196 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);
|
|
}
|
|
}
|