From 3654bc4ecab5c690a9648925a1d917f36d38e507 Mon Sep 17 00:00:00 2001 From: Karolis Vycius Date: Mon, 14 Aug 2017 16:11:43 +0300 Subject: [PATCH 1/2] Change all Android unsupported functions --- CMakeLists.txt | 2 ++ src/json.hpp | 83 ++++++++++++++++++++------------------------- src/std_support.cpp | 24 +++++++++++++ src/std_support.hpp | 22 ++++++++++++ 4 files changed, 85 insertions(+), 46 deletions(-) create mode 100644 src/std_support.cpp create mode 100644 src/std_support.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 30e39666a..4ae93ca16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,7 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") # create and configure the library target add_library(${JSON_TARGET_NAME} INTERFACE) + target_include_directories(${JSON_TARGET_NAME} INTERFACE $ $) @@ -48,6 +49,7 @@ export(PACKAGE ${JSON_PACKAGE_NAME}) install(TARGETS ${JSON_TARGET_NAME} EXPORT ${JSON_PACKAGE_NAME}) install(FILES "src/json.hpp" + "src/std_support.hpp" DESTINATION ${JSON_INCLUDE_DESTINATION}) install(EXPORT ${JSON_PACKAGE_NAME} FILE ${JSON_TARGETS_FILENAME} diff --git a/src/json.hpp b/src/json.hpp index 6dfc1831f..cde770d7b 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -25,7 +25,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - #ifndef NLOHMANN_JSON_HPP #define NLOHMANN_JSON_HPP @@ -37,7 +36,7 @@ SOFTWARE. #include // isfinite, labs, ldexp, signbit #include // nullptr_t, ptrdiff_t, size_t #include // int64_t, uint64_t -#include // abort, strtod, strtof, strtold, strtoul, strtoll, strtoull +#include // abort, strtod, strtod, strtold, strtoul, strtoll, strtoull #include // strlen #include // forward_list #include // function, hash, less @@ -56,6 +55,8 @@ SOFTWARE. #include // add_pointer, conditional, decay, enable_if, false_type, integral_constant, is_arithmetic, is_base_of, is_const, is_constructible, is_convertible, is_default_constructible, is_enum, is_floating_point, is_integral, is_nothrow_move_assignable, is_nothrow_move_constructible, is_pointer, is_reference, is_same, is_scalar, is_signed, remove_const, remove_cv, remove_pointer, remove_reference, true_type, underlying_type #include // declval, forward, make_pair, move, pair, swap #include // vector +#include +#include // exclude unsupported compilers #if defined(__clang__) @@ -1159,7 +1160,7 @@ class basic_json #elif defined(__ICC) || defined(__INTEL_COMPILER) result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; #elif defined(__GNUC__) || defined(__GNUG__) - result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}}; + result["compiler"] = {{"family", "gcc"}, {"version", std_support::to_string(__GNUC__) + "." + std_support::to_string(__GNUC_MINOR__) + "." + std_support::to_string(__GNUC_PATCHLEVEL__)}}; #elif defined(__HP_cc) || defined(__HP_aCC) result["compiler"] = "hp" #elif defined(__IBMCPP__) @@ -1175,7 +1176,7 @@ class basic_json #endif #ifdef __cplusplus - result["compiler"]["c++"] = std::to_string(__cplusplus); + result["compiler"]["c++"] = std_support::to_string(__cplusplus); #else result["compiler"]["c++"] = "unknown"; #endif @@ -3540,7 +3541,7 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(std::out_of_range("array index " + std_support::to_string(idx) + " is out of range")); } } else @@ -3583,7 +3584,7 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(std::out_of_range("array index " + std_support::to_string(idx) + " is out of range")); } } else @@ -4535,7 +4536,7 @@ class basic_json { if (idx >= size()) { - JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(std::out_of_range("array index " + std_support::to_string(idx) + " is out of range")); } m_value.array->erase(m_value.array->begin() + static_cast(idx)); @@ -6627,7 +6628,7 @@ class basic_json { if (current_index + sizeof(T) + 1 > vec.size()) { - JSON_THROW(std::out_of_range("cannot read " + std::to_string(sizeof(T)) + " bytes from vector")); + JSON_THROW(std::out_of_range("cannot read " + std_support::to_string(sizeof(T)) + " bytes from vector")); } T result; @@ -7425,7 +7426,7 @@ class basic_json default: { - JSON_THROW(std::invalid_argument("error parsing a msgpack @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast(v[current_idx])))); + JSON_THROW(std::invalid_argument("error parsing a msgpack @ " + std_support::to_string(current_idx) + ": " + std_support::to_string(static_cast(v[current_idx])))); } } } @@ -7910,7 +7911,7 @@ class basic_json default: // anything else (0xFF is handled inside the other types) { - JSON_THROW(std::invalid_argument("error parsing a CBOR @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast(v[current_idx])))); + JSON_THROW(std::invalid_argument("error parsing a CBOR @ " + std_support::to_string(current_idx) + ": " + std_support::to_string(static_cast(v[current_idx])))); } } } @@ -8331,14 +8332,9 @@ class basic_json // check if buffer was large enough assert(static_cast(written_bytes) < m_buf.size()); - // read information from locale - const auto loc = localeconv(); - assert(loc != nullptr); - const char thousands_sep = !loc->thousands_sep ? '\0' - : loc->thousands_sep[0]; - const char decimal_point = !loc->decimal_point ? '\0' - : loc->decimal_point[0]; + const char thousands_sep = '\0'; + const char decimal_point = '\0'; // erase thousands separator if (thousands_sep != '\0') @@ -8767,7 +8763,7 @@ class basic_json // use integer array index as key case value_t::array: { - return std::to_string(array_index); + return std_support::to_string(array_index); } // use key from the object @@ -11122,21 +11118,21 @@ basic_json_parser_74: // floating-point conversion - // overloaded wrappers for strtod/strtof/strtold + // overloaded wrappers for strtod/strtod/strtold // that will be called from parse static void strtof(float& f, const char* str, char** endptr) { - f = std::strtof(str, endptr); + f = std_support::strtod(str, endptr); } static void strtof(double& f, const char* str, char** endptr) { - f = std::strtod(str, endptr); + f = std_support::strtod(str, endptr); } static void strtof(long double& f, const char* str, char** endptr) { - f = std::strtold(str, endptr); + f = std_support::strtod(str, endptr); } template @@ -11152,12 +11148,7 @@ basic_json_parser_74: // lexer will reject empty numbers assert(len > 0); - // since dealing with strtod family of functions, we're - // getting the decimal point char from the C locale facilities - // instead of C++'s numpunct facet of the current std::locale - const auto loc = localeconv(); - assert(loc != nullptr); - const char decimal_point_char = (loc->decimal_point == nullptr) ? '.' : loc->decimal_point[0]; + const char decimal_point_char = '.'; const char* data = m_start; @@ -11191,7 +11182,7 @@ basic_json_parser_74: // this calls appropriate overload depending on T strtof(value, data, &endptr); - // parsing was successful iff strtof parsed exactly the number + // parsing was successful iff strtod parsed exactly the number // of characters determined by the lexer (len) const bool ok = (endptr == (data + len)); @@ -11208,12 +11199,12 @@ basic_json_parser_74: signed long long parse_integral(char** endptr, /*is_signed*/std::true_type) const { - return std::strtoll(m_start, endptr, 10); + return std_support::strtoll(m_start, endptr, 10); } unsigned long long parse_integral(char** endptr, /*is_signed*/std::false_type) const { - return std::strtoull(m_start, endptr, 10); + return std_support::strtoull(m_start, endptr, 10); } template @@ -11778,7 +11769,7 @@ basic_json_parser_74: case value_t::array: { // create an entry in the array - result = &result->operator[](static_cast(std::stoi(reference_token))); + result = &result->operator[](static_cast(std_support::stoi(reference_token))); break; } @@ -11870,7 +11861,7 @@ basic_json_parser_74: else { // convert array index to number; unchecked access - ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); + ptr = &ptr->operator[](static_cast(std_support::stoi(reference_token))); } break; } @@ -11904,7 +11895,7 @@ basic_json_parser_74: { // "-" always fails the range check JSON_THROW(std::out_of_range("array index '-' (" + - std::to_string(ptr->m_value.array->size()) + + std_support::to_string(ptr->m_value.array->size()) + ") is out of range")); } @@ -11915,7 +11906,7 @@ basic_json_parser_74: } // note: at performs range check - ptr = &ptr->at(static_cast(std::stoi(reference_token))); + ptr = &ptr->at(static_cast(std_support::stoi(reference_token))); break; } @@ -11956,7 +11947,7 @@ basic_json_parser_74: { // "-" cannot be used for const access JSON_THROW(std::out_of_range("array index '-' (" + - std::to_string(ptr->m_value.array->size()) + + std_support::to_string(ptr->m_value.array->size()) + ") is out of range")); } @@ -11967,7 +11958,7 @@ basic_json_parser_74: } // use unchecked array access - ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); + ptr = &ptr->operator[](static_cast(std_support::stoi(reference_token))); break; } @@ -12000,7 +11991,7 @@ basic_json_parser_74: { // "-" always fails the range check JSON_THROW(std::out_of_range("array index '-' (" + - std::to_string(ptr->m_value.array->size()) + + std_support::to_string(ptr->m_value.array->size()) + ") is out of range")); } @@ -12011,7 +12002,7 @@ basic_json_parser_74: } // note: at performs range check - ptr = &ptr->at(static_cast(std::stoi(reference_token))); + ptr = &ptr->at(static_cast(std_support::stoi(reference_token))); break; } @@ -12156,7 +12147,7 @@ basic_json_parser_74: // iterate array and use index as reference string for (size_t i = 0; i < value.m_value.array->size(); ++i) { - flatten(reference_string + "/" + std::to_string(i), + flatten(reference_string + "/" + std_support::to_string(i), value.m_value.array->operator[](i), result); } } @@ -12547,11 +12538,11 @@ basic_json_parser_74: } else { - const auto idx = std::stoi(last_path); + const auto idx = std_support::stoi(last_path); if (static_cast(idx) > parent.size()) { // avoid undefined behavior - JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(std::out_of_range("array index " + std_support::to_string(idx) + " is out of range")); } else { @@ -12595,7 +12586,7 @@ basic_json_parser_74: else if (parent.is_array()) { // note erase performs range check - parent.erase(static_cast(std::stoi(last_path))); + parent.erase(static_cast(std_support::stoi(last_path))); } }; @@ -12796,7 +12787,7 @@ basic_json_parser_74: while (i < source.size() and i < target.size()) { // recursive call to compare array values at index i - auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); + auto temp_diff = diff(source[i], target[i], path + "/" + std_support::to_string(i)); result.insert(result.end(), temp_diff.begin(), temp_diff.end()); ++i; } @@ -12813,7 +12804,7 @@ basic_json_parser_74: result.insert(result.begin() + end_index, object( { {"op", "remove"}, - {"path", path + "/" + std::to_string(i)} + {"path", path + "/" + std_support::to_string(i)} })); ++i; } @@ -12824,7 +12815,7 @@ basic_json_parser_74: result.push_back( { {"op", "add"}, - {"path", path + "/" + std::to_string(i)}, + {"path", path + "/" + std_support::to_string(i)}, {"value", target[i]} }); ++i; diff --git a/src/std_support.cpp b/src/std_support.cpp new file mode 100644 index 000000000..90452c961 --- /dev/null +++ b/src/std_support.cpp @@ -0,0 +1,24 @@ +#include "std_support.hpp" +#include + +unsigned long long std_support::strtoull(const std::string &str, char **endptr, int base) { + std::stringstream ss(str); + unsigned long long val; + ss >> val; + return val; +} + +long long std_support::strtoll(const std::string &str, char **endptr, int base) { + std::stringstream ss(str); + long long val; + ss >> val; + return val; +} + +int std_support::stoi(const std::string &str) { + return std::atoi(str.c_str()); +} + +double std_support::strtod(const char *str, char **endptr) { + return std::strtod(str, endptr); +} \ No newline at end of file diff --git a/src/std_support.hpp b/src/std_support.hpp new file mode 100644 index 000000000..6f7493a30 --- /dev/null +++ b/src/std_support.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +namespace std_support { + + template + std::string to_string(const T &n) { + std::stringstream ss; + ss << n; + return ss.str(); + } + + double strtod(const char *str, char **endptr); + + int stoi(const std::string &str); + + long long strtoll(const std::string &str, char **endptr = nullptr, int base = 10); + + unsigned long long strtoull(const std::string &str, char **endptr = nullptr, int base = 10); +}; \ No newline at end of file From 2876b7deb297ec84cb3ceee1513bf4befe14015b Mon Sep 17 00:00:00 2001 From: Karolis Vycius Date: Mon, 14 Aug 2017 16:48:11 +0300 Subject: [PATCH 2/2] Fix strtoull and strtoll errors --- src/std_support.cpp | 217 ++++++++++++++++++++++++++++++++++++++++++-- src/std_support.hpp | 4 +- 2 files changed, 209 insertions(+), 12 deletions(-) diff --git a/src/std_support.cpp b/src/std_support.cpp index 90452c961..526ad94e6 100644 --- a/src/std_support.cpp +++ b/src/std_support.cpp @@ -1,18 +1,215 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + #include "std_support.hpp" #include +#include -unsigned long long std_support::strtoull(const std::string &str, char **endptr, int base) { - std::stringstream ss(str); - unsigned long long val; - ss >> val; - return val; + +unsigned long long std_support::strtoull(const char *nptr, char **endptr, int base) { + const char *s; + unsigned long long acc; + char c; + unsigned long long cutoff; + int neg, any, cutlim; + + /* + * See strtoq for comments as to the logic used. + */ + s = nptr; + do { + c = *s++; + } while (isspace((unsigned char)c)); + if (c == '-') { + neg = 1; + c = *s++; + } else { + neg = 0; + if (c == '+') + c = *s++; + } + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + acc = any = 0; + if (base < 2 || base > 36) + goto noconv; + + cutoff = ULLONG_MAX / base; + cutlim = ULLONG_MAX % base; + for ( ; ; c = *s++) { + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'A' && c <= 'Z') + c -= 'A' - 10; + else if (c >= 'a' && c <= 'z') + c -= 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = ULLONG_MAX; + errno = ERANGE; + } else if (!any) { + noconv: + errno = EINVAL; + } else if (neg) + acc = -acc; + if (endptr != NULL) + *endptr = (char *)(any ? s - 1 : nptr); + return (acc); } -long long std_support::strtoll(const std::string &str, char **endptr, int base) { - std::stringstream ss(str); - long long val; - ss >> val; - return val; +// https://opensource.apple.com/source/lukemftp/lukemftp-15/tnftp/libnetbsd/strtoll.c +long long std_support::strtoll(const char *nptr, char **endptr, int base) { + const char *s; + /* LONGLONG */ + long long int acc, cutoff; + int c; + int neg, any, cutlim; + + /* endptr may be NULL */ + +#ifdef __GNUC__ + /* This outrageous construct just to shut up a GCC warning. */ + (void) &acc; (void) &cutoff; +#endif + + /* + * Skip white space and pick up leading +/- sign if any. + * If base is 0, allow 0x for hex and 0 for octal, else + * assume decimal; if base is already 16, allow 0x. + */ + s = nptr; + do { + c = (unsigned char) *s++; + } while (isspace(c)); + if (c == '-') { + neg = 1; + c = *s++; + } else { + neg = 0; + if (c == '+') + c = *s++; + } + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + + /* + * Compute the cutoff value between legal numbers and illegal + * numbers. That is the largest legal value, divided by the + * base. An input number that is greater than this value, if + * followed by a legal input character, is too big. One that + * is equal to this value may be valid or not; the limit + * between valid and invalid numbers is then based on the last + * digit. For instance, if the range for long longs is + * [-9223372036854775808..9223372036854775807] and the input base + * is 10, cutoff will be set to 922337203685477580 and cutlim to + * either 7 (neg==0) or 8 (neg==1), meaning that if we have + * accumulated a value > 922337203685477580, or equal but the + * next digit is > 7 (or 8), the number is too big, and we will + * return a range error. + * + * Set any if any `digits' consumed; make it negative to indicate + * overflow. + */ + cutoff = neg ? LLONG_MIN : LLONG_MAX; + cutlim = (int)(cutoff % base); + cutoff /= base; + if (neg) { + if (cutlim > 0) { + cutlim -= base; + cutoff += 1; + } + cutlim = -cutlim; + } + for (acc = 0, any = 0;; c = (unsigned char) *s++) { + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0) + continue; + if (neg) { + if (acc < cutoff || (acc == cutoff && c > cutlim)) { + any = -1; + acc = LLONG_MIN; + errno = ERANGE; + } else { + any = 1; + acc *= base; + acc -= c; + } + } else { + if (acc > cutoff || (acc == cutoff && c > cutlim)) { + any = -1; + acc = LLONG_MAX; + errno = ERANGE; + } else { + any = 1; + acc *= base; + acc += c; + } + } + } + if (endptr != 0) + /* LINTED interface specification */ + *endptr = (char *)(any ? s - 1 : nptr); + return (acc); } int std_support::stoi(const std::string &str) { diff --git a/src/std_support.hpp b/src/std_support.hpp index 6f7493a30..dde97c086 100644 --- a/src/std_support.hpp +++ b/src/std_support.hpp @@ -16,7 +16,7 @@ namespace std_support { int stoi(const std::string &str); - long long strtoll(const std::string &str, char **endptr = nullptr, int base = 10); + long long strtoll(const char *str, char **endptr = nullptr, int base = 10); - unsigned long long strtoull(const std::string &str, char **endptr = nullptr, int base = 10); + unsigned long long strtoull(const char *str, char **endptr = nullptr, int base = 10); }; \ No newline at end of file