diff --git a/include/rapidjson/encodedstream.h b/include/rapidjson/encodedstream.h index 37daaf6..efa0acf 100644 --- a/include/rapidjson/encodedstream.h +++ b/include/rapidjson/encodedstream.h @@ -5,13 +5,18 @@ namespace rapidjson { -//! Adapts an input byte stream with an specified encoding. -template +//! Input byte stream wrapper with a statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam InputByteStream Type of input byte stream. For example, FileReadStream. +*/ +template class EncodedInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); public: typedef typename Encoding::Ch Ch; - EncodedInputStream(InputStream& is) : is_(is) { + EncodedInputStream(InputByteStream& is) : is_(is) { current_ = Encoding::TakeBOM(is_); } @@ -26,17 +31,22 @@ public: size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } private: - InputStream& is_; + InputByteStream& is_; Ch current_; }; -//! Adapts an output byte stream with an specified encoding. -template +//! Output byte stream wrapper with statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam InputByteStream Type of input byte stream. For example, FileWriteStream. +*/ +template class EncodedOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); public: typedef typename Encoding::Ch Ch; - EncodedOutputStream(OutputStream& os, bool putBOM = true) : os_(os) { + EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { if (putBOM) Encoding::PutBOM(os_); } @@ -52,24 +62,36 @@ public: size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } private: - OutputStream& os_; + OutputByteStream& os_; }; -#define ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x -template +//! Input stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for reading. + \tparam InputByteStream type of input byte stream to be wrapped. +*/ +template class AutoUTFInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); public: typedef CharType Ch; - AutoUTFInputStream(InputStream& is, UTFType type = kUTF8) : is_(is), type_(type) { + //! Constructor. + /*! + \param is input stream to be wrapped. + \param type UTF encoding type if it is not detected from the stream. + */ + AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(is), type_(type), hasBOM_(false) { DetectType(is); - static const TakeFunc f[] = { ENCODINGS_FUNC(Take) }; + static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; takeFunc_ = f[type_]; current_ = takeFunc_(is_); } UTFType GetType() const { return type_; } + bool HasBOM() const { return hasBOM_; } Ch Peek() const { return current_; } Ch Take() { Ch c = current_; current_ = takeFunc_(is_); return c; } @@ -83,7 +105,7 @@ public: private: // Detect encoding type with BOM or RFC 4627 - void DetectType(InputStream& is) { + void DetectType(InputByteStream& is) { // BOM (Byte Order Mark): // 00 00 FE FF UTF-32BE // FF FE 00 00 UTF-32LE @@ -96,11 +118,12 @@ private: return; unsigned bom = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24); - if (bom == 0xFFFE0000) { type_ = kUTF32BE; is.Take(); is.Take(); is.Take(); is.Take(); goto sizecheck; } - else if (bom == 0x0000FEFF) { type_ = kUTF32LE; is.Take(); is.Take(); is.Take(); is.Take(); goto sizecheck; } - else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; is.Take(); is.Take(); goto sizecheck; } - else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; is.Take(); is.Take(); goto sizecheck; } - else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; is.Take(); is.Take(); is.Take(); goto sizecheck; } + hasBOM_ = false; + if (bom == 0xFFFE0000) { type_ = kUTF32BE; is.Take(); is.Take(); is.Take(); is.Take(); hasBOM_ = true; } + else if (bom == 0x0000FEFF) { type_ = kUTF32LE; is.Take(); is.Take(); is.Take(); is.Take(); hasBOM_ = true; } + else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; is.Take(); is.Take(); hasBOM_ = true; } + else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; is.Take(); is.Take(); hasBOM_ = true; } + else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; is.Take(); is.Take(); is.Take(); hasBOM_ = true; } // RFC 4627: Section 3 // "Since the first two characters of a JSON text will always be ASCII @@ -113,16 +136,17 @@ private: // xx 00 xx 00 UTF-16LE // xx xx xx xx UTF-8 - unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); - switch (pattern) { - case 0x08: type_ = kUTF32BE; break; - case 0x0A: type_ = kUTF16BE; break; - case 0x01: type_ = kUTF32LE; break; - case 0x05: type_ = kUTF16LE; break; - case 0x0F: type_ = kUTF8; break; + if (!hasBOM_) { + unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); + switch (pattern) { + case 0x08: type_ = kUTF32BE; break; + case 0x0A: type_ = kUTF16BE; break; + case 0x01: type_ = kUTF32LE; break; + case 0x05: type_ = kUTF16LE; break; + case 0x0F: type_ = kUTF8; break; + } } - sizecheck: // RUntime check whether the size of character type is sufficient. It only perform checks with assertion. switch (type_) { case kUTF16LE: @@ -136,19 +160,32 @@ private: } } - typedef Ch (*TakeFunc)(InputStream& is); - InputStream& is_; + typedef Ch (*TakeFunc)(InputByteStream& is); + InputByteStream& is_; UTFType type_; Ch current_; TakeFunc takeFunc_; + bool hasBOM_; }; -template +//! Output stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for writing. + \tparam InputByteStream type of output byte stream to be wrapped. +*/ +template class AutoUTFOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); public: typedef CharType Ch; - AutoUTFOutputStream(OutputStream& os, UTFType type, bool putBOM) : os_(os), type_(type) { + //! Constructor. + /*! + \param os output stream to be wrapped. + \param type UTF encoding type. + \param putBOM Whether to write BOM at the beginning of the stream. + */ + AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(os), type_(type) { // RUntime check whether the size of character type is sufficient. It only perform checks with assertion. switch (type_) { case kUTF16LE: @@ -161,7 +198,7 @@ public: break; } - static const PutFunc f[] = { ENCODINGS_FUNC(Put) }; + static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; putFunc_ = f[type_]; if (putBOM) @@ -170,10 +207,7 @@ public: UTFType GetType() const { return type_; } - void Put(Ch c) { - putFunc_(os_, c); - } - + void Put(Ch c) { putFunc_(os_, c); } void Flush() { os_.Flush(); } // Not implemented @@ -185,19 +219,19 @@ public: private: void PutBOM() { - typedef void (*PutBOMFunc)(OutputStream&); - static const PutBOMFunc f[] = { ENCODINGS_FUNC(PutBOM) }; + typedef void (*PutBOMFunc)(OutputByteStream&); + static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; f[type_](os_); } - typedef void (*PutFunc)(OutputStream&, Ch); + typedef void (*PutFunc)(OutputByteStream&, Ch); - OutputStream& os_; + OutputByteStream& os_; UTFType type_; PutFunc putFunc_; }; -#undef ENCODINGS_FUNC +#undef RAPIDJSON_ENCODINGS_FUNC } // namespace rapidjson diff --git a/include/rapidjson/encodings.h b/include/rapidjson/encodings.h index 4e745ee..80cca97 100644 --- a/include/rapidjson/encodings.h +++ b/include/rapidjson/encodings.h @@ -13,13 +13,20 @@ namespace rapidjson { \code concept Encoding { - typename Ch; //! Type of character. + typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition. - //! \brief Encode a Unicode codepoint to a stream. + //! \brief Encode a Unicode codepoint to an output stream. //! \param os Output stream. //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively. template - static void Encode(OutputStream& os, unsigned codepoint) { + static void Encode(OutputStream& os, unsigned codepoint); + + //! \brief Decode a Unicode codepoint from an input stream. + //! \param is Input stream. + //! \param codepoint Output of the unicode codepoint. + //! \return true if a valid codepoint can be decoded from the stream. + template + static bool Decode(InputStream& is, unsigned* codepoint); //! \brief Validate one Unicode codepoint from an encoded stream. //! \param is Input stream to obtain codepoint. @@ -27,7 +34,25 @@ concept Encoding { //! \return true if it is valid. //! \note This function just validating and copying the codepoint without actually decode it. template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static bool Validate(InputStream& is, OutputStream& os); + + // The following functions are deal with byte streams. + + //! Take a character from input byte stream, skip BOM if exist. + template + static CharType TakeBOM(InputByteStream& is); + + //! Take a character from input byte stream. + template + static Ch Take(InputByteStream& is); + + //! Put BOM to output byte stream. + template + static void PutBOM(OutputByteStream& os); + + //! Put a character to output byte stream. + template + static void Put(OutputByteStream& os, Ch c); }; \endcode */ @@ -38,7 +63,7 @@ concept Encoding { //! UTF-8 encoding. /*! http://en.wikipedia.org/wiki/UTF-8 http://tools.ietf.org/html/rfc3629 - \tparam CharType Type for storing 8-bit UTF-8 data. Default is char. + \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char. \implements Encoding */ template @@ -68,7 +93,7 @@ struct UTF8 { } template - RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) { + static bool Decode(InputStream& is, unsigned* codepoint) { #define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | ((unsigned char)c & 0x3Fu) #define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0) #define TAIL() COPY(); TRANS(0x70) @@ -97,7 +122,7 @@ struct UTF8 { } template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static bool Validate(InputStream& is, OutputStream& os) { #define COPY() os.Put(c = is.Take()) #define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0) #define TAIL() COPY(); TRANS(0x70) @@ -122,7 +147,7 @@ struct UTF8 { #undef TAIL } - RAPIDJSON_FORCEINLINE static unsigned char GetRange(unsigned char c) { + static unsigned char GetRange(unsigned char c) { // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types. static const unsigned char type[] = { @@ -140,8 +165,9 @@ struct UTF8 { return type[c]; } - template - static CharType TakeBOM(InputStream& is) { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); Ch c = Take(is); if ((unsigned char)c != 0xEFu) return c; c = is.Take(); @@ -152,18 +178,21 @@ struct UTF8 { return c; } - template - RAPIDJSON_FORCEINLINE static Ch Take(InputStream& is) { + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); return is.Take(); } - template - static void PutBOM(OutputStream& os) { + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(0xEFu); os.Put(0xBBu); os.Put(0xBFu); } - template - static void Put(OutputStream& os, Ch c) { + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(c); } }; @@ -176,13 +205,18 @@ struct UTF8 { http://tools.ietf.org/html/rfc2781 \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead. \implements Encoding + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF16LE and UTF16BE, which handle endianness. */ template struct UTF16 { typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); template static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); if (codepoint <= 0xFFFF) { RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair os.Put(codepoint); @@ -196,7 +230,8 @@ struct UTF16 { } template - RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) { + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); Ch c = is.Take(); if (c < 0xD800 || c > 0xDFFF) { *codepoint = c; @@ -213,7 +248,9 @@ struct UTF16 { } template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); Ch c; os.Put(c = is.Take()); if (c < 0xD800 || c > 0xDFFF) @@ -226,55 +263,65 @@ struct UTF16 { } }; +//! UTF-16 little endian encoding. template struct UTF16LE : UTF16 { - template - static CharType TakeBOM(InputStream& is) { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); return (unsigned short)c == 0xFEFFu ? Take(is) : c; } - template - RAPIDJSON_FORCEINLINE static CharType Take(InputStream& is) { + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = (unsigned char)is.Take(); c |= (unsigned char)is.Take() << 8; return c; } - template - static void PutBOM(OutputStream& os) { + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(0xFFu); os.Put(0xFEu); } - template - static void Put(OutputStream& os, CharType c) { + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(c & 0xFFu); os.Put((c >> 8) & 0xFFu); } }; +//! UTF-16 big endian encoding. template struct UTF16BE : UTF16 { - template - static CharType TakeBOM(InputStream& is) { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); return (unsigned short)c == 0xFEFFu ? Take(is) : c; } - template - RAPIDJSON_FORCEINLINE static CharType Take(InputStream& is) { + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = (unsigned char)is.Take() << 8; c |= (unsigned char)is.Take(); return c; } - template - static void PutBOM(OutputStream& os) { + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(0xFEu); os.Put(0xFFu); } - template - static void Put(OutputStream& os, CharType c) { + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put((c >> 8) & 0xFFu); os.Put(c & 0xFFu); } @@ -287,42 +334,52 @@ struct UTF16BE : UTF16 { /*! http://en.wikipedia.org/wiki/UTF-32 \tparam Ch Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead. \implements Encoding + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF32LE and UTF32BE, which handle endianness. */ template struct UTF32 { typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); template static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); os.Put(codepoint); } template - RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) { + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); Ch c = is.Take(); *codepoint = c; return c <= 0x10FFFF; } template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); Ch c; os.Put(c = is.Take()); return c <= 0x10FFFF; } }; +//! UTF-32 little endian enocoding. template struct UTF32LE : UTF32 { - template - static CharType TakeBOM(InputStream& is) { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); return (unsigned)c == 0x0000FEFFu ? Take(is) : c; } - template - RAPIDJSON_FORCEINLINE static CharType Take(InputStream& is) { + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = (unsigned char)is.Take(); c |= (unsigned char)is.Take() << 8; c |= (unsigned char)is.Take() << 16; @@ -330,13 +387,15 @@ struct UTF32LE : UTF32 { return c; } - template - static void PutBOM(OutputStream& os) { + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(0xFFu); os.Put(0xFEu); os.Put(0x00u); os.Put(0x00u); } - template - static void Put(OutputStream& os, CharType c) { + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(c & 0xFFu); os.Put((c >> 8) & 0xFFu); os.Put((c >> 16) & 0xFFu); @@ -344,16 +403,19 @@ struct UTF32LE : UTF32 { } }; +//! UTF-32 big endian encoding. template struct UTF32BE : UTF32 { - template - static CharType TakeBOM(InputStream& is) { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = Take(is); return (unsigned)c == 0x0000FEFFu ? Take(is) : c; } - template - RAPIDJSON_FORCEINLINE static CharType Take(InputStream& is) { + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); CharType c = (unsigned char)is.Take() << 24; c |= (unsigned char)is.Take() << 16; c |= (unsigned char)is.Take() << 8; @@ -361,13 +423,15 @@ struct UTF32BE : UTF32 { return c; } - template - static void PutBOM(OutputStream& os) { + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put(0x00u); os.Put(0x00u); os.Put(0xFEu); os.Put(0xFFu); } - template - static void Put(OutputStream& os, CharType c) { + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); os.Put((c >> 24) & 0xFFu); os.Put((c >> 16) & 0xFFu); os.Put((c >> 8) & 0xFFu); @@ -378,52 +442,57 @@ struct UTF32BE : UTF32 { /////////////////////////////////////////////////////////////////////////////// // AutoUTF +//! Runtime-specified UTF encoding type of a stream. enum UTFType { - kUTF8 = 0, - kUTF16LE = 1, - kUTF16BE = 2, - kUTF32LE = 3, - kUTF32BE = 4, + kUTF8 = 0, //!< UTF-8. + kUTF16LE = 1, //!< UTF-16 little endian. + kUTF16BE = 2, //!< UTF-16 big endian. + kUTF32LE = 3, //!< UTF-32 little endian. + kUTF32BE = 4, //!< UTF-32 big endian. }; -// Dynamically select encoding according to BOM or user setting. +//! Dynamically select encoding according to stream's runtime-specified UTF encoding type. +/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType(). +*/ template struct AutoUTF { typedef CharType Ch; -#define ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x template RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) { typedef void (*EncodeFunc)(OutputStream&, unsigned); - static const EncodeFunc f[] = { ENCODINGS_FUNC(Encode) }; + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; (*f[os.GetType()])(os, codepoint); } template RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) { typedef bool (*DecodeFunc)(InputStream&, unsigned*); - static const DecodeFunc f[] = { ENCODINGS_FUNC(Decode) }; + static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; return (*f[is.GetType()])(is, codepoint); } template RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { - typedef bool (*ValidateFunc)(InputStream&, unsigned*); - static const ValidateFunc f[] = { ENCODINGS_FUNC(Validate) }; + typedef bool (*ValidateFunc)(InputStream&, OutputStream&); + static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; return (*f[is.GetType()])(is, os); } -#undef ENCODINGS_FUNC +#undef RAPIDJSON_ENCODINGS_FUNC }; /////////////////////////////////////////////////////////////////////////////// // Transcoder +//! Encoding conversion. template struct Transcoder { + //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. template - static bool Transcode(InputStream& is, OutputStream& os) { + RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { unsigned codepoint; if (!SourceEncoding::Decode(is, &codepoint)) return false; @@ -431,9 +500,10 @@ struct Transcoder { return true; } + //! Validate one Unicode codepoint from an encoded stream. template - static bool Validate(InputStream& is, OutputStream& os) { - return Transcode(is, os); + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + return Transcode(is, os); // Since source/target encoding is different, must transcode. } }; @@ -441,14 +511,14 @@ struct Transcoder { template struct Transcoder { template - static bool Transcode(InputStream& is, OutputStream& os) { - os.Put(is.Take()); + RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. return true; } template - static bool Validate(InputStream& is, OutputStream& os) { - return Encoding::Validate(is, os); + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + return Encoding::Validate(is, os); // source/target encoding are the same } }; diff --git a/include/rapidjson/filereadstream.h b/include/rapidjson/filereadstream.h index 41e54dc..d1fc855 100644 --- a/include/rapidjson/filereadstream.h +++ b/include/rapidjson/filereadstream.h @@ -6,32 +6,38 @@ namespace rapidjson { -//! Wrapper of C file stream for input using fread(). +//! File byte stream for input using fread(). /*! \implements Stream */ class FileReadStream { public: - typedef char Ch; //!< Character type. Only support char. + typedef char Ch; //!< Character type (byte). + //! Constructor. + /*! + \param fp File pointer opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ FileReadStream(FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { RAPIDJSON_ASSERT(fp_ != 0); RAPIDJSON_ASSERT(bufferSize >= 4); Read(); } - char Peek() const { return *current_; } - char Take() { char c = *current_; Read(); return c; } + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } size_t Tell() const { return count_ + (current_ - buffer_); } // Not implemented - void Put(char c) { RAPIDJSON_ASSERT(false); } + void Put(Ch c) { RAPIDJSON_ASSERT(false); } void Flush() { RAPIDJSON_ASSERT(false); } - char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } // For encoding detection only. - const char* Peek4() const { + const Ch* Peek4() const { return (current_ + 4 <= bufferLast_) ? current_ : 0; } @@ -39,12 +45,7 @@ private: void Read() { if (current_ < bufferLast_) ++current_; - else - FillBuffer(); - } - - void FillBuffer() { - if (!eof_) { + else if (!eof_) { count_ += readCount_; readCount_ = fread(buffer_, 1, bufferSize_, fp_); bufferLast_ = buffer_ + readCount_ - 1; @@ -59,10 +60,10 @@ private: } FILE* fp_; - char *buffer_; + Ch *buffer_; size_t bufferSize_; - char *bufferLast_; - char *current_; + Ch *bufferLast_; + Ch *current_; size_t readCount_; size_t count_; //!< Number of characters read bool eof_; diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h index 6aba86b..3f3e4cd 100644 --- a/include/rapidjson/prettywriter.h +++ b/include/rapidjson/prettywriter.h @@ -7,23 +7,23 @@ namespace rapidjson { //! Writer with indentation and spacing. /*! - \tparam Stream Type of ouptut stream. + \tparam OutputStream Type of ouptut os. \tparam Encoding Encoding of both source strings and output. \tparam Allocator Type of allocator for allocating memory of stack. */ -template, typename TargetEncoding = UTF8<>, typename Allocator = MemoryPoolAllocator<> > -class PrettyWriter : public Writer { +template, typename TargetEncoding = UTF8<>, typename Allocator = MemoryPoolAllocator<> > +class PrettyWriter : public Writer { public: - typedef Writer Base; + typedef Writer Base; typedef typename Base::Ch Ch; //! Constructor - /*! \param stream Output stream. + /*! \param os Output os. \param allocator User supplied allocator. If it is null, it will create a private one. \param levelDepth Initial capacity of */ - PrettyWriter(Stream& stream, Allocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : - Base(stream, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} + PrettyWriter(OutputStream& os, Allocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} //! Set custom indentation. /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\t', '\n', '\r'). @@ -67,12 +67,12 @@ public: bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; if (!empty) { - Base::stream_.Put('\n'); + Base::os_.Put('\n'); WriteIndent(); } Base::WriteEndObject(); if (Base::level_stack_.Empty()) // end of json text - Base::stream_.Flush(); + Base::os_.Flush(); return *this; } @@ -89,12 +89,12 @@ public: bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; if (!empty) { - Base::stream_.Put('\n'); + Base::os_.Put('\n'); WriteIndent(); } Base::WriteEndArray(); if (Base::level_stack_.Empty()) // end of json text - Base::stream_.Flush(); + Base::os_.Flush(); return *this; } @@ -110,26 +110,26 @@ protected: if (level->inArray) { if (level->valueCount > 0) { - Base::stream_.Put(','); // add comma if it is not the first element in array - Base::stream_.Put('\n'); + Base::os_.Put(','); // add comma if it is not the first element in array + Base::os_.Put('\n'); } else - Base::stream_.Put('\n'); + Base::os_.Put('\n'); WriteIndent(); } else { // in object if (level->valueCount > 0) { if (level->valueCount % 2 == 0) { - Base::stream_.Put(','); - Base::stream_.Put('\n'); + Base::os_.Put(','); + Base::os_.Put('\n'); } else { - Base::stream_.Put(':'); - Base::stream_.Put(' '); + Base::os_.Put(':'); + Base::os_.Put(' '); } } else - Base::stream_.Put('\n'); + Base::os_.Put('\n'); if (level->valueCount % 2 == 0) WriteIndent(); @@ -144,7 +144,7 @@ protected: void WriteIndent() { size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; - PutN(Base::stream_, indentChar_, count); + PutN(Base::os_, indentChar_, count); } Ch indentChar_; diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h index 5ea713a..3ca1027 100644 --- a/include/rapidjson/rapidjson.h +++ b/include/rapidjson/rapidjson.h @@ -82,6 +82,29 @@ typedef unsigned SizeType; #define RAPIDJSON_ASSERT(x) assert(x) #endif // RAPIDJSON_ASSERT +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_STATIC_ASSERT + +// Adopt from boost +#ifndef RAPIDJSON_STATIC_ASSERT +namespace rapidjson { +template struct STATIC_ASSERTION_FAILURE; +template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; +template struct StaticAssertTest {}; +} // namespace rapidjson + +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y + +#define RAPIDJSON_STATIC_ASSERT(x) typedef ::rapidjson::StaticAssertTest<\ + sizeof(::rapidjson::STATIC_ASSERTION_FAILURE)>\ + RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Allocators and Encodings + #include "allocators.h" #include "encodings.h" diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index ab2ceac..955886e 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -89,12 +89,12 @@ struct BaseReaderHandler { /*! \param stream A input stream for skipping white spaces. \note This function has SSE2/SSE4.2 specialization. */ -template -void SkipWhitespace(Stream& stream) { - Stream s = stream; // Use a local copy for optimization +template +void SkipWhitespace(InputStream& is) { + InputStream s = is; // Use a local copy for optimization while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t') s.Take(); - stream = s; + is = s; } #ifdef RAPIDJSON_SSE42 @@ -162,13 +162,13 @@ inline const char *SkipWhitespace_SIMD(const char* p) { #ifdef RAPIDJSON_SIMD //! Template function specialization for InsituStringStream -template<> inline void SkipWhitespace(InsituStringStream& stream) { - stream.src_ = const_cast(SkipWhitespace_SIMD(stream.src_)); +template<> inline void SkipWhitespace(InsituStringStream& is) { + is.src_ = const_cast(SkipWhitespace_SIMD(is.src_)); } //! Template function specialization for StringStream -template<> inline void SkipWhitespace(StringStream& stream) { - stream.src_ = SkipWhitespace_SIMD(stream.src_); +template<> inline void SkipWhitespace(StringStream& is) { + is.src_ = SkipWhitespace_SIMD(is.src_); } #endif // RAPIDJSON_SIMD @@ -187,7 +187,8 @@ template<> inline void SkipWhitespace(StringStream& stream) { A GenericReader object can be reused for parsing multiple JSON text. - \tparam Encoding Encoding of both the stream and the parse output. + \tparam SourceEncoding Encoding of the input stream. + \tparam TargetEncoding Encoding of the parse output. \tparam Allocator Allocator type for stack. */ template > @@ -203,14 +204,14 @@ public: //! Parse JSON text. /*! \tparam parseFlags Combination of ParseFlag. - \tparam Stream Type of input stream. + \tparam InputStream Type of input stream. \tparam Handler Type of handler which must implement Handler concept. \param stream Input stream to be parsed. \param handler The handler to receive events. \return Whether the parsing is successful. */ - template - bool Parse(Stream& stream, Handler& handler) { + template + bool Parse(InputStream& is, Handler& handler) { parseError_ = 0; errorOffset_ = 0; @@ -219,20 +220,20 @@ public: return false; } - SkipWhitespace(stream); + SkipWhitespace(is); - if (stream.Peek() == '\0') - RAPIDJSON_PARSE_ERROR("Text only contains white space(s)", stream.Tell()); + if (is.Peek() == '\0') + RAPIDJSON_PARSE_ERROR("Text only contains white space(s)", is.Tell()); else { - switch (stream.Peek()) { - case '{': ParseObject(stream, handler); break; - case '[': ParseArray(stream, handler); break; - default: RAPIDJSON_PARSE_ERROR("Expect either an object or array at root", stream.Tell()); + switch (is.Peek()) { + case '{': ParseObject(is, handler); break; + case '[': ParseArray(is, handler); break; + default: RAPIDJSON_PARSE_ERROR("Expect either an object or array at root", is.Tell()); } - SkipWhitespace(stream); + SkipWhitespace(is); - if (stream.Peek() != '\0') - RAPIDJSON_PARSE_ERROR("Nothing should follow the root object or array.", stream.Tell()); + if (is.Peek() != '\0') + RAPIDJSON_PARSE_ERROR("Nothing should follow the root object or array.", is.Tell()); } return true; @@ -244,108 +245,108 @@ public: private: // Parse object: { string : value, ... } - template - void ParseObject(Stream& stream, Handler& handler) { - RAPIDJSON_ASSERT(stream.Peek() == '{'); - stream.Take(); // Skip '{' + template + void ParseObject(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '{'); + is.Take(); // Skip '{' handler.StartObject(); - SkipWhitespace(stream); + SkipWhitespace(is); - if (stream.Peek() == '}') { - stream.Take(); + if (is.Peek() == '}') { + is.Take(); handler.EndObject(0); // empty object return; } for (SizeType memberCount = 0;;) { - if (stream.Peek() != '"') - RAPIDJSON_PARSE_ERROR("Name of an object member must be a string", stream.Tell()); + if (is.Peek() != '"') + RAPIDJSON_PARSE_ERROR("Name of an object member must be a string", is.Tell()); - ParseString(stream, handler); - SkipWhitespace(stream); + ParseString(is, handler); + SkipWhitespace(is); - if (stream.Take() != ':') - RAPIDJSON_PARSE_ERROR("There must be a colon after the name of object member", stream.Tell()); + if (is.Take() != ':') + RAPIDJSON_PARSE_ERROR("There must be a colon after the name of object member", is.Tell()); - SkipWhitespace(stream); + SkipWhitespace(is); - ParseValue(stream, handler); - SkipWhitespace(stream); + ParseValue(is, handler); + SkipWhitespace(is); ++memberCount; - switch(stream.Take()) { - case ',': SkipWhitespace(stream); break; + switch(is.Take()) { + case ',': SkipWhitespace(is); break; case '}': handler.EndObject(memberCount); return; - default: RAPIDJSON_PARSE_ERROR("Must be a comma or '}' after an object member", stream.Tell()); + default: RAPIDJSON_PARSE_ERROR("Must be a comma or '}' after an object member", is.Tell()); } } } // Parse array: [ value, ... ] - template - void ParseArray(Stream& stream, Handler& handler) { - RAPIDJSON_ASSERT(stream.Peek() == '['); - stream.Take(); // Skip '[' + template + void ParseArray(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '['); + is.Take(); // Skip '[' handler.StartArray(); - SkipWhitespace(stream); + SkipWhitespace(is); - if (stream.Peek() == ']') { - stream.Take(); + if (is.Peek() == ']') { + is.Take(); handler.EndArray(0); // empty array return; } for (SizeType elementCount = 0;;) { - ParseValue(stream, handler); + ParseValue(is, handler); ++elementCount; - SkipWhitespace(stream); + SkipWhitespace(is); - switch (stream.Take()) { - case ',': SkipWhitespace(stream); break; + switch (is.Take()) { + case ',': SkipWhitespace(is); break; case ']': handler.EndArray(elementCount); return; - default: RAPIDJSON_PARSE_ERROR("Must be a comma or ']' after an array element.", stream.Tell()); + default: RAPIDJSON_PARSE_ERROR("Must be a comma or ']' after an array element.", is.Tell()); } } } - template - void ParseNull(Stream& stream, Handler& handler) { - RAPIDJSON_ASSERT(stream.Peek() == 'n'); - stream.Take(); + template + void ParseNull(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'n'); + is.Take(); - if (stream.Take() == 'u' && stream.Take() == 'l' && stream.Take() == 'l') + if (is.Take() == 'u' && is.Take() == 'l' && is.Take() == 'l') handler.Null(); else - RAPIDJSON_PARSE_ERROR("Invalid value", stream.Tell() - 1); + RAPIDJSON_PARSE_ERROR("Invalid value", is.Tell() - 1); } - template - void ParseTrue(Stream& stream, Handler& handler) { - RAPIDJSON_ASSERT(stream.Peek() == 't'); - stream.Take(); + template + void ParseTrue(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 't'); + is.Take(); - if (stream.Take() == 'r' && stream.Take() == 'u' && stream.Take() == 'e') + if (is.Take() == 'r' && is.Take() == 'u' && is.Take() == 'e') handler.Bool(true); else - RAPIDJSON_PARSE_ERROR("Invalid value", stream.Tell()); + RAPIDJSON_PARSE_ERROR("Invalid value", is.Tell()); } - template - void ParseFalse(Stream& stream, Handler& handler) { - RAPIDJSON_ASSERT(stream.Peek() == 'f'); - stream.Take(); + template + void ParseFalse(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'f'); + is.Take(); - if (stream.Take() == 'a' && stream.Take() == 'l' && stream.Take() == 's' && stream.Take() == 'e') + if (is.Take() == 'a' && is.Take() == 'l' && is.Take() == 's' && is.Take() == 'e') handler.Bool(false); else - RAPIDJSON_PARSE_ERROR("Invalid value", stream.Tell() - 1); + RAPIDJSON_PARSE_ERROR("Invalid value", is.Tell() - 1); } // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). - template - unsigned ParseHex4(Stream& stream) { - Stream s = stream; // Use a local copy for optimization + template + unsigned ParseHex4(InputStream& is) { + InputStream s = is; // Use a local copy for optimization unsigned codepoint = 0; for (int i = 0; i < 4; i++) { Ch c = s.Take(); @@ -360,14 +361,16 @@ private: else RAPIDJSON_PARSE_ERROR("Incorrect hex digit after \\u escape", s.Tell() - 1); } - stream = s; // Restore stream + is = s; // Restore is return codepoint; } struct StackStream { + typedef typename TargetEncoding::Ch Ch; + StackStream(internal::Stack& stack) : stack_(stack), length_(0) {} - void Put(typename TargetEncoding::Ch c) { - *stack_.template Push() = c; + void Put(Ch c) { + *stack_.template Push() = c; ++length_; } internal::Stack& stack_; @@ -375,28 +378,28 @@ private: }; // Parse string and generate String event. Different code paths for kParseInsituFlag. - template - void ParseString(Stream& stream, Handler& handler) { - Stream s = stream; // Local copy for optimization + template + void ParseString(InputStream& is, Handler& handler) { + InputStream s = is; // Local copy for optimization if (parseFlags & kParseInsituFlag) { Ch *head = s.PutBegin(); - ParseStringToStream(s, s); + ParseStringToStream(s, s); size_t length = s.PutEnd(head) - 1; RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); handler.String((typename TargetEncoding::Ch*)head, SizeType(length), false); } else { StackStream stackStream(stack_); - ParseStringToStream(s, stackStream); + ParseStringToStream(s, stackStream); handler.String(stack_.template Pop(stackStream.length_), stackStream.length_ - 1, true); } - stream = s; // Restore stream + is = s; // Restore is } - // Parse string to an output stream + // Parse string to an output is // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. - template - RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& input, OutputStream& output) { + template + RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) { #define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 static const char escape[256] = { Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', @@ -407,53 +410,53 @@ private: }; #undef Z16 - RAPIDJSON_ASSERT(input.Peek() == '\"'); - input.Take(); // Skip '\"' + RAPIDJSON_ASSERT(is.Peek() == '\"'); + is.Take(); // Skip '\"' for (;;) { - Ch c = input.Peek(); + Ch c = is.Peek(); if (c == '\\') { // Escape - input.Take(); - Ch e = input.Take(); + is.Take(); + Ch e = is.Take(); if ((sizeof(Ch) == 1 || e < 256) && escape[(unsigned char)e]) - output.Put(escape[(unsigned char)e]); + os.Put(escape[(unsigned char)e]); else if (e == 'u') { // Unicode - unsigned codepoint = ParseHex4(input); + unsigned codepoint = ParseHex4(is); if (codepoint >= 0xD800 && codepoint <= 0xDBFF) { // Handle UTF-16 surrogate pair - if (input.Take() != '\\' || input.Take() != 'u') - RAPIDJSON_PARSE_ERROR("Missing the second \\u in surrogate pair", input.Tell() - 2); - unsigned codepoint2 = ParseHex4(input); + if (is.Take() != '\\' || is.Take() != 'u') + RAPIDJSON_PARSE_ERROR("Missing the second \\u in surrogate pair", is.Tell() - 2); + unsigned codepoint2 = ParseHex4(is); if (codepoint2 < 0xDC00 || codepoint2 > 0xDFFF) - RAPIDJSON_PARSE_ERROR("The second \\u in surrogate pair is invalid", input.Tell() - 2); + RAPIDJSON_PARSE_ERROR("The second \\u in surrogate pair is invalid", is.Tell() - 2); codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; } - TargetEncoding::Encode(output, codepoint); + TEncoding::Encode(os, codepoint); } else - RAPIDJSON_PARSE_ERROR("Unknown escape character", input.Tell() - 1); + RAPIDJSON_PARSE_ERROR("Unknown escape character", is.Tell() - 1); } else if (c == '"') { // Closing double quote - input.Take(); - output.Put('\0'); // null-terminate the string + is.Take(); + os.Put('\0'); // null-terminate the string return; } else if (c == '\0') - RAPIDJSON_PARSE_ERROR("lacks ending quotation before the end of string", input.Tell() - 1); + RAPIDJSON_PARSE_ERROR("lacks ending quotation before the end of string", is.Tell() - 1); else if ((unsigned)c < 0x20) // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF - RAPIDJSON_PARSE_ERROR("Incorrect unescaped character in string", input.Tell() - 1); + RAPIDJSON_PARSE_ERROR("Incorrect unescaped character in string", is.Tell() - 1); else { if (parseFlags & kParseValidateEncodingFlag ? - !Transcoder::Validate(input, output) : - !Transcoder::Transcode(input, output)) - RAPIDJSON_PARSE_ERROR("Invalid encoding", input.Tell()); + !Transcoder::Validate(is, os) : + !Transcoder::Transcode(is, os)) + RAPIDJSON_PARSE_ERROR("Invalid encoding", is.Tell()); } } } - template - void ParseNumber(Stream& stream, Handler& handler) { - Stream s = stream; // Local copy for optimization + template + void ParseNumber(InputStream& is, Handler& handler) { + InputStream s = is; // Local copy for optimization // Parse minus bool minus = false; if (s.Peek() == '-') { @@ -493,7 +496,7 @@ private: } } else - RAPIDJSON_PARSE_ERROR("Expect a value here.", stream.Tell()); + RAPIDJSON_PARSE_ERROR("Expect a value here.", is.Tell()); // Parse 64bit int uint64_t i64; @@ -526,7 +529,7 @@ private: d = (double)i64; while (s.Peek() >= '0' && s.Peek() <= '9') { if (d >= 1E307) - RAPIDJSON_PARSE_ERROR("Number too big to store in double", stream.Tell()); + RAPIDJSON_PARSE_ERROR("Number too big to store in double", is.Tell()); d = d * 10 + (s.Take() - '0'); } } @@ -545,7 +548,7 @@ private: --expFrac; } else - RAPIDJSON_PARSE_ERROR("At least one digit in fraction part", stream.Tell()); + RAPIDJSON_PARSE_ERROR("At least one digit in fraction part", is.Tell()); while (s.Peek() >= '0' && s.Peek() <= '9') { if (expFrac > -16) { @@ -578,7 +581,7 @@ private: while (s.Peek() >= '0' && s.Peek() <= '9') { exp = exp * 10 + (s.Take() - '0'); if (exp > 308) - RAPIDJSON_PARSE_ERROR("Number too big to store in double", stream.Tell()); + RAPIDJSON_PARSE_ERROR("Number too big to store in double", is.Tell()); } } else @@ -608,20 +611,20 @@ private: } } - stream = s; // restore stream + is = s; // restore is } // Parse any JSON value - template - void ParseValue(Stream& stream, Handler& handler) { - switch (stream.Peek()) { - case 'n': ParseNull (stream, handler); break; - case 't': ParseTrue (stream, handler); break; - case 'f': ParseFalse (stream, handler); break; - case '"': ParseString(stream, handler); break; - case '{': ParseObject(stream, handler); break; - case '[': ParseArray (stream, handler); break; - default : ParseNumber(stream, handler); + template + void ParseValue(InputStream& is, Handler& handler) { + switch (is.Peek()) { + case 'n': ParseNull (is, handler); break; + case 't': ParseTrue (is, handler); break; + case 'f': ParseFalse (is, handler); break; + case '"': ParseString(is, handler); break; + case '{': ParseObject(is, handler); break; + case '[': ParseArray (is, handler); break; + default : ParseNumber(is, handler); } } diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index 7ff9944..516dc33 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -11,7 +11,7 @@ namespace rapidjson { //! JSON writer /*! Writer implements the concept Handler. - It generates JSON text by events to an output stream. + It generates JSON text by events to an output os. User may programmatically calls the functions of a writer to generate JSON text. @@ -19,17 +19,18 @@ namespace rapidjson { for example Reader::Parse() and Document::Accept(). - \tparam Stream Type of ouptut stream. - \tparam Encoding Encoding of both source strings and output. + \tparam OutputStream Type of output stream. + \tparam SourceEncoding Encoding of both source strings. + \tparam TargetEncoding Encoding of and output stream. \implements Handler */ -template, typename TargetEncoding = UTF8<>, typename Allocator = MemoryPoolAllocator<> > +template, typename TargetEncoding = UTF8<>, typename Allocator = MemoryPoolAllocator<> > class Writer { public: typedef typename SourceEncoding::Ch Ch; - Writer(Stream& stream, Allocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : - stream_(stream), level_stack_(allocator, levelDepth * sizeof(Level)) {} + Writer(OutputStream& os, Allocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(os), level_stack_(allocator, levelDepth * sizeof(Level)) {} //@name Implementation of Handler //@{ @@ -60,7 +61,7 @@ public: level_stack_.template Pop(1); WriteEndObject(); if (level_stack_.Empty()) // end of json text - stream_.Flush(); + os_.Flush(); return *this; } @@ -77,7 +78,7 @@ public: level_stack_.template Pop(1); WriteEndArray(); if (level_stack_.Empty()) // end of json text - stream_.Flush(); + os_.Flush(); return *this; } //@} @@ -96,21 +97,21 @@ protected: static const size_t kDefaultLevelDepth = 32; void WriteNull() { - stream_.Put('n'); stream_.Put('u'); stream_.Put('l'); stream_.Put('l'); + os_.Put('n'); os_.Put('u'); os_.Put('l'); os_.Put('l'); } void WriteBool(bool b) { if (b) { - stream_.Put('t'); stream_.Put('r'); stream_.Put('u'); stream_.Put('e'); + os_.Put('t'); os_.Put('r'); os_.Put('u'); os_.Put('e'); } else { - stream_.Put('f'); stream_.Put('a'); stream_.Put('l'); stream_.Put('s'); stream_.Put('e'); + os_.Put('f'); os_.Put('a'); os_.Put('l'); os_.Put('s'); os_.Put('e'); } } void WriteInt(int i) { if (i < 0) { - stream_.Put('-'); + os_.Put('-'); i = -i; } WriteUint((unsigned)i); @@ -126,13 +127,13 @@ protected: do { --p; - stream_.Put(*p); + os_.Put(*p); } while (p != buffer); } void WriteInt64(int64_t i64) { if (i64 < 0) { - stream_.Put('-'); + os_.Put('-'); i64 = -i64; } WriteUint64((uint64_t)i64); @@ -148,7 +149,7 @@ protected: do { --p; - stream_.Put(*p); + os_.Put(*p); } while (p != buffer); } @@ -162,7 +163,7 @@ protected: #endif RAPIDJSON_ASSERT(ret >= 1); for (int i = 0; i < ret; i++) - stream_.Put(buffer[i]); + os_.Put(buffer[i]); } void WriteString(const Ch* str, SizeType length) { @@ -179,40 +180,40 @@ protected: #undef Z16 }; - stream_.Put('\"'); + os_.Put('\"'); GenericStringStream is(str); while (is.Tell() < length) { const Ch c = is.Peek(); if ((sizeof(Ch) == 1 || (unsigned)c < 256) && escape[(unsigned char)c]) { is.Take(); - stream_.Put('\\'); - stream_.Put(escape[(unsigned char)c]); + os_.Put('\\'); + os_.Put(escape[(unsigned char)c]); if (escape[(unsigned char)c] == 'u') { - stream_.Put('0'); - stream_.Put('0'); - stream_.Put(hexDigits[(unsigned char)c >> 4]); - stream_.Put(hexDigits[(unsigned char)c & 0xF]); + os_.Put('0'); + os_.Put('0'); + os_.Put(hexDigits[(unsigned char)c >> 4]); + os_.Put(hexDigits[(unsigned char)c & 0xF]); } } else - Transcoder::Transcode(is, stream_); + Transcoder::Transcode(is, os_); } - stream_.Put('\"'); + os_.Put('\"'); } - void WriteStartObject() { stream_.Put('{'); } - void WriteEndObject() { stream_.Put('}'); } - void WriteStartArray() { stream_.Put('['); } - void WriteEndArray() { stream_.Put(']'); } + void WriteStartObject() { os_.Put('{'); } + void WriteEndObject() { os_.Put('}'); } + void WriteStartArray() { os_.Put('['); } + void WriteEndArray() { os_.Put(']'); } void Prefix(Type type) { if (level_stack_.GetSize() != 0) { // this value is not at root Level* level = level_stack_.template Top(); if (level->valueCount > 0) { if (level->inArray) - stream_.Put(','); // add comma if it is not the first element in array + os_.Put(','); // add comma if it is not the first element in array else // in object - stream_.Put((level->valueCount % 2 == 0) ? ',' : ':'); + 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 @@ -222,7 +223,7 @@ protected: RAPIDJSON_ASSERT(type == kObjectType || type == kArrayType); } - Stream& stream_; + OutputStream& os_; internal::Stack level_stack_; };