diff --git a/include/rapidjson/internal/itoa.h b/include/rapidjson/internal/itoa.h new file mode 100644 index 000000000..7e040ef4e --- /dev/null +++ b/include/rapidjson/internal/itoa.h @@ -0,0 +1,280 @@ +#ifndef RAPIDJSON_ITOA_ +#define RAPIDJSON_ITOA_ + +namespace rapidjson { +namespace internal { + +// Modified from https://github.com/miloyip/itoa-benchmark/blob/master/src/branchlut.cpp +// API is changed to return the character passed the end of string, without writing '\0' + +inline const char* GetDigitsLut() { + static const char cDigitsLut[200] = { + '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', + '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', + '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', + '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' + }; + return cDigitsLut; +} + +inline char* u32toa(uint32_t value, char* buffer) { + const char* cDigitsLut = GetDigitsLut(); + + if (value < 10000) { + const uint32_t d1 = (value / 100) << 1; + const uint32_t d2 = (value % 100) << 1; + + if (value >= 1000) + *buffer++ = cDigitsLut[d1]; + if (value >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else if (value < 100000000) { + // value = bbbbcccc + const uint32_t b = value / 10000; + const uint32_t c = value % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + else { + // value = aabbbbcccc in decimal + + const uint32_t a = value / 100000000; // 1 to 42 + value %= 100000000; + + if (a >= 10) { + const unsigned i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else + *buffer++ = '0' + static_cast(a); + + const uint32_t b = value / 10000; // 0 to 9999 + const uint32_t c = value % 10000; // 0 to 9999 + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + return buffer; +} + +inline char* i32toa(int32_t value, char* buffer) { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + + return u32toa(static_cast(value), buffer); +} + +inline char* u64toa(uint64_t value, char* buffer) { + const char* cDigitsLut = GetDigitsLut(); + + if (value < 100000000) { + uint32_t v = static_cast(value); + if (v < 10000) { + const uint32_t d1 = (v / 100) << 1; + const uint32_t d2 = (v % 100) << 1; + + if (v >= 1000) + *buffer++ = cDigitsLut[d1]; + if (v >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (v >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else { + // value = bbbbcccc + const uint32_t b = v / 10000; + const uint32_t c = v % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + } + else if (value < 10000000000000000) { + const uint32_t v0 = static_cast(value / 100000000); + const uint32_t v1 = static_cast(value % 100000000); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + if (value >= 1000000000000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 100000000000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 10000000000000) + *buffer++ = cDigitsLut[d2]; + if (value >= 1000000000000) + *buffer++ = cDigitsLut[d2 + 1]; + if (value >= 100000000000) + *buffer++ = cDigitsLut[d3]; + if (value >= 10000000000) + *buffer++ = cDigitsLut[d3 + 1]; + if (value >= 1000000000) + *buffer++ = cDigitsLut[d4]; + if (value >= 100000000) + *buffer++ = cDigitsLut[d4 + 1]; + + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + else { + const uint32_t a = static_cast(value / 10000000000000000); // 1 to 1844 + value %= 10000000000000000; + + if (a < 10) + *buffer++ = '0' + static_cast(a); + else if (a < 100) { + const uint32_t i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else if (a < 1000) { + *buffer++ = '0' + static_cast(a / 100); + + const uint32_t i = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else { + const uint32_t i = (a / 100) << 1; + const uint32_t j = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + *buffer++ = cDigitsLut[j]; + *buffer++ = cDigitsLut[j + 1]; + } + + const uint32_t v0 = static_cast(value / 100000000); + const uint32_t v1 = static_cast(value % 100000000); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + + return buffer; +} + +inline char* i64toa(int64_t value, char* buffer) { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + + return u64toa(static_cast(value), buffer); +} + +} // namespace internal +} // namespace rapidjson + +#endif // RAPIDJSON_ITOA_ diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h index 80ddce012..903dee15e 100644 --- a/include/rapidjson/rapidjson.h +++ b/include/rapidjson/rapidjson.h @@ -360,11 +360,14 @@ struct GenericInsituStringStream { size_t Tell() { return static_cast(src_ - head_); } // Write - Ch* PutBegin() { return dst_ = src_; } void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } - Ch* Allocate(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } - void Flush() {} + + Ch* PutBegin() { return dst_ = src_; } size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } + void Flush() {} + + Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } + void Pop(size_t count) { dst_ -= count; } Ch* src_; Ch* dst_; diff --git a/include/rapidjson/stringbuffer.h b/include/rapidjson/stringbuffer.h index 8c7eb4f17..0e89f959c 100644 --- a/include/rapidjson/stringbuffer.h +++ b/include/rapidjson/stringbuffer.h @@ -22,7 +22,8 @@ struct GenericStringBuffer { void Flush() {} void Clear() { stack_.Clear(); } - Ch* Allocate(size_t count) { return stack_.template Push(count); } + Ch* Push(size_t count) { return stack_.template Push(count); } + void Pop(size_t count) { stack_.template Pop(count); } const Ch* GetString() const { // Push and pop a null terminator. This is safe. diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index 0b0be0876..c6cdfdd4c 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -4,6 +4,8 @@ #include "rapidjson.h" #include "internal/stack.h" #include "internal/strfunc.h" +#include "internal/itoa.h" +#include "stringbuffer.h" #include // snprintf() or _sprintf_s() #include // placement new @@ -44,6 +46,10 @@ class Writer { os_(&os), level_stack_(allocator, levelDepth * sizeof(Level)), doublePrecision_(kDefaultDoublePrecision), hasRoot_(false) {} + Writer(Allocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), + doublePrecision_(kDefaultDoublePrecision), hasRoot_(false) {} + //! Reset the writer with a new stream. /*! This function reset the writer with a new stream and default settings, @@ -208,49 +214,34 @@ class Writer { } bool WriteInt(int i) { - if (i < 0) { - os_->Put('-'); - i = -i; - } - return WriteUint((unsigned)i); + char buffer[11]; + const char* end = internal::i32toa(i, buffer); + for (const char* p = buffer; p != end; ++p) + os_->Put(*p); + return true; } bool WriteUint(unsigned u) { char buffer[10]; - char *p = buffer; - do { - *p++ = char(u % 10) + '0'; - u /= 10; - } while (u > 0); - - do { - --p; + const char* end = internal::u32toa(u, buffer); + for (const char* p = buffer; p != end; ++p) os_->Put(*p); - } while (p != buffer); return true; } bool WriteInt64(int64_t i64) { - if (i64 < 0) { - os_->Put('-'); - i64 = -i64; - } - WriteUint64((uint64_t)i64); + char buffer[21]; + const char* end = internal::i64toa(i64, buffer); + for (const char* p = buffer; p != end; ++p) + os_->Put(*p); return true; } bool WriteUint64(uint64_t u64) { - char buffer[20]; - char *p = buffer; - do { - *p++ = char(u64 % 10) + '0'; - u64 /= 10; - } while (u64 > 0); - - do { - --p; + char buffer[11]; + const char* end = internal::u64toa(u64, buffer); + for (const char* p = buffer; p != end; ++p) os_->Put(*p); - } while (p != buffer); return true; } @@ -378,6 +369,40 @@ class Writer { Writer& operator=(const Writer&); }; +// Full specialization for StringStream to prevent memory copying + +template<> +inline bool Writer::WriteInt(int i) { + char *buffer = os_->Push(11); + const char* end = internal::i32toa(i, buffer); + os_->Pop(11 - (end - buffer)); + return true; +} + +template<> +inline bool Writer::WriteUint(unsigned u) { + char *buffer = os_->Push(10); + const char* end = internal::u32toa(u, buffer); + os_->Pop(10 - (end - buffer)); + return true; +} + +template<> +inline bool Writer::WriteInt64(int64_t i64) { + char *buffer = os_->Push(21); + const char* end = internal::i64toa(i64, buffer); + os_->Pop(21 - (end - buffer)); + return true; +} + +template<> +inline bool Writer::WriteUint64(uint64_t u) { + char *buffer = os_->Push(20); + const char* end = internal::u64toa(u, buffer); + os_->Pop(20 - (end - buffer)); + return true; +} + } // namespace rapidjson #ifdef _MSC_VER diff --git a/test/perftest/misctest.cpp b/test/perftest/misctest.cpp index f844fb71e..dfa50f839 100644 --- a/test/perftest/misctest.cpp +++ b/test/perftest/misctest.cpp @@ -29,6 +29,10 @@ #pragma warning (pop) #endif +#define protected public +#include "rapidjson/writer.h" +#undef private + class Misc : public PerfTest { }; @@ -358,7 +362,7 @@ static const char digits[201] = // Prevent code being optimized out //#define OUTPUT_LENGTH(length) printf("", length) -#define OUTPUT_LENGTH(length) printf("%d\n", length) +#define OUTPUT_LENGTH(length) printf("%u\n", (unsigned)length) template class Writer1 { @@ -430,7 +434,7 @@ bool Writer1::WriteUint(unsigned u) { u /= 10; } while (u > 0); - char* d = os_->Allocate(p - buffer); + char* d = os_->Push(p - buffer); do { --p; *d++ = *p; @@ -597,28 +601,28 @@ class Writer3 { template<> inline bool Writer3::WriteUint(unsigned u) { unsigned digit = CountDecimalDigit_fast(u); - WriteUintReverse(os_->Allocate(digit) + digit, u); + WriteUintReverse(os_->Push(digit) + digit, u); return true; } template<> inline bool Writer3::WriteUint(unsigned u) { unsigned digit = CountDecimalDigit_fast(u); - WriteUintReverse(os_->Allocate(digit) + digit, u); + WriteUintReverse(os_->Push(digit) + digit, u); return true; } template<> inline bool Writer3::WriteUint64(uint64_t u) { unsigned digit = CountDecimalDigit64_fast(u); - WriteUint64Reverse(os_->Allocate(digit) + digit, u); + WriteUint64Reverse(os_->Push(digit) + digit, u); return true; } template<> inline bool Writer3::WriteUint64(uint64_t u) { unsigned digit = CountDecimalDigit64_fast(u); - WriteUint64Reverse(os_->Allocate(digit) + digit, u); + WriteUint64Reverse(os_->Push(digit) + digit, u); return true; } @@ -739,28 +743,28 @@ class Writer4 { template<> inline bool Writer4::WriteUint(unsigned u) { unsigned digit = CountDecimalDigit_fast(u); - WriteUintReverse(os_->Allocate(digit) + digit, u); + WriteUintReverse(os_->Push(digit) + digit, u); return true; } template<> inline bool Writer4::WriteUint(unsigned u) { unsigned digit = CountDecimalDigit_fast(u); - WriteUintReverse(os_->Allocate(digit) + digit, u); + WriteUintReverse(os_->Push(digit) + digit, u); return true; } template<> inline bool Writer4::WriteUint64(uint64_t u) { unsigned digit = CountDecimalDigit64_fast(u); - WriteUint64Reverse(os_->Allocate(digit) + digit, u); + WriteUint64Reverse(os_->Push(digit) + digit, u); return true; } template<> inline bool Writer4::WriteUint64(uint64_t u) { unsigned digit = CountDecimalDigit64_fast(u); - WriteUint64Reverse(os_->Allocate(digit) + digit, u); + WriteUint64Reverse(os_->Push(digit) + digit, u); return true; } @@ -896,35 +900,82 @@ void itoa64_Writer_InsituStringStream() { OUTPUT_LENGTH(length); }; +// Full specialization for InsituStringStream to prevent memory copying +// (normally we will not use InsituStringStream for writing, just for testing) + +namespace rapidjson { + +template<> +bool rapidjson::Writer::WriteInt(int i) { + char *buffer = os_->Push(11); + const char* end = internal::i32toa(i, buffer); + os_->Pop(11 - (end - buffer)); + return true; +} + +template<> +bool Writer::WriteUint(unsigned u) { + char *buffer = os_->Push(10); + const char* end = internal::u32toa(u, buffer); + os_->Pop(10 - (end - buffer)); + return true; +} + +template<> +bool Writer::WriteInt64(int64_t i64) { + char *buffer = os_->Push(21); + const char* end = internal::i64toa(i64, buffer); + os_->Pop(21 - (end - buffer)); + return true; +} + +template<> +bool Writer::WriteUint64(uint64_t u) { + char *buffer = os_->Push(20); + const char* end = internal::u64toa(u, buffer); + os_->Pop(20 - (end - buffer)); + return true; +} + +} // namespace rapidjson + +TEST_F(Misc, itoa_Writer_StringBufferVerify) { itoa_Writer_StringBufferVerify >(); } TEST_F(Misc, itoa_Writer1_StringBufferVerify) { itoa_Writer_StringBufferVerify >(); } TEST_F(Misc, itoa_Writer2_StringBufferVerify) { itoa_Writer_StringBufferVerify >(); } TEST_F(Misc, itoa_Writer3_StringBufferVerify) { itoa_Writer_StringBufferVerify >(); } TEST_F(Misc, itoa_Writer4_StringBufferVerify) { itoa_Writer_StringBufferVerify >(); } +TEST_F(Misc, itoa_Writer_InsituStringStreamVerify) { itoa_Writer_InsituStringStreamVerify >(); } TEST_F(Misc, itoa_Writer1_InsituStringStreamVerify) { itoa_Writer_InsituStringStreamVerify >(); } TEST_F(Misc, itoa_Writer2_InsituStringStreamVerify) { itoa_Writer_InsituStringStreamVerify >(); } TEST_F(Misc, itoa_Writer3_InsituStringStreamVerify) { itoa_Writer_InsituStringStreamVerify >(); } TEST_F(Misc, itoa_Writer4_InsituStringStreamVerify) { itoa_Writer_InsituStringStreamVerify >(); } +TEST_F(Misc, itoa_Writer_StringBuffer) { itoa_Writer_StringBuffer >(); } TEST_F(Misc, itoa_Writer1_StringBuffer) { itoa_Writer_StringBuffer >(); } TEST_F(Misc, itoa_Writer2_StringBuffer) { itoa_Writer_StringBuffer >(); } TEST_F(Misc, itoa_Writer3_StringBuffer) { itoa_Writer_StringBuffer >(); } TEST_F(Misc, itoa_Writer4_StringBuffer) { itoa_Writer_StringBuffer >(); } +TEST_F(Misc, itoa_Writer_InsituStringStream) { itoa_Writer_InsituStringStream >(); } TEST_F(Misc, itoa_Writer1_InsituStringStream) { itoa_Writer_InsituStringStream >(); } TEST_F(Misc, itoa_Writer2_InsituStringStream) { itoa_Writer_InsituStringStream >(); } TEST_F(Misc, itoa_Writer3_InsituStringStream) { itoa_Writer_InsituStringStream >(); } TEST_F(Misc, itoa_Writer4_InsituStringStream) { itoa_Writer_InsituStringStream >(); } +TEST_F(Misc, itoa64_Writer_StringBufferVerify) { itoa64_Writer_StringBufferVerify >(); } TEST_F(Misc, itoa64_Writer1_StringBufferVerify) { itoa64_Writer_StringBufferVerify >(); } TEST_F(Misc, itoa64_Writer2_StringBufferVerify) { itoa64_Writer_StringBufferVerify >(); } TEST_F(Misc, itoa64_Writer3_StringBufferVerify) { itoa64_Writer_StringBufferVerify >(); } TEST_F(Misc, itoa64_Writer4_StringBufferVerify) { itoa64_Writer_StringBufferVerify >(); } +TEST_F(Misc, itoa64_Writer_InsituStringStreamVerify) { itoa64_Writer_InsituStringStreamVerify >(); } TEST_F(Misc, itoa64_Writer1_InsituStringStreamVerify) { itoa64_Writer_InsituStringStreamVerify >(); } TEST_F(Misc, itoa64_Writer2_InsituStringStreamVerify) { itoa64_Writer_InsituStringStreamVerify >(); } TEST_F(Misc, itoa64_Writer3_InsituStringStreamVerify) { itoa64_Writer_InsituStringStreamVerify >(); } TEST_F(Misc, itoa64_Writer4_InsituStringStreamVerify) { itoa64_Writer_InsituStringStreamVerify >(); } +TEST_F(Misc, itoa64_Writer_StringBuffer) { itoa64_Writer_StringBuffer >(); } TEST_F(Misc, itoa64_Writer1_StringBuffer) { itoa64_Writer_StringBuffer >(); } TEST_F(Misc, itoa64_Writer2_StringBuffer) { itoa64_Writer_StringBuffer >(); } TEST_F(Misc, itoa64_Writer3_StringBuffer) { itoa64_Writer_StringBuffer >(); } TEST_F(Misc, itoa64_Writer4_StringBuffer) { itoa64_Writer_StringBuffer >(); } +TEST_F(Misc, itoa64_Writer_InsituStringStream) { itoa64_Writer_InsituStringStream >(); } TEST_F(Misc, itoa64_Writer1_InsituStringStream) { itoa64_Writer_InsituStringStream >(); } TEST_F(Misc, itoa64_Writer2_InsituStringStream) { itoa64_Writer_InsituStringStream >(); } TEST_F(Misc, itoa64_Writer3_InsituStringStream) { itoa64_Writer_InsituStringStream >(); }