Merge branch 'master' into optimization
This commit is contained in:
commit
f8e417818f
@ -145,7 +145,7 @@ inline char* WriteExponent(int K, char* buffer) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
inline char* Prettify(char* buffer, int length, int k) {
|
||||
inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) {
|
||||
const int kk = length + k; // 10^(kk-1) <= v < 10^kk
|
||||
|
||||
if (length <= kk && kk <= 21) {
|
||||
@ -160,7 +160,16 @@ inline char* Prettify(char* buffer, int length, int k) {
|
||||
// 1234e-2 -> 12.34
|
||||
std::memmove(&buffer[kk + 1], &buffer[kk], static_cast<size_t>(length - kk));
|
||||
buffer[kk] = '.';
|
||||
return &buffer[length + 1];
|
||||
if (length > kk + maxDecimalPlaces) {
|
||||
// When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1
|
||||
// Remove extra trailing zeros (at least one) after truncation.
|
||||
for (int i = kk + maxDecimalPlaces; i > kk + 1; i--)
|
||||
if (buffer[i] != '0')
|
||||
return &buffer[i + 1];
|
||||
return &buffer[kk + 2]; // Reserve one zero
|
||||
}
|
||||
else
|
||||
return &buffer[length + 1];
|
||||
}
|
||||
else if (-6 < kk && kk <= 0) {
|
||||
// 1234e-6 -> 0.001234
|
||||
@ -170,7 +179,23 @@ inline char* Prettify(char* buffer, int length, int k) {
|
||||
buffer[1] = '.';
|
||||
for (int i = 2; i < offset; i++)
|
||||
buffer[i] = '0';
|
||||
return &buffer[length + offset];
|
||||
if (length + offset > maxDecimalPlaces) {
|
||||
// When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1
|
||||
// Remove extra trailing zeros (at least one) after truncation.
|
||||
for (int i = maxDecimalPlaces + 1; i > 2; i--)
|
||||
if (buffer[i] != '0')
|
||||
return &buffer[i + 1];
|
||||
return &buffer[3]; // Reserve one zero
|
||||
}
|
||||
else
|
||||
return &buffer[length + offset];
|
||||
}
|
||||
else if (kk < -maxDecimalPlaces) {
|
||||
// Truncate to zero
|
||||
buffer[0] = '0';
|
||||
buffer[1] = '.';
|
||||
buffer[2] = '0';
|
||||
return &buffer[3];
|
||||
}
|
||||
else if (length == 1) {
|
||||
// 1e30
|
||||
@ -186,7 +211,8 @@ inline char* Prettify(char* buffer, int length, int k) {
|
||||
}
|
||||
}
|
||||
|
||||
inline char* dtoa(double value, char* buffer) {
|
||||
inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) {
|
||||
RAPIDJSON_ASSERT(maxDecimalPlaces >= 1);
|
||||
Double d(value);
|
||||
if (d.IsZero()) {
|
||||
if (d.Sign())
|
||||
@ -203,7 +229,7 @@ inline char* dtoa(double value, char* buffer) {
|
||||
}
|
||||
int length, K;
|
||||
Grisu2(value, buffer, &length, &K);
|
||||
return Prettify(buffer, length, K);
|
||||
return Prettify(buffer, length, K, maxDecimalPlaces);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,6 +146,18 @@ public:
|
||||
bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); }
|
||||
|
||||
//@}
|
||||
|
||||
//! Write a raw JSON value.
|
||||
/*!
|
||||
For user to write a stringified JSON as a value.
|
||||
|
||||
\param json A well-formed JSON value. It should not contain null character within [0, length - 1] range.
|
||||
\param length Length of the json.
|
||||
\param type Type of the root of json.
|
||||
\note When using PrettyWriter::RawValue(), the result json may not be indented correctly.
|
||||
*/
|
||||
bool RawValue(const Ch* json, size_t length, Type type) { PrettyPrefix(type); return Base::WriteRawValue(json, length); }
|
||||
|
||||
protected:
|
||||
void PrettyPrefix(Type type) {
|
||||
(void)type;
|
||||
|
@ -76,6 +76,8 @@ class Writer {
|
||||
public:
|
||||
typedef typename SourceEncoding::Ch Ch;
|
||||
|
||||
static const int kDefaultMaxDecimalPlaces = 324;
|
||||
|
||||
//! Constructor
|
||||
/*! \param os Output stream.
|
||||
\param stackAllocator User supplied allocator. If it is null, it will create a private one.
|
||||
@ -83,11 +85,11 @@ public:
|
||||
*/
|
||||
explicit
|
||||
Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) :
|
||||
os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), hasRoot_(false) {}
|
||||
os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {}
|
||||
|
||||
explicit
|
||||
Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) :
|
||||
os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), hasRoot_(false) {}
|
||||
os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {}
|
||||
|
||||
//! Reset the writer with a new stream.
|
||||
/*!
|
||||
@ -121,6 +123,35 @@ public:
|
||||
return hasRoot_ && level_stack_.Empty();
|
||||
}
|
||||
|
||||
int GetMaxDecimalPlaces() const {
|
||||
return maxDecimalPlaces_;
|
||||
}
|
||||
|
||||
//! Sets the maximum number of decimal places for double output.
|
||||
/*!
|
||||
This setting truncates the output with specified number of decimal places.
|
||||
|
||||
For example,
|
||||
|
||||
\code
|
||||
writer.SetMaxDecimalPlaces(3);
|
||||
writer.StartArray();
|
||||
writer.Double(0.12345); // "0.123"
|
||||
writer.Double(0.0001); // "0.0"
|
||||
writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent)
|
||||
writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent)
|
||||
writer.EndArray();
|
||||
\endcode
|
||||
|
||||
The default setting does not truncate any decimal places. You can restore to this setting by calling
|
||||
\code
|
||||
writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces);
|
||||
\endcode
|
||||
*/
|
||||
void SetMaxDecimalPlaces(int maxDecimalPlaces) {
|
||||
maxDecimalPlaces_ = maxDecimalPlaces;
|
||||
}
|
||||
|
||||
/*!@name Implementation of Handler
|
||||
\see Handler
|
||||
*/
|
||||
@ -198,6 +229,16 @@ public:
|
||||
|
||||
//@}
|
||||
|
||||
//! Write a raw JSON value.
|
||||
/*!
|
||||
For user to write a stringified JSON as a value.
|
||||
|
||||
\param json A well-formed JSON value. It should not contain null character within [0, length - 1] range.
|
||||
\param length Length of the json.
|
||||
\param type Type of the root of json.
|
||||
*/
|
||||
bool RawValue(const Ch* json, size_t length, Type type) { Prefix(type); return WriteRawValue(json, length); }
|
||||
|
||||
protected:
|
||||
//! Information for each nested level
|
||||
struct Level {
|
||||
@ -266,7 +307,7 @@ protected:
|
||||
return false;
|
||||
|
||||
char buffer[25];
|
||||
char* end = internal::dtoa(d, buffer);
|
||||
char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
|
||||
PutReserve(*os_, static_cast<size_t>(end - buffer));
|
||||
for (char* p = buffer; p != end; ++p)
|
||||
PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
|
||||
@ -356,6 +397,15 @@ protected:
|
||||
bool WriteStartArray() { os_->Put('['); return true; }
|
||||
bool WriteEndArray() { os_->Put(']'); return true; }
|
||||
|
||||
bool WriteRawValue(const Ch* json, size_t length) {
|
||||
PutReserve(*os_, length);
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
RAPIDJSON_ASSERT(json[i] != '\0');
|
||||
PutUnsafe(*os_, json[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Prefix(Type type) {
|
||||
(void)type;
|
||||
if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root
|
||||
@ -378,6 +428,7 @@ protected:
|
||||
|
||||
OutputStream* os_;
|
||||
internal::Stack<StackAllocator> level_stack_;
|
||||
int maxDecimalPlaces_;
|
||||
bool hasRoot_;
|
||||
|
||||
private:
|
||||
@ -426,7 +477,7 @@ inline bool Writer<StringBuffer>::WriteDouble(double d) {
|
||||
return false;
|
||||
|
||||
char *buffer = os_->Push(25);
|
||||
char* end = internal::dtoa(d, buffer);
|
||||
char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
|
||||
os_->Pop(static_cast<size_t>(25 - (end - buffer)));
|
||||
return true;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ set(UNITTEST_SOURCES
|
||||
allocatorstest.cpp
|
||||
bigintegertest.cpp
|
||||
documenttest.cpp
|
||||
dtoatest.cpp
|
||||
encodedstreamtest.cpp
|
||||
encodingstest.cpp
|
||||
fwdtest.cpp
|
||||
|
91
test/unittest/dtoatest.cpp
Normal file
91
test/unittest/dtoatest.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#include "unittest.h"
|
||||
#include "rapidjson/internal/dtoa.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(type-limits)
|
||||
#endif
|
||||
|
||||
using namespace rapidjson::internal;
|
||||
|
||||
TEST(dtoa, normal) {
|
||||
char buffer[30];
|
||||
|
||||
#define TEST_DTOA(d, a)\
|
||||
*dtoa(d, buffer) = '\0';\
|
||||
EXPECT_STREQ(a, buffer)
|
||||
|
||||
TEST_DTOA(0.0, "0.0");
|
||||
TEST_DTOA(-0.0, "-0.0");
|
||||
TEST_DTOA(1.0, "1.0");
|
||||
TEST_DTOA(-1.0, "-1.0");
|
||||
TEST_DTOA(1.2345, "1.2345");
|
||||
TEST_DTOA(1.2345678, "1.2345678");
|
||||
TEST_DTOA(0.123456789012, "0.123456789012");
|
||||
TEST_DTOA(1234567.8, "1234567.8");
|
||||
TEST_DTOA(0.000001, "0.000001");
|
||||
TEST_DTOA(0.0000001, "1e-7");
|
||||
TEST_DTOA(1e30, "1e30");
|
||||
TEST_DTOA(1.234567890123456e30, "1.234567890123456e30");
|
||||
TEST_DTOA(5e-324, "5e-324"); // Min subnormal positive double
|
||||
TEST_DTOA(2.225073858507201e-308, "2.225073858507201e-308"); // Max subnormal positive double
|
||||
TEST_DTOA(2.2250738585072014e-308, "2.2250738585072014e-308"); // Min normal positive double
|
||||
TEST_DTOA(1.7976931348623157e308, "1.7976931348623157e308"); // Max double
|
||||
|
||||
#undef TEST_DTOA
|
||||
}
|
||||
|
||||
TEST(dtoa, maxDecimalPlaces) {
|
||||
char buffer[30];
|
||||
|
||||
#define TEST_DTOA(m, d, a)\
|
||||
*dtoa(d, buffer, m) = '\0';\
|
||||
EXPECT_STREQ(a, buffer)
|
||||
|
||||
TEST_DTOA(3, 0.0, "0.0");
|
||||
TEST_DTOA(1, 0.0, "0.0");
|
||||
TEST_DTOA(3, -0.0, "-0.0");
|
||||
TEST_DTOA(3, 1.0, "1.0");
|
||||
TEST_DTOA(3, -1.0, "-1.0");
|
||||
TEST_DTOA(3, 1.2345, "1.234");
|
||||
TEST_DTOA(2, 1.2345, "1.23");
|
||||
TEST_DTOA(1, 1.2345, "1.2");
|
||||
TEST_DTOA(3, 1.2345678, "1.234");
|
||||
TEST_DTOA(3, 1.0001, "1.0");
|
||||
TEST_DTOA(2, 1.0001, "1.0");
|
||||
TEST_DTOA(1, 1.0001, "1.0");
|
||||
TEST_DTOA(3, 0.123456789012, "0.123");
|
||||
TEST_DTOA(2, 0.123456789012, "0.12");
|
||||
TEST_DTOA(1, 0.123456789012, "0.1");
|
||||
TEST_DTOA(4, 0.0001, "0.0001");
|
||||
TEST_DTOA(3, 0.0001, "0.0");
|
||||
TEST_DTOA(2, 0.0001, "0.0");
|
||||
TEST_DTOA(1, 0.0001, "0.0");
|
||||
TEST_DTOA(3, 1234567.8, "1234567.8");
|
||||
TEST_DTOA(3, 1e30, "1e30");
|
||||
TEST_DTOA(3, 5e-324, "0.0"); // Min subnormal positive double
|
||||
TEST_DTOA(3, 2.225073858507201e-308, "0.0"); // Max subnormal positive double
|
||||
TEST_DTOA(3, 2.2250738585072014e-308, "0.0"); // Min normal positive double
|
||||
TEST_DTOA(3, 1.7976931348623157e308, "1.7976931348623157e308"); // Max double
|
||||
|
||||
#undef TEST_DTOA
|
||||
}
|
||||
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
@ -159,3 +159,22 @@ TEST(PrettyWriter, FileWriteStream) {
|
||||
EXPECT_STREQ(kPrettyJson, json);
|
||||
free(json);
|
||||
}
|
||||
|
||||
TEST(PrettyWriter, RawValue) {
|
||||
StringBuffer buffer;
|
||||
PrettyWriter<StringBuffer> writer(buffer);
|
||||
writer.StartObject();
|
||||
writer.Key("a");
|
||||
writer.Int(1);
|
||||
writer.Key("raw");
|
||||
const char json[] = "[\"Hello\\nWorld\", 123.456]";
|
||||
writer.RawValue(json, strlen(json), kArrayType);
|
||||
writer.EndObject();
|
||||
EXPECT_TRUE(writer.IsComplete());
|
||||
EXPECT_STREQ(
|
||||
"{\n"
|
||||
" \"a\": 1,\n"
|
||||
" \"raw\": [\"Hello\\nWorld\", 123.456]\n" // no indentation within raw value
|
||||
"}",
|
||||
buffer.GetString());
|
||||
}
|
||||
|
@ -425,3 +425,17 @@ TEST(Writer, Inf) {
|
||||
EXPECT_FALSE(writer.Double(-inf));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Writer, RawValue) {
|
||||
StringBuffer buffer;
|
||||
Writer<StringBuffer> writer(buffer);
|
||||
writer.StartObject();
|
||||
writer.Key("a");
|
||||
writer.Int(1);
|
||||
writer.Key("raw");
|
||||
const char json[] = "[\"Hello\\nWorld\", 123.456]";
|
||||
writer.RawValue(json, strlen(json), kArrayType);
|
||||
writer.EndObject();
|
||||
EXPECT_TRUE(writer.IsComplete());
|
||||
EXPECT_STREQ("{\"a\":1,\"raw\":[\"Hello\\nWorld\", 123.456]}", buffer.GetString());
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user