625 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			625 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Tencent is pleased to support the open source community by making RapidJSON available.
 | |
| // 
 | |
| // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
 | |
| //
 | |
| // Licensed under the MIT License (the "License"); you may not use this file except
 | |
| // in compliance with the License. You may obtain a copy of the License at
 | |
| //
 | |
| // http://opensource.org/licenses/MIT
 | |
| //
 | |
| // Unless required by applicable law or agreed to in writing, software distributed 
 | |
| // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
 | |
| // CONDITIONS OF ANY KIND, either express or implied. See the License for the 
 | |
| // specific language governing permissions and limitations under the License.
 | |
| 
 | |
| #ifndef RAPIDJSON_WRITER_H_
 | |
| #define RAPIDJSON_WRITER_H_
 | |
| 
 | |
| #include "stream.h"
 | |
| #include "internal/stack.h"
 | |
| #include "internal/strfunc.h"
 | |
| #include "internal/dtoa.h"
 | |
| #include "internal/itoa.h"
 | |
| #include "stringbuffer.h"
 | |
| #include <new>      // placement new
 | |
| 
 | |
| #if defined(RAPIDJSON_SIMD) && defined(_MSC_VER)
 | |
| #include <intrin.h>
 | |
| #pragma intrinsic(_BitScanForward)
 | |
| #endif
 | |
| #ifdef RAPIDJSON_SSE42
 | |
| #include <nmmintrin.h>
 | |
| #elif defined(RAPIDJSON_SSE2)
 | |
| #include <emmintrin.h>
 | |
| #endif
 | |
| 
 | |
| #ifdef _MSC_VER
 | |
| RAPIDJSON_DIAG_PUSH
 | |
| RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
 | |
| #endif
 | |
| 
 | |
| #ifdef __clang__
 | |
| RAPIDJSON_DIAG_PUSH
 | |
| RAPIDJSON_DIAG_OFF(padded)
 | |
| RAPIDJSON_DIAG_OFF(unreachable-code)
 | |
| RAPIDJSON_DIAG_OFF(c++98-compat)
 | |
| #endif
 | |
| 
 | |
| RAPIDJSON_NAMESPACE_BEGIN
 | |
| 
 | |
| ///////////////////////////////////////////////////////////////////////////////
 | |
| // WriteFlag
 | |
| 
 | |
| /*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS 
 | |
|     \ingroup RAPIDJSON_CONFIG
 | |
|     \brief User-defined kWriteDefaultFlags definition.
 | |
| 
 | |
|     User can define this as any \c WriteFlag combinations.
 | |
| */
 | |
| #ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS
 | |
| #define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags
 | |
| #endif
 | |
| 
 | |
| //! Combination of writeFlags
 | |
| enum WriteFlag {
 | |
|     kWriteNoFlags = 0,              //!< No flags are set.
 | |
|     kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings.
 | |
|     kWriteNanAndInfFlag = 2,        //!< Allow writing of Infinity, -Infinity and NaN.
 | |
|     kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS  //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS
 | |
| };
 | |
| 
 | |
| //! JSON writer
 | |
| /*! Writer implements the concept Handler.
 | |
|     It generates JSON text by events to an output os.
 | |
| 
 | |
|     User may programmatically calls the functions of a writer to generate JSON text.
 | |
| 
 | |
|     On the other side, a writer can also be passed to objects that generates events, 
 | |
| 
 | |
|     for example Reader::Parse() and Document::Accept().
 | |
| 
 | |
|     \tparam OutputStream Type of output stream.
 | |
|     \tparam SourceEncoding Encoding of source string.
 | |
|     \tparam TargetEncoding Encoding of output stream.
 | |
|     \tparam StackAllocator Type of allocator for allocating memory of stack.
 | |
|     \note implements Handler concept
 | |
| */
 | |
| template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags>
 | |
| class Writer {
 | |
| public:
 | |
|     typedef typename SourceEncoding::Ch Ch;
 | |
| 
 | |
|     static const int kDefaultMaxDecimalPlaces = 324;
 | |
| 
 | |
|     //! Constructor
 | |
|     /*! \param os Output stream.
 | |
|         \param stackAllocator User supplied allocator. If it is null, it will create a private one.
 | |
|         \param levelDepth Initial capacity of stack.
 | |
|     */
 | |
|     explicit
 | |
|     Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : 
 | |
|         os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {}
 | |
| 
 | |
|     explicit
 | |
|     Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) :
 | |
