Merge branch 'master' into issue728_threadsafe
This commit is contained in:
commit
9b6af55525
@ -128,7 +128,7 @@ GenericDocument& GenericDocument::Parse(const Ch* str);
|
||||
|
||||
## 解析错误 {#ParseError}
|
||||
|
||||
当解析过程顺利完成,`Document` 便会含有解析结果。当过程出现错误,原来的 DOM 会 * 维持不便 *。可使用 `bool HasParseError()`、`ParseErrorCode GetParseError()` 及 `size_t GetParseOffset()` 获取解析的错误状态。
|
||||
当解析过程顺利完成,`Document` 便会含有解析结果。当过程出现错误,原来的 DOM 会*维持不变*。可使用 `bool HasParseError()`、`ParseErrorCode GetParseError()` 及 `size_t GetParseOffset()` 获取解析的错误状态。
|
||||
|
||||
解析错误代号 | 描述
|
||||
--------------------------------------------|---------------------------------------------------
|
||||
|
@ -163,9 +163,9 @@
|
||||
|
||||
## Document/Value (DOM)
|
||||
|
||||
1. 什么是转移语意?为什么?
|
||||
1. 什么是转移语义?为什么?
|
||||
|
||||
`Value` 不用复制语意,而使用了转移语意。这是指,当把来源值赋值于目标值时,来源值的所有权会转移至目标值。
|
||||
`Value` 不用复制语义,而使用了转移语义。这是指,当把来源值赋值于目标值时,来源值的所有权会转移至目标值。
|
||||
|
||||
由于转移快于复制,此设计决定强迫使用者注意到复制的消耗。
|
||||
|
||||
|
@ -157,7 +157,7 @@ As `SchemaDocument` does not know how to resolve such URI, it needs a user-provi
|
||||
~~~
|
||||
class MyRemoteSchemaDocumentProvider : public IRemoteSchemaDocumentProvider {
|
||||
public:
|
||||
virtual const SchemaDocument* GetRemoteDocument(const char* uri, SizeTyp length) {
|
||||
virtual const SchemaDocument* GetRemoteDocument(const char* uri, SizeType length) {
|
||||
// Resolve the uri and returns a pointer to that schema.
|
||||
}
|
||||
};
|
||||
@ -185,7 +185,7 @@ RapidJSON implemented a simple NFA regular expression engine, which is used by d
|
||||
|Syntax|Description|
|
||||
|------|-----------|
|
||||
|`ab` | Concatenation |
|
||||
|`a|b` | Alternation |
|
||||
|<code>a|b</code> | Alternation |
|
||||
|`a?` | Zero or one |
|
||||
|`a*` | Zero or more |
|
||||
|`a+` | One or more |
|
||||
@ -202,7 +202,7 @@ RapidJSON implemented a simple NFA regular expression engine, which is used by d
|
||||
|`[^abc]` | Negated character classes |
|
||||
|`[^a-c]` | Negated character class range |
|
||||
|`[\b]` | Backspace (U+0008) |
|
||||
|`\|`, `\\`, ... | Escape characters |
|
||||
|<code>\\|</code>, `\\`, ... | Escape characters |
|
||||
|`\f` | Form feed (U+000C) |
|
||||
|`\n` | Line feed (U+000A) |
|
||||
|`\r` | Carriage return (U+000D) |
|
||||
|
@ -157,7 +157,7 @@ JSON Schema 支持 [`$ref` 关键字](http://spacetelescope.github.io/understand
|
||||
~~~
|
||||
class MyRemoteSchemaDocumentProvider : public IRemoteSchemaDocumentProvider {
|
||||
public:
|
||||
virtual const SchemaDocument* GetRemoteDocument(const char* uri, SizeTyp length) {
|
||||
virtual const SchemaDocument* GetRemoteDocument(const char* uri, SizeType length) {
|
||||
// Resolve the uri and returns a pointer to that schema.
|
||||
}
|
||||
};
|
||||
@ -185,7 +185,7 @@ RapidJSON 实现了一个简单的 NFA 正则表达式引擎,并预设使用
|
||||
|语法|描述|
|
||||
|------|-----------|
|
||||
|`ab` | 串联 |
|
||||
|`a|b` | 交替 |
|
||||
|<code>a|b</code> | 交替 |
|
||||
|`a?` | 零或一次 |
|
||||
|`a*` | 零或多次 |
|
||||
|`a+` | 一或多次 |
|
||||
@ -202,7 +202,7 @@ RapidJSON 实现了一个简单的 NFA 正则表达式引擎,并预设使用
|
||||
|`[^abc]` | 字符组取反 |
|
||||
|`[^a-c]` | 字符组范围取反 |
|
||||
|`[\b]` | 退格符 (U+0008) |
|
||||
|`\|`, `\\`, ... | 转义字符 |
|
||||
|<code>\\|</code>, `\\`, ... | 转义字符 |
|
||||
|`\f` | 馈页 (U+000C) |
|
||||
|`\n` | 馈行 (U+000A) |
|
||||
|`\r` | 回车 (U+000D) |
|
||||
|
@ -292,7 +292,7 @@ Value o(kObjectType);
|
||||
Value a(kArrayType);
|
||||
~~~~~~~~~~
|
||||
|
||||
## 转移语意(Move Semantics) {#MoveSemantics}
|
||||
## 转移语义(Move Semantics) {#MoveSemantics}
|
||||
|
||||
在设计 RapidJSON 时有一个非常特别的决定,就是 Value 赋值并不是把来源 Value 复制至目的 Value,而是把把来源 Value 转移(move)至目的 Value。例如:
|
||||
|
||||
@ -302,13 +302,13 @@ Value b(456);
|
||||
b = a; // a 变成 Null,b 变成数字 123。
|
||||
~~~~~~~~~~
|
||||
|
||||

|
||||

|
||||
|
||||
为什么?此语意有何优点?
|
||||
为什么?此语义有何优点?
|
||||
|
||||
最简单的答案就是性能。对于固定大小的 JSON 类型(Number、True、False、Null),复制它们是简单快捷。然而,对于可变大小的 JSON 类型(String、Array、Object),复制它们会产生大量开销,而且这些开销常常不被察觉。尤其是当我们需要创建临时 Object,把它复制至另一变量,然后再析构它。
|
||||
|
||||
例如,若使用正常 * 复制 * 语意:
|
||||
例如,若使用正常 * 复制 * 语义:
|
||||
|
||||
~~~~~~~~~~cpp
|
||||
Value o(kObjectType);
|
||||
@ -321,15 +321,15 @@ Value o(kObjectType);
|
||||
}
|
||||
~~~~~~~~~~
|
||||
|
||||

|
||||

|
||||
|
||||
那个 `o` Object 需要分配一个和 contacts 相同大小的缓冲区,对 conacts 做深度复制,并最终要析构 contacts。这样会产生大量无必要的内存分配/释放,以及内存复制。
|
||||
|
||||
有一些方案可避免实质地复制这些数据,例如引用计数(reference counting)、垃圾回收(garbage collection, GC)。
|
||||
|
||||
为了使 RapidJSON 简单及快速,我们选择了对赋值采用 * 转移 * 语意。这方法与 `std::auto_ptr` 相似,都是在赋值时转移拥有权。转移快得多简单得多,只需要析构原来的 Value,把来源 `memcpy()` 至目标,最后把来源设置为 Null 类型。
|
||||
为了使 RapidJSON 简单及快速,我们选择了对赋值采用 * 转移 * 语义。这方法与 `std::auto_ptr` 相似,都是在赋值时转移拥有权。转移快得多简单得多,只需要析构原来的 Value,把来源 `memcpy()` 至目标,最后把来源设置为 Null 类型。
|
||||
|
||||
因此,使用转移语意后,上面的例子变成:
|
||||
因此,使用转移语义后,上面的例子变成:
|
||||
|
||||
~~~~~~~~~~cpp
|
||||
Value o(kObjectType);
|
||||
@ -341,11 +341,11 @@ Value o(kObjectType);
|
||||
}
|
||||
~~~~~~~~~~
|
||||
|
||||

|
||||

|
||||
|
||||
在 C++11 中这称为转移赋值操作(move assignment operator)。由于 RapidJSON 支持 C++03,它在赋值操作采用转移语意,其它修改形函数如 `AddMember()`, `PushBack()` 也采用转移语意。
|
||||
在 C++11 中这称为转移赋值操作(move assignment operator)。由于 RapidJSON 支持 C++03,它在赋值操作采用转移语义,其它修改形函数如 `AddMember()`, `PushBack()` 也采用转移语义。
|
||||
|
||||
### 转移语意及临时值 {#TemporaryValues}
|
||||
### 转移语义及临时值 {#TemporaryValues}
|
||||
|
||||
有时候,我们想直接构造一个 Value 并传递给一个“转移”函数(如 `PushBack()`、`AddMember()`)。由于临时对象是不能转换为正常的 Value 引用,我们加入了一个方便的 `Move()` 函数:
|
||||
|
||||
|
@ -672,6 +672,9 @@ public:
|
||||
//! Constructor for double value.
|
||||
explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; }
|
||||
|
||||
//! Constructor for float value.
|
||||
explicit GenericValue(float f) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = static_cast<double>(f); data_.f.flags = kNumberDoubleFlag; }
|
||||
|
||||
//! Constructor for constant string (i.e. do not make a copy of string)
|
||||
GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); }
|
||||
|
||||
@ -1671,7 +1674,7 @@ public:
|
||||
GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; }
|
||||
GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; }
|
||||
GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; }
|
||||
GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(f); return *this; }
|
||||
GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(static_cast<double>(f)); return *this; }
|
||||
|
||||
//@}
|
||||
|
||||
|
@ -22,6 +22,11 @@ RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(c++98-compat)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Combination of PrettyWriter format flags.
|
||||
@ -57,6 +62,11 @@ public:
|
||||
explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
|
||||
Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {}
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
PrettyWriter(PrettyWriter&& rhs) :
|
||||
Base(std::forward<PrettyWriter>(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {}
|
||||
#endif
|
||||
|
||||
//! Set custom indentation.
|
||||
/*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r').
|
||||
\param indentCharCount Number of indent characters for each indentation level.
|
||||
@ -254,6 +264,10 @@ private:
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
@ -575,7 +575,7 @@ private:
|
||||
}
|
||||
}
|
||||
else if (RAPIDJSON_LIKELY(Consume(is, '/')))
|
||||
while (is.Peek() != '\0' && is.Take() != '\n');
|
||||
while (is.Peek() != '\0' && is.Take() != '\n') {}
|
||||
else
|
||||
RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell());
|
||||
|
||||
|
@ -78,8 +78,12 @@ public:
|
||||
return stack_.template Bottom<Ch>();
|
||||
}
|
||||
|
||||
//! Get the size of string in bytes in the string buffer.
|
||||
size_t GetSize() const { return stack_.GetSize(); }
|
||||
|
||||
//! Get the length of string in Ch in the string buffer.
|
||||
size_t GetLength() const { return stack_.GetSize() / sizeof(Ch); }
|
||||
|
||||
static const size_t kDefaultCapacity = 256;
|
||||
mutable internal::Stack<Allocator> stack_;
|
||||
|
||||
|
@ -42,6 +42,7 @@ RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(padded)
|
||||
RAPIDJSON_DIAG_OFF(unreachable-code)
|
||||
RAPIDJSON_DIAG_OFF(c++98-compat)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
@ -103,6 +104,13 @@ public:
|
||||
Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) :
|
||||
os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {}
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
Writer(Writer&& rhs) :
|
||||
os_(rhs.os_), level_stack_(std::move(rhs.level_stack_)), maxDecimalPlaces_(rhs.maxDecimalPlaces_), hasRoot_(rhs.hasRoot_) {
|
||||
rhs.os_ = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
//! Reset the writer with a new stream.
|
||||
/*!
|
||||
This function reset the writer with a new stream and default settings,
|
||||
|
@ -18,6 +18,11 @@
|
||||
#include "rapidjson/stringbuffer.h"
|
||||
#include "rapidjson/filewritestream.h"
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(c++98-compat)
|
||||
#endif
|
||||
|
||||
using namespace rapidjson;
|
||||
|
||||
static const char kJson[] = "{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3,-1],\"u64\":1234567890123456789,\"i64\":-1234567890123456789}";
|
||||
@ -201,3 +206,30 @@ TEST(PrettyWriter, RawValue) {
|
||||
"}",
|
||||
buffer.GetString());
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
|
||||
static PrettyWriter<StringBuffer> WriterGen(StringBuffer &target) {
|
||||
PrettyWriter<StringBuffer> writer(target);
|
||||
writer.StartObject();
|
||||
writer.Key("a");
|
||||
writer.Int(1);
|
||||
return writer;
|
||||
}
|
||||
|
||||
TEST(PrettyWriter, MoveCtor) {
|
||||
StringBuffer buffer;
|
||||
PrettyWriter<StringBuffer> writer(WriterGen(buffer));
|
||||
writer.EndObject();
|
||||
EXPECT_TRUE(writer.IsComplete());
|
||||
EXPECT_STREQ(
|
||||
"{\n"
|
||||
" \"a\": 1\n"
|
||||
"}",
|
||||
buffer.GetString());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
@ -26,6 +26,7 @@ using namespace rapidjson;
|
||||
TEST(StringBuffer, InitialSize) {
|
||||
StringBuffer buffer;
|
||||
EXPECT_EQ(0u, buffer.GetSize());
|
||||
EXPECT_EQ(0u, buffer.GetLength());
|
||||
EXPECT_STREQ("", buffer.GetString());
|
||||
}
|
||||
|
||||
@ -34,14 +35,17 @@ TEST(StringBuffer, Put) {
|
||||
buffer.Put('A');
|
||||
|
||||
EXPECT_EQ(1u, buffer.GetSize());
|
||||
EXPECT_EQ(1u, buffer.GetLength());
|
||||
EXPECT_STREQ("A", buffer.GetString());
|
||||
}
|
||||
|
||||
TEST(StringBuffer, PutN_Issue672) {
|
||||
GenericStringBuffer<UTF8<>, MemoryPoolAllocator<> > buffer;
|
||||
EXPECT_EQ(0, buffer.GetSize());
|
||||
EXPECT_EQ(0, buffer.GetLength());
|
||||
rapidjson::PutN(buffer, ' ', 1);
|
||||
EXPECT_EQ(1, buffer.GetSize());
|
||||
EXPECT_EQ(1, buffer.GetLength());
|
||||
}
|
||||
|
||||
TEST(StringBuffer, Clear) {
|
||||
@ -52,6 +56,7 @@ TEST(StringBuffer, Clear) {
|
||||
buffer.Clear();
|
||||
|
||||
EXPECT_EQ(0u, buffer.GetSize());
|
||||
EXPECT_EQ(0u, buffer.GetLength());
|
||||
EXPECT_STREQ("", buffer.GetString());
|
||||
}
|
||||
|
||||
@ -60,6 +65,7 @@ TEST(StringBuffer, Push) {
|
||||
buffer.Push(5);
|
||||
|
||||
EXPECT_EQ(5u, buffer.GetSize());
|
||||
EXPECT_EQ(5u, buffer.GetLength());
|
||||
|
||||
// Causes sudden expansion to make the stack's capacity equal to size
|
||||
buffer.Push(65536u);
|
||||
@ -76,9 +82,19 @@ TEST(StringBuffer, Pop) {
|
||||
buffer.Pop(3);
|
||||
|
||||
EXPECT_EQ(2u, buffer.GetSize());
|
||||
EXPECT_EQ(2u, buffer.GetLength());
|
||||
EXPECT_STREQ("AB", buffer.GetString());
|
||||
}
|
||||
|
||||
TEST(StringBuffer, GetLength_Issue744) {
|
||||
GenericStringBuffer<UTF16<wchar_t> > buffer;
|
||||
buffer.Put('A');
|
||||
buffer.Put('B');
|
||||
buffer.Put('C');
|
||||
EXPECT_EQ(3u * sizeof(wchar_t), buffer.GetSize());
|
||||
EXPECT_EQ(3u, buffer.GetLength());
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
|
||||
#if 0 // Many old compiler does not support these. Turn it off temporaily.
|
||||
@ -130,18 +146,23 @@ TEST(StringBuffer, MoveConstructor) {
|
||||
x.Put('D');
|
||||
|
||||
EXPECT_EQ(4u, x.GetSize());
|
||||
EXPECT_EQ(4u, x.GetLength());
|
||||
EXPECT_STREQ("ABCD", x.GetString());
|
||||
|
||||
// StringBuffer y(x); // does not compile (!is_copy_constructible)
|
||||
StringBuffer y(std::move(x));
|
||||
EXPECT_EQ(0u, x.GetSize());
|
||||
EXPECT_EQ(0u, x.GetLength());
|
||||
EXPECT_EQ(4u, y.GetSize());
|
||||
EXPECT_EQ(4u, y.GetLength());
|
||||
EXPECT_STREQ("ABCD", y.GetString());
|
||||
|
||||
// StringBuffer z = y; // does not compile (!is_copy_assignable)
|
||||
StringBuffer z = std::move(y);
|
||||
EXPECT_EQ(0u, y.GetSize());
|
||||
EXPECT_EQ(0u, y.GetLength());
|
||||
EXPECT_EQ(4u, z.GetSize());
|
||||
EXPECT_EQ(4u, z.GetLength());
|
||||
EXPECT_STREQ("ABCD", z.GetString());
|
||||
}
|
||||
|
||||
@ -153,13 +174,14 @@ TEST(StringBuffer, MoveAssignment) {
|
||||
x.Put('D');
|
||||
|
||||
EXPECT_EQ(4u, x.GetSize());
|
||||
EXPECT_EQ(4u, x.GetLength());
|
||||
EXPECT_STREQ("ABCD", x.GetString());
|
||||
|
||||
StringBuffer y;
|
||||
// y = x; // does not compile (!is_copy_assignable)
|
||||
y = std::move(x);
|
||||
EXPECT_EQ(0u, x.GetSize());
|
||||
EXPECT_EQ(4u, y.GetSize());
|
||||
EXPECT_EQ(4u, y.GetLength());
|
||||
EXPECT_STREQ("ABCD", y.GetString());
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,11 @@
|
||||
#include "rapidjson/stringbuffer.h"
|
||||
#include "rapidjson/memorybuffer.h"
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(c++98-compat)
|
||||
#endif
|
||||
|
||||
using namespace rapidjson;
|
||||
|
||||
TEST(Writer, Compact) {
|
||||
@ -495,3 +500,25 @@ TEST(Writer, RawValue) {
|
||||
EXPECT_TRUE(writer.IsComplete());
|
||||
EXPECT_STREQ("{\"a\":1,\"raw\":[\"Hello\\nWorld\", 123.456]}", buffer.GetString());
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
static Writer<StringBuffer> WriterGen(StringBuffer &target) {
|
||||
Writer<StringBuffer> writer(target);
|
||||
writer.StartObject();
|
||||
writer.Key("a");
|
||||
writer.Int(1);
|
||||
return writer;
|
||||
}
|
||||
|
||||
TEST(Writer, MoveCtor) {
|
||||
StringBuffer buffer;
|
||||
Writer<StringBuffer> writer(WriterGen(buffer));
|
||||
writer.EndObject();
|
||||
EXPECT_TRUE(writer.IsComplete());
|
||||
EXPECT_STREQ("{\"a\":1}", buffer.GetString());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user