mirror of
https://github.com/Tencent/rapidjson.git
synced 2025-03-06 13:41:35 +01:00
Added RAPIDJSON_STATIC_ASSERT() and applied it to check size of character types in encodings.
Modified API documentation to previous changes. Rename Stream to InputStream/OutputStream/InputByteStream/OutputByteStream, and stream to is/os according to the context. git-svn-id: https://rapidjson.googlecode.com/svn/trunk@49 c5894555-1306-4e8d-425f-1f6f381ee07c
This commit is contained in:
parent
f516a01769
commit
7c914a9d4c
@ -5,13 +5,18 @@
|
||||
|
||||
namespace rapidjson {
|
||||
|
||||
//! Adapts an input byte stream with an specified encoding.
|
||||
template <typename Encoding, typename InputStream>
|
||||
//! 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 <typename Encoding, typename InputByteStream>
|
||||
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 <typename Encoding, typename OutputStream>
|
||||
//! 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 <typename Encoding, typename OutputByteStream>
|
||||
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<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
|
||||
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
|
||||
|
||||
template <typename CharType, typename InputStream>
|
||||
//! 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 <typename CharType, typename InputByteStream>
|
||||
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 <typename CharType, typename OutputStream>
|
||||
//! 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 <typename CharType, typename OutputByteStream>
|
||||
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
|
||||
|
||||
|
@ -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<typename OutputStream>
|
||||
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 <typename InputStream>
|
||||
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 <typename InputStream, typename OutputStream>
|
||||
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 <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is);
|
||||
|
||||
//! Take a character from input byte stream.
|
||||
template <typename InputByteStream>
|
||||
static Ch Take(InputByteStream& is);
|
||||
|
||||
//! Put BOM to output byte stream.
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os);
|
||||
|
||||
//! Put a character to output byte stream.
|
||||
template <typename OutputByteStream>
|
||||
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<typename CharType = char>
|
||||
@ -68,7 +93,7 @@ struct UTF8 {
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
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 <typename InputStream, typename OutputStream>
|
||||
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 <typename InputStream>
|
||||
static CharType TakeBOM(InputStream& is) {
|
||||
template <typename InputByteStream>
|
||||
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 <typename InputStream>
|
||||
RAPIDJSON_FORCEINLINE static Ch Take(InputStream& is) {
|
||||
template <typename InputByteStream>
|
||||
static Ch Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
return is.Take();
|
||||
}
|
||||
|
||||
template <typename OutputStream>
|
||||
static void PutBOM(OutputStream& os) {
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(0xEFu); os.Put(0xBBu); os.Put(0xBFu);
|
||||
}
|
||||
|
||||
template <typename OutputStream>
|
||||
static void Put(OutputStream& os, Ch c) {
|
||||
template <typename OutputByteStream>
|
||||
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<typename CharType = wchar_t>
|
||||
struct UTF16 {
|
||||
typedef CharType Ch;
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2);
|
||||
|
||||
template<typename OutputStream>
|
||||
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 <typename InputStream>
|
||||
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 <typename InputStream, typename OutputStream>
|
||||
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<typename CharType = wchar_t>
|
||||
struct UTF16LE : UTF16<CharType> {
|
||||
template <typename InputStream>
|
||||
static CharType TakeBOM(InputStream& is) {
|
||||
template <typename InputByteStream>
|
||||
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 <typename InputStream>
|
||||
RAPIDJSON_FORCEINLINE static CharType Take(InputStream& is) {
|
||||
template <typename InputByteStream>
|
||||
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 <typename OutputStream>
|
||||
static void PutBOM(OutputStream& os) {
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(0xFFu); os.Put(0xFEu);
|
||||
}
|
||||
|
||||
template <typename OutputStream>
|
||||
static void Put(OutputStream& os, CharType c) {
|
||||
template <typename OutputByteStream>
|
||||
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<typename CharType = wchar_t>
|
||||
struct UTF16BE : UTF16<CharType> {
|
||||
template <typename InputStream>
|
||||
static CharType TakeBOM(InputStream& is) {
|
||||
template <typename InputByteStream>
|
||||
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 <typename InputStream>
|
||||
RAPIDJSON_FORCEINLINE static CharType Take(InputStream& is) {
|
||||
template <typename InputByteStream>
|
||||
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 <typename OutputStream>
|
||||
static void PutBOM(OutputStream& os) {
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(0xFEu); os.Put(0xFFu);
|
||||
}
|
||||
|
||||
template <typename OutputStream>
|
||||
static void Put(OutputStream& os, CharType c) {
|
||||
template <typename OutputByteStream>
|
||||
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<CharType> {
|
||||
/*! 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<typename CharType = unsigned>
|
||||
struct UTF32 {
|
||||
typedef CharType Ch;
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4);
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
os.Put(codepoint);
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
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 <typename InputStream, typename OutputStream>
|
||||
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<typename CharType = unsigned>
|
||||
struct UTF32LE : UTF32<CharType> {
|
||||
template <typename InputStream>
|
||||
static CharType TakeBOM(InputStream& is) {
|
||||
template <typename InputByteStream>
|
||||
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 <typename InputStream>
|
||||
RAPIDJSON_FORCEINLINE static CharType Take(InputStream& is) {
|
||||
template <typename InputByteStream>
|
||||
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<CharType> {
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename OutputStream>
|
||||
static void PutBOM(OutputStream& os) {
|
||||
template <typename OutputByteStream>
|
||||
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 <typename OutputStream>
|
||||
static void Put(OutputStream& os, CharType c) {
|
||||
template <typename OutputByteStream>
|
||||
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<CharType> {
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-32 big endian encoding.
|
||||
template<typename CharType = unsigned>
|
||||
struct UTF32BE : UTF32<CharType> {
|
||||
template <typename InputStream>
|
||||
static CharType TakeBOM(InputStream& is) {
|
||||
template <typename InputByteStream>
|
||||
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 <typename InputStream>
|
||||
RAPIDJSON_FORCEINLINE static CharType Take(InputStream& is) {
|
||||
template <typename InputByteStream>
|
||||
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<CharType> {
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename OutputStream>
|
||||
static void PutBOM(OutputStream& os) {
|
||||
template <typename OutputByteStream>
|
||||
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 <typename OutputStream>
|
||||
static void Put(OutputStream& os, CharType c) {
|
||||
template <typename OutputByteStream>
|
||||
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<CharType> {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// 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<typename CharType>
|
||||
struct AutoUTF {
|
||||
typedef CharType Ch;
|
||||
|
||||
#define ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
|
||||
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
|
||||
|
||||
template<typename OutputStream>
|
||||
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 <typename InputStream>
|
||||
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 <typename InputStream, typename OutputStream>
|
||||
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<typename SourceEncoding, typename TargetEncoding>
|
||||
struct Transcoder {
|
||||
//! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream.
|
||||
template<typename InputStream, typename OutputStream>
|
||||
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<typename InputStream, typename OutputStream>
|
||||
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<typename Encoding>
|
||||
struct Transcoder<Encoding, Encoding> {
|
||||
template<typename InputStream, typename OutputStream>
|
||||
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<typename InputStream, typename OutputStream>
|
||||
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
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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_;
|
||||
|
@ -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 Stream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename Allocator = MemoryPoolAllocator<> >
|
||||
class PrettyWriter : public Writer<Stream, SourceEncoding, TargetEncoding, Allocator> {
|
||||
template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename Allocator = MemoryPoolAllocator<> >
|
||||
class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, Allocator> {
|
||||
public:
|
||||
typedef Writer<Stream, SourceEncoding, TargetEncoding, Allocator> Base;
|
||||
typedef Writer<OutputStream, SourceEncoding, TargetEncoding, Allocator> 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<typename Base::Level>(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<typename Base::Level>(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_;
|
||||
|
@ -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 <bool x> struct STATIC_ASSERTION_FAILURE;
|
||||
template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; };
|
||||
template<int x> 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<bool(x) >)>\
|
||||
RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__)
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Allocators and Encodings
|
||||
|
||||
#include "allocators.h"
|
||||
#include "encodings.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<typename Stream>
|
||||
void SkipWhitespace(Stream& stream) {
|
||||
Stream s = stream; // Use a local copy for optimization
|
||||
template<typename InputStream>
|
||||
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<char*>(SkipWhitespace_SIMD(stream.src_));
|
||||
template<> inline void SkipWhitespace(InsituStringStream& is) {
|
||||
is.src_ = const_cast<char*>(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 <typename SourceEncoding, typename TargetEncoding, typename Allocator = MemoryPoolAllocator<> >
|
||||
@ -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 <unsigned parseFlags, typename Stream, typename Handler>
|
||||
bool Parse(Stream& stream, Handler& handler) {
|
||||
template <unsigned parseFlags, typename InputStream, typename Handler>
|
||||
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<parseFlags>(stream, handler); break;
|
||||
case '[': ParseArray<parseFlags>(stream, handler); break;
|
||||
default: RAPIDJSON_PARSE_ERROR("Expect either an object or array at root", stream.Tell());
|
||||
switch (is.Peek()) {
|
||||
case '{': ParseObject<parseFlags>(is, handler); break;
|
||||
case '[': ParseArray<parseFlags>(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<unsigned parseFlags, typename Stream, typename Handler>
|
||||
void ParseObject(Stream& stream, Handler& handler) {
|
||||
RAPIDJSON_ASSERT(stream.Peek() == '{');
|
||||
stream.Take(); // Skip '{'
|
||||
template<unsigned parseFlags, typename InputStream, typename Handler>
|
||||
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<parseFlags>(stream, handler);
|
||||
SkipWhitespace(stream);
|
||||
ParseString<parseFlags>(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<parseFlags>(stream, handler);
|
||||
SkipWhitespace(stream);
|
||||
ParseValue<parseFlags>(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<unsigned parseFlags, typename Stream, typename Handler>
|
||||
void ParseArray(Stream& stream, Handler& handler) {
|
||||
RAPIDJSON_ASSERT(stream.Peek() == '[');
|
||||
stream.Take(); // Skip '['
|
||||
template<unsigned parseFlags, typename InputStream, typename Handler>
|
||||
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<parseFlags>(stream, handler);
|
||||
ParseValue<parseFlags>(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<unsigned parseFlags, typename Stream, typename Handler>
|
||||
void ParseNull(Stream& stream, Handler& handler) {
|
||||
RAPIDJSON_ASSERT(stream.Peek() == 'n');
|
||||
stream.Take();
|
||||
template<unsigned parseFlags, typename InputStream, typename Handler>
|
||||
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<unsigned parseFlags, typename Stream, typename Handler>
|
||||
void ParseTrue(Stream& stream, Handler& handler) {
|
||||
RAPIDJSON_ASSERT(stream.Peek() == 't');
|
||||
stream.Take();
|
||||
template<unsigned parseFlags, typename InputStream, typename Handler>
|
||||
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<unsigned parseFlags, typename Stream, typename Handler>
|
||||
void ParseFalse(Stream& stream, Handler& handler) {
|
||||
RAPIDJSON_ASSERT(stream.Peek() == 'f');
|
||||
stream.Take();
|
||||
template<unsigned parseFlags, typename InputStream, typename Handler>
|
||||
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<typename Stream>
|
||||
unsigned ParseHex4(Stream& stream) {
|
||||
Stream s = stream; // Use a local copy for optimization
|
||||
template<typename InputStream>
|
||||
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<Allocator>& stack) : stack_(stack), length_(0) {}
|
||||
void Put(typename TargetEncoding::Ch c) {
|
||||
*stack_.template Push<typename TargetEncoding::Ch>() = c;
|
||||
void Put(Ch c) {
|
||||
*stack_.template Push<Ch>() = c;
|
||||
++length_;
|
||||
}
|
||||
internal::Stack<Allocator>& stack_;
|
||||
@ -375,28 +378,28 @@ private:
|
||||
};
|
||||
|
||||
// Parse string and generate String event. Different code paths for kParseInsituFlag.
|
||||
template<unsigned parseFlags, typename Stream, typename Handler>
|
||||
void ParseString(Stream& stream, Handler& handler) {
|
||||
Stream s = stream; // Local copy for optimization
|
||||
template<unsigned parseFlags, typename InputStream, typename Handler>
|
||||
void ParseString(InputStream& is, Handler& handler) {
|
||||
InputStream s = is; // Local copy for optimization
|
||||
if (parseFlags & kParseInsituFlag) {
|
||||
Ch *head = s.PutBegin();
|
||||
ParseStringToStream<parseFlags>(s, s);
|
||||
ParseStringToStream<parseFlags, SourceEncoding, SourceEncoding>(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<parseFlags>(s, stackStream);
|
||||
ParseStringToStream<parseFlags, SourceEncoding, TargetEncoding>(s, stackStream);
|
||||
handler.String(stack_.template Pop<typename TargetEncoding::Ch>(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<unsigned parseFlags, typename InputStream, typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& input, OutputStream& output) {
|
||||
template<unsigned parseFlags, typename SEncoding, typename TEncoding, typename InputStream, typename OutputStream>
|
||||
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<SourceEncoding, TargetEncoding>::Validate(input, output) :
|
||||
!Transcoder<SourceEncoding, TargetEncoding>::Transcode(input, output))
|
||||
RAPIDJSON_PARSE_ERROR("Invalid encoding", input.Tell());
|
||||
!Transcoder<SEncoding, TEncoding>::Validate(is, os) :
|
||||
!Transcoder<SEncoding, TEncoding>::Transcode(is, os))
|
||||
RAPIDJSON_PARSE_ERROR("Invalid encoding", is.Tell());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned parseFlags, typename Stream, typename Handler>
|
||||
void ParseNumber(Stream& stream, Handler& handler) {
|
||||
Stream s = stream; // Local copy for optimization
|
||||
template<unsigned parseFlags, typename InputStream, typename Handler>
|
||||
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<unsigned parseFlags, typename Stream, typename Handler>
|
||||
void ParseValue(Stream& stream, Handler& handler) {
|
||||
switch (stream.Peek()) {
|
||||
case 'n': ParseNull <parseFlags>(stream, handler); break;
|
||||
case 't': ParseTrue <parseFlags>(stream, handler); break;
|
||||
case 'f': ParseFalse <parseFlags>(stream, handler); break;
|
||||
case '"': ParseString<parseFlags>(stream, handler); break;
|
||||
case '{': ParseObject<parseFlags>(stream, handler); break;
|
||||
case '[': ParseArray <parseFlags>(stream, handler); break;
|
||||
default : ParseNumber<parseFlags>(stream, handler);
|
||||
template<unsigned parseFlags, typename InputStream, typename Handler>
|
||||
void ParseValue(InputStream& is, Handler& handler) {
|
||||
switch (is.Peek()) {
|
||||
case 'n': ParseNull <parseFlags>(is, handler); break;
|
||||
case 't': ParseTrue <parseFlags>(is, handler); break;
|
||||
case 'f': ParseFalse <parseFlags>(is, handler); break;
|
||||
case '"': ParseString<parseFlags>(is, handler); break;
|
||||
case '{': ParseObject<parseFlags>(is, handler); break;
|
||||
case '[': ParseArray <parseFlags>(is, handler); break;
|
||||
default : ParseNumber<parseFlags>(is, handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 Stream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename Allocator = MemoryPoolAllocator<> >
|
||||
template<typename OutputStream, typename SourceEncoding = UTF8<>, 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<Level>(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<Level>(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<SourceEncoding> 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<SourceEncoding, TargetEncoding>::Transcode(is, stream_);
|
||||
Transcoder<SourceEncoding, TargetEncoding>::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<Level>();
|
||||
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<Allocator> level_stack_;
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user