diff --git a/include/rapidjson/memorybuffer.h b/include/rapidjson/memorybuffer.h new file mode 100644 index 0000000..3628ebc --- /dev/null +++ b/include/rapidjson/memorybuffer.h @@ -0,0 +1,55 @@ +#ifndef RAPIDJSON_MEMORYBUFFER_H_ +#define RAPIDJSON_MEMORYBUFFER_H_ + +#include "rapidjson.h" +#include "internal/stack.h" + +namespace rapidjson { + +//! Represents an in-memory output byte stream. +/*! + This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream. + + It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file. + + Differences between MemoryBuffer and StringBuffer: + 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer. + 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator. + + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +struct GenericMemoryBuffer { + typedef char Ch; // byte + + GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + + void Put(Ch c) { *stack_.template Push() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + Ch* Push(size_t count) { return stack_.template Push(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetBuffer() const { + return stack_.template Bottom(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; +}; + +typedef GenericMemoryBuffer<> MemoryBuffer; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) { + memset(memoryBuffer.stack_.Push(n), c, n * sizeof(c)); +} + +} // namespace rapidjson + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/include/rapidjson/memorystream.h b/include/rapidjson/memorystream.h new file mode 100644 index 0000000..158fbb5 --- /dev/null +++ b/include/rapidjson/memorystream.h @@ -0,0 +1,47 @@ +#ifndef RAPIDJSON_MEMORYSTREAM_H_ +#define RAPIDJSON_MEMORYSTREAM_H_ + +#include "rapidjson.h" + +namespace rapidjson { + +//! Represents an in-memory input byte stream. +/*! + This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream. + + It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file. + + Differences between MemoryStream and StringStream: + 1. StringStream has encoding but MemoryStream is a byte stream. + 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source. + 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4(). + \note implements Stream concept +*/ +struct MemoryStream { + typedef char Ch; // byte + + MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} + + Ch Peek() const { return *src_; } + Ch Take() { return (src_ == end_) ? '\0' : *src_++; } + size_t Tell() const { return static_cast(src_ - begin_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return Tell() + 4 <= size_ ? src_ : 0; + } + + const Ch* src_; //!< Current read position. + const Ch* begin_; //!< Original head of the string. + const Ch* end_; //!< End of stream. + size_t size_; //!< Size of the stream. +}; + +} // namespace rapidjson + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/test/unittest/encodedstreamtest.cpp b/test/unittest/encodedstreamtest.cpp index 03415aa..4e4db2d 100644 --- a/test/unittest/encodedstreamtest.cpp +++ b/test/unittest/encodedstreamtest.cpp @@ -3,6 +3,8 @@ #include "rapidjson/filewritestream.h" #include "rapidjson/encodedstream.h" #include "rapidjson/stringbuffer.h" +#include "rapidjson/memorystream.h" +#include "rapidjson/memorybuffer.h" using namespace rapidjson; @@ -55,61 +57,154 @@ protected: template void TestEncodedInputStream(const char* filename) { - char buffer[16]; - FILE *fp = Open(filename); - ASSERT_TRUE(fp != 0); - FileReadStream fs(fp, buffer, sizeof(buffer)); - EncodedInputStream eis(fs); - StringStream s(json_); + // Test FileReadStream + { + char buffer[16]; + FILE *fp = Open(filename); + ASSERT_TRUE(fp != 0); + FileReadStream fs(fp, buffer, sizeof(buffer)); + EncodedInputStream eis(fs); + StringStream s(json_); - while (eis.Peek() != '\0') { - unsigned expected, actual; - EXPECT_TRUE(UTF8<>::Decode(s, &expected)); - EXPECT_TRUE(MemoryEncoding::Decode(eis, &actual)); - EXPECT_EQ(expected, actual); + while (eis.Peek() != '\0') { + unsigned expected, actual; + EXPECT_TRUE(UTF8<>::Decode(s, &expected)); + EXPECT_TRUE(MemoryEncoding::Decode(eis, &actual)); + EXPECT_EQ(expected, actual); + } + EXPECT_EQ('\0', s.Peek()); + fclose(fp); + } + + // Test MemoryStream + { + size_t size; + char* data = ReadFile(filename, true, &size); + MemoryStream ms(data, size); + EncodedInputStream eis(ms); + StringStream s(json_); + + while (eis.Peek() != '\0') { + unsigned expected, actual; + EXPECT_TRUE(UTF8<>::Decode(s, &expected)); + EXPECT_TRUE(MemoryEncoding::Decode(eis, &actual)); + EXPECT_EQ(expected, actual); + } + EXPECT_EQ('\0', s.Peek()); + free(data); } - EXPECT_EQ('\0', s.Peek()); - fclose(fp); } void TestAutoUTFInputStream(const char *filename) { - char buffer[16]; - FILE *fp = Open(filename); - ASSERT_TRUE(fp != 0); - FileReadStream fs(fp, buffer, sizeof(buffer)); - AutoUTFInputStream eis(fs); - StringStream s(json_); - while (eis.Peek() != '\0') { - unsigned expected, actual; - EXPECT_TRUE(UTF8<>::Decode(s, &expected)); - EXPECT_TRUE(AutoUTF::Decode(eis, &actual)); - EXPECT_EQ(expected, actual); + // Test FileReadStream + { + char buffer[16]; + FILE *fp = Open(filename); + ASSERT_TRUE(fp != 0); + FileReadStream fs(fp, buffer, sizeof(buffer)); + AutoUTFInputStream eis(fs); + StringStream s(json_); + while (eis.Peek() != '\0') { + unsigned expected, actual; + EXPECT_TRUE(UTF8<>::Decode(s, &expected)); + EXPECT_TRUE(AutoUTF::Decode(eis, &actual)); + EXPECT_EQ(expected, actual); + } + EXPECT_EQ('\0', s.Peek()); + fclose(fp); + } + + // Test MemoryStream + { + size_t size; + char* data = ReadFile(filename, true, &size); + MemoryStream ms(data, size); + AutoUTFInputStream eis(ms); + StringStream s(json_); + + while (eis.Peek() != '\0') { + unsigned expected, actual; + EXPECT_TRUE(UTF8<>::Decode(s, &expected)); + EXPECT_TRUE(AutoUTF::Decode(eis, &actual)); + EXPECT_EQ(expected, actual); + } + EXPECT_EQ('\0', s.Peek()); + free(data); } - EXPECT_EQ('\0', s.Peek()); - fclose(fp); } template void TestEncodedOutputStream(const char* expectedFilename, bool putBOM) { - char filename[L_tmpnam]; - TempFilename(filename); + // Test FileWriteStream + { + char filename[L_tmpnam]; + TempFilename(filename); - FILE *fp = fopen(filename, "wb"); - char buffer[16]; - FileWriteStream os(fp, buffer, sizeof(buffer)); - EncodedOutputStream eos(os, putBOM); - StringStream s(json_); - while (s.Peek() != '\0') { - bool success = Transcoder, MemoryEncoding>::Transcode(s, eos); - EXPECT_TRUE(success); + FILE *fp = fopen(filename, "wb"); + char buffer[16]; + FileWriteStream os(fp, buffer, sizeof(buffer)); + EncodedOutputStream eos(os, putBOM); + StringStream s(json_); + while (s.Peek() != '\0') { + bool success = Transcoder, MemoryEncoding>::Transcode(s, eos); + EXPECT_TRUE(success); + } + eos.Flush(); + fclose(fp); + EXPECT_TRUE(CompareFile(filename, expectedFilename)); + remove(filename); + } + + // Test MemoryBuffer + { + MemoryBuffer mb; + EncodedOutputStream eos(mb, putBOM); + StringStream s(json_); + while (s.Peek() != '\0') { + bool success = Transcoder, MemoryEncoding>::Transcode(s, eos); + EXPECT_TRUE(success); + } + eos.Flush(); + EXPECT_TRUE(CompareBufferFile(mb.GetBuffer(), mb.GetSize(), expectedFilename)); } - eos.Flush(); - fclose(fp); - EXPECT_TRUE(CompareFile(filename, expectedFilename)); - remove(filename); } - bool CompareFile(char * filename, const char* expectedFilename) { + void TestAutoUTFOutputStream(UTFType type, bool putBOM, const char *expectedFilename) { + // Test FileWriteStream + { + char filename[L_tmpnam]; + TempFilename(filename); + + FILE *fp = fopen(filename, "wb"); + char buffer[16]; + FileWriteStream os(fp, buffer, sizeof(buffer)); + AutoUTFOutputStream eos(os, type, putBOM); + StringStream s(json_); + while (s.Peek() != '\0') { + bool success = Transcoder, AutoUTF >::Transcode(s, eos); + EXPECT_TRUE(success); + } + eos.Flush(); + fclose(fp); + EXPECT_TRUE(CompareFile(filename, expectedFilename)); + remove(filename); + } + + // Test MemoryBuffer + { + MemoryBuffer mb; + AutoUTFOutputStream eos(mb, type, putBOM); + StringStream s(json_); + while (s.Peek() != '\0') { + bool success = Transcoder, AutoUTF >::Transcode(s, eos); + EXPECT_TRUE(success); + } + eos.Flush(); + EXPECT_TRUE(CompareBufferFile(mb.GetBuffer(), mb.GetSize(), expectedFilename)); + } + } + + bool CompareFile(const char* filename, const char* expectedFilename) { size_t actualLength, expectedLength; char* actualBuffer = ReadFile(filename, false, &actualLength); char* expectedBuffer = ReadFile(expectedFilename, true, &expectedLength); @@ -119,23 +214,12 @@ protected: return ret; } - void TestAutoUTFOutputStream(UTFType type, bool putBOM, const char *expectedFilename) { - char filename[L_tmpnam]; - TempFilename(filename); - - FILE *fp = fopen(filename, "wb"); - char buffer[16]; - FileWriteStream os(fp, buffer, sizeof(buffer)); - AutoUTFOutputStream eos(os, type, putBOM); - StringStream s(json_); - while (s.Peek() != '\0') { - bool success = Transcoder, AutoUTF >::Transcode(s, eos); - EXPECT_TRUE(success); - } - eos.Flush(); - fclose(fp); - EXPECT_TRUE(CompareFile(filename, expectedFilename)); - remove(filename); + bool CompareBufferFile(const char* actualBuffer, size_t actualLength, const char* expectedFilename) { + size_t expectedLength; + char* expectedBuffer = ReadFile(expectedFilename, true, &expectedLength); + bool ret = (expectedLength == actualLength) && memcmp(expectedBuffer, actualBuffer, actualLength) == 0; + free(expectedBuffer); + return ret; } char *json_;