Commit 7c914a9d authored by miloyip@gmail.com's avatar miloyip@gmail.com

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
parent f516a017
......@@ -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_;
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment