mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 01:48:05 +00:00
Bug 1344629 - Part 3: Move const accessors from nsTSubstring to nsTStringRepr. r=dbaron
All nsTStringRepr methods must be const, so the mutators remain on nsTSubstring. I left a small number of const methods on nsTSubstring, e.g. Capacity(), the rationale being that you would only be interested in this method if you intend to mutate the string. I considered splitting up the typedefs block and leaving behind the ones related to mutation (e.g. nsWritingIterator) but I think it makes for clearer documentation to have them all in one place. MozReview-Commit-ID: 7dEaRgc8NLK --HG-- extra : rebase_source : 01b387b7e5bf2f21d6af1afcccf6ec0d7e8a2ac7
This commit is contained in:
parent
fc4fab5ae2
commit
b7e6bde277
@ -15,6 +15,15 @@
|
||||
#error Internal string headers are not available from external-linkage code.
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace detail {
|
||||
|
||||
class nsStringRepr;
|
||||
class nsCStringRepr;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mozilla
|
||||
|
||||
/**
|
||||
* double-byte (char16_t) string types
|
||||
*/
|
||||
|
@ -27,8 +27,8 @@ public:
|
||||
typedef const CharT& reference;
|
||||
|
||||
private:
|
||||
friend class nsAString;
|
||||
friend class nsACString;
|
||||
friend class mozilla::detail::nsStringRepr;
|
||||
friend class mozilla::detail::nsCStringRepr;
|
||||
|
||||
// unfortunately, the API for nsReadingIterator requires that the
|
||||
// iterator know its start and end positions. this was needed when
|
||||
|
@ -32,21 +32,6 @@ AsFixedString(const nsTSubstring_CharT* aStr)
|
||||
return static_cast<const nsTFixedString_CharT*>(aStr);
|
||||
}
|
||||
|
||||
|
||||
nsTSubstring_CharT::char_type
|
||||
nsTSubstring_CharT::First() const
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mLength > 0, "|First()| called on an empty string");
|
||||
return mData[0];
|
||||
}
|
||||
|
||||
nsTSubstring_CharT::char_type
|
||||
nsTSubstring_CharT::Last() const
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mLength > 0, "|Last()| called on an empty string");
|
||||
return mData[mLength - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* this function is called to prepare mData for writing. the given capacity
|
||||
* indicates the required minimum storage size for mData, in sizeof(char_type)
|
||||
@ -762,23 +747,40 @@ nsTSubstring_CharT::SetIsVoid(bool aVal)
|
||||
}
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
namespace detail {
|
||||
|
||||
nsTStringRepr_CharT::char_type
|
||||
nsTStringRepr_CharT::First() const
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mLength > 0, "|First()| called on an empty string");
|
||||
return mData[0];
|
||||
}
|
||||
|
||||
nsTStringRepr_CharT::char_type
|
||||
nsTStringRepr_CharT::Last() const
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mLength > 0, "|Last()| called on an empty string");
|
||||
return mData[mLength - 1];
|
||||
}
|
||||
|
||||
bool
|
||||
nsTSubstring_CharT::Equals(const self_type& aStr) const
|
||||
nsTStringRepr_CharT::Equals(const self_type& aStr) const
|
||||
{
|
||||
return mLength == aStr.mLength &&
|
||||
char_traits::compare(mData, aStr.mData, mLength) == 0;
|
||||
}
|
||||
|
||||
bool
|
||||
nsTSubstring_CharT::Equals(const self_type& aStr,
|
||||
const comparator_type& aComp) const
|
||||
nsTStringRepr_CharT::Equals(const self_type& aStr,
|
||||
const comparator_type& aComp) const
|
||||
{
|
||||
return mLength == aStr.mLength &&
|
||||
aComp(mData, aStr.mData, mLength, aStr.mLength) == 0;
|
||||
}
|
||||
|
||||
bool
|
||||
nsTSubstring_CharT::Equals(const char_type* aData) const
|
||||
nsTStringRepr_CharT::Equals(const char_type* aData) const
|
||||
{
|
||||
// unfortunately, some callers pass null :-(
|
||||
if (!aData) {
|
||||
@ -793,8 +795,8 @@ nsTSubstring_CharT::Equals(const char_type* aData) const
|
||||
}
|
||||
|
||||
bool
|
||||
nsTSubstring_CharT::Equals(const char_type* aData,
|
||||
const comparator_type& aComp) const
|
||||
nsTStringRepr_CharT::Equals(const char_type* aData,
|
||||
const comparator_type& aComp) const
|
||||
{
|
||||
// unfortunately, some callers pass null :-(
|
||||
if (!aData) {
|
||||
@ -808,36 +810,36 @@ nsTSubstring_CharT::Equals(const char_type* aData,
|
||||
}
|
||||
|
||||
bool
|
||||
nsTSubstring_CharT::EqualsASCII(const char* aData, size_type aLen) const
|
||||
nsTStringRepr_CharT::EqualsASCII(const char* aData, size_type aLen) const
|
||||
{
|
||||
return mLength == aLen &&
|
||||
char_traits::compareASCII(mData, aData, aLen) == 0;
|
||||
}
|
||||
|
||||
bool
|
||||
nsTSubstring_CharT::EqualsASCII(const char* aData) const
|
||||
nsTStringRepr_CharT::EqualsASCII(const char* aData) const
|
||||
{
|
||||
return char_traits::compareASCIINullTerminated(mData, mLength, aData) == 0;
|
||||
}
|
||||
|
||||
bool
|
||||
nsTSubstring_CharT::LowerCaseEqualsASCII(const char* aData,
|
||||
size_type aLen) const
|
||||
nsTStringRepr_CharT::LowerCaseEqualsASCII(const char* aData,
|
||||
size_type aLen) const
|
||||
{
|
||||
return mLength == aLen &&
|
||||
char_traits::compareLowerCaseToASCII(mData, aData, aLen) == 0;
|
||||
}
|
||||
|
||||
bool
|
||||
nsTSubstring_CharT::LowerCaseEqualsASCII(const char* aData) const
|
||||
nsTStringRepr_CharT::LowerCaseEqualsASCII(const char* aData) const
|
||||
{
|
||||
return char_traits::compareLowerCaseToASCIINullTerminated(mData,
|
||||
mLength,
|
||||
aData) == 0;
|
||||
}
|
||||
|
||||
nsTSubstring_CharT::size_type
|
||||
nsTSubstring_CharT::CountChar(char_type aChar) const
|
||||
nsTStringRepr_CharT::size_type
|
||||
nsTStringRepr_CharT::CountChar(char_type aChar) const
|
||||
{
|
||||
const char_type* start = mData;
|
||||
const char_type* end = mData + mLength;
|
||||
@ -846,7 +848,7 @@ nsTSubstring_CharT::CountChar(char_type aChar) const
|
||||
}
|
||||
|
||||
int32_t
|
||||
nsTSubstring_CharT::FindChar(char_type aChar, index_type aOffset) const
|
||||
nsTStringRepr_CharT::FindChar(char_type aChar, index_type aOffset) const
|
||||
{
|
||||
if (aOffset < mLength) {
|
||||
const char_type* result = char_traits::find(mData + aOffset,
|
||||
@ -858,6 +860,9 @@ nsTSubstring_CharT::FindChar(char_type aChar, index_type aOffset) const
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mozilla
|
||||
|
||||
void
|
||||
nsTSubstring_CharT::StripChar(char_type aChar, int32_t aOffset)
|
||||
{
|
||||
|
@ -72,54 +72,18 @@ namespace detail {
|
||||
*/
|
||||
class nsTStringRepr_CharT
|
||||
{
|
||||
public:
|
||||
typedef CharT char_type;
|
||||
|
||||
typedef uint32_t size_type;
|
||||
|
||||
protected:
|
||||
nsTStringRepr_CharT() = delete; // Never instantiate directly
|
||||
|
||||
constexpr
|
||||
nsTStringRepr_CharT(char_type* aData, size_type aLength, uint32_t aFlags)
|
||||
: mData(aData)
|
||||
, mLength(aLength)
|
||||
, mFlags(aFlags)
|
||||
{
|
||||
}
|
||||
|
||||
char_type* mData;
|
||||
size_type mLength;
|
||||
uint32_t mFlags;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mozilla
|
||||
|
||||
/**
|
||||
* nsTSubstring is an abstract string class. From an API perspective, this
|
||||
* class is the root of the string class hierarchy. It represents a single
|
||||
* contiguous array of characters, which may or may not be null-terminated.
|
||||
* This type is not instantiated directly. A sub-class is instantiated
|
||||
* instead. For example, see nsTString.
|
||||
*
|
||||
* NAMES:
|
||||
* nsAString for wide characters
|
||||
* nsACString for narrow characters
|
||||
*
|
||||
*/
|
||||
class nsTSubstring_CharT : public mozilla::detail::nsTStringRepr_CharT
|
||||
{
|
||||
public:
|
||||
typedef mozilla::fallible_t fallible_t;
|
||||
|
||||
typedef CharT char_type;
|
||||
|
||||
typedef nsCharTraits<char_type> char_traits;
|
||||
typedef char_traits::incompatible_char_type incompatible_char_type;
|
||||
|
||||
typedef nsTSubstring_CharT self_type;
|
||||
typedef nsTStringRepr_CharT self_type;
|
||||
typedef self_type base_string_type;
|
||||
|
||||
typedef self_type substring_type;
|
||||
typedef nsTSubstring_CharT substring_type;
|
||||
typedef nsTSubstringTuple_CharT substring_tuple_type;
|
||||
typedef nsTString_CharT string_type;
|
||||
|
||||
@ -132,14 +96,7 @@ public:
|
||||
typedef const char_type* const_char_iterator;
|
||||
|
||||
typedef uint32_t index_type;
|
||||
|
||||
public:
|
||||
|
||||
// this acts like a virtual destructor
|
||||
~nsTSubstring_CharT()
|
||||
{
|
||||
Finalize();
|
||||
}
|
||||
typedef uint32_t size_type;
|
||||
|
||||
/**
|
||||
* reading iterators
|
||||
@ -184,81 +141,6 @@ public:
|
||||
return aIter = mData + mLength;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* writing iterators
|
||||
*/
|
||||
|
||||
char_iterator BeginWriting()
|
||||
{
|
||||
if (!EnsureMutable()) {
|
||||
AllocFailed(mLength);
|
||||
}
|
||||
|
||||
return mData;
|
||||
}
|
||||
|
||||
char_iterator BeginWriting(const fallible_t&)
|
||||
{
|
||||
return EnsureMutable() ? mData : char_iterator(0);
|
||||
}
|
||||
|
||||
char_iterator EndWriting()
|
||||
{
|
||||
if (!EnsureMutable()) {
|
||||
AllocFailed(mLength);
|
||||
}
|
||||
|
||||
return mData + mLength;
|
||||
}
|
||||
|
||||
char_iterator EndWriting(const fallible_t&)
|
||||
{
|
||||
return EnsureMutable() ? (mData + mLength) : char_iterator(0);
|
||||
}
|
||||
|
||||
char_iterator& BeginWriting(char_iterator& aIter)
|
||||
{
|
||||
return aIter = BeginWriting();
|
||||
}
|
||||
|
||||
char_iterator& BeginWriting(char_iterator& aIter, const fallible_t& aFallible)
|
||||
{
|
||||
return aIter = BeginWriting(aFallible);
|
||||
}
|
||||
|
||||
char_iterator& EndWriting(char_iterator& aIter)
|
||||
{
|
||||
return aIter = EndWriting();
|
||||
}
|
||||
|
||||
char_iterator& EndWriting(char_iterator& aIter, const fallible_t& aFallible)
|
||||
{
|
||||
return aIter = EndWriting(aFallible);
|
||||
}
|
||||
|
||||
/**
|
||||
* deprecated writing iterators
|
||||
*/
|
||||
|
||||
iterator& BeginWriting(iterator& aIter)
|
||||
{
|
||||
char_type* data = BeginWriting();
|
||||
aIter.mStart = data;
|
||||
aIter.mEnd = data + mLength;
|
||||
aIter.mPosition = aIter.mStart;
|
||||
return aIter;
|
||||
}
|
||||
|
||||
iterator& EndWriting(iterator& aIter)
|
||||
{
|
||||
char_type* data = BeginWriting();
|
||||
aIter.mStart = data;
|
||||
aIter.mEnd = data + mLength;
|
||||
aIter.mPosition = aIter.mEnd;
|
||||
return aIter;
|
||||
}
|
||||
|
||||
/**
|
||||
* accessors
|
||||
*/
|
||||
@ -394,6 +276,195 @@ public:
|
||||
return LowerCaseEqualsASCII(aStr, N - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if this string overlaps with the given string fragment.
|
||||
*/
|
||||
bool IsDependentOn(const char_type* aStart, const char_type* aEnd) const
|
||||
{
|
||||
/**
|
||||
* if it _isn't_ the case that one fragment starts after the other ends,
|
||||
* or ends before the other starts, then, they conflict:
|
||||
*
|
||||
* !(f2.begin >= f1.aEnd || f2.aEnd <= f1.begin)
|
||||
*
|
||||
* Simplified, that gives us:
|
||||
*/
|
||||
return (aStart < (mData + mLength) && aEnd > mData);
|
||||
}
|
||||
|
||||
protected:
|
||||
nsTStringRepr_CharT() = delete; // Never instantiate directly
|
||||
|
||||
constexpr
|
||||
nsTStringRepr_CharT(char_type* aData, size_type aLength, uint32_t aFlags)
|
||||
: mData(aData)
|
||||
, mLength(aLength)
|
||||
, mFlags(aFlags)
|
||||
{
|
||||
}
|
||||
|
||||
char_type* mData;
|
||||
size_type mLength;
|
||||
uint32_t mFlags;
|
||||
|
||||
public:
|
||||
// mFlags is a bitwise combination of the following flags. the meaning
|
||||
// and interpretation of these flags is an implementation detail.
|
||||
//
|
||||
// NOTE: these flags are declared public _only_ for convenience inside
|
||||
// the string implementation.
|
||||
|
||||
enum
|
||||
{
|
||||
F_NONE = 0, // no flags
|
||||
|
||||
// data flags are in the lower 16-bits
|
||||
F_TERMINATED = 1 << 0, // IsTerminated returns true
|
||||
F_VOIDED = 1 << 1, // IsVoid returns true
|
||||
F_SHARED = 1 << 2, // mData points to a heap-allocated, shared buffer
|
||||
F_OWNED = 1 << 3, // mData points to a heap-allocated, raw buffer
|
||||
F_FIXED = 1 << 4, // mData points to a fixed-size writable, dependent buffer
|
||||
F_LITERAL = 1 << 5, // mData points to a string literal; F_TERMINATED will also be set
|
||||
|
||||
// class flags are in the upper 16-bits
|
||||
F_CLASS_FIXED = 1 << 16 // indicates that |this| is of type nsTFixedString
|
||||
};
|
||||
|
||||
//
|
||||
// Some terminology:
|
||||
//
|
||||
// "dependent buffer" A dependent buffer is one that the string class
|
||||
// does not own. The string class relies on some
|
||||
// external code to ensure the lifetime of the
|
||||
// dependent buffer.
|
||||
//
|
||||
// "shared buffer" A shared buffer is one that the string class
|
||||
// allocates. When it allocates a shared string
|
||||
// buffer, it allocates some additional space at
|
||||
// the beginning of the buffer for additional
|
||||
// fields, including a reference count and a
|
||||
// buffer length. See nsStringHeader.
|
||||
//
|
||||
// "adopted buffer" An adopted buffer is a raw string buffer
|
||||
// allocated on the heap (using moz_xmalloc)
|
||||
// of which the string class subsumes ownership.
|
||||
//
|
||||
// Some comments about the string flags:
|
||||
//
|
||||
// F_SHARED, F_OWNED, and F_FIXED are all mutually exlusive. They
|
||||
// indicate the allocation type of mData. If none of these flags
|
||||
// are set, then the string buffer is dependent.
|
||||
//
|
||||
// F_SHARED, F_OWNED, or F_FIXED imply F_TERMINATED. This is because
|
||||
// the string classes always allocate null-terminated buffers, and
|
||||
// non-terminated substrings are always dependent.
|
||||
//
|
||||
// F_VOIDED implies F_TERMINATED, and moreover it implies that mData
|
||||
// points to char_traits::sEmptyBuffer. Therefore, F_VOIDED is
|
||||
// mutually exclusive with F_SHARED, F_OWNED, and F_FIXED.
|
||||
//
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mozilla
|
||||
|
||||
/**
|
||||
* nsTSubstring is an abstract string class. From an API perspective, this
|
||||
* class is the root of the string class hierarchy. It represents a single
|
||||
* contiguous array of characters, which may or may not be null-terminated.
|
||||
* This type is not instantiated directly. A sub-class is instantiated
|
||||
* instead. For example, see nsTString.
|
||||
*
|
||||
* NAMES:
|
||||
* nsAString for wide characters
|
||||
* nsACString for narrow characters
|
||||
*
|
||||
*/
|
||||
class nsTSubstring_CharT : public mozilla::detail::nsTStringRepr_CharT
|
||||
{
|
||||
public:
|
||||
typedef nsTSubstring_CharT self_type;
|
||||
|
||||
// this acts like a virtual destructor
|
||||
~nsTSubstring_CharT()
|
||||
{
|
||||
Finalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* writing iterators
|
||||
*/
|
||||
|
||||
char_iterator BeginWriting()
|
||||
{
|
||||
if (!EnsureMutable()) {
|
||||
AllocFailed(mLength);
|
||||
}
|
||||
|
||||
return mData;
|
||||
}
|
||||
|
||||
char_iterator BeginWriting(const fallible_t&)
|
||||
{
|
||||
return EnsureMutable() ? mData : char_iterator(0);
|
||||
}
|
||||
|
||||
char_iterator EndWriting()
|
||||
{
|
||||
if (!EnsureMutable()) {
|
||||
AllocFailed(mLength);
|
||||
}
|
||||
|
||||
return mData + mLength;
|
||||
}
|
||||
|
||||
char_iterator EndWriting(const fallible_t&)
|
||||
{
|
||||
return EnsureMutable() ? (mData + mLength) : char_iterator(0);
|
||||
}
|
||||
|
||||
char_iterator& BeginWriting(char_iterator& aIter)
|
||||
{
|
||||
return aIter = BeginWriting();
|
||||
}
|
||||
|
||||
char_iterator& BeginWriting(char_iterator& aIter, const fallible_t& aFallible)
|
||||
{
|
||||
return aIter = BeginWriting(aFallible);
|
||||
}
|
||||
|
||||
char_iterator& EndWriting(char_iterator& aIter)
|
||||
{
|
||||
return aIter = EndWriting();
|
||||
}
|
||||
|
||||
char_iterator& EndWriting(char_iterator& aIter, const fallible_t& aFallible)
|
||||
{
|
||||
return aIter = EndWriting(aFallible);
|
||||
}
|
||||
|
||||
/**
|
||||
* deprecated writing iterators
|
||||
*/
|
||||
|
||||
iterator& BeginWriting(iterator& aIter)
|
||||
{
|
||||
char_type* data = BeginWriting();
|
||||
aIter.mStart = data;
|
||||
aIter.mEnd = data + mLength;
|
||||
aIter.mPosition = aIter.mStart;
|
||||
return aIter;
|
||||
}
|
||||
|
||||
iterator& EndWriting(iterator& aIter)
|
||||
{
|
||||
char_type* data = BeginWriting();
|
||||
aIter.mStart = data;
|
||||
aIter.mEnd = data + mLength;
|
||||
aIter.mPosition = aIter.mEnd;
|
||||
return aIter;
|
||||
}
|
||||
|
||||
/**
|
||||
* assignment
|
||||
*/
|
||||
@ -1035,22 +1106,6 @@ protected:
|
||||
MOZ_MUST_USE bool NS_FASTCALL EnsureMutable(
|
||||
size_type aNewLen = size_type(-1));
|
||||
|
||||
/**
|
||||
* returns true if this string overlaps with the given string fragment.
|
||||
*/
|
||||
bool IsDependentOn(const char_type* aStart, const char_type* aEnd) const
|
||||
{
|
||||
/**
|
||||
* if it _isn't_ the case that one fragment starts after the other ends,
|
||||
* or ends before the other starts, then, they conflict:
|
||||
*
|
||||
* !(f2.begin >= f1.aEnd || f2.aEnd <= f1.begin)
|
||||
*
|
||||
* Simplified, that gives us:
|
||||
*/
|
||||
return (aStart < (mData + mLength) && aEnd > mData);
|
||||
}
|
||||
|
||||
/**
|
||||
* this helper function stores the specified dataFlags in mFlags
|
||||
*/
|
||||
@ -1068,62 +1123,6 @@ public:
|
||||
// NOTE: this method is declared public _only_ for convenience for
|
||||
// callers who don't have access to the original nsLiteralString_CharT.
|
||||
void NS_FASTCALL AssignLiteral(const char_type* aData, size_type aLength);
|
||||
|
||||
// mFlags is a bitwise combination of the following flags. the meaning
|
||||
// and interpretation of these flags is an implementation detail.
|
||||
//
|
||||
// NOTE: these flags are declared public _only_ for convenience inside
|
||||
// the string implementation.
|
||||
|
||||
enum
|
||||
{
|
||||
F_NONE = 0, // no flags
|
||||
|
||||
// data flags are in the lower 16-bits
|
||||
F_TERMINATED = 1 << 0, // IsTerminated returns true
|
||||
F_VOIDED = 1 << 1, // IsVoid returns true
|
||||
F_SHARED = 1 << 2, // mData points to a heap-allocated, shared buffer
|
||||
F_OWNED = 1 << 3, // mData points to a heap-allocated, raw buffer
|
||||
F_FIXED = 1 << 4, // mData points to a fixed-size writable, dependent buffer
|
||||
F_LITERAL = 1 << 5, // mData points to a string literal; F_TERMINATED will also be set
|
||||
|
||||
// class flags are in the upper 16-bits
|
||||
F_CLASS_FIXED = 1 << 16 // indicates that |this| is of type nsTFixedString
|
||||
};
|
||||
|
||||
//
|
||||
// Some terminology:
|
||||
//
|
||||
// "dependent buffer" A dependent buffer is one that the string class
|
||||
// does not own. The string class relies on some
|
||||
// external code to ensure the lifetime of the
|
||||
// dependent buffer.
|
||||
//
|
||||
// "shared buffer" A shared buffer is one that the string class
|
||||
// allocates. When it allocates a shared string
|
||||
// buffer, it allocates some additional space at
|
||||
// the beginning of the buffer for additional
|
||||
// fields, including a reference count and a
|
||||
// buffer length. See nsStringHeader.
|
||||
//
|
||||
// "adopted buffer" An adopted buffer is a raw string buffer
|
||||
// allocated on the heap (using moz_xmalloc)
|
||||
// of which the string class subsumes ownership.
|
||||
//
|
||||
// Some comments about the string flags:
|
||||
//
|
||||
// F_SHARED, F_OWNED, and F_FIXED are all mutually exlusive. They
|
||||
// indicate the allocation type of mData. If none of these flags
|
||||
// are set, then the string buffer is dependent.
|
||||
//
|
||||
// F_SHARED, F_OWNED, or F_FIXED imply F_TERMINATED. This is because
|
||||
// the string classes always allocate null-terminated buffers, and
|
||||
// non-terminated substrings are always dependent.
|
||||
//
|
||||
// F_VOIDED implies F_TERMINATED, and moreover it implies that mData
|
||||
// points to char_traits::sEmptyBuffer. Therefore, F_VOIDED is
|
||||
// mutually exclusive with F_SHARED, F_OWNED, and F_FIXED.
|
||||
//
|
||||
};
|
||||
|
||||
static_assert(sizeof(nsTSubstring_CharT) ==
|
||||
|
Loading…
Reference in New Issue
Block a user