mirror of
https://github.com/Tencent/rapidjson.git
synced 2025-03-09 11:09:32 +01:00
Merge remote-tracking branch 'origin/master' into document
This commit is contained in:
commit
2579eca7e2
@ -1990,7 +1990,9 @@ INCLUDE_FILE_PATTERNS =
|
||||
# recursively expanded use the := operator instead of the = operator.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
PREDEFINED =
|
||||
PREDEFINED = \
|
||||
RAPIDJSON_DOXYGEN_RUNNING \
|
||||
RAPIDJSON_DISABLEIF_RETURN(cond,returntype)=returntype
|
||||
|
||||
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
|
||||
# tag can be used to specify a list of macro names that should be expanded. The
|
||||
|
@ -313,6 +313,17 @@ Value o(kObjectType);
|
||||
|
||||
This is called move assignment operator in C++11. As RapidJSON supports C++03, it adopts move semantics using assignment operator, and all other modifying function like `AddMember()`, `PushBack()`.
|
||||
|
||||
### Move semantics and temporary values {#TemporaryValues}
|
||||
|
||||
Sometimes, it is convenient to construct a Value in place, before passing it to one of the "moving" functions, like `PushBack()` or `AddMember()`. As temporary objects can't be converted to proper Value references, the convenience function `Move()` is available:
|
||||
|
||||
~~~~~~~~~~cpp
|
||||
Value a(kArrayType);
|
||||
// a.PushBack(Value(42)); // will not compile
|
||||
a.PushBack(Value().SetInt(42)); // fluent API
|
||||
a.PushBack(Value(42).Move()); // same as above
|
||||
~~~~~~~~~~
|
||||
|
||||
## Create String {#CreateString}
|
||||
RapidJSON provide two strategies for storing string.
|
||||
|
||||
@ -339,15 +350,28 @@ In this example, we get the allocator from a `Document` instance. This is a comm
|
||||
|
||||
Besides, the above `SetString()` requires length. This can handle null characters within a string. There is another `SetString()` overloaded function without the length parameter. And it assumes the input is null-terminated and calls a `strlen()`-like function to obtain the length.
|
||||
|
||||
Finally, for literal string or string with safe life-cycle can use const-string version of `SetString()`, which lacks allocator parameter:
|
||||
Finally, for literal string or string with safe life-cycle can use const-string version of `SetString()`, which lacks allocator parameter. For string literals (or constant character arrays), simply passing the literal as parameter is safe and efficient:
|
||||
|
||||
~~~~~~~~~~cpp
|
||||
Value s;
|
||||
s.SetString("rapidjson", 9); // faster, can contain null character
|
||||
s.SetString("rapidjson"); // slower, assumes null-terminated
|
||||
s.SetString("rapidjson"); // can contain null character, length derived at compile time
|
||||
s = "rapidjson"; // shortcut, same as above
|
||||
~~~~~~~~~~
|
||||
|
||||
For plain string pointers, the RapidJSON requires to mark a string as safe before using it without copying. This can be achieved by using the `StringRef` function:
|
||||
|
||||
~~~~~~~~~cpp
|
||||
const char * cstr = getenv("USER");
|
||||
size_t cstr_len = ...; // in case length is available
|
||||
Value s;
|
||||
// s.SetString(cstr); // will not compile
|
||||
s.SetString(StringRef(cstr)); // ok, assume safe lifetime, null-terminated
|
||||
s = StringRef(cstr); // shortcut, same as above
|
||||
s.SetString(StringRef(cstr,cstr_len)); // faster, can contain null character
|
||||
s = StringRef(cstr,cstr_len); // shortcut, same as above
|
||||
|
||||
~~~~~~~~~
|
||||
|
||||
## Modify Array {#ModifyArray}
|
||||
Value with array type provides similar APIs as `std::vector`.
|
||||
|
||||
@ -357,7 +381,7 @@ Value with array type provides similar APIs as `std::vector`.
|
||||
* `template <typename T> GenericValue& PushBack(T, Allocator&)`
|
||||
* `Value& PopBack()`
|
||||
|
||||
Note that, `Reserve(...)` and `PushBack(...)` may allocate memory, therefore requires an allocator.
|
||||
Note that, `Reserve(...)` and `PushBack(...)` may allocate memory for the array elements, therefore require an allocator.
|
||||
|
||||
Here is an example of `PushBack()`:
|
||||
|
||||
@ -372,14 +396,26 @@ for (int i = 5; i <= 10; i++)
|
||||
a.PushBack("Lua", allocator).PushBack("Mio", allocator);
|
||||
~~~~~~~~~~
|
||||
|
||||
Differs from STL, `PushBack()`/`PopBack()` returns the array reference itself. This is called fluent interface.
|
||||
Differs from STL, `PushBack()`/`PopBack()` returns the array reference itself. This is called _fluent interface_.
|
||||
|
||||
If you want to add a non-constant string or a string without sufficient lifetime (see [Create String](#CreateString)) to the array, you need to create a string Value by using the copy-string API. To avoid the need for an intermediate variable, you can use a [temporary value](#TemporaryValues) in place:
|
||||
|
||||
~~~~~~~~~~cpp
|
||||
// in-place Value parameter
|
||||
contact.PushBack(Value("copy", document.GetAllocator()).Move(), // copy string
|
||||
document.GetAllocator());
|
||||
|
||||
// explicit parameters
|
||||
Value val("key", document.GetAllocator()); // copy string
|
||||
contact.PushBack(val, document.GetAllocator());
|
||||
~~~~~~~~~~
|
||||
|
||||
## Modify Object {#ModifyObject}
|
||||
Object is a collection of key-value pairs. Each key must be a string value. The way to manipulating object is to add/remove members:
|
||||
|
||||
* `Value& AddMember(Value&, Value&, Allocator& allocator)`
|
||||
* `Value& AddMember(const Ch*, Value&, Allocator&)`
|
||||
* `template <typename T> Value& AddMember(const Ch*, T value, Allocator&)`
|
||||
* `Value& AddMember(StringRefType, Value&, Allocator&)`
|
||||
* `template <typename T> Value& AddMember(StringRefType, T value, Allocator&)`
|
||||
* `bool RemoveMember(const Ch*)`
|
||||
|
||||
Here is an example.
|
||||
@ -390,6 +426,22 @@ contact.AddMember("name", "Milo", document.GetAllocator());
|
||||
contact.AddMember("married", true, document.GetAllocator());
|
||||
~~~~~~~~~~
|
||||
|
||||
The `StringRefType` used as name parameter assumes the same interface as the `SetString` function for string values. These overloads are used to avoid the need for copying the `name` string, as constant key names are very common in JSON objects.
|
||||
|
||||
If you need to create a name from a non-constant string or a string without sufficient lifetime (see [Create String](#CreateString)), you need to create a string Value by using the copy-string API. To avoid the need for an intermediate variable, you can use a [temporary value](#TemporaryValues) in place:
|
||||
|
||||
~~~~~~~~~~cpp
|
||||
// in-place Value parameter
|
||||
contact.AddMember(Value("copy", document.GetAllocator()).Move(), // copy string
|
||||
Value().Move(), // null value
|
||||
document.GetAllocator());
|
||||
|
||||
// explicit parameters
|
||||
Value key("key", document.GetAllocator()); // copy name string
|
||||
Value val(42); // some value
|
||||
contact.AddMember(key, val, document.GetAllocator());
|
||||
~~~~~~~~~~
|
||||
|
||||
## Deep Copy Value {#DeepCopyValue}
|
||||
If we really need to copy a DOM tree, we can use two APIs for deep copy: constructor with allocator, and `CopyFrom()`.
|
||||
|
||||
|
@ -174,6 +174,150 @@ struct GenericMemberIterator<true,Encoding,Allocator> {
|
||||
|
||||
#endif // RAPIDJSON_NOMEMBERITERATORCLASS
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// GenericStringRef
|
||||
|
||||
//! Reference to a constant string (not taking a copy)
|
||||
/*!
|
||||
\tparam CharType character type of the string
|
||||
|
||||
This helper class is used to automatically infer constant string
|
||||
references for string literals, especially from \c const \b (!)
|
||||
character arrays.
|
||||
|
||||
The main use is for creating JSON string values without copying the
|
||||
source string via an \ref Allocator. This requires that the referenced
|
||||
string pointers have a sufficient lifetime, which exceeds the lifetime
|
||||
of the associated GenericValue.
|
||||
|
||||
\b Example
|
||||
\code
|
||||
Value v("foo"); // ok, no need to copy & calculate length
|
||||
const char foo[] = "foo";
|
||||
v.SetString(foo); // ok
|
||||
|
||||
const char* bar = foo;
|
||||
// Value x(bar); // not ok, can't rely on bar's lifetime
|
||||
Value x(StringRef(bar)); // lifetime explicitly guaranteed by user
|
||||
Value y(StringRef(bar, 3)); // ok, explicitly pass length
|
||||
\endcode
|
||||
|
||||
\see StringRef, GenericValue::SetString
|
||||
*/
|
||||
template<typename CharType>
|
||||
struct GenericStringRef {
|
||||
typedef CharType Ch; //!< character type of the string
|
||||
|
||||
//! Create string reference from \c const character array
|
||||
/*!
|
||||
This constructor implicitly creates a constant string reference from
|
||||
a \c const character array. It has better performance than
|
||||
\ref StringRef(const CharType*) by inferring the string \ref length
|
||||
from the array length, and also supports strings containing null
|
||||
characters.
|
||||
|
||||
\tparam N length of the string, automatically inferred
|
||||
|
||||
\param str Constant character array, lifetime assumed to be longer
|
||||
than the use of the string in e.g. a GenericValue
|
||||
|
||||
\post \ref s == str
|
||||
|
||||
\note Constant complexity.
|
||||
\note There is a hidden, private overload to disallow references to
|
||||
non-const character arrays to be created via this constructor.
|
||||
By this, e.g. function-scope arrays used to be filled via
|
||||
\c snprintf are excluded from consideration.
|
||||
In such cases, the referenced string should be \b copied to the
|
||||
GenericValue instead.
|
||||
*/
|
||||
template<SizeType N>
|
||||
GenericStringRef(const CharType (&str)[N])
|
||||
: s(str), length(N-1) {}
|
||||
|
||||
//! Explicitly create string reference from \c const character pointer
|
||||
/*!
|
||||
This constructor can be used to \b explicitly create a reference to
|
||||
a constant string pointer.
|
||||
|
||||
\see StringRef(const CharType*)
|
||||
|
||||
\param str Constant character pointer, lifetime assumed to be longer
|
||||
than the use of the string in e.g. a GenericValue
|
||||
|
||||
\post \ref s == str
|
||||
|
||||
\note There is a hidden, private overload to disallow references to
|
||||
non-const character arrays to be created via this constructor.
|
||||
By this, e.g. function-scope arrays used to be filled via
|
||||
\c snprintf are excluded from consideration.
|
||||
In such cases, the referenced string should be \b copied to the
|
||||
GenericValue instead.
|
||||
*/
|
||||
explicit GenericStringRef(const CharType* str)
|
||||
: s(str), length(internal::StrLen(str)){}
|
||||
|
||||
//! Create constant string reference from pointer and length
|
||||
/*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue
|
||||
\param len length of the string, excluding the trailing NULL terminator
|
||||
|
||||
\post \ref s == str && \ref length == len
|
||||
\note Constant complexity.
|
||||
*/
|
||||
GenericStringRef(const CharType* str, SizeType len)
|
||||
: s(str), length(len) { RAPIDJSON_ASSERT(s != NULL); }
|
||||
|
||||
//! implicit conversion to plain CharType pointer
|
||||
operator const Ch *() const { return s; }
|
||||
|
||||
const Ch* const s; //!< plain CharType pointer
|
||||
const SizeType length; //!< length of the string (excluding the trailing NULL terminator)
|
||||
|
||||
private:
|
||||
//! Disallow copy-assignment
|
||||
GenericStringRef operator=(const GenericStringRef&);
|
||||
//! Disallow construction from non-const array
|
||||
template<SizeType N>
|
||||
GenericStringRef(CharType (&str)[N]) /* = delete */;
|
||||
};
|
||||
|
||||
//! Mark a character pointer as constant string
|
||||
/*! Mark a plain character pointer as a "string literal". This function
|
||||
can be used to avoid copying a character string to be referenced as a
|
||||
value in a JSON GenericValue object, if the string's lifetime is known
|
||||
to be valid long enough.
|
||||
\tparam CharType Character type of the string
|
||||
\param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue
|
||||
\return GenericStringRef string reference object
|
||||
\relatesalso GenericStringRef
|
||||
|
||||
\see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember
|
||||
*/
|
||||
template<typename CharType>
|
||||
inline GenericStringRef<CharType> StringRef(const CharType* str) {
|
||||
return GenericStringRef<CharType>(str, internal::StrLen(str));
|
||||
}
|
||||
|
||||
//! Mark a character pointer as constant string
|
||||
/*! Mark a plain character pointer as a "string literal". This function
|
||||
can be used to avoid copying a character string to be referenced as a
|
||||
value in a JSON GenericValue object, if the string's lifetime is known
|
||||
to be valid long enough.
|
||||
|
||||
This version has better performance with supplied length, and also
|
||||
supports string containing null characters.
|
||||
|
||||
\tparam CharType character type of the string
|
||||
\param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue
|
||||
\param length The length of source string.
|
||||
\return GenericStringRef string reference object
|
||||
\relatesalso GenericStringRef
|
||||
*/
|
||||
template<typename CharType>
|
||||
inline GenericStringRef<CharType> StringRef(const CharType* str, size_t length) {
|
||||
return GenericStringRef<CharType>(str, SizeType(length));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// GenericValue
|
||||
|
||||
@ -196,6 +340,7 @@ public:
|
||||
typedef Encoding EncodingType; //!< Encoding type from template parameter.
|
||||
typedef Allocator AllocatorType; //!< Allocator type from template parameter.
|
||||
typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding.
|
||||
typedef GenericStringRef<Ch> StringRefType; //!< Reference to a constant string
|
||||
typedef typename GenericMemberIterator<false,Encoding,Allocator>::Iterator MemberIterator; //!< Member iterator for iterating in object.
|
||||
typedef typename GenericMemberIterator<true,Encoding,Allocator>::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object.
|
||||
typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array.
|
||||
@ -207,8 +352,8 @@ public:
|
||||
//! Default constructor creates a null value.
|
||||
GenericValue() : data_(), flags_(kNullFlag) {}
|
||||
|
||||
//! Copy constructor is not permitted.
|
||||
private:
|
||||
//! Copy constructor is not permitted.
|
||||
GenericValue(const GenericValue& rhs);
|
||||
|
||||
public:
|
||||
@ -231,14 +376,25 @@ public:
|
||||
/*! Creates a copy of a Value by using the given Allocator
|
||||
\tparam SourceAllocator allocator of \c rhs
|
||||
\param rhs Value to copy from (read-only)
|
||||
\param allocator Allocator to use for copying
|
||||
\param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator().
|
||||
\see CopyFrom()
|
||||
*/
|
||||
template< typename SourceAllocator >
|
||||
GenericValue(const GenericValue<Encoding,SourceAllocator>& rhs, Allocator & allocator);
|
||||
|
||||
//! Constructor for boolean value.
|
||||
explicit GenericValue(bool b) : data_(), flags_(b ? kTrueFlag : kFalseFlag) {}
|
||||
/*! \param b Boolean value
|
||||
\note This constructor is limited to \em real boolean values and rejects
|
||||
implicitly converted types like arbitrary pointers. Use an explicit cast
|
||||
to \c bool, if you want to construct a boolean JSON value in such cases.
|
||||
*/
|
||||
#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen
|
||||
template <typename T>
|
||||
explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame<T,bool>)))
|
||||
#else
|
||||
explicit GenericValue(bool b)
|
||||
#endif
|
||||
: data_(), flags_(b ? kTrueFlag : kFalseFlag) {}
|
||||
|
||||
//! Constructor for int value.
|
||||
explicit GenericValue(int i) : data_(), flags_(kNumberIntFlag) {
|
||||
@ -283,16 +439,16 @@ public:
|
||||
explicit GenericValue(double d) : data_(), flags_(kNumberDoubleFlag) { data_.n.d = d; }
|
||||
|
||||
//! Constructor for constant string (i.e. do not make a copy of string)
|
||||
GenericValue(const Ch* s, SizeType length) : data_(), flags_() { SetStringRaw(s, length); }
|
||||
GenericValue(const Ch* s, SizeType length) : data_(), flags_() { SetStringRaw(StringRef(s, length)); }
|
||||
|
||||
//! Constructor for constant string (i.e. do not make a copy of string)
|
||||
explicit GenericValue(const Ch* s) : data_(), flags_() { SetStringRaw(s, internal::StrLen(s)); }
|
||||
explicit GenericValue(StringRefType s) : data_(), flags_() { SetStringRaw(s); }
|
||||
|
||||
//! Constructor for copy-string (i.e. do make a copy of string)
|
||||
GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_(), flags_() { SetStringRaw(s, length, allocator); }
|
||||
GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s, length), allocator); }
|
||||
|
||||
//! Constructor for copy-string (i.e. do make a copy of string)
|
||||
GenericValue(const Ch*s, Allocator& allocator) : data_(), flags_() { SetStringRaw(s, internal::StrLen(s), allocator); }
|
||||
GenericValue(const Ch*s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); }
|
||||
|
||||
//! Destructor.
|
||||
/*! Need to destruct elements of array, members of object, or copy-string.
|
||||
@ -339,12 +495,30 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! Assignment of constant string reference (no copy)
|
||||
/*! \param str Constant string reference to be assigned
|
||||
\note This overload is needed to avoid clashes with the generic primitive type assignment overload below.
|
||||
\see GenericStringRef, operator=(T)
|
||||
*/
|
||||
GenericValue& operator=(StringRefType str) {
|
||||
return (*this).template operator=<StringRefType>(str);
|
||||
}
|
||||
|
||||
//! Assignment with primitive types.
|
||||
/*! \tparam T Either Type, int, unsigned, int64_t, uint64_t, const Ch*
|
||||
/*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t
|
||||
\param value The value to be assigned.
|
||||
|
||||
\note The source type \c T explicitly disallows all pointer types,
|
||||
especially (\c const) \ref Ch*. This helps avoiding implicitly
|
||||
referencing character strings with insufficient lifetime, use
|
||||
\ref SetString(const Ch*, Allocator&) (for copying) or
|
||||
\ref StringRef() (to explicitly mark the pointer as constant) instead.
|
||||
All other pointer types would implicitly convert to \c bool,
|
||||
use \ref SetBool() instead.
|
||||
*/
|
||||
template <typename T>
|
||||
GenericValue& operator=(T value) {
|
||||
RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer<T>,GenericValue&)
|
||||
operator=(T value) {
|
||||
this->~GenericValue();
|
||||
new (this) GenericValue(value);
|
||||
return *this;
|
||||
@ -377,6 +551,9 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! Prepare Value for move semantics
|
||||
/*! \return *this */
|
||||
GenericValue& Move() { return *this; }
|
||||
//@}
|
||||
|
||||
//!@name Type
|
||||
@ -410,6 +587,8 @@ public:
|
||||
//@{
|
||||
|
||||
bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return flags_ == kTrueFlag; }
|
||||
//!< Set boolean value
|
||||
/*! \post IsBool() == true */
|
||||
GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; }
|
||||
|
||||
//@}
|
||||
@ -418,6 +597,7 @@ public:
|
||||
//@{
|
||||
|
||||
//! Set this value as an empty object.
|
||||
/*! \post IsObject() == true */
|
||||
GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; }
|
||||
|
||||
//! Get the value associated with the name.
|
||||
@ -428,7 +608,7 @@ public:
|
||||
A better approach is to use the now public FindMember().
|
||||
*/
|
||||
GenericValue& operator[](const Ch* name) {
|
||||
GenericValue n(name, internal::StrLen(name));
|
||||
GenericValue n(StringRef(name));
|
||||
return (*this)[n];
|
||||
}
|
||||
const GenericValue& operator[](const Ch* name) const { return const_cast<GenericValue&>(*this)[name]; }
|
||||
@ -481,7 +661,7 @@ public:
|
||||
\c std::map, this has been changed to MemberEnd() now.
|
||||
*/
|
||||
MemberIterator FindMember(const Ch* name) {
|
||||
GenericValue n(name, internal::StrLen(name));
|
||||
GenericValue n(StringRef(name));
|
||||
return FindMember(n);
|
||||
}
|
||||
|
||||
@ -504,9 +684,11 @@ public:
|
||||
//! Add a member (name-value pair) to the object.
|
||||
/*! \param name A string value as name of member.
|
||||
\param value Value of any type.
|
||||
\param allocator Allocator for reallocating memory.
|
||||
\return The value itself for fluent API.
|
||||
\note The ownership of name and value will be transfered to this object if success.
|
||||
\param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
|
||||
\return The value itself for fluent API.
|
||||
\note The ownership of \c name and \c value will be transferred to this object on success.
|
||||
\pre IsObject() && name.IsString()
|
||||
\post name.IsNull() && value.IsNull()
|
||||
*/
|
||||
GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) {
|
||||
RAPIDJSON_ASSERT(IsObject());
|
||||
@ -530,19 +712,53 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
GenericValue& AddMember(const Ch* name, Allocator& nameAllocator, GenericValue& value, Allocator& allocator) {
|
||||
GenericValue n(name, internal::StrLen(name), nameAllocator);
|
||||
//! Add a member (name-value pair) to the object.
|
||||
/*! \param name A constant string reference as name of member.
|
||||
\param value Value of any type.
|
||||
\param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
|
||||
\return The value itself for fluent API.
|
||||
\note The ownership of \c value will be transferred to this object on success.
|
||||
\pre IsObject()
|
||||
\post value.IsNull()
|
||||
*/
|
||||
GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) {
|
||||
GenericValue n(name);
|
||||
return AddMember(n, value, allocator);
|
||||
}
|
||||
|
||||
GenericValue& AddMember(const Ch* name, GenericValue& value, Allocator& allocator) {
|
||||
GenericValue n(name, internal::StrLen(name));
|
||||
return AddMember(n, value, allocator);
|
||||
//! Add a constant string value as member (name-value pair) to the object.
|
||||
/*! \param name A constant string reference as name of member.
|
||||
\param value constant string reference as value of member.
|
||||
\param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
|
||||
\return The value itself for fluent API.
|
||||
\pre IsObject()
|
||||
\note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below.
|
||||
*/
|
||||
GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) {
|
||||
GenericValue v(value);
|
||||
return AddMember(name, v, allocator);
|
||||
}
|
||||
|
||||
//! Add any primitive value as member (name-value pair) to the object.
|
||||
/*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t
|
||||
\param name A constant string reference as name of member.
|
||||
\param value Value of primitive type \c T as value of member
|
||||
\param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator().
|
||||
\return The value itself for fluent API.
|
||||
\pre IsObject()
|
||||
|
||||
\note The source type \c T explicitly disallows all pointer types,
|
||||
especially (\c const) \ref Ch*. This helps avoiding implicitly
|
||||
referencing character strings with insufficient lifetime, use
|
||||
\ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref
|
||||
AddMember(StringRefType, StringRefType, Allocator&).
|
||||
All other pointer types would implicitly convert to \c bool,
|
||||
use an explicit cast instead, if needed.
|
||||
*/
|
||||
template <typename T>
|
||||
GenericValue& AddMember(const Ch* name, T value, Allocator& allocator) {
|
||||
GenericValue n(name, internal::StrLen(name));
|
||||
RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer<T>,GenericValue&)
|
||||
AddMember(StringRefType name, T value, Allocator& allocator) {
|
||||
GenericValue n(name);
|
||||
GenericValue v(value);
|
||||
return AddMember(n, v, allocator);
|
||||
}
|
||||
@ -553,7 +769,7 @@ public:
|
||||
\note Removing member is implemented by moving the last member. So the ordering of members is changed.
|
||||
*/
|
||||
bool RemoveMember(const Ch* name) {
|
||||
GenericValue n(name, internal::StrLen(name));
|
||||
GenericValue n(StringRef(name));
|
||||
return RemoveMember(n);
|
||||
}
|
||||
|
||||
@ -599,6 +815,7 @@ public:
|
||||
//@{
|
||||
|
||||
//! Set this value as an empty array.
|
||||
/*! \post IsArray == true */
|
||||
GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; }
|
||||
|
||||
//! Get the number of elements in array.
|
||||
@ -645,7 +862,7 @@ int z = a[0u].GetInt(); // This works too.
|
||||
|
||||
//! Request the array to have enough capacity to store elements.
|
||||
/*! \param newCapacity The capacity that the array at least need to have.
|
||||
\param allocator The allocator for allocating memory. It must be the same one use previously.
|
||||
\param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
|
||||
\return The value itself for fluent API.
|
||||
*/
|
||||
GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) {
|
||||
@ -657,12 +874,14 @@ int z = a[0u].GetInt(); // This works too.
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! Append a value at the end of the array.
|
||||
/*! \param value The value to be appended.
|
||||
\param allocator The allocator for allocating memory. It must be the same one use previously.
|
||||
\return The value itself for fluent API.
|
||||
\note The ownership of the value will be transfered to this object if success.
|
||||
\note If the number of elements to be appended is known, calls Reserve() once first may be more efficient.
|
||||
//! Append a GenericValue at the end of the array.
|
||||
/*! \param value Value to be appended.
|
||||
\param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
|
||||
\pre IsArray() == true
|
||||
\post value.IsNull() == true
|
||||
\return The value itself for fluent API.
|
||||
\note The ownership of \c value will be transferred to this array on success.
|
||||
\note If the number of elements to be appended is known, calls Reserve() once first may be more efficient.
|
||||
*/
|
||||
GenericValue& PushBack(GenericValue& value, Allocator& allocator) {
|
||||
RAPIDJSON_ASSERT(IsArray());
|
||||
@ -672,8 +891,37 @@ int z = a[0u].GetInt(); // This works too.
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! Append a constant string reference at the end of the array.
|
||||
/*! \param value Constant string reference to be appended.
|
||||
\param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator().
|
||||
\pre IsArray() == true
|
||||
\return The value itself for fluent API.
|
||||
\note If the number of elements to be appended is known, calls Reserve() once first may be more efficient.
|
||||
\see GenericStringRef
|
||||
*/
|
||||
GenericValue& PushBack(StringRefType value, Allocator& allocator) {
|
||||
return (*this).template PushBack<StringRefType>(value, allocator);
|
||||
}
|
||||
|
||||
//! Append a primitive value at the end of the array(.)
|
||||
/*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t
|
||||
\param value Value of primitive type T to be appended.
|
||||
\param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
|
||||
\pre IsArray() == true
|
||||
\return The value itself for fluent API.
|
||||
\note If the number of elements to be appended is known, calls Reserve() once first may be more efficient.
|
||||
|
||||
\note The source type \c T explicitly disallows all pointer types,
|
||||
especially (\c const) \ref Ch*. This helps avoiding implicitly
|
||||
referencing character strings with insufficient lifetime, use
|
||||
\ref PushBack(GenericValue&, Allocator&) or \ref
|
||||
PushBack(StringRefType, Allocator&).
|
||||
All other pointer types would implicitly convert to \c bool,
|
||||
use an explicit cast instead, if needed.
|
||||
*/
|
||||
template <typename T>
|
||||
GenericValue& PushBack(T value, Allocator& allocator) {
|
||||
RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer<T>,GenericValue&)
|
||||
PushBack(T value, Allocator& allocator) {
|
||||
GenericValue v(value);
|
||||
return PushBack(v, allocator);
|
||||
}
|
||||
@ -727,30 +975,35 @@ int z = a[0u].GetInt(); // This works too.
|
||||
\param s source string pointer.
|
||||
\param length The length of source string, excluding the trailing null terminator.
|
||||
\return The value itself for fluent API.
|
||||
\post IsString() == true && GetString() == s && GetStringLength() == length
|
||||
\see SetString(StringRefType)
|
||||
*/
|
||||
GenericValue& SetString(const Ch* s, SizeType length) { this->~GenericValue(); SetStringRaw(s, length); return *this; }
|
||||
GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); }
|
||||
|
||||
//! Set this value as a string without copying source string.
|
||||
/*! \param s source string pointer.
|
||||
/*! \param s source string reference
|
||||
\return The value itself for fluent API.
|
||||
\post IsString() == true && GetString() == s && GetStringLength() == s.length
|
||||
*/
|
||||
GenericValue& SetString(const Ch* s) { return SetString(s, internal::StrLen(s)); }
|
||||
GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; }
|
||||
|
||||
//! Set this value as a string by copying from source string.
|
||||
/*! This version has better performance with supplied length, and also support string containing null character.
|
||||
\param s source string.
|
||||
\param length The length of source string, excluding the trailing null terminator.
|
||||
\param allocator Allocator for allocating copied buffer. Commonly use document.GetAllocator().
|
||||
\param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator().
|
||||
\return The value itself for fluent API.
|
||||
\post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length
|
||||
*/
|
||||
GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(s, length, allocator); return *this; }
|
||||
GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; }
|
||||
|
||||
//! Set this value as a string by copying from source string.
|
||||
/*! \param s source string.
|
||||
\param allocator Allocator for allocating copied buffer. Commonly use document.GetAllocator().
|
||||
\param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator().
|
||||
\return The value itself for fluent API.
|
||||
\post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length
|
||||
*/
|
||||
GenericValue& SetString(const Ch* s, Allocator& allocator) { SetString(s, internal::StrLen(s), allocator); return *this; }
|
||||
GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); }
|
||||
|
||||
//@}
|
||||
|
||||
@ -906,21 +1159,19 @@ private:
|
||||
}
|
||||
|
||||
//! Initialize this value as constant string, without calling destructor.
|
||||
void SetStringRaw(const Ch* s, SizeType length) {
|
||||
RAPIDJSON_ASSERT(s != NULL);
|
||||
void SetStringRaw(StringRefType s) {
|
||||
flags_ = kConstStringFlag;
|
||||
data_.s.str = s;
|
||||
data_.s.length = length;
|
||||
data_.s.length = s.length;
|
||||
}
|
||||
|
||||
//! Initialize this value as copy string with initial data, without calling destructor.
|
||||
void SetStringRaw(const Ch* s, SizeType length, Allocator& allocator) {
|
||||
RAPIDJSON_ASSERT(s != NULL);
|
||||
void SetStringRaw(StringRefType s, Allocator& allocator) {
|
||||
flags_ = kCopyStringFlag;
|
||||
data_.s.str = (Ch *)allocator.Malloc((length + 1) * sizeof(Ch));
|
||||
data_.s.length = length;
|
||||
memcpy(const_cast<Ch*>(data_.s.str), s, length * sizeof(Ch));
|
||||
const_cast<Ch*>(data_.s.str)[length] = '\0';
|
||||
data_.s.str = (Ch *)allocator.Malloc((s.length + 1) * sizeof(Ch));
|
||||
data_.s.length = s.length;
|
||||
memcpy(const_cast<Ch*>(data_.s.str), s, s.length * sizeof(Ch));
|
||||
const_cast<Ch*>(data_.s.str)[s.length] = '\0';
|
||||
}
|
||||
|
||||
//! Assignment without calling destructor
|
||||
|
@ -24,11 +24,14 @@ struct SelectIf : SelectIfCond<Condition::Value,T1,T2> {};
|
||||
template <bool Constify, typename T>
|
||||
struct MaybeAddConst : SelectIfCond<Constify, const T, T> {};
|
||||
|
||||
template <typename T, typename U> struct IsSame { enum { Value = false }; };
|
||||
template <typename T> struct IsSame<T,T> { enum { Value = true }; };
|
||||
template <typename T, typename U> struct IsSame : FalseType {};
|
||||
template <typename T> struct IsSame<T,T> : TrueType {};
|
||||
|
||||
template <typename T> struct IsConst { enum { Value = false }; };
|
||||
template <typename T> struct IsConst<const T> { enum { Value = true }; };
|
||||
template <typename T> struct IsConst : FalseType {};
|
||||
template <typename T> struct IsConst<const T> : TrueType {};
|
||||
|
||||
template <typename T> struct IsPointer : FalseType {};
|
||||
template <typename T> struct IsPointer<T*> : TrueType {};
|
||||
|
||||
template <typename CT, typename T>
|
||||
struct IsMoreConst {
|
||||
@ -64,6 +67,9 @@ template <typename T> struct RemoveSfinaeFptr<SfinaeResultTag&(*)(T)> { typedef
|
||||
typename ::rapidjson::internal::EnableIf \
|
||||
<RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
|
||||
|
||||
#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \
|
||||
typename ::rapidjson::internal::DisableIf<cond,returntype>::Type
|
||||
|
||||
} // namespace internal
|
||||
} // namespace rapidjson
|
||||
//@endcond
|
||||
|
@ -23,6 +23,25 @@ TEST(Value, assignment_operator) {
|
||||
y = x;
|
||||
EXPECT_TRUE(x.IsNull()); // move semantic
|
||||
EXPECT_EQ(1234, y.GetInt());
|
||||
|
||||
y = 5678;
|
||||
EXPECT_TRUE(y.IsInt());
|
||||
EXPECT_EQ(5678, y.GetInt());
|
||||
|
||||
x = "Hello";
|
||||
EXPECT_TRUE(x.IsString());
|
||||
EXPECT_STREQ(x.GetString(),"Hello");
|
||||
|
||||
y = StringRef(x.GetString(),x.GetStringLength());
|
||||
EXPECT_TRUE(y.IsString());
|
||||
EXPECT_EQ(y.GetString(),x.GetString());
|
||||
EXPECT_EQ(y.GetStringLength(),x.GetStringLength());
|
||||
|
||||
static char mstr[] = "mutable";
|
||||
// y = mstr; // should not compile
|
||||
y = StringRef(mstr);
|
||||
EXPECT_TRUE(y.IsString());
|
||||
EXPECT_EQ(y.GetString(),mstr);
|
||||
}
|
||||
|
||||
template <typename Value>
|
||||
@ -350,8 +369,8 @@ TEST(Value, Double) {
|
||||
}
|
||||
|
||||
TEST(Value, String) {
|
||||
// Constructor with const string
|
||||
Value x("Hello", 5);
|
||||
// Construction with const string
|
||||
Value x("Hello", 5); // literal
|
||||
EXPECT_EQ(kStringType, x.GetType());
|
||||
EXPECT_TRUE(x.IsString());
|
||||
EXPECT_STREQ("Hello", x.GetString());
|
||||
@ -365,9 +384,41 @@ TEST(Value, String) {
|
||||
EXPECT_FALSE(x.IsObject());
|
||||
EXPECT_FALSE(x.IsArray());
|
||||
|
||||
static const char cstr[] = "World"; // const array
|
||||
Value(cstr).Swap(x);
|
||||
EXPECT_TRUE(x.IsString());
|
||||
EXPECT_EQ(x.GetString(), cstr);
|
||||
EXPECT_EQ(x.GetStringLength(), sizeof(cstr)-1);
|
||||
|
||||
static char mstr[] = "Howdy"; // non-const array
|
||||
// Value(mstr).Swap(x); // should not compile
|
||||
Value(StringRef(mstr)).Swap(x);
|
||||
EXPECT_TRUE(x.IsString());
|
||||
EXPECT_EQ(x.GetString(), mstr);
|
||||
EXPECT_EQ(x.GetStringLength(), sizeof(mstr)-1);
|
||||
strncpy(mstr,"Hello", sizeof(mstr));
|
||||
EXPECT_STREQ(x.GetString(), "Hello");
|
||||
|
||||
const char* pstr = cstr;
|
||||
//Value(pstr).Swap(x); // should not compile
|
||||
Value(StringRef(pstr)).Swap(x);
|
||||
EXPECT_TRUE(x.IsString());
|
||||
EXPECT_EQ(x.GetString(), cstr);
|
||||
EXPECT_EQ(x.GetStringLength(), sizeof(cstr)-1);
|
||||
|
||||
char* mpstr = mstr;
|
||||
Value(StringRef(mpstr,sizeof(mstr)-1)).Swap(x);
|
||||
EXPECT_TRUE(x.IsString());
|
||||
EXPECT_EQ(x.GetString(), mstr);
|
||||
EXPECT_EQ(x.GetStringLength(), 5u);
|
||||
EXPECT_STREQ(x.GetString(), "Hello");
|
||||
|
||||
// Constructor with copy string
|
||||
MemoryPoolAllocator<> allocator;
|
||||
Value c(x.GetString(), x.GetStringLength(), allocator);
|
||||
EXPECT_NE(x.GetString(), c.GetString());
|
||||
EXPECT_EQ(x.GetStringLength(), c.GetStringLength());
|
||||
EXPECT_STREQ(x.GetString(), c.GetString());
|
||||
//x.SetString("World");
|
||||
x.SetString("World", 5);
|
||||
EXPECT_STREQ("Hello", c.GetString());
|
||||
@ -381,11 +432,31 @@ TEST(Value, String) {
|
||||
|
||||
// SetConsttring()
|
||||
Value z;
|
||||
//z.SetString("Hello");
|
||||
z.SetString("Hello");
|
||||
EXPECT_TRUE(x.IsString());
|
||||
z.SetString("Hello", 5);
|
||||
EXPECT_STREQ("Hello", z.GetString());
|
||||
EXPECT_STREQ("Hello", z.GetString());
|
||||
EXPECT_EQ(5u, z.GetStringLength());
|
||||
|
||||
z.SetString("Hello");
|
||||
EXPECT_TRUE(z.IsString());
|
||||
EXPECT_STREQ("Hello", z.GetString());
|
||||
|
||||
//z.SetString(mstr); // should not compile
|
||||
//z.SetString(pstr); // should not compile
|
||||
z.SetString(StringRef(mstr));
|
||||
EXPECT_TRUE(z.IsString());
|
||||
EXPECT_STREQ(z.GetString(), mstr);
|
||||
|
||||
z.SetString(cstr);
|
||||
EXPECT_TRUE(z.IsString());
|
||||
EXPECT_EQ(cstr, z.GetString());
|
||||
|
||||
z = cstr;
|
||||
EXPECT_TRUE(z.IsString());
|
||||
EXPECT_EQ(cstr, z.GetString());
|
||||
|
||||
// SetString()
|
||||
char s[] = "World";
|
||||
Value w;
|
||||
@ -424,11 +495,13 @@ TEST(Value, Array) {
|
||||
x.PushBack(v, allocator);
|
||||
v.SetInt(123);
|
||||
x.PushBack(v, allocator);
|
||||
//x.PushBack((const char*)"foo", allocator); // should not compile
|
||||
x.PushBack("foo", allocator);
|
||||
|
||||
EXPECT_FALSE(x.Empty());
|
||||
EXPECT_EQ(4u, x.Size());
|
||||
EXPECT_EQ(5u, x.Size());
|
||||
EXPECT_FALSE(y.Empty());
|
||||
EXPECT_EQ(4u, y.Size());
|
||||
EXPECT_EQ(5u, y.Size());
|
||||
EXPECT_TRUE(x[SizeType(0)].IsNull());
|
||||
EXPECT_TRUE(x[1u].IsTrue());
|
||||
EXPECT_TRUE(x[2u].IsFalse());
|
||||
@ -439,6 +512,8 @@ TEST(Value, Array) {
|
||||
EXPECT_TRUE(y[2u].IsFalse());
|
||||
EXPECT_TRUE(y[3u].IsInt());
|
||||
EXPECT_EQ(123, y[3u].GetInt());
|
||||
EXPECT_TRUE(y[4u].IsString());
|
||||
EXPECT_STREQ("foo", y[4u].GetString());
|
||||
|
||||
// iterator
|
||||
Value::ValueIterator itr = x.Begin();
|
||||
@ -454,6 +529,10 @@ TEST(Value, Array) {
|
||||
EXPECT_TRUE(itr != x.End());
|
||||
EXPECT_TRUE(itr->IsInt());
|
||||
EXPECT_EQ(123, itr->GetInt());
|
||||
++itr;
|
||||
EXPECT_TRUE(itr != x.End());
|
||||
EXPECT_TRUE(itr->IsString());
|
||||
EXPECT_STREQ("foo", itr->GetString());
|
||||
|
||||
// const iterator
|
||||
Value::ConstValueIterator citr = y.Begin();
|
||||
@ -469,13 +548,18 @@ TEST(Value, Array) {
|
||||
EXPECT_TRUE(citr != y.End());
|
||||
EXPECT_TRUE(citr->IsInt());
|
||||
EXPECT_EQ(123, citr->GetInt());
|
||||
++citr;
|
||||
EXPECT_TRUE(citr != y.End());
|
||||
EXPECT_TRUE(citr->IsString());
|
||||
EXPECT_STREQ("foo", citr->GetString());
|
||||
|
||||
// PopBack()
|
||||
x.PopBack();
|
||||
EXPECT_EQ(3u, x.Size());
|
||||
EXPECT_EQ(4u, x.Size());
|
||||
EXPECT_TRUE(y[SizeType(0)].IsNull());
|
||||
EXPECT_TRUE(y[1].IsTrue());
|
||||
EXPECT_TRUE(y[2].IsFalse());
|
||||
EXPECT_TRUE(y[1u].IsTrue());
|
||||
EXPECT_TRUE(y[2u].IsFalse());
|
||||
EXPECT_TRUE(y[3u].IsInt());
|
||||
|
||||
// Clear()
|
||||
x.Clear();
|
||||
@ -502,16 +586,34 @@ TEST(Value, Object) {
|
||||
EXPECT_TRUE(y.IsObject());
|
||||
|
||||
// AddMember()
|
||||
Value name("A", 1);
|
||||
Value value("Apple", 5);
|
||||
x.AddMember(name, value, allocator);
|
||||
//name.SetString("B");
|
||||
name.SetString("B", 1);
|
||||
//value.SetString("Banana");
|
||||
value.SetString("Banana", 6);
|
||||
x.AddMember(name, value, allocator);
|
||||
x.AddMember("A", "Apple", allocator);
|
||||
|
||||
Value value("Banana", 6);
|
||||
x.AddMember("B", "Banana", allocator);
|
||||
|
||||
// AddMember<T>(StringRefType, T, Allocator)
|
||||
{
|
||||
Value o(kObjectType);
|
||||
o.AddMember("true", true, allocator);
|
||||
o.AddMember("false", false, allocator);
|
||||
o.AddMember("int", -1, allocator);
|
||||
o.AddMember("uint", 1u, allocator);
|
||||
o.AddMember("int64", INT64_C(-4294967296), allocator);
|
||||
o.AddMember("uint64", UINT64_C(4294967296), allocator);
|
||||
o.AddMember("double", 3.14, allocator);
|
||||
o.AddMember("string", "Jelly", allocator);
|
||||
|
||||
EXPECT_TRUE(o["true"].GetBool());
|
||||
EXPECT_FALSE(o["false"].GetBool());
|
||||
EXPECT_EQ(-1, o["int"].GetInt());
|
||||
EXPECT_EQ(1u, o["uint"].GetUint());
|
||||
EXPECT_EQ(INT64_C(-4294967296), o["int64"].GetInt64());
|
||||
EXPECT_EQ(UINT64_C(4294967296), o["uint64"].GetUint64());
|
||||
EXPECT_STREQ("Jelly",o["string"].GetString());
|
||||
}
|
||||
|
||||
// Tests a member with null character
|
||||
Value name;
|
||||
const Value C0D("C\0D", 3);
|
||||
name.SetString(C0D.GetString(), 3);
|
||||
value.SetString("CherryD", 7);
|
||||
@ -523,7 +625,7 @@ TEST(Value, Object) {
|
||||
EXPECT_TRUE(y.HasMember("A"));
|
||||
EXPECT_TRUE(y.HasMember("B"));
|
||||
|
||||
name.SetString("C\0D", 3);
|
||||
name.SetString("C\0D");
|
||||
EXPECT_TRUE(x.HasMember(name));
|
||||
EXPECT_TRUE(y.HasMember(name));
|
||||
|
||||
@ -617,6 +719,7 @@ TEST(Value, BigNestedObject) {
|
||||
char name1[10];
|
||||
sprintf(name1, "%d", i);
|
||||
|
||||
// Value name(name1); // should not compile
|
||||
Value name(name1, (SizeType)strlen(name1), allocator);
|
||||
Value object(kObjectType);
|
||||
|
||||
@ -629,6 +732,7 @@ TEST(Value, BigNestedObject) {
|
||||
object.AddMember(name, number, allocator);
|
||||
}
|
||||
|
||||
// x.AddMember(name1, object, allocator); // should not compile
|
||||
x.AddMember(name, object, allocator);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user