|         os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {}
 | |
| 
 | |
| #if RAPIDJSON_HAS_CXX11_RVALUE_REFS
 | |
|     Writer(Writer&& rhs) :
 | |
|         os_(rhs.os_), level_stack_(std::move(rhs.level_stack_)), maxDecimalPlaces_(rhs.maxDecimalPlaces_), hasRoot_(rhs.hasRoot_) {
 | |
|         rhs.os_ = 0;
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     //! Reset the writer with a new stream.
 | |
|     /*!
 | |
|         This function reset the writer with a new stream and default settings,
 | |
|         in order to make a Writer object reusable for output multiple JSONs.
 | |
| 
 | |
|         \param os New output stream.
 | |
|         \code
 | |
|         Writer<OutputStream> writer(os1);
 | |
|         writer.StartObject();
 | |
|         // ...
 | |
|         writer.EndObject();
 | |
| 
 | |
|         writer.Reset(os2);
 | |
|         writer.StartObject();
 | |
|         // ...
 | |
|         writer.EndObject();
 | |
|         \endcode
 | |
|     */
 | |
|     void Reset(OutputStream& os) {
 | |
|         os_ = &os;
 | |
|         hasRoot_ = false;
 | |
|         level_stack_.Clear();
 | |
|     }
 | |
| 
 | |
|     //! Checks whether the output is a complete JSON.
 | |
|     /*!
 | |
|         A complete JSON has a complete root object or array.
 | |
|     */
 | |
|     bool IsComplete() const {
 | |
|         return hasRoot_ && level_stack_.Empty();
 | |
|     }
 | |
| 
 | |
|     int GetMaxDecimalPlaces() const {
 | |
|         return maxDecimalPlaces_;
 | |
|     }
 | |
| 
 | |
|     //! Sets the maximum number of decimal places for double output.
 | |
|     /*!
 | |
|         This setting truncates the output with specified number of decimal places.
 | |
| 
 | |
|         For example, 
 | |
| 
 | |
|         \code
 | |
|         writer.SetMaxDecimalPlaces(3);
 | |
|         writer.StartArray();
 | |
|         writer.Double(0.12345);                 // "0.123"
 | |
|         writer.Double(0.0001);                  // "0.0"
 | |
|         writer.Double(1.234567890123456e30);    // "1.234567890123456e30" (do not truncate significand for positive exponent)
 | |
|         writer.Double(1.23e-4);                 // "0.0"                  (do truncate significand for negative exponent)
 | |
|         writer.EndArray();
 | |
|         \endcode
 | |
| 
 | |
|         The default setting does not truncate any decimal places. You can restore to this setting by calling
 | |
|         \code
 | |
|         writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces);
 | |
|         \endcode
 | |
|     */
 | |
|     void SetMaxDecimalPlaces(int maxDecimalPlaces) {
 | |
|         maxDecimalPlaces_ = maxDecimalPlaces;
 | |
|     }
 | |
| 
 | |
|     /*!@name Implementation of Handler
 | |
|         \see Handler
 | |
|     */
 | |
|     //@{
 | |
| 
 | |
|     bool Null()                 { Prefix(kNullType);   return EndValue(WriteNull()); }
 | |
|     bool Bool(bool b)           { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); }
 | |
|     bool Int(int i)             { Prefix(kNumberType); return EndValue(WriteInt(i)); }
 | |
|     bool Uint(unsigned u)       { Prefix(kNumberType); return EndValue(WriteUint(u)); }
 | |
|     bool Int64(int64_t i64)     { Prefix(kNumberType); return EndValue(WriteInt64(i64)); }
 | |
|     bool Uint64(uint64_t u64)   { Prefix(kNumberType); return EndValue(WriteUint64(u64)); }
 | |
| 
 | |
|     //! Writes the given \c double value to the stream
 | |
|     /*!
 | |
|         \param d The value to be written.
 | |
|         \return Whether it is succeed.
 | |
|     */
 | |
|     bool Double(double d)       { Prefix(kNumberType); return EndValue(WriteDouble(d)); }
 | |
| 
 | |
|     bool RawNumber(const Ch* str, SizeType length, bool copy = false) {
 | |
|         RAPIDJSON_ASSERT(str != 0);
 | |
|         (void)copy;
 | |
|         Prefix(kNumberType);
 | |
|         return EndValue(WriteString(str, length));
 | |
|     }
 | |
| 
 | |
|     bool String(const Ch* str, SizeType length, bool copy = false) {
 | |
|         RAPIDJSON_ASSERT(str != 0);
 | |
|         (void)copy;
 | |
|         Prefix(kStringType);
 | |
|         return EndValue(WriteString(str, length));
 | |
|     }
 | |
| 
 | |
| #if RAPIDJSON_HAS_STDSTRING
 | |
|     bool String(const std::basic_string<Ch>& str) {
 | |
|         return String(str.data(), SizeType(str.size()));
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     bool StartObject() {
 | |
|         Prefix(kObjectType);
 | |
|         new (level_stack_.template Push<Level>()) Level(false);
 | |
|         return WriteStartObject();
 | |
|     }
 | |
| 
 | |
|     bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); }
 | |
| 
 | |
|     bool EndObject(SizeType memberCount = 0) {
 | |
|         (void)memberCount;
 | |
|         RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
 | |
|         RAPIDJSON_ASSERT(!level_stack_.template Top<Level>()->inArray);
 | |
|         level_stack_.template Pop<Level>(1);
 | |
|         return EndValue(WriteEndObject());
 | |
|     }
 | |
| 
 | |
|     bool StartArray() {
 | |
|         Prefix(kArrayType);
 | |
|         new (level_stack_.template Push<Level>()) Level(true);
 | |
|         return WriteStartArray();
 | |
|     }
 | |
| 
 | |
|     bool EndArray(SizeType elementCount = 0) {
 | |
|         (void)elementCount;
 | |
|         RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
 | |
|         RAPIDJSON_ASSERT(level_stack_.template Top<Level>()->inArray);
 | |
|         level_stack_.template Pop<Level>(1);
 | |
|         return EndValue(WriteEndArray());
 | |
|     }
 | |
|     //@}
 | |
| 
 | |
|     /*! @name Convenience extensions */
 | |
|     //@{
 | |
| 
 | |
|     //! Simpler but slower overload.
 | |
|     bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
 | |
|     bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); }
 | |
| 
 | |
|     //@}
 | |
| 
 | |
|     //! Write a raw JSON value.
 | |
|     /*!
 | |
|         For user to write a stringified JSON as a value.
 | |
| 
 | |
|         \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range.
 | |
|         \param length Length of the json.
 | |
|         \param type Type of the root of json.
 | |
|     */
 | |
|     bool RawValue(const Ch* json, size_t length, Type type) {
 | |
|         RAPIDJSON_ASSERT(json != 0);
 | |
|         Prefix(type);
 | |
|         return EndValue(WriteRawValue(json, length));
 | |
|     }
 | |
| 
 | |
| protected:
 | |
|     //! Information for each nested level
 | |
|     struct Level {
 | |
|         Level(bool inArray_) : valueCount(0), inArray(inArray_) {}
 | |
|         size_t valueCount;  //!< number of values in this level
 | |
|         bool inArray;       //!< true if in array, otherwise in object
 | |
|     };
 | |
| 
 | |
|     static const size_t kDefaultLevelDepth = 32;
 | |
| 
 | |
|     bool WriteNull()  {
 | |
|         PutReserve(*os_, 4);
 | |
|         PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true;
 | |
|     }
 | |
| 
 | |
|     bool WriteBool(bool b)  {
 | |
|         if (b) {
 | |
|             PutReserve(*os_, 4);
 | |
|             PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e');
 | |
|         }
 | |
|         else {
 | |
|             PutReserve(*os_, 5);
 | |
|             PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e');
 | |
|         }
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     bool WriteInt(int i) {
 | |
|         char buffer[11];
 | |
|         const char* end = internal::i32toa(i, buffer);
 | |
|         PutReserve(*os_, static_cast<size_t>(end - buffer));
 | |
|         for (const char* p = buffer; p != end; ++p)
 | |
|             PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     bool WriteUint(unsigned u) {
 | |
|         char buffer[10];
 | |
|         const char* end = internal::u32toa(u, buffer);
 | |
|         PutReserve(*os_, static_cast<size_t>(end - buffer));
 | |
|         for (const char* p = buffer; p != end; ++p)
 | |
|             PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     bool WriteInt64(int64_t i64) {
 | |
|         char buffer[21];
 | |
|         const char* end = internal::i64toa(i64, buffer);
 | |
|         PutReserve(*os_, static_cast<size_t>(end - buffer));
 | |
|         for (const char* p = buffer; p != end; ++p)
 | |
|             PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     bool WriteUint64(uint64_t u64) {
 | |
|         char buffer[20];
 | |
|         char* end = internal::u64toa(u64, buffer);
 | |
|         PutReserve(*os_, static_cast<size_t>(end - buffer));
 | |
|         for (char* p = buffer; p != end; ++p)
 | |
|             PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     bool WriteDouble(double d) {
 | |
|         if (internal::Double(d).IsNanOrInf()) {
 | |
|             if (!(writeFlags & kWriteNanAndInfFlag))
 | |
|                 return false;
 | |
|             if (internal::Double(d).IsNan()) {
 | |
|                 PutReserve(*os_, 3);
 | |
|                 PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N');
 | |
|                 return true;
 | |
|             }
 | |
|             if (internal::Double(d).Sign()) {
 | |
|                 PutReserve(*os_, 9);
 | |
|                 PutUnsafe(*os_, '-');
 | |
|             }
 | |
|             else
 | |
|                 PutReserve(*os_, 8);
 | |
|             PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f');
 | |
|             PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y');
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         char buffer[25];
 | |
|         char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
 | |
|         PutReserve(*os_, static_cast<size_t>(end - buffer));
 | |
|         for (char* p = buffer; p != end; ++p)
 | |
|             PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     bool WriteString(const Ch* str, SizeType length)  {
 | |
|         static const typename OutputStream::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
 | |
|         static const char escape[256] = {
 | |
| #define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 | |
|             //0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
 | |
|             'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00
 | |
|             'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10
 | |
|               0,   0, '"',   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // 20
 | |
|             Z16, Z16,                                                                       // 30~4F
 | |
|               0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,'\\',   0,   0,   0, // 50
 | |
|             Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16                                // 60~FF
 | |
| #undef Z16
 | |
|         };
 | |
| 
 | |
|         if (TargetEncoding::supportUnicode)
 | |
|             PutReserve(*os_, 2 + length * 6); // "\uxxxx..."
 | |
|         else
 | |
|             PutReserve(*os_, 2 + length * 12);  // "\uxxxx\uyyyy..."
 | |
| 
 | |
|         PutUnsafe(*os_, '\"');
 | |
|         GenericStringStream<SourceEncoding> is(str);
 | |
|         while (ScanWriteUnescapedString(is, length)) {
 | |
|             const Ch c = is.Peek();
 | |
|             if (!TargetEncoding::supportUnicode && static_cast<unsigned>(c) >= 0x80) {
 | |
|                 // Unicode escaping
 | |
|                 unsigned codepoint;
 | |
|                 if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint)))
 | |
|                     return false;
 | |
|                 PutUnsafe(*os_, '\\');
 | |
|                 PutUnsafe(*os_, 'u');
 | |
|                 if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) {
 | |
|                     PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]);
 | |
|                     PutUnsafe(*os_, hexDigits[(codepoint >>  8) & 15]);
 | |
|                     PutUnsafe(*os_, hexDigits[(codepoint >>  4) & 15]);
 | |
|                     PutUnsafe(*os_, hexDigits[(codepoint      ) & 15]);
 | |
|                 }
 | |
|                 else {
 | |
|                     RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF);
 | |
|                     // Surrogate pair
 | |
|                     unsigned s = codepoint - 0x010000;
 | |
|                     unsigned lead = (s >> 10) + 0xD800;
 | |
|                     unsigned trail = (s & 0x3FF) + 0xDC00;
 | |
|                     PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]);
 | |
|                     PutUnsafe(*os_, hexDigits[(lead >>  8) & 15]);
 | |
|                     PutUnsafe(*os_, hexDigits[(lead >>  4) & 15]);
 | |
|                     PutUnsafe(*os_, hexDigits[(lead      ) & 15]);
 | |
|                     PutUnsafe(*os_, '\\');
 | |
|                     PutUnsafe(*os_, 'u');
 | |
|                     PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]);
 | |
|                     PutUnsafe(*os_, hexDigits[(trail >>  8) & 15]);
 | |
|                     PutUnsafe(*os_, hexDigits[(trail >>  4) & 15]);
 | |
|                     PutUnsafe(*os_, hexDigits[(trail      ) & 15]);                    
 | |
|                 }
 | |
|             }
 | |
|             else if ((sizeof(Ch) == 1 || static_cast<unsigned>(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast<unsigned char>(c)]))  {
 | |
|                 is.Take();
 | |
|                 PutUnsafe(*os_, '\\');
 | |
|                 PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(escape[static_cast<unsigned char>(c)]));
 | |
|                 if (escape[static_cast<unsigned char>(c)] == 'u') {
 | |
|                     PutUnsafe(*os_, '0');
 | |
|                     PutUnsafe(*os_, '0');
 | |
|                     PutUnsafe(*os_, hexDigits[static_cast<unsigned char>(c) >> 4]);
 | |
|                     PutUnsafe(*os_, hexDigits[static_cast<unsigned char>(c) & 0xF]);
 | |
|                 }
 | |
|             }
 | |
|             else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? 
 | |
|                 Transcoder<SourceEncoding, TargetEncoding>::Validate(is, *os_) :
 | |
|                 Transcoder<SourceEncoding, TargetEncoding>::TranscodeUnsafe(is, *os_))))
 | |
|                 return false;
 | |
|         }
 | |
|         PutUnsafe(*os_, '\"');
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     bool ScanWriteUnescapedString(GenericStringStream<SourceEncoding>& is, size_t length) {
 | |
|         return RAPIDJSON_LIKELY(is.Tell() < length);
 | |
|     }
 | |
| 
 | |
|     bool WriteStartObject() { os_->Put('{'); return true; }
 | |
|     bool WriteEndObject()   { os_->Put('}'); return true; }
 | |
|     bool WriteStartArray()  { os_->Put('['); return true; }
 | |
|     bool WriteEndArray()    { os_->Put(']'); return true; }
 | |
| 
 | |
|     bool WriteRawValue(const Ch* json, size_t length) {
 | |
|         PutReserve(*os_, length);
 | |
|         for (size_t i = 0; i < length; i++) {
 | |
|             RAPIDJSON_ASSERT(json[i] != '\0');
 | |
|             PutUnsafe(*os_, json[i]);
 | |
|         }
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     void Prefix(Type type) {
 | |
|         (void)type;
 | |
|         if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root
 | |
|             Level* level = level_stack_.template Top<Level>();
 | |
|             if (level->valueCount > 0) {
 | |
|                 if (level->inArray) 
 | |
|                     os_->Put(','); // add comma if it is not the first element in array
 | |
|                 else  // in object
 | |
|                     os_->Put((level->valueCount % 2 == 0) ? ',' : ':');
 | |
|             }
 | |
|             if (!level->inArray && level->valueCount % 2 == 0)
 | |
|                 RAPIDJSON_ASSERT(type == kStringType);  // if it's in object, then even number should be a name
 | |
|             level->valueCount++;
 | |
|         }
 | |
|         else {
 | |
|             RAPIDJSON_ASSERT(!hasRoot_);    // Should only has one and only one root.
 | |
|             hasRoot_ = true;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Flush the value if it is the top level one.
 | |
|     bool EndValue(bool ret) {
 | |
|         if (RAPIDJSON_UNLIKELY(level_stack_.Empty()))   // end of json text
 | |
|             os_->Flush();
 | |
|         return ret;
 | |
|     }
 | |
| 
 | |
|     OutputStream* os_;
 | |
|     internal::Stack<StackAllocator> level_stack_;
 | |
|     int maxDecimalPlaces_;
 | |
|     bool hasRoot_;
 | |
| 
 | |
| private:
 | |
|     // Prohibit copy constructor & assignment operator.
 | |
|     Writer(const Writer&);
 | |
|     Writer& operator=(const Writer&);
 | |
| };
 | |
| 
 | |
| // Full specialization for StringStream to prevent memory copying
 | |
| 
 | |
| template<>
 | |
| inline bool Writer<StringBuffer>::WriteInt(int i) {
 | |
|     char *buffer = os_->Push(11);
 | |
|     const char* end = internal::i32toa(i, buffer);
 | |
|     os_->Pop(static_cast<size_t>(11 - (end - buffer)));
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| template<>
 | |
| inline bool Writer<StringBuffer>::WriteUint(unsigned u) {
 | |
|     char *buffer = os_->Push(10);
 | |
|     const char* end = internal::u32toa(u, buffer);
 | |
|     os_->Pop(static_cast<size_t>(10 - (end - buffer)));
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| template<>
 | |
| inline bool Writer<StringBuffer>::WriteInt64(int64_t i64) {
 | |
|     char *buffer = os_->Push(21);
 | |
|     const char* end = internal::i64toa(i64, buffer);
 | |
|     os_->Pop(static_cast<size_t>(21 - (end - buffer)));
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| template<>
 | |
| inline bool Writer<StringBuffer>::WriteUint64(uint64_t u) {
 | |
|     char *buffer = os_->Push(20);
 | |
|     const char* end = internal::u64toa(u, buffer);
 | |
|     os_->Pop(static_cast<size_t>(20 - (end - buffer)));
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| template<>
 | |
| inline bool Writer<StringBuffer>::WriteDouble(double d) {
 | |
|     if (internal::Double(d).IsNanOrInf()) {
 | |
|         // Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag).
 | |
|         if (!(kWriteDefaultFlags & kWriteNanAndInfFlag))
 | |
|             return false;
 | |
|         if (internal::Double(d).IsNan()) {
 | |
|             PutReserve(*os_, 3);
 | |
|             PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N');
 | |
|             return true;
 | |
|         }
 | |
|         if (internal::Double(d).Sign()) {
 | |
|             PutReserve(*os_, 9);
 | |
|             PutUnsafe(*os_, '-');
 | |
|         }
 | |
|         else
 | |
|             PutReserve(*os_, 8);
 | |
|         PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f');
 | |
|         PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y');
 | |
|         return true;
 | |
|     }
 | |
|     
 | |
|     char *buffer = os_->Push(25);
 | |
|     char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
 | |
|     os_->Pop(static_cast<size_t>(25 - (end - buffer)));
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| #if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42)
 | |
| template<>
 | |
| inline bool Writer<StringBuffer>::ScanWriteUnescapedString(StringStream& is, size_t length) {
 | |
|     if (length < 16)
 | |
|         return RAPIDJSON_LIKELY(is.Tell() < length);
 | |
| 
 | |
|     if (!RAPIDJSON_LIKELY(is.Tell() < length))
 | |
|         return false;
 | |
| 
 | |
|     const char* p = is.src_;
 | |
|     const char* end = is.head_ + length;
 | |
|     const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
 | |
|     const char* endAligned = reinterpret_cast<const char*>(reinterpret_cast<size_t>(end) & static_cast<size_t>(~15));
 | |
|     if (nextAligned > end)
 | |
|         return true;
 | |
| 
 | |
|     while (p != nextAligned)
 | |
|         if (*p < 0x20 || *p == '\"' || *p == '\\') {
 | |
|             is.src_ = p;
 | |
|             return RAPIDJSON_LIKELY(is.Tell() < length);
 | |
|         }
 | |
|         else
 | |
|             os_->PutUnsafe(*p++);
 | |
| 
 | |
|     // The rest of string using SIMD
 | |
|     static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' };
 | |
|     static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' };
 | |
|     static const char space[16]  = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 };
 | |
|     const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0]));
 | |
|     const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
 | |
|     const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
 | |
| 
 | |
|     for (; p != endAligned; p += 16) {
 | |
|         const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
 | |
|         const __m128i t1 = _mm_cmpeq_epi8(s, dq);
 | |
|         const __m128i t2 = _mm_cmpeq_epi8(s, bs);
 | |
|         const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19
 | |
|         const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3);
 | |
|         unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x));
 | |
|         if (RAPIDJSON_UNLIKELY(r != 0)) {   // some of characters is escaped
 | |
|             SizeType len;
 | |
| #ifdef _MSC_VER         // Find the index of first escaped
 | |
|             unsigned long offset;
 | |
|             _BitScanForward(&offset, r);
 | |
|             len = offset;
 | |
| #else
 | |
|             len = static_cast<SizeType>(__builtin_ffs(r) - 1);
 | |
| #endif
 | |
|             char* q = reinterpret_cast<char*>(os_->PushUnsafe(len));
 | |
|             for (size_t i = 0; i < len; i++)
 | |
|                 q[i] = p[i];
 | |
| 
 | |
|             p += len;
 | |
|             break;
 | |
|         }
 | |
|         _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s);
 | |
|     }
 | |
| 
 | |
|     is.src_ = p;
 | |
|     return RAPIDJSON_LIKELY(is.Tell() < length);
 | |
| }
 | |
| #endif // defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42)
 | |
| 
 | |
| RAPIDJSON_NAMESPACE_END
 | |
| 
 | |
| #ifdef _MSC_VER
 | |
| RAPIDJSON_DIAG_POP
 | |
| #endif
 | |
| 
 | |
| #ifdef __clang__
 | |
| RAPIDJSON_DIAG_POP
 | |
| #endif
 | |
| 
 | |
| #endif // RAPIDJSON_RAPIDJSON_H_
 |