Add internal JSON backend option
diff --git a/tests/tester.cc b/tests/tester.cc index c3d1944..3eb0484 100644 --- a/tests/tester.cc +++ b/tests/tester.cc
@@ -4,7 +4,9 @@ #include "tiny_gltf.h" // Nlohmann json(include ../json.hpp) +#if !defined(TINYGLTF_USE_INTERNAL_JSON) && !defined(TINYGLTF_USE_RAPIDJSON) #include "json.hpp" +#endif #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file #include "catch.hpp"
diff --git a/tiny_gltf.h b/tiny_gltf.h index 4526548..c5f1cd6 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h
@@ -1670,6 +1670,15 @@ #include <fstream> #endif #include <sstream> +#ifdef TINYGLTF_USE_INTERNAL_JSON +#include <cmath> +#include <cctype> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <iomanip> +#include <type_traits> +#endif #ifdef __clang__ // Disable some warnings for external files. @@ -1732,10 +1741,12 @@ #pragma GCC diagnostic ignored "-Wtype-limits" #endif // __GNUC__ +#if defined(TINYGLTF_USE_RAPIDJSON) && defined(TINYGLTF_USE_INTERNAL_JSON) +#error "TINYGLTF_USE_RAPIDJSON and TINYGLTF_USE_INTERNAL_JSON cannot both be defined." +#endif + #ifndef TINYGLTF_NO_INCLUDE_JSON -#ifndef TINYGLTF_USE_RAPIDJSON -#include "json.hpp" -#else +#if defined(TINYGLTF_USE_RAPIDJSON) #ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON #include "document.h" #include "prettywriter.h" @@ -1743,6 +1754,8 @@ #include "stringbuffer.h" #include "writer.h" #endif +#elif !defined(TINYGLTF_USE_INTERNAL_JSON) +#include "json.hpp" #endif #endif @@ -1885,6 +1898,949 @@ #endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR +#elif defined(TINYGLTF_USE_INTERNAL_JSON) +// Minimal built-in JSON backend that avoids RTTI and exceptions. +class MiniJsonValue { + public: + enum class value_t { + null, + object, + array, + string, + boolean, + number_integer, + number_unsigned, + number_float, + binary, + discarded + }; + + class iterator; + class const_iterator; + class const_array_iterator; + + MiniJsonValue() + : type_(value_t::null), + bool_value_(false), + number_value_(0.0), + int_value_(0), + uint_value_(0), + parse_error_(false) {} + + MiniJsonValue(double v) + : type_(value_t::number_float), + bool_value_(false), + number_value_(v), + int_value_(static_cast<int64_t>(v)), + uint_value_(v < 0.0 ? 0 : static_cast<uint64_t>(v)), + parse_error_(false) {} + + MiniJsonValue(bool v) + : type_(value_t::boolean), + bool_value_(v), + number_value_(v ? 1.0 : 0.0), + int_value_(v ? 1 : 0), + uint_value_(v ? 1 : 0), + parse_error_(false) {} + + MiniJsonValue(const char *s) + : type_(value_t::string), + bool_value_(false), + number_value_(0.0), + int_value_(0), + uint_value_(0), + string_value_(s ? s : ""), + parse_error_(false) {} + + MiniJsonValue(const std::string &s) + : type_(value_t::string), + bool_value_(false), + number_value_(0.0), + int_value_(0), + uint_value_(0), + string_value_(s), + parse_error_(false) {} + + template <typename T, + typename std::enable_if<std::is_integral<T>::value && + !std::is_same<T, bool>::value, + int>::type = 0> + MiniJsonValue(T v) + : type_((std::is_signed<T>::value && (v < 0)) + ? value_t::number_integer + : value_t::number_unsigned), + bool_value_(false), + number_value_(static_cast<double>(v)), + int_value_(static_cast<int64_t>(v)), + uint_value_((std::is_signed<T>::value && (v < 0)) + ? 0 + : static_cast<uint64_t>(v)), + parse_error_(false) {} + + MiniJsonValue(const MiniJsonValue &) = default; + MiniJsonValue(MiniJsonValue &&) noexcept = default; + MiniJsonValue &operator=(const MiniJsonValue &) = default; + MiniJsonValue &operator=(MiniJsonValue &&) noexcept = default; + + value_t type() const { return type_; } + + bool is_null() const { return type_ == value_t::null; } + bool is_object() const { return type_ == value_t::object; } + bool is_array() const { return type_ == value_t::array; } + bool is_boolean() const { return type_ == value_t::boolean; } + bool is_string() const { return type_ == value_t::string; } + bool is_number() const { + return (type_ == value_t::number_integer) || + (type_ == value_t::number_unsigned) || + (type_ == value_t::number_float); + } + bool is_number_integer() const { return type_ == value_t::number_integer; } + bool is_number_unsigned() const { return type_ == value_t::number_unsigned; } + + size_t size() const { + if (is_array()) { + return array_value_.size(); + } + if (is_object()) { + return object_value_.size(); + } + return 0; + } + + bool empty() const { return size() == 0; } + + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + const_iterator cbegin() const; + const_iterator cend() const; + + MiniJsonValue &operator[](const std::string &key) { + EnsureObject(); + for (size_t i = 0; i < object_value_.size(); ++i) { + if (object_value_[i].first == key) { + return object_value_[i].second; + } + } + object_value_.push_back(std::make_pair(key, MiniJsonValue())); + return object_value_.back().second; + } + + MiniJsonValue &operator[](const char *key) { + return (*this)[std::string(key ? key : "")]; + } + + iterator find(const std::string &key) { + if (!is_object()) { + return end(); + } + for (size_t i = 0; i < object_value_.size(); ++i) { + if (object_value_[i].first == key) { + return iterator::Object(this, i); + } + } + return end(); + } + + const_iterator find(const std::string &key) const { + if (!is_object()) { + return end(); + } + for (size_t i = 0; i < object_value_.size(); ++i) { + if (object_value_[i].first == key) { + return const_iterator::Object(this, i); + } + } + return end(); + } + + void erase(iterator it) { + if (!is_object()) { + return; + } + if (it.owner_ != this || !it.is_object_) { + return; + } + if (it.index_ < object_value_.size()) { + object_value_.erase(object_value_.begin() + static_cast<long>(it.index_)); + } + } + + void push_back(const MiniJsonValue &v) { + EnsureArray(); + array_value_.push_back(v); + } + + void push_back(MiniJsonValue &&v) { + EnsureArray(); + array_value_.push_back(std::move(v)); + } + + void SetObject() { + type_ = value_t::object; + array_value_.clear(); + } + + void SetArray() { + type_ = value_t::array; + object_value_.clear(); + } + + void ReserveArray(size_t s) { + EnsureArray(); + array_value_.reserve(s); + } + + template <typename T> + T get() const; + + bool HasParseError() const { return parse_error_; } + void ClearParseError() { + parse_error_ = false; + parse_error_message_.clear(); + } + void SetParseError(const std::string &msg) { + parse_error_ = true; + parse_error_message_ = msg; + } + const std::string &ParseErrorMessage() const { return parse_error_message_; } + + std::string dump(int spacing = -1, int indent = 0) const { + return DumpImpl(spacing, indent); + } + + class iterator { + public: + iterator() : owner_(nullptr), index_(0), is_object_(false) {} + + static iterator Object(MiniJsonValue *owner, size_t index) { + iterator it; + it.owner_ = owner; + it.index_ = index; + it.is_object_ = true; + return it; + } + + static iterator Array(MiniJsonValue *owner, size_t index) { + iterator it; + it.owner_ = owner; + it.index_ = index; + it.is_object_ = false; + return it; + } + + static iterator Invalid(MiniJsonValue *owner) { + iterator it; + it.owner_ = owner; + return it; + } + + MiniJsonValue &value() const { + return is_object_ ? owner_->object_value_[index_].second + : owner_->array_value_[index_]; + } + + const std::string &key() const { + return owner_->object_value_[index_].first; + } + + MiniJsonValue &operator*() const { return value(); } + + iterator &operator++() { + ++index_; + return *this; + } + + bool operator!=(const iterator &rhs) const { + return (owner_ != rhs.owner_) || (index_ != rhs.index_) || + (is_object_ != rhs.is_object_); + } + + private: + MiniJsonValue *owner_; + size_t index_; + bool is_object_; + friend class MiniJsonValue; + }; + + class const_iterator { + public: + const_iterator() : owner_(nullptr), index_(0), is_object_(false) {} + + static const_iterator Object(const MiniJsonValue *owner, size_t index) { + const_iterator it; + it.owner_ = owner; + it.index_ = index; + it.is_object_ = true; + return it; + } + + static const_iterator Array(const MiniJsonValue *owner, size_t index) { + const_iterator it; + it.owner_ = owner; + it.index_ = index; + it.is_object_ = false; + return it; + } + + static const_iterator Invalid(const MiniJsonValue *owner) { + const_iterator it; + it.owner_ = owner; + return it; + } + + const MiniJsonValue &value() const { + return is_object_ ? owner_->object_value_[index_].second + : owner_->array_value_[index_]; + } + + const std::string &key() const { + return owner_->object_value_[index_].first; + } + + const MiniJsonValue &operator*() const { return value(); } + + const_iterator &operator++() { + ++index_; + return *this; + } + + bool operator!=(const const_iterator &rhs) const { + return (owner_ != rhs.owner_) || (index_ != rhs.index_) || + (is_object_ != rhs.is_object_); + } + + private: + const MiniJsonValue *owner_; + size_t index_; + bool is_object_; + friend class MiniJsonValue; + }; + + using const_array_iterator = const_iterator; + + private: + friend class MiniJsonParser; + void EnsureObject() { + if (!is_object()) { + type_ = value_t::object; + object_value_.clear(); + array_value_.clear(); + } + } + + void EnsureArray() { + if (!is_array()) { + type_ = value_t::array; + array_value_.clear(); + object_value_.clear(); + } + } + + static void DumpString(const std::string &s, std::string &out) { + out.push_back('"'); + for (size_t i = 0; i < s.size(); ++i) { + const unsigned char c = static_cast<unsigned char>(s[i]); + switch (c) { + case '\\': + out += "\\\\"; + break; + case '"': + out += "\\\""; + break; + case '\b': + out += "\\b"; + break; + case '\f': + out += "\\f"; + break; + case '\n': + out += "\\n"; + break; + case '\r': + out += "\\r"; + break; + case '\t': + out += "\\t"; + break; + default: { + if (c < 0x20) { + char buf[7]; + std::snprintf(buf, sizeof(buf), "\\u%04x", + static_cast<unsigned int>(c)); + out += buf; + } else { + out.push_back(static_cast<char>(c)); + } + } break; + } + } + out.push_back('"'); + } + + std::string DumpImpl(int spacing, int indent) const { + (void)indent; + if (type_ == value_t::null) { + return "null"; + } + if (type_ == value_t::boolean) { + return bool_value_ ? "true" : "false"; + } + if (type_ == value_t::number_integer) { + return std::to_string(int_value_); + } + if (type_ == value_t::number_unsigned) { + return std::to_string(uint_value_); + } + if (type_ == value_t::number_float) { + std::ostringstream ss; + ss.setf(std::ios::fmtflags(0), std::ios::floatfield); + ss << std::setprecision(17) << number_value_; + return ss.str(); + } + if (type_ == value_t::string) { + std::string out; + DumpString(string_value_, out); + return out; + } + const bool pretty = spacing >= 0; + const int next_indent = pretty ? indent + spacing : 0; + std::string out; + if (type_ == value_t::array) { + out.push_back('['); + if (pretty && !array_value_.empty()) { + out.push_back('\n'); + } + for (size_t i = 0; i < array_value_.size(); ++i) { + if (pretty) out.append(static_cast<size_t>(next_indent), ' '); + out += array_value_[i].DumpImpl(spacing, next_indent); + if (i + 1 < array_value_.size()) { + out.push_back(','); + } + if (pretty) out.push_back('\n'); + } + if (pretty && !array_value_.empty()) { + out.append(static_cast<size_t>(indent), ' '); + } + out.push_back(']'); + return out; + } + if (type_ == value_t::object) { + out.push_back('{'); + if (pretty && !object_value_.empty()) { + out.push_back('\n'); + } + for (size_t i = 0; i < object_value_.size(); ++i) { + if (pretty) out.append(static_cast<size_t>(next_indent), ' '); + DumpString(object_value_[i].first, out); + out += pretty ? ": " : ":"; + out += object_value_[i].second.DumpImpl(spacing, next_indent); + if (i + 1 < object_value_.size()) { + out.push_back(','); + } + if (pretty) out.push_back('\n'); + } + if (pretty && !object_value_.empty()) { + out.append(static_cast<size_t>(indent), ' '); + } + out.push_back('}'); + return out; + } + return "null"; + } + + value_t type_; + bool bool_value_; + double number_value_; + int64_t int_value_; + uint64_t uint_value_; + std::string string_value_; + std::vector<MiniJsonValue> array_value_; + std::vector<std::pair<std::string, MiniJsonValue> > object_value_; + bool parse_error_; + std::string parse_error_message_; +}; + +inline MiniJsonValue::iterator MiniJsonValue::begin() { + if (is_object()) { + return iterator::Object(this, 0); + } + if (is_array()) { + return iterator::Array(this, 0); + } + return iterator::Invalid(this); +} + +inline MiniJsonValue::iterator MiniJsonValue::end() { + if (is_object()) { + return iterator::Object(this, object_value_.size()); + } + if (is_array()) { + return iterator::Array(this, array_value_.size()); + } + return iterator::Invalid(this); +} + +inline MiniJsonValue::const_iterator MiniJsonValue::begin() const { + if (is_object()) { + return const_iterator::Object(this, 0); + } + if (is_array()) { + return const_iterator::Array(this, 0); + } + return const_iterator::Invalid(this); +} + +inline MiniJsonValue::const_iterator MiniJsonValue::end() const { + if (is_object()) { + return const_iterator::Object(this, object_value_.size()); + } + if (is_array()) { + return const_iterator::Array(this, array_value_.size()); + } + return const_iterator::Invalid(this); +} + +inline MiniJsonValue::const_iterator MiniJsonValue::cbegin() const { + return begin(); +} + +inline MiniJsonValue::const_iterator MiniJsonValue::cend() const { + return end(); +} + +template <> +inline int64_t MiniJsonValue::get<int64_t>() const { + if (type_ == value_t::number_integer) { + return int_value_; + } + if (type_ == value_t::number_unsigned) { + return static_cast<int64_t>(uint_value_); + } + if (type_ == value_t::number_float) { + return static_cast<int64_t>(number_value_); + } + return 0; +} + +template <> +inline size_t MiniJsonValue::get<size_t>() const { + if (type_ == value_t::number_unsigned) { + return static_cast<size_t>(uint_value_); + } + if (type_ == value_t::number_integer && int_value_ >= 0) { + return static_cast<size_t>(int_value_); + } + if (type_ == value_t::number_float && number_value_ >= 0.0) { + return static_cast<size_t>(number_value_); + } + return 0; +} + +template <> +inline double MiniJsonValue::get<double>() const { + if (type_ == value_t::number_float) { + return number_value_; + } + if (type_ == value_t::number_integer) { + return static_cast<double>(int_value_); + } + if (type_ == value_t::number_unsigned) { + return static_cast<double>(uint_value_); + } + return 0.0; +} + +template <> +inline std::string MiniJsonValue::get<std::string>() const { + return string_value_; +} + +template <> +inline bool MiniJsonValue::get<bool>() const { + if (type_ == value_t::boolean) { + return bool_value_; + } + return false; +} + +class MiniJsonParser { + public: + MiniJsonParser(const char *str, size_t length) + : cur_(str), end_(str + length), depth_(0) {} + + bool Parse(MiniJsonValue &out) { + out = MiniJsonValue(); + out.ClearParseError(); + depth_ = 0; + SkipWhitespace(); + if (!ParseValue(out)) { + out.SetParseError(err_); + return false; + } + SkipWhitespace(); + if (cur_ != end_) { + SetError("Trailing characters after JSON document"); + out.SetParseError(err_); + return false; + } + return true; + } + + const std::string &GetError() const { return err_; } + + private: + static bool IsHex(char c) { + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F'); + } + + bool ParseValue(MiniJsonValue &out) { + if (depth_ > 512) { + return SetError("Exceeded maximum JSON nesting depth"); + } + if (cur_ == end_) { + return SetError("Unexpected end of JSON input"); + } + + ++depth_; + const char c = *cur_; + bool result = false; + if (c == '{') { + result = ParseObject(out); + } else if (c == '[') { + result = ParseArray(out); + } else if (c == '"') { + std::string s; + result = ParseString(s); + if (result) { + out = MiniJsonValue(s); + } + } else if ((c == '-') || (c >= '0' && c <= '9')) { + result = ParseNumber(out); + } else if (MatchLiteral("true", 4)) { + out = MiniJsonValue(true); + result = true; + } else if (MatchLiteral("false", 5)) { + out = MiniJsonValue(false); + result = true; + } else if (MatchLiteral("null", 4)) { + out = MiniJsonValue(); + result = true; + } else { + result = SetError("Invalid JSON value"); + } + --depth_; + return result; + } + + bool ParseObject(MiniJsonValue &out) { + if (!Consume('{')) { + return SetError("Expected '{' to start object"); + } + out.SetObject(); + SkipWhitespace(); + if (Consume('}')) { + return true; + } + while (cur_ < end_) { + SkipWhitespace(); + std::string key; + if (!ParseString(key)) { + return SetError("Expected string for object key"); + } + SkipWhitespace(); + if (!Consume(':')) { + return SetError("Expected ':' after object key"); + } + SkipWhitespace(); + MiniJsonValue value; + if (!ParseValue(value)) { + return false; + } + out.object_value_.push_back( + std::make_pair(std::move(key), std::move(value))); + SkipWhitespace(); + if (Consume('}')) { + return true; + } + if (!Consume(',')) { + return SetError("Expected ',' between object entries"); + } + SkipWhitespace(); + } + return SetError("Unterminated object"); + } + + bool ParseArray(MiniJsonValue &out) { + if (!Consume('[')) { + return SetError("Expected '[' to start array"); + } + out.SetArray(); + SkipWhitespace(); + if (Consume(']')) { + return true; + } + while (cur_ < end_) { + MiniJsonValue value; + if (!ParseValue(value)) { + return false; + } + out.push_back(std::move(value)); + SkipWhitespace(); + if (Consume(']')) { + return true; + } + if (!Consume(',')) { + return SetError("Expected ',' between array entries"); + } + SkipWhitespace(); + } + return SetError("Unterminated array"); + } + + bool ParseString(std::string &out) { + if (!Consume('"')) { + return SetError("Expected '\"' to start string"); + } + while (cur_ < end_) { + const unsigned char c = static_cast<unsigned char>(*cur_++); + if (c == '"') { + return true; + } + if (c == '\\') { + if (cur_ >= end_) { + return SetError("Bad escape sequence"); + } + const char esc = *cur_++; + switch (esc) { + case '"': + out.push_back('"'); + break; + case '\\': + out.push_back('\\'); + break; + case '/': + out.push_back('/'); + break; + case 'b': + out.push_back('\b'); + break; + case 'f': + out.push_back('\f'); + break; + case 'n': + out.push_back('\n'); + break; + case 'r': + out.push_back('\r'); + break; + case 't': + out.push_back('\t'); + break; + case 'u': { + uint32_t codepoint = 0; + if (!ParseUnicodeEscape(codepoint)) { + return false; + } + AppendCodepoint(codepoint, out); + break; + } + default: + return SetError("Unknown escape sequence"); + } + } else { + out.push_back(static_cast<char>(c)); + } + } + return SetError("Unterminated string"); + } + + bool ParseNumber(MiniJsonValue &out) { + const char *start = cur_; + bool negative = false; + if (*cur_ == '-') { + negative = true; + ++cur_; + if (cur_ == end_) { + return SetError("Invalid number"); + } + } + + if (*cur_ == '0') { + ++cur_; + } else { + if (!std::isdigit(static_cast<unsigned char>(*cur_))) { + return SetError("Invalid number"); + } + while (cur_ < end_ && + std::isdigit(static_cast<unsigned char>(*cur_))) { + ++cur_; + } + } + + bool is_float = false; + if (cur_ < end_ && *cur_ == '.') { + is_float = true; + ++cur_; + if (cur_ == end_ || + !std::isdigit(static_cast<unsigned char>(*cur_))) { + return SetError("Invalid fractional number"); + } + while (cur_ < end_ && + std::isdigit(static_cast<unsigned char>(*cur_))) { + ++cur_; + } + } + + if (cur_ < end_ && (*cur_ == 'e' || *cur_ == 'E')) { + is_float = true; + ++cur_; + if (cur_ < end_ && (*cur_ == '+' || *cur_ == '-')) { + ++cur_; + } + if (cur_ == end_ || + !std::isdigit(static_cast<unsigned char>(*cur_))) { + return SetError("Invalid exponent"); + } + while (cur_ < end_ && + std::isdigit(static_cast<unsigned char>(*cur_))) { + ++cur_; + } + } + + const std::string number_str(start, static_cast<size_t>(cur_ - start)); + if (is_float) { + char *end_ptr = nullptr; + double v = std::strtod(number_str.c_str(), &end_ptr); + if (end_ptr != (number_str.c_str() + number_str.size()) || + !std::isfinite(v)) { + return SetError("Invalid floating point number"); + } + out = MiniJsonValue(v); + return true; + } + + if (negative) { + char *end_ptr = nullptr; + const long long v = std::strtoll(number_str.c_str(), &end_ptr, 10); + if (end_ptr != (number_str.c_str() + number_str.size())) { + return SetError("Invalid signed integer"); + } + out = MiniJsonValue(static_cast<int64_t>(v)); + return true; + } + + char *end_ptr = nullptr; + const unsigned long long v = + std::strtoull(number_str.c_str(), &end_ptr, 10); + if (end_ptr != (number_str.c_str() + number_str.size())) { + return SetError("Invalid unsigned integer"); + } + out = MiniJsonValue(static_cast<uint64_t>(v)); + return true; + } + + bool ParseUnicodeEscape(uint32_t &codepoint) { + if ((end_ - cur_) < 4) { + return SetError("Truncated unicode escape"); + } + uint32_t value = 0; + for (int i = 0; i < 4; ++i) { + const char c = cur_[i]; + if (!IsHex(c)) { + return SetError("Invalid unicode escape"); + } + value = (value << 4) | + static_cast<uint32_t>(std::isdigit(static_cast<unsigned char>(c)) + ? (c - '0') + : ((std::tolower(static_cast<unsigned char>(c)) - 'a') + + 10)); + } + cur_ += 4; + if (value >= 0xD800 && value <= 0xDBFF) { + // High surrogate, expect another \uXXXX sequence + if ((end_ - cur_) < 6 || cur_[0] != '\\' || cur_[1] != 'u') { + return SetError("Invalid unicode surrogate pair"); + } + cur_ += 2; // Skip "\u" + uint32_t low = 0; + if (!ParseUnicodeEscape(low)) { + return false; + } + if (low < 0xDC00 || low > 0xDFFF) { + return SetError("Invalid unicode surrogate pair"); + } + value = 0x10000 + (((value - 0xD800) << 10) | (low - 0xDC00)); + } + codepoint = value; + return true; + } + + static void AppendCodepoint(uint32_t cp, std::string &out) { + if (cp <= 0x7F) { + out.push_back(static_cast<char>(cp)); + } else if (cp <= 0x7FF) { + out.push_back(static_cast<char>(0xC0 | ((cp >> 6) & 0x1F))); + out.push_back(static_cast<char>(0x80 | (cp & 0x3F))); + } else if (cp <= 0xFFFF) { + out.push_back(static_cast<char>(0xE0 | ((cp >> 12) & 0x0F))); + out.push_back(static_cast<char>(0x80 | ((cp >> 6) & 0x3F))); + out.push_back(static_cast<char>(0x80 | (cp & 0x3F))); + } else { + out.push_back(static_cast<char>(0xF0 | ((cp >> 18) & 0x07))); + out.push_back(static_cast<char>(0x80 | ((cp >> 12) & 0x3F))); + out.push_back(static_cast<char>(0x80 | ((cp >> 6) & 0x3F))); + out.push_back(static_cast<char>(0x80 | (cp & 0x3F))); + } + } + + bool MatchLiteral(const char *literal, size_t len) { + if (static_cast<size_t>(end_ - cur_) < len) { + return false; + } + if (std::strncmp(cur_, literal, len) != 0) { + return false; + } + cur_ += len; + return true; + } + + bool Consume(char expected) { + if (cur_ >= end_ || *cur_ != expected) { + return false; + } + ++cur_; + return true; + } + + void SkipWhitespace() { + while (cur_ < end_ && + std::isspace(static_cast<unsigned char>(*cur_)) != 0) { + ++cur_; + } + } + + bool SetError(const std::string &msg) { + if (err_.empty()) { + err_ = msg; + } + return false; + } + + const char *cur_; + const char *end_; + std::string err_; + size_t depth_; +}; + +using json = MiniJsonValue; +using json_iterator = json::iterator; +using json_const_iterator = json::const_iterator; +using json_const_array_iterator = json::const_array_iterator; +using JsonDocument = json; + #else using nlohmann::json; using json_iterator = json::iterator; @@ -1898,6 +2854,13 @@ #ifdef TINYGLTF_USE_RAPIDJSON (void)throwExc; doc.Parse(str, length); +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + (void)throwExc; + MiniJsonParser parser(str, length); + doc.ClearParseError(); + if (!parser.Parse(doc)) { + doc.SetParseError(parser.GetError()); + } #else doc = detail::json::parse(str, str + length, nullptr, throwExc); #endif @@ -3533,6 +4496,16 @@ } return false; +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + auto type = o.type(); + + if ((type == detail::json::value_t::number_integer) || + (type == detail::json::value_t::number_unsigned)) { + val = static_cast<int>(o.get<int64_t>()); + return true; + } + + return false; #else auto type = o.type(); @@ -3565,6 +4538,13 @@ } return false; +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + if (o.is_number()) { + val = o.get<double>(); + return true; + } + + return false; #else if (o.is_number()) { val = o.get<double>(); @@ -3583,6 +4563,13 @@ } return false; +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + if (o.type() == detail::json::value_t::string) { + val = o.get<std::string>(); + return true; + } + + return false; #else if (o.type() == detail::json::value_t::string) { val = o.get<std::string>(); @@ -3596,6 +4583,8 @@ bool IsArray(const detail::json &o) { #ifdef TINYGLTF_USE_RAPIDJSON return o.IsArray(); +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + return o.is_array(); #else return o.is_array(); #endif @@ -3604,6 +4593,8 @@ detail::json_const_array_iterator ArrayBegin(const detail::json &o) { #ifdef TINYGLTF_USE_RAPIDJSON return o.Begin(); +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + return o.cbegin(); #else return o.begin(); #endif @@ -3612,6 +4603,8 @@ detail::json_const_array_iterator ArrayEnd(const detail::json &o) { #ifdef TINYGLTF_USE_RAPIDJSON return o.End(); +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + return o.cend(); #else return o.end(); #endif @@ -3620,6 +4613,8 @@ bool IsObject(const detail::json &o) { #ifdef TINYGLTF_USE_RAPIDJSON return o.IsObject(); +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + return o.is_object(); #else return o.is_object(); #endif @@ -3628,6 +4623,8 @@ detail::json_const_iterator ObjectBegin(const detail::json &o) { #ifdef TINYGLTF_USE_RAPIDJSON return o.MemberBegin(); +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + return o.cbegin(); #else return o.begin(); #endif @@ -3636,6 +4633,8 @@ detail::json_const_iterator ObjectEnd(const detail::json &o) { #ifdef TINYGLTF_USE_RAPIDJSON return o.MemberEnd(); +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + return o.cend(); #else return o.end(); #endif @@ -3646,6 +4645,8 @@ std::string GetKey(detail::json_const_iterator &it) { #ifdef TINYGLTF_USE_RAPIDJSON return it->name.GetString(); +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + return it.key(); #else return it.key().c_str(); #endif @@ -3659,6 +4660,9 @@ } it = o.FindMember(member); return it != o.MemberEnd(); +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + it = o.find(member); + return it != o.end(); #else it = o.find(member); return it != o.end(); @@ -3673,6 +4677,9 @@ } it = o.FindMember(member); return it != o.MemberEnd(); +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + it = o.find(member); + return it != o.end(); #else it = o.find(member); return it != o.end(); @@ -3682,6 +4689,8 @@ void Erase(detail::json &o, detail::json_iterator &it) { #ifdef TINYGLTF_USE_RAPIDJSON o.EraseMember(it); +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + o.erase(it); #else o.erase(it); #endif @@ -3690,6 +4699,8 @@ bool IsEmpty(const detail::json &o) { #ifdef TINYGLTF_USE_RAPIDJSON return o.ObjectEmpty(); +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + return o.empty(); #else return o.empty(); #endif @@ -3698,6 +4709,8 @@ const detail::json &GetValue(detail::json_const_iterator &it) { #ifdef TINYGLTF_USE_RAPIDJSON return it->value; +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + return it.value(); #else return it.value(); #endif @@ -3706,6 +4719,8 @@ detail::json &GetValue(detail::json_iterator &it) { #ifdef TINYGLTF_USE_RAPIDJSON return it->value; +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + return it.value(); #else return it.value(); #endif @@ -3730,6 +4745,8 @@ } } return buffer.GetString(); +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + return o.dump(spacing); #else return o.dump(spacing); #endif @@ -6109,6 +7126,15 @@ } #endif +#ifdef TINYGLTF_USE_INTERNAL_JSON + if (v.HasParseError()) { + if (err) { + (*err) = v.ParseErrorMessage(); + } + return false; + } +#endif + if (!detail::IsObject(v)) { // root is not an object. if (err) { @@ -7171,6 +8197,8 @@ detail::json JsonFromString(const char *s) { #ifdef TINYGLTF_USE_RAPIDJSON return detail::json(s, detail::GetAllocator()); +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + return detail::json(s ? s : ""); #else return detail::json(s); #endif @@ -7179,6 +8207,9 @@ void JsonAssign(detail::json &dest, const detail::json &src) { #ifdef TINYGLTF_USE_RAPIDJSON dest.CopyFrom(src, detail::GetAllocator()); +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + dest = src; + dest.ClearParseError(); #else dest = src; #endif @@ -7209,6 +8240,8 @@ void JsonPushBack(detail::json &o, detail::json &&value) { #ifdef TINYGLTF_USE_RAPIDJSON o.PushBack(std::move(value), detail::GetAllocator()); +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + o.push_back(std::move(value)); #else o.push_back(std::move(value)); #endif @@ -7217,6 +8250,8 @@ bool JsonIsNull(const detail::json &o) { #ifdef TINYGLTF_USE_RAPIDJSON return o.IsNull(); +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + return o.is_null(); #else return o.is_null(); #endif @@ -7225,8 +8260,10 @@ void JsonSetObject(detail::json &o) { #ifdef TINYGLTF_USE_RAPIDJSON o.SetObject(); +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + o.SetObject(); #else - o = o.object({}); + o = detail::json::object(); #endif } @@ -7234,6 +8271,9 @@ #ifdef TINYGLTF_USE_RAPIDJSON o.SetArray(); o.Reserve(static_cast<rapidjson::SizeType>(s), detail::GetAllocator()); +#elif defined(TINYGLTF_USE_INTERNAL_JSON) + o.SetArray(); + o.ReserveArray(s); #endif (void)(o); (void)(s);