1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-06-27 16:36:03 +00:00

Update jsoncpp copy to 1.9.6

note: the version number is different due to https://github.com/open-source-parsers/jsoncpp/issues/1571
This commit is contained in:
sfan5 2024-10-12 22:28:56 +02:00
parent cbc741f464
commit dbbe0ca065
4 changed files with 278 additions and 146 deletions

View file

@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
cd .. cd ..
git clone https://github.com/open-source-parsers/jsoncpp -b 1.9.5 --depth 1 git clone https://github.com/open-source-parsers/jsoncpp -b 1.9.6 --depth 1
cd jsoncpp cd jsoncpp
./amalgamate.py ./amalgamate.py
cp -R dist/json ../json cp -R dist/json ../json

View file

@ -1,4 +1,4 @@
/// Json-cpp amalgamated forward header (http://jsoncpp.sourceforge.net/). /// Json-cpp amalgamated forward header (https://github.com/open-source-parsers/jsoncpp/).
/// It is intended to be used with #include "json/json-forwards.h" /// It is intended to be used with #include "json/json-forwards.h"
/// This header provides forward declaration for all JsonCpp types. /// This header provides forward declaration for all JsonCpp types.
@ -94,19 +94,18 @@ license you like.
// 3. /CMakeLists.txt // 3. /CMakeLists.txt
// IMPORTANT: also update the SOVERSION!! // IMPORTANT: also update the SOVERSION!!
#define JSONCPP_VERSION_STRING "1.9.5" #define JSONCPP_VERSION_STRING "1.9.7"
#define JSONCPP_VERSION_MAJOR 1 #define JSONCPP_VERSION_MAJOR 1
#define JSONCPP_VERSION_MINOR 9 #define JSONCPP_VERSION_MINOR 9
#define JSONCPP_VERSION_PATCH 5 #define JSONCPP_VERSION_PATCH 7
#define JSONCPP_VERSION_QUALIFIER #define JSONCPP_VERSION_QUALIFIER
#define JSONCPP_VERSION_HEXA \ #define JSONCPP_VERSION_HEXA \
((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \ ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \
(JSONCPP_VERSION_PATCH << 8)) (JSONCPP_VERSION_PATCH << 8))
#ifdef JSONCPP_USING_SECURE_MEMORY #if !defined(JSONCPP_USE_SECURE_MEMORY)
#undef JSONCPP_USING_SECURE_MEMORY #define JSONCPP_USE_SECURE_MEMORY 0
#endif #endif
#define JSONCPP_USING_SECURE_MEMORY 0
// If non-zero, the library zeroes any memory that it has allocated before // If non-zero, the library zeroes any memory that it has allocated before
// it frees its memory. // it frees its memory.
@ -133,10 +132,12 @@ license you like.
#ifndef JSON_ALLOCATOR_H_INCLUDED #ifndef JSON_ALLOCATOR_H_INCLUDED
#define JSON_ALLOCATOR_H_INCLUDED #define JSON_ALLOCATOR_H_INCLUDED
#include <algorithm>
#include <cstring> #include <cstring>
#include <memory> #include <memory>
#pragma pack(push, 8) #pragma pack(push)
#pragma pack()
namespace Json { namespace Json {
template <typename T> class SecureAllocator { template <typename T> class SecureAllocator {
@ -164,8 +165,16 @@ public:
* The memory block is filled with zeroes before being released. * The memory block is filled with zeroes before being released.
*/ */
void deallocate(pointer p, size_type n) { void deallocate(pointer p, size_type n) {
// memset_s is used because memset may be optimized away by the compiler // These constructs will not be removed by the compiler during optimization,
// unlike memset.
#if defined(HAVE_MEMSET_S)
memset_s(p, n * sizeof(T), 0, n * sizeof(T)); memset_s(p, n * sizeof(T), 0, n * sizeof(T));
#elif defined(_WIN32)
RtlSecureZeroMemory(p, n * sizeof(T));
#else
std::fill_n(reinterpret_cast<volatile unsigned char*>(p), n, 0);
#endif
// free using "global operator delete" // free using "global operator delete"
::operator delete(p); ::operator delete(p);
} }
@ -195,7 +204,9 @@ public:
// Boilerplate // Boilerplate
SecureAllocator() {} SecureAllocator() {}
template <typename U> SecureAllocator(const SecureAllocator<U>&) {} template <typename U> SecureAllocator(const SecureAllocator<U>&) {}
template <typename U> struct rebind { using other = SecureAllocator<U>; }; template <typename U> struct rebind {
using other = SecureAllocator<U>;
};
}; };
template <typename T, typename U> template <typename T, typename U>
@ -356,7 +367,7 @@ using LargestUInt = UInt64;
template <typename T> template <typename T>
using Allocator = using Allocator =
typename std::conditional<JSONCPP_USING_SECURE_MEMORY, SecureAllocator<T>, typename std::conditional<JSONCPP_USE_SECURE_MEMORY, SecureAllocator<T>,
std::allocator<T>>::type; std::allocator<T>>::type;
using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>; using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
using IStringStream = using IStringStream =

View file

@ -1,4 +1,4 @@
/// Json-cpp amalgamated header (http://jsoncpp.sourceforge.net/). /// Json-cpp amalgamated header (https://github.com/open-source-parsers/jsoncpp/).
/// It is intended to be used with #include "json/json.h" /// It is intended to be used with #include "json/json.h"
// ////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////
@ -93,19 +93,18 @@ license you like.
// 3. /CMakeLists.txt // 3. /CMakeLists.txt
// IMPORTANT: also update the SOVERSION!! // IMPORTANT: also update the SOVERSION!!
#define JSONCPP_VERSION_STRING "1.9.5" #define JSONCPP_VERSION_STRING "1.9.7"
#define JSONCPP_VERSION_MAJOR 1 #define JSONCPP_VERSION_MAJOR 1
#define JSONCPP_VERSION_MINOR 9 #define JSONCPP_VERSION_MINOR 9
#define JSONCPP_VERSION_PATCH 5 #define JSONCPP_VERSION_PATCH 7
#define JSONCPP_VERSION_QUALIFIER #define JSONCPP_VERSION_QUALIFIER
#define JSONCPP_VERSION_HEXA \ #define JSONCPP_VERSION_HEXA \
((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \ ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \
(JSONCPP_VERSION_PATCH << 8)) (JSONCPP_VERSION_PATCH << 8))
#ifdef JSONCPP_USING_SECURE_MEMORY #if !defined(JSONCPP_USE_SECURE_MEMORY)
#undef JSONCPP_USING_SECURE_MEMORY #define JSONCPP_USE_SECURE_MEMORY 0
#endif #endif
#define JSONCPP_USING_SECURE_MEMORY 0
// If non-zero, the library zeroes any memory that it has allocated before // If non-zero, the library zeroes any memory that it has allocated before
// it frees its memory. // it frees its memory.
@ -132,10 +131,12 @@ license you like.
#ifndef JSON_ALLOCATOR_H_INCLUDED #ifndef JSON_ALLOCATOR_H_INCLUDED
#define JSON_ALLOCATOR_H_INCLUDED #define JSON_ALLOCATOR_H_INCLUDED
#include <algorithm>
#include <cstring> #include <cstring>
#include <memory> #include <memory>
#pragma pack(push, 8) #pragma pack(push)
#pragma pack()
namespace Json { namespace Json {
template <typename T> class SecureAllocator { template <typename T> class SecureAllocator {
@ -163,8 +164,16 @@ public:
* The memory block is filled with zeroes before being released. * The memory block is filled with zeroes before being released.
*/ */
void deallocate(pointer p, size_type n) { void deallocate(pointer p, size_type n) {
// memset_s is used because memset may be optimized away by the compiler // These constructs will not be removed by the compiler during optimization,
// unlike memset.
#if defined(HAVE_MEMSET_S)
memset_s(p, n * sizeof(T), 0, n * sizeof(T)); memset_s(p, n * sizeof(T), 0, n * sizeof(T));
#elif defined(_WIN32)
RtlSecureZeroMemory(p, n * sizeof(T));
#else
std::fill_n(reinterpret_cast<volatile unsigned char*>(p), n, 0);
#endif
// free using "global operator delete" // free using "global operator delete"
::operator delete(p); ::operator delete(p);
} }
@ -194,7 +203,9 @@ public:
// Boilerplate // Boilerplate
SecureAllocator() {} SecureAllocator() {}
template <typename U> SecureAllocator(const SecureAllocator<U>&) {} template <typename U> SecureAllocator(const SecureAllocator<U>&) {}
template <typename U> struct rebind { using other = SecureAllocator<U>; }; template <typename U> struct rebind {
using other = SecureAllocator<U>;
};
}; };
template <typename T, typename U> template <typename T, typename U>
@ -355,7 +366,7 @@ using LargestUInt = UInt64;
template <typename T> template <typename T>
using Allocator = using Allocator =
typename std::conditional<JSONCPP_USING_SECURE_MEMORY, SecureAllocator<T>, typename std::conditional<JSONCPP_USE_SECURE_MEMORY, SecureAllocator<T>,
std::allocator<T>>::type; std::allocator<T>>::type;
using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>; using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
using IStringStream = using IStringStream =
@ -459,7 +470,8 @@ class ValueConstIterator;
#include "forwards.h" #include "forwards.h"
#endif // if !defined(JSON_IS_AMALGAMATION) #endif // if !defined(JSON_IS_AMALGAMATION)
#pragma pack(push, 8) #pragma pack(push)
#pragma pack()
namespace Json { namespace Json {
@ -527,8 +539,8 @@ public:
// recognized in your jurisdiction. // recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_H_INCLUDED #ifndef JSON_VALUE_H_INCLUDED
#define JSON_H_INCLUDED #define JSON_VALUE_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION) #if !defined(JSON_IS_AMALGAMATION)
#include "forwards.h" #include "forwards.h"
@ -577,7 +589,8 @@ public:
#pragma warning(disable : 4251 4275) #pragma warning(disable : 4251 4275)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma pack(push, 8) #pragma pack(push)
#pragma pack()
/** \brief JSON (JavaScript Object Notation). /** \brief JSON (JavaScript Object Notation).
*/ */
@ -898,7 +911,7 @@ public:
int compare(const Value& other) const; int compare(const Value& other) const;
const char* asCString() const; ///< Embedded zeroes could cause you trouble! const char* asCString() const; ///< Embedded zeroes could cause you trouble!
#if JSONCPP_USING_SECURE_MEMORY #if JSONCPP_USE_SECURE_MEMORY
unsigned getCStringLength() const; // Allows you to understand the length of unsigned getCStringLength() const; // Allows you to understand the length of
// the CString // the CString
#endif #endif
@ -960,7 +973,7 @@ public:
/// \post type() is arrayValue /// \post type() is arrayValue
void resize(ArrayIndex newSize); void resize(ArrayIndex newSize);
//@{ ///@{
/// Access an array element (zero based index). If the array contains less /// Access an array element (zero based index). If the array contains less
/// than index element, then null value are inserted in the array so that /// than index element, then null value are inserted in the array so that
/// its size is index+1. /// its size is index+1.
@ -968,15 +981,15 @@ public:
/// this from the operator[] which takes a string.) /// this from the operator[] which takes a string.)
Value& operator[](ArrayIndex index); Value& operator[](ArrayIndex index);
Value& operator[](int index); Value& operator[](int index);
//@} ///@}
//@{ ///@{
/// Access an array element (zero based index). /// Access an array element (zero based index).
/// (You may need to say 'value[0u]' to get your compiler to distinguish /// (You may need to say 'value[0u]' to get your compiler to distinguish
/// this from the operator[] which takes a string.) /// this from the operator[] which takes a string.)
const Value& operator[](ArrayIndex index) const; const Value& operator[](ArrayIndex index) const;
const Value& operator[](int index) const; const Value& operator[](int index) const;
//@} ///@}
/// If the array contains at least index+1 elements, returns the element /// If the array contains at least index+1 elements, returns the element
/// value, otherwise returns defaultValue. /// value, otherwise returns defaultValue.
@ -1036,6 +1049,9 @@ public:
/// and operator[]const /// and operator[]const
/// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
Value const* find(char const* begin, char const* end) const; Value const* find(char const* begin, char const* end) const;
/// Most general and efficient version of isMember()const, get()const,
/// and operator[]const
Value const* find(const String& key) const;
/// Most general and efficient version of object-mutators. /// Most general and efficient version of object-mutators.
/// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
/// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue.
@ -1108,6 +1124,26 @@ public:
iterator begin(); iterator begin();
iterator end(); iterator end();
/// \brief Returns a reference to the first element in the `Value`.
/// Requires that this value holds an array or json object, with at least one
/// element.
const Value& front() const;
/// \brief Returns a reference to the first element in the `Value`.
/// Requires that this value holds an array or json object, with at least one
/// element.
Value& front();
/// \brief Returns a reference to the last element in the `Value`.
/// Requires that value holds an array or json object, with at least one
/// element.
const Value& back() const;
/// \brief Returns a reference to the last element in the `Value`.
/// Requires that this value holds an array or json object, with at least one
/// element.
Value& back();
// Accessors for the [start, limit) range of bytes within the JSON text from // Accessors for the [start, limit) range of bytes within the JSON text from
// which this value was parsed, if any. // which this value was parsed, if any.
void setOffsetStart(ptrdiff_t start); void setOffsetStart(ptrdiff_t start);
@ -1448,6 +1484,14 @@ public:
inline void swap(Value& a, Value& b) { a.swap(b); } inline void swap(Value& a, Value& b) { a.swap(b); }
inline const Value& Value::front() const { return *begin(); }
inline Value& Value::front() { return *begin(); }
inline const Value& Value::back() const { return *(--end()); }
inline Value& Value::back() { return *(--end()); }
} // namespace Json } // namespace Json
#pragma pack(pop) #pragma pack(pop)
@ -1496,7 +1540,8 @@ inline void swap(Value& a, Value& b) { a.swap(b); }
#pragma warning(disable : 4251) #pragma warning(disable : 4251)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma pack(push, 8) #pragma pack(push)
#pragma pack()
namespace Json { namespace Json {
@ -1523,12 +1568,12 @@ public:
}; };
/** \brief Constructs a Reader allowing all features for parsing. /** \brief Constructs a Reader allowing all features for parsing.
* \deprecated Use CharReader and CharReaderBuilder. * \deprecated Use CharReader and CharReaderBuilder.
*/ */
Reader(); Reader();
/** \brief Constructs a Reader allowing the specified feature set for parsing. /** \brief Constructs a Reader allowing the specified feature set for parsing.
* \deprecated Use CharReader and CharReaderBuilder. * \deprecated Use CharReader and CharReaderBuilder.
*/ */
Reader(const Features& features); Reader(const Features& features);
@ -1662,6 +1707,7 @@ private:
using Errors = std::deque<ErrorInfo>; using Errors = std::deque<ErrorInfo>;
bool readToken(Token& token); bool readToken(Token& token);
bool readTokenSkippingComments(Token& token);
void skipSpaces(); void skipSpaces();
bool match(const Char* pattern, int patternLength); bool match(const Char* pattern, int patternLength);
bool readComment(); bool readComment();
@ -1693,7 +1739,6 @@ private:
int& column) const; int& column) const;
String getLocationLineAndColumn(Location location) const; String getLocationLineAndColumn(Location location) const;
void addComment(Location begin, Location end, CommentPlacement placement); void addComment(Location begin, Location end, CommentPlacement placement);
void skipCommentTokens(Token& token);
static bool containsNewLine(Location begin, Location end); static bool containsNewLine(Location begin, Location end);
static String normalizeEOL(Location begin, Location end); static String normalizeEOL(Location begin, Location end);
@ -1716,6 +1761,12 @@ private:
*/ */
class JSON_API CharReader { class JSON_API CharReader {
public: public:
struct JSON_API StructuredError {
ptrdiff_t offset_start;
ptrdiff_t offset_limit;
String message;
};
virtual ~CharReader() = default; virtual ~CharReader() = default;
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
* document. The document must be a UTF-8 encoded string containing the * document. The document must be a UTF-8 encoded string containing the
@ -1734,7 +1785,12 @@ public:
* error occurred. * error occurred.
*/ */
virtual bool parse(char const* beginDoc, char const* endDoc, Value* root, virtual bool parse(char const* beginDoc, char const* endDoc, Value* root,
String* errs) = 0; String* errs);
/** \brief Returns a vector of structured errors encountered while parsing.
* Each parse call resets the stored list of errors.
*/
std::vector<StructuredError> getStructuredErrors() const;
class JSON_API Factory { class JSON_API Factory {
public: public:
@ -1744,7 +1800,21 @@ public:
*/ */
virtual CharReader* newCharReader() const = 0; virtual CharReader* newCharReader() const = 0;
}; // Factory }; // Factory
}; // CharReader
protected:
class Impl {
public:
virtual ~Impl() = default;
virtual bool parse(char const* beginDoc, char const* endDoc, Value* root,
String* errs) = 0;
virtual std::vector<StructuredError> getStructuredErrors() const = 0;
};
explicit CharReader(std::unique_ptr<Impl> impl) : _impl(std::move(impl)) {}
private:
std::unique_ptr<Impl> _impl;
}; // CharReader
/** \brief Build a CharReader implementation. /** \brief Build a CharReader implementation.
* *
@ -1832,6 +1902,12 @@ public:
* \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode
*/ */
static void strictMode(Json::Value* settings); static void strictMode(Json::Value* settings);
/** ECMA-404 mode.
* \pre 'settings' != NULL (but Json::null is fine)
* \remark Defaults:
* \snippet src/lib_json/json_reader.cpp CharReaderBuilderECMA404Mode
*/
static void ecma404Mode(Json::Value* settings);
}; };
/** Consume entire stream and use its begin/end. /** Consume entire stream and use its begin/end.
@ -1912,7 +1988,8 @@ JSON_API IStream& operator>>(IStream&, Value&);
#pragma warning(disable : 4251) #pragma warning(disable : 4251)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma pack(push, 8) #pragma pack(push)
#pragma pack()
namespace Json { namespace Json {
@ -1955,7 +2032,7 @@ public:
*/ */
virtual StreamWriter* newStreamWriter() const = 0; virtual StreamWriter* newStreamWriter() const = 0;
}; // Factory }; // Factory
}; // StreamWriter }; // StreamWriter
/** \brief Write into stringstream, then return string, for convenience. /** \brief Write into stringstream, then return string, for convenience.
* A StreamWriter will be created from the factory, used, and then deleted. * A StreamWriter will be created from the factory, used, and then deleted.
@ -2059,8 +2136,7 @@ public:
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4996) // Deriving from deprecated class #pragma warning(disable : 4996) // Deriving from deprecated class
#endif #endif
class JSON_API FastWriter class JSON_API FastWriter : public Writer {
: public Writer {
public: public:
FastWriter(); FastWriter();
~FastWriter() override = default; ~FastWriter() override = default;
@ -2109,7 +2185,7 @@ private:
* - otherwise, it the values do not fit on one line, or the array contains * - otherwise, it the values do not fit on one line, or the array contains
* object or non empty array, then print one value per line. * object or non empty array, then print one value per line.
* *
* If the Value have comments then they are outputed according to their * If the Value have comments then they are outputted according to their
*#CommentPlacement. *#CommentPlacement.
* *
* \sa Reader, Value, Value::setComment() * \sa Reader, Value, Value::setComment()
@ -2119,8 +2195,7 @@ private:
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4996) // Deriving from deprecated class #pragma warning(disable : 4996) // Deriving from deprecated class
#endif #endif
class JSON_API class JSON_API StyledWriter : public Writer {
StyledWriter : public Writer {
public: public:
StyledWriter(); StyledWriter();
~StyledWriter() override = default; ~StyledWriter() override = default;
@ -2178,7 +2253,7 @@ private:
* - otherwise, it the values do not fit on one line, or the array contains * - otherwise, it the values do not fit on one line, or the array contains
* object or non empty array, then print one value per line. * object or non empty array, then print one value per line.
* *
* If the Value have comments then they are outputed according to their * If the Value have comments then they are outputted according to their
#CommentPlacement. #CommentPlacement.
* *
* \sa Reader, Value, Value::setComment() * \sa Reader, Value, Value::setComment()
@ -2188,8 +2263,7 @@ private:
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4996) // Deriving from deprecated class #pragma warning(disable : 4996) // Deriving from deprecated class
#endif #endif
class JSON_API class JSON_API StyledStreamWriter {
StyledStreamWriter {
public: public:
/** /**
* \param indentation Each level will be indented by this amount extra. * \param indentation Each level will be indented by this amount extra.
@ -2245,6 +2319,7 @@ String JSON_API valueToString(
PrecisionType precisionType = PrecisionType::significantDigits); PrecisionType precisionType = PrecisionType::significantDigits);
String JSON_API valueToString(bool value); String JSON_API valueToString(bool value);
String JSON_API valueToQuotedString(const char* value); String JSON_API valueToQuotedString(const char* value);
String JSON_API valueToQuotedString(const char* value, size_t length);
/// \brief Output using the StyledStreamWriter. /// \brief Output using the StyledStreamWriter.
/// \see Json::operator>>() /// \see Json::operator>>()

View file

@ -1,4 +1,4 @@
/// Json-cpp amalgamated source (http://jsoncpp.sourceforge.net/). /// Json-cpp amalgamated source (https://github.com/open-source-parsers/jsoncpp/).
/// It is intended to be used with #include "json/json.h" /// It is intended to be used with #include "json/json.h"
// ////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////
@ -250,6 +250,7 @@ Iter fixZerosInTheEnd(Iter begin, Iter end, unsigned int precision) {
#endif // if !defined(JSON_IS_AMALGAMATION) #endif // if !defined(JSON_IS_AMALGAMATION)
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <cmath>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <istream> #include <istream>
@ -366,7 +367,7 @@ bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root,
bool successful = readValue(); bool successful = readValue();
Token token; Token token;
skipCommentTokens(token); readTokenSkippingComments(token);
if (collectComments_ && !commentsBefore_.empty()) if (collectComments_ && !commentsBefore_.empty())
root.setComment(commentsBefore_, commentAfter); root.setComment(commentsBefore_, commentAfter);
if (features_.strictRoot_) { if (features_.strictRoot_) {
@ -394,7 +395,7 @@ bool Reader::readValue() {
throwRuntimeError("Exceeded stackLimit in readValue()."); throwRuntimeError("Exceeded stackLimit in readValue().");
Token token; Token token;
skipCommentTokens(token); readTokenSkippingComments(token);
bool successful = true; bool successful = true;
if (collectComments_ && !commentsBefore_.empty()) { if (collectComments_ && !commentsBefore_.empty()) {
@ -462,14 +463,14 @@ bool Reader::readValue() {
return successful; return successful;
} }
void Reader::skipCommentTokens(Token& token) { bool Reader::readTokenSkippingComments(Token& token) {
bool success = readToken(token);
if (features_.allowComments_) { if (features_.allowComments_) {
do { while (success && token.type_ == tokenComment) {
readToken(token); success = readToken(token);
} while (token.type_ == tokenComment); }
} else {
readToken(token);
} }
return success;
} }
bool Reader::readToken(Token& token) { bool Reader::readToken(Token& token) {
@ -683,12 +684,7 @@ bool Reader::readObject(Token& token) {
Value init(objectValue); Value init(objectValue);
currentValue().swapPayload(init); currentValue().swapPayload(init);
currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetStart(token.start_ - begin_);
while (readToken(tokenName)) { while (readTokenSkippingComments(tokenName)) {
bool initialTokenOk = true;
while (tokenName.type_ == tokenComment && initialTokenOk)
initialTokenOk = readToken(tokenName);
if (!initialTokenOk)
break;
if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
return true; return true;
name.clear(); name.clear();
@ -717,15 +713,11 @@ bool Reader::readObject(Token& token) {
return recoverFromError(tokenObjectEnd); return recoverFromError(tokenObjectEnd);
Token comma; Token comma;
if (!readToken(comma) || if (!readTokenSkippingComments(comma) ||
(comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
comma.type_ != tokenComment)) {
return addErrorAndRecover("Missing ',' or '}' in object declaration", return addErrorAndRecover("Missing ',' or '}' in object declaration",
comma, tokenObjectEnd); comma, tokenObjectEnd);
} }
bool finalizeTokenOk = true;
while (comma.type_ == tokenComment && finalizeTokenOk)
finalizeTokenOk = readToken(comma);
if (comma.type_ == tokenObjectEnd) if (comma.type_ == tokenObjectEnd)
return true; return true;
} }
@ -755,10 +747,7 @@ bool Reader::readArray(Token& token) {
Token currentToken; Token currentToken;
// Accept Comment after last item in the array. // Accept Comment after last item in the array.
ok = readToken(currentToken); ok = readTokenSkippingComments(currentToken);
while (currentToken.type_ == tokenComment && ok) {
ok = readToken(currentToken);
}
bool badTokenType = (currentToken.type_ != tokenArraySeparator && bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
currentToken.type_ != tokenArrayEnd); currentToken.type_ != tokenArrayEnd);
if (!ok || badTokenType) { if (!ok || badTokenType) {
@ -836,11 +825,16 @@ bool Reader::decodeDouble(Token& token) {
bool Reader::decodeDouble(Token& token, Value& decoded) { bool Reader::decodeDouble(Token& token, Value& decoded) {
double value = 0; double value = 0;
String buffer(token.start_, token.end_); IStringStream is(String(token.start_, token.end_));
IStringStream is(buffer); if (!(is >> value)) {
if (!(is >> value)) if (value == std::numeric_limits<double>::max())
return addError( value = std::numeric_limits<double>::infinity();
"'" + String(token.start_, token.end_) + "' is not a number.", token); else if (value == std::numeric_limits<double>::lowest())
value = -std::numeric_limits<double>::infinity();
else if (!std::isinf(value))
return addError(
"'" + String(token.start_, token.end_) + "' is not a number.", token);
}
decoded = value; decoded = value;
return true; return true;
} }
@ -1004,7 +998,7 @@ void Reader::getLocationLineAndColumn(Location location, int& line,
while (current < location && current != end_) { while (current < location && current != end_) {
Char c = *current++; Char c = *current++;
if (c == '\r') { if (c == '\r') {
if (*current == '\n') if (current != end_ && *current == '\n')
++current; ++current;
lastLineStart = current; lastLineStart = current;
++line; ++line;
@ -1121,17 +1115,12 @@ class OurReader {
public: public:
using Char = char; using Char = char;
using Location = const Char*; using Location = const Char*;
struct StructuredError {
ptrdiff_t offset_start;
ptrdiff_t offset_limit;
String message;
};
explicit OurReader(OurFeatures const& features); explicit OurReader(OurFeatures const& features);
bool parse(const char* beginDoc, const char* endDoc, Value& root, bool parse(const char* beginDoc, const char* endDoc, Value& root,
bool collectComments = true); bool collectComments = true);
String getFormattedErrorMessages() const; String getFormattedErrorMessages() const;
std::vector<StructuredError> getStructuredErrors() const; std::vector<CharReader::StructuredError> getStructuredErrors() const;
private: private:
OurReader(OurReader const&); // no impl OurReader(OurReader const&); // no impl
@ -1174,6 +1163,7 @@ private:
using Errors = std::deque<ErrorInfo>; using Errors = std::deque<ErrorInfo>;
bool readToken(Token& token); bool readToken(Token& token);
bool readTokenSkippingComments(Token& token);
void skipSpaces(); void skipSpaces();
void skipBom(bool skipBom); void skipBom(bool skipBom);
bool match(const Char* pattern, int patternLength); bool match(const Char* pattern, int patternLength);
@ -1207,7 +1197,6 @@ private:
int& column) const; int& column) const;
String getLocationLineAndColumn(Location location) const; String getLocationLineAndColumn(Location location) const;
void addComment(Location begin, Location end, CommentPlacement placement); void addComment(Location begin, Location end, CommentPlacement placement);
void skipCommentTokens(Token& token);
static String normalizeEOL(Location begin, Location end); static String normalizeEOL(Location begin, Location end);
static bool containsNewLine(Location begin, Location end); static bool containsNewLine(Location begin, Location end);
@ -1261,7 +1250,7 @@ bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root,
bool successful = readValue(); bool successful = readValue();
nodes_.pop(); nodes_.pop();
Token token; Token token;
skipCommentTokens(token); readTokenSkippingComments(token);
if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) { if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) {
addError("Extra non-whitespace after JSON value.", token); addError("Extra non-whitespace after JSON value.", token);
return false; return false;
@ -1289,7 +1278,7 @@ bool OurReader::readValue() {
if (nodes_.size() > features_.stackLimit_) if (nodes_.size() > features_.stackLimit_)
throwRuntimeError("Exceeded stackLimit in readValue()."); throwRuntimeError("Exceeded stackLimit in readValue().");
Token token; Token token;
skipCommentTokens(token); readTokenSkippingComments(token);
bool successful = true; bool successful = true;
if (collectComments_ && !commentsBefore_.empty()) { if (collectComments_ && !commentsBefore_.empty()) {
@ -1376,14 +1365,14 @@ bool OurReader::readValue() {
return successful; return successful;
} }
void OurReader::skipCommentTokens(Token& token) { bool OurReader::readTokenSkippingComments(Token& token) {
bool success = readToken(token);
if (features_.allowComments_) { if (features_.allowComments_) {
do { while (success && token.type_ == tokenComment) {
readToken(token); success = readToken(token);
} while (token.type_ == tokenComment); }
} else {
readToken(token);
} }
return success;
} }
bool OurReader::readToken(Token& token) { bool OurReader::readToken(Token& token) {
@ -1680,12 +1669,7 @@ bool OurReader::readObject(Token& token) {
Value init(objectValue); Value init(objectValue);
currentValue().swapPayload(init); currentValue().swapPayload(init);
currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetStart(token.start_ - begin_);
while (readToken(tokenName)) { while (readTokenSkippingComments(tokenName)) {
bool initialTokenOk = true;
while (tokenName.type_ == tokenComment && initialTokenOk)
initialTokenOk = readToken(tokenName);
if (!initialTokenOk)
break;
if (tokenName.type_ == tokenObjectEnd && if (tokenName.type_ == tokenObjectEnd &&
(name.empty() || (name.empty() ||
features_.allowTrailingCommas_)) // empty object or trailing comma features_.allowTrailingCommas_)) // empty object or trailing comma
@ -1722,15 +1706,11 @@ bool OurReader::readObject(Token& token) {
return recoverFromError(tokenObjectEnd); return recoverFromError(tokenObjectEnd);
Token comma; Token comma;
if (!readToken(comma) || if (!readTokenSkippingComments(comma) ||
(comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
comma.type_ != tokenComment)) {
return addErrorAndRecover("Missing ',' or '}' in object declaration", return addErrorAndRecover("Missing ',' or '}' in object declaration",
comma, tokenObjectEnd); comma, tokenObjectEnd);
} }
bool finalizeTokenOk = true;
while (comma.type_ == tokenComment && finalizeTokenOk)
finalizeTokenOk = readToken(comma);
if (comma.type_ == tokenObjectEnd) if (comma.type_ == tokenObjectEnd)
return true; return true;
} }
@ -1764,10 +1744,7 @@ bool OurReader::readArray(Token& token) {
Token currentToken; Token currentToken;
// Accept Comment after last item in the array. // Accept Comment after last item in the array.
ok = readToken(currentToken); ok = readTokenSkippingComments(currentToken);
while (currentToken.type_ == tokenComment && ok) {
ok = readToken(currentToken);
}
bool badTokenType = (currentToken.type_ != tokenArraySeparator && bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
currentToken.type_ != tokenArrayEnd); currentToken.type_ != tokenArrayEnd);
if (!ok || badTokenType) { if (!ok || badTokenType) {
@ -1845,7 +1822,7 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) {
const auto digit(static_cast<Value::UInt>(c - '0')); const auto digit(static_cast<Value::UInt>(c - '0'));
if (value >= threshold) { if (value >= threshold) {
// We've hit or exceeded the max value divided by 10 (rounded down). If // We've hit or exceeded the max value divided by 10 (rounded down). If
// a) we've only just touched the limit, meaing value == threshold, // a) we've only just touched the limit, meaning value == threshold,
// b) this is the last digit, or // b) this is the last digit, or
// c) it's small enough to fit in that rounding delta, we're okay. // c) it's small enough to fit in that rounding delta, we're okay.
// Otherwise treat this number as a double to avoid overflow. // Otherwise treat this number as a double to avoid overflow.
@ -1882,11 +1859,15 @@ bool OurReader::decodeDouble(Token& token) {
bool OurReader::decodeDouble(Token& token, Value& decoded) { bool OurReader::decodeDouble(Token& token, Value& decoded) {
double value = 0; double value = 0;
const String buffer(token.start_, token.end_); IStringStream is(String(token.start_, token.end_));
IStringStream is(buffer);
if (!(is >> value)) { if (!(is >> value)) {
return addError( if (value == std::numeric_limits<double>::max())
"'" + String(token.start_, token.end_) + "' is not a number.", token); value = std::numeric_limits<double>::infinity();
else if (value == std::numeric_limits<double>::lowest())
value = -std::numeric_limits<double>::infinity();
else if (!std::isinf(value))
return addError(
"'" + String(token.start_, token.end_) + "' is not a number.", token);
} }
decoded = value; decoded = value;
return true; return true;
@ -2051,7 +2032,7 @@ void OurReader::getLocationLineAndColumn(Location location, int& line,
while (current < location && current != end_) { while (current < location && current != end_) {
Char c = *current++; Char c = *current++;
if (c == '\r') { if (c == '\r') {
if (*current == '\n') if (current != end_ && *current == '\n')
++current; ++current;
lastLineStart = current; lastLineStart = current;
++line; ++line;
@ -2086,10 +2067,11 @@ String OurReader::getFormattedErrorMessages() const {
return formattedMessage; return formattedMessage;
} }
std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const { std::vector<CharReader::StructuredError>
std::vector<OurReader::StructuredError> allErrors; OurReader::getStructuredErrors() const {
std::vector<CharReader::StructuredError> allErrors;
for (const auto& error : errors_) { for (const auto& error : errors_) {
OurReader::StructuredError structured; CharReader::StructuredError structured;
structured.offset_start = error.token_.start_ - begin_; structured.offset_start = error.token_.start_ - begin_;
structured.offset_limit = error.token_.end_ - begin_; structured.offset_limit = error.token_.end_ - begin_;
structured.message = error.message_; structured.message = error.message_;
@ -2099,20 +2081,36 @@ std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
} }
class OurCharReader : public CharReader { class OurCharReader : public CharReader {
bool const collectComments_;
OurReader reader_;
public: public:
OurCharReader(bool collectComments, OurFeatures const& features) OurCharReader(bool collectComments, OurFeatures const& features)
: collectComments_(collectComments), reader_(features) {} : CharReader(
bool parse(char const* beginDoc, char const* endDoc, Value* root, std::unique_ptr<OurImpl>(new OurImpl(collectComments, features))) {}
String* errs) override {
bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); protected:
if (errs) { class OurImpl : public Impl {
*errs = reader_.getFormattedErrorMessages(); public:
OurImpl(bool collectComments, OurFeatures const& features)
: collectComments_(collectComments), reader_(features) {}
bool parse(char const* beginDoc, char const* endDoc, Value* root,
String* errs) override {
bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
if (errs) {
*errs = reader_.getFormattedErrorMessages();
}
return ok;
} }
return ok;
} std::vector<CharReader::StructuredError>
getStructuredErrors() const override {
return reader_.getStructuredErrors();
}
private:
bool const collectComments_;
OurReader reader_;
};
}; };
CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); } CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
@ -2201,6 +2199,32 @@ void CharReaderBuilder::setDefaults(Json::Value* settings) {
(*settings)["skipBom"] = true; (*settings)["skipBom"] = true;
//! [CharReaderBuilderDefaults] //! [CharReaderBuilderDefaults]
} }
// static
void CharReaderBuilder::ecma404Mode(Json::Value* settings) {
//! [CharReaderBuilderECMA404Mode]
(*settings)["allowComments"] = false;
(*settings)["allowTrailingCommas"] = false;
(*settings)["strictRoot"] = false;
(*settings)["allowDroppedNullPlaceholders"] = false;
(*settings)["allowNumericKeys"] = false;
(*settings)["allowSingleQuotes"] = false;
(*settings)["stackLimit"] = 1000;
(*settings)["failIfExtra"] = true;
(*settings)["rejectDupKeys"] = false;
(*settings)["allowSpecialFloats"] = false;
(*settings)["skipBom"] = false;
//! [CharReaderBuilderECMA404Mode]
}
std::vector<CharReader::StructuredError>
CharReader::getStructuredErrors() const {
return _impl->getStructuredErrors();
}
bool CharReader::parse(char const* beginDoc, char const* endDoc, Value* root,
String* errs) {
return _impl->parse(beginDoc, endDoc, root, errs);
}
////////////////////////////////// //////////////////////////////////
// global functions // global functions
@ -2209,7 +2233,7 @@ bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root,
String* errs) { String* errs) {
OStringStream ssin; OStringStream ssin;
ssin << sin.rdbuf(); ssin << sin.rdbuf();
String doc = ssin.str(); String doc = std::move(ssin).str();
char const* begin = doc.data(); char const* begin = doc.data();
char const* end = begin + doc.size(); char const* end = begin + doc.size();
// Note that we do not actually need a null-terminator. // Note that we do not actually need a null-terminator.
@ -2501,7 +2525,8 @@ template <typename T, typename U>
static inline bool InRange(double d, T min, U max) { static inline bool InRange(double d, T min, U max) {
// The casts can lose precision, but we are looking only for // The casts can lose precision, but we are looking only for
// an approximate range. Might fail on edge cases though. ~cdunn // an approximate range. Might fail on edge cases though. ~cdunn
return d >= static_cast<double>(min) && d <= static_cast<double>(max); return d >= static_cast<double>(min) && d <= static_cast<double>(max) &&
!(static_cast<U>(d) == min && d != static_cast<double>(min));
} }
#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) #else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
static inline double integerToDouble(Json::UInt64 value) { static inline double integerToDouble(Json::UInt64 value) {
@ -2515,7 +2540,8 @@ template <typename T> static inline double integerToDouble(T value) {
template <typename T, typename U> template <typename T, typename U>
static inline bool InRange(double d, T min, U max) { static inline bool InRange(double d, T min, U max) {
return d >= integerToDouble(min) && d <= integerToDouble(max); return d >= integerToDouble(min) && d <= integerToDouble(max) &&
!(static_cast<U>(d) == min && d != integerToDouble(min));
} }
#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) #endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
@ -2577,7 +2603,7 @@ inline static void decodePrefixedString(bool isPrefixed, char const* prefixed,
/** Free the string duplicated by /** Free the string duplicated by
* duplicateStringValue()/duplicateAndPrefixStringValue(). * duplicateStringValue()/duplicateAndPrefixStringValue().
*/ */
#if JSONCPP_USING_SECURE_MEMORY #if JSONCPP_USE_SECURE_MEMORY
static inline void releasePrefixedStringValue(char* value) { static inline void releasePrefixedStringValue(char* value) {
unsigned length = 0; unsigned length = 0;
char const* valueDecoded; char const* valueDecoded;
@ -2592,10 +2618,10 @@ static inline void releaseStringValue(char* value, unsigned length) {
memset(value, 0, size); memset(value, 0, size);
free(value); free(value);
} }
#else // !JSONCPP_USING_SECURE_MEMORY #else // !JSONCPP_USE_SECURE_MEMORY
static inline void releasePrefixedStringValue(char* value) { free(value); } static inline void releasePrefixedStringValue(char* value) { free(value); }
static inline void releaseStringValue(char* value, unsigned) { free(value); } static inline void releaseStringValue(char* value, unsigned) { free(value); }
#endif // JSONCPP_USING_SECURE_MEMORY #endif // JSONCPP_USE_SECURE_MEMORY
} // namespace Json } // namespace Json
@ -3013,7 +3039,7 @@ const char* Value::asCString() const {
return this_str; return this_str;
} }
#if JSONCPP_USING_SECURE_MEMORY #if JSONCPP_USE_SECURE_MEMORY
unsigned Value::getCStringLength() const { unsigned Value::getCStringLength() const {
JSON_ASSERT_MESSAGE(type() == stringValue, JSON_ASSERT_MESSAGE(type() == stringValue,
"in Json::Value::asCString(): requires stringValue"); "in Json::Value::asCString(): requires stringValue");
@ -3119,6 +3145,11 @@ Value::Int64 Value::asInt64() const {
JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range"); JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range");
return Int64(value_.uint_); return Int64(value_.uint_);
case realValue: case realValue:
// If the double value is in proximity to minInt64, it will be rounded to
// minInt64. The correct value in this scenario is indeterminable
JSON_ASSERT_MESSAGE(
value_.real_ != minInt64,
"Double value is minInt64, precise value cannot be determined");
JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64),
"double out of Int64 range"); "double out of Int64 range");
return Int64(value_.real_); return Int64(value_.real_);
@ -3506,6 +3537,9 @@ Value const* Value::find(char const* begin, char const* end) const {
return nullptr; return nullptr;
return &(*it).second; return &(*it).second;
} }
Value const* Value::find(const String& key) const {
return find(key.data(), key.data() + key.length());
}
Value* Value::demand(char const* begin, char const* end) { Value* Value::demand(char const* begin, char const* end) {
JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
"in Json::Value::demand(begin, end): requires " "in Json::Value::demand(begin, end): requires "
@ -3519,7 +3553,7 @@ const Value& Value::operator[](const char* key) const {
return *found; return *found;
} }
Value const& Value::operator[](const String& key) const { Value const& Value::operator[](const String& key) const {
Value const* found = find(key.data(), key.data() + key.length()); Value const* found = find(key);
if (!found) if (!found)
return nullSingleton(); return nullSingleton();
return *found; return *found;
@ -3619,7 +3653,7 @@ bool Value::removeIndex(ArrayIndex index, Value* removed) {
return false; return false;
} }
if (removed) if (removed)
*removed = it->second; *removed = std::move(it->second);
ArrayIndex oldSize = size(); ArrayIndex oldSize = size();
// shift left all items left, into the place of the "removed" // shift left all items left, into the place of the "removed"
for (ArrayIndex i = index; i < (oldSize - 1); ++i) { for (ArrayIndex i = index; i < (oldSize - 1); ++i) {
@ -3722,8 +3756,12 @@ bool Value::isInt64() const {
// Note that maxInt64 (= 2^63 - 1) is not exactly representable as a // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a
// double, so double(maxInt64) will be rounded up to 2^63. Therefore we // double, so double(maxInt64) will be rounded up to 2^63. Therefore we
// require the value to be strictly less than the limit. // require the value to be strictly less than the limit.
return value_.real_ >= double(minInt64) && // minInt64 is -2^63 which can be represented as a double, but since double
value_.real_ < double(maxInt64) && IsIntegral(value_.real_); // values in its proximity are also rounded to -2^63, we require the value
// to be strictly greater than the limit to avoid returning 'true' for
// values that are not in the range
return value_.real_ > double(minInt64) && value_.real_ < double(maxInt64) &&
IsIntegral(value_.real_);
default: default:
break; break;
} }
@ -3761,7 +3799,11 @@ bool Value::isIntegral() const {
// Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
// double, so double(maxUInt64) will be rounded up to 2^64. Therefore we // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
// require the value to be strictly less than the limit. // require the value to be strictly less than the limit.
return value_.real_ >= double(minInt64) && // minInt64 is -2^63 which can be represented as a double, but since double
// values in its proximity are also rounded to -2^63, we require the value
// to be strictly greater than the limit to avoid returning 'true' for
// values that are not in the range
return value_.real_ > double(minInt64) &&
value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_); value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_);
#else #else
return value_.real_ >= minInt && value_.real_ <= maxUInt && return value_.real_ >= minInt && value_.real_ <= maxUInt &&
@ -3824,9 +3866,8 @@ void Value::setComment(String comment, CommentPlacement placement) {
// Always discard trailing newline, to aid indentation. // Always discard trailing newline, to aid indentation.
comment.pop_back(); comment.pop_back();
} }
JSON_ASSERT(!comment.empty());
JSON_ASSERT_MESSAGE( JSON_ASSERT_MESSAGE(
comment[0] == '\0' || comment[0] == '/', comment.empty() || comment[0] == '/',
"in Json::Value::setComment(): Comments must start with /"); "in Json::Value::setComment(): Comments must start with /");
comments_.set(placement, std::move(comment)); comments_.set(placement, std::move(comment));
} }
@ -4194,8 +4235,9 @@ String valueToString(double value, bool useSpecialFloats,
if (!isfinite(value)) { if (!isfinite(value)) {
static const char* const reps[2][3] = {{"NaN", "-Infinity", "Infinity"}, static const char* const reps[2][3] = {{"NaN", "-Infinity", "Infinity"},
{"null", "-1e+9999", "1e+9999"}}; {"null", "-1e+9999", "1e+9999"}};
return reps[useSpecialFloats ? 0 : 1] return reps[useSpecialFloats ? 0 : 1][isnan(value) ? 0
[isnan(value) ? 0 : (value < 0) ? 1 : 2]; : (value < 0) ? 1
: 2];
} }
String buffer(size_t(36), '\0'); String buffer(size_t(36), '\0');
@ -4415,6 +4457,10 @@ String valueToQuotedString(const char* value) {
return valueToQuotedStringN(value, strlen(value)); return valueToQuotedStringN(value, strlen(value));
} }
String valueToQuotedString(const char* value, size_t length) {
return valueToQuotedStringN(value, length);
}
// Class Writer // Class Writer
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
Writer::~Writer() = default; Writer::~Writer() = default;
@ -4552,7 +4598,7 @@ void StyledWriter::writeValue(const Value& value) {
const String& name = *it; const String& name = *it;
const Value& childValue = value[name]; const Value& childValue = value[name];
writeCommentBeforeValue(childValue); writeCommentBeforeValue(childValue);
writeWithIndent(valueToQuotedString(name.c_str())); writeWithIndent(valueToQuotedString(name.c_str(), name.size()));
document_ += " : "; document_ += " : ";
writeValue(childValue); writeValue(childValue);
if (++it == members.end()) { if (++it == members.end()) {
@ -4770,7 +4816,7 @@ void StyledStreamWriter::writeValue(const Value& value) {
const String& name = *it; const String& name = *it;
const Value& childValue = value[name]; const Value& childValue = value[name];
writeCommentBeforeValue(childValue); writeCommentBeforeValue(childValue);
writeWithIndent(valueToQuotedString(name.c_str())); writeWithIndent(valueToQuotedString(name.c_str(), name.size()));
*document_ << " : "; *document_ << " : ";
writeValue(childValue); writeValue(childValue);
if (++it == members.end()) { if (++it == members.end()) {
@ -5308,7 +5354,7 @@ String writeString(StreamWriter::Factory const& factory, Value const& root) {
OStringStream sout; OStringStream sout;
StreamWriterPtr const writer(factory.newStreamWriter()); StreamWriterPtr const writer(factory.newStreamWriter());
writer->write(root, &sout); writer->write(root, &sout);
return sout.str(); return std::move(sout).str();
} }
OStream& operator<<(OStream& sout, Value const& root) { OStream& operator<<(OStream& sout, Value const& root) {