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
......
This diff is collapsed.
......@@ -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"
......
This diff is collapsed.
......@@ -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