ENH: PtrList iterate over non-null entries (#2702)
- the iterator/const_iterator now skip any nullptr entries, which enables the following code to work even if the PtrList contains nullptr: for (const auto& intf : interfaces) { // Do something ... } - this is a change in behaviour compared to OpenFOAM-v2212 and earlier, but is non-breaking: * Lists without null entries will traverse exactly as before. * Lists with null entries will now traverse correctly without provoking a FatalError.
This commit is contained in:
parent
afee861af9
commit
9729617ae3
@ -268,10 +268,7 @@ Ostream& report
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
PtrList<Scalar> list1(10);
|
||||
PtrList<Scalar> list2(15);
|
||||
PtrList<Scalar> listApp;
|
||||
|
||||
#if 0
|
||||
{
|
||||
DLPtrList<Scalar> llist1;
|
||||
llist1.push_front(new Scalar(100));
|
||||
@ -301,8 +298,10 @@ int main(int argc, char *argv[])
|
||||
<< "for-: " << it << endl;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Same but as SLPtrList
|
||||
#if 0
|
||||
{
|
||||
SLPtrList<Scalar> llist1;
|
||||
llist1.push_front(new Scalar(100));
|
||||
@ -318,18 +317,23 @@ int main(int argc, char *argv[])
|
||||
PtrList<Scalar> list1b(llist1);
|
||||
Info<< list1b << endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
PtrList<Scalar> list1(10);
|
||||
|
||||
forAll(list1, i)
|
||||
{
|
||||
list1.set(i, new Scalar(1.3*i));
|
||||
}
|
||||
|
||||
PtrList<Scalar> list2(15);
|
||||
Info<< "Emplace set " << list2.size() << " values" << nl;
|
||||
forAll(list2, i)
|
||||
{
|
||||
list2.emplace(i, (10 + 1.3*i));
|
||||
}
|
||||
|
||||
PtrList<Scalar> listApp;
|
||||
for (label i = 0; i < 5; ++i)
|
||||
{
|
||||
listApp.emplace_back(1.3*i);
|
||||
@ -375,6 +379,24 @@ int main(int argc, char *argv[])
|
||||
list1.set(i, nullptr);
|
||||
}
|
||||
|
||||
{
|
||||
Info<< "range-for of list (" << list1.count() << '/'
|
||||
<< list1.size() << ") non-null entries" << nl
|
||||
<< "(" << nl;
|
||||
for (const auto& item : list1)
|
||||
{
|
||||
Info<< " " << item << nl;
|
||||
}
|
||||
Info<< ")" << nl;
|
||||
}
|
||||
{
|
||||
Info<< "iterate on non-null:" << endl;
|
||||
forAllConstIters(list1, iter)
|
||||
{
|
||||
Info<< " " << iter.key() << " : " << iter.val() << nl;
|
||||
}
|
||||
}
|
||||
|
||||
Info<< "release some items:" << endl;
|
||||
|
||||
for (label i = -2; i < 5; i++)
|
||||
|
@ -6,7 +6,7 @@
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2011-2016 OpenFOAM Foundation
|
||||
Copyright (C) 2018-2022 OpenCFD Ltd.
|
||||
Copyright (C) 2018-2023 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
@ -30,7 +30,41 @@ Class
|
||||
Description
|
||||
A list of pointers to objects of type \<T\>, without allocation/deallocation
|
||||
management of the pointers - this is to be done elsewhere.
|
||||
The operator[] returns a reference to the object, not the pointer.
|
||||
The operator[] returns a reference to the object (not the pointer).
|
||||
|
||||
The iterators are similar to bitSet in that they skip nullptr entries,
|
||||
and also return a value (like the list operator[] does).
|
||||
|
||||
When traversing lists, it possible to test the validity directly:
|
||||
\code
|
||||
forAll(interfaces, i)
|
||||
{
|
||||
if (interfaces.test(i))
|
||||
{
|
||||
// Interface is set, do something
|
||||
const auto& intf = interfaces[i];
|
||||
...
|
||||
}
|
||||
}
|
||||
\endcode
|
||||
The lists can also be traversed with a for-range
|
||||
(in OpenFOAM-v2212 and earlier this would have failed on nullptr entries):
|
||||
\code
|
||||
for (const auto& intf : interfaces)
|
||||
{
|
||||
// Do something
|
||||
...
|
||||
}
|
||||
\endcode
|
||||
|
||||
It is also possible to traverse with non-null entries
|
||||
and use key/val access (like HashTable naming):
|
||||
\code
|
||||
forAllConstIters(interfaces, iter)
|
||||
{
|
||||
Info<< "entry " << iter.key() << " : " << iter.val() << nl;
|
||||
}
|
||||
\endcode
|
||||
|
||||
Note
|
||||
The class definition is such that it contains a list of pointers, but
|
||||
@ -221,7 +255,7 @@ public:
|
||||
inline label size() const noexcept;
|
||||
|
||||
//- The number of non-null entries in the list
|
||||
inline label size_nonNull() const noexcept;
|
||||
inline label count() const noexcept;
|
||||
|
||||
//- Reference to the first element of the list
|
||||
inline T& front();
|
||||
@ -344,81 +378,209 @@ public:
|
||||
friend Ostream& operator<< <T>(Ostream& os, const UPtrList<T>& list);
|
||||
|
||||
|
||||
// Iterators
|
||||
protected:
|
||||
|
||||
//- Forward iterator with non-const access
|
||||
class iterator
|
||||
// Iterators and helpers
|
||||
|
||||
//- Internally used base for iterator and const_iterator
|
||||
template<bool Const> class Iterator;
|
||||
|
||||
//- Allow iterator access to internals
|
||||
friend class Iterator<true>;
|
||||
|
||||
//- Allow iterator access to internals
|
||||
friend class Iterator<false>;
|
||||
|
||||
|
||||
//- The iterator base for UPtrList (internal use only).
|
||||
// Iterates non-nullptr entries.
|
||||
|
||||
template<bool Const>
|
||||
class Iterator
|
||||
{
|
||||
// Pointer to parent
|
||||
T** ptr_;
|
||||
public:
|
||||
|
||||
// Typedefs
|
||||
|
||||
//- The list container type
|
||||
using list_type = typename std::conditional
|
||||
<
|
||||
Const,
|
||||
const UPtrList<T>,
|
||||
UPtrList<T>
|
||||
>::type;
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
// Protected Data
|
||||
|
||||
//- The parent being iterated
|
||||
// Uses pointer for default copy/assignment
|
||||
list_type* list_;
|
||||
|
||||
//- The position within the list
|
||||
label pos_;
|
||||
|
||||
// Friendship with UPtrList, for begin constructor
|
||||
friend class UPtrList;
|
||||
|
||||
|
||||
// Protected Constructors
|
||||
|
||||
//- Default construct. Also the same as the end iterator
|
||||
inline constexpr Iterator() noexcept;
|
||||
|
||||
//- Construct begin iterator
|
||||
inline explicit Iterator(list_type* list);
|
||||
|
||||
|
||||
// Protected Member Functions
|
||||
|
||||
//- Increment to the next non-null position
|
||||
inline void increment();
|
||||
|
||||
//- Permit explicit cast to the other (const/non-const) iterator
|
||||
template<bool Any>
|
||||
explicit operator const Iterator<Any>&() const
|
||||
{
|
||||
return *reinterpret_cast<const Iterator<Any>*>(this);
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = T;
|
||||
using difference_type = label;
|
||||
// Member Functions/Operators
|
||||
|
||||
//- True if iterator points to a non-null entry
|
||||
bool good() const noexcept { return (list_ && pos_ >= 0); }
|
||||
|
||||
//- The iterator position/index within the list
|
||||
label key() const noexcept { return pos_; }
|
||||
|
||||
//- Compare hash-entry element pointers.
|
||||
// Independent of const/non-const access
|
||||
template<bool Any>
|
||||
bool operator==(const Iterator<Any>& iter) const noexcept
|
||||
{
|
||||
return (pos_ == iter.pos_);
|
||||
}
|
||||
|
||||
template<bool Any>
|
||||
bool operator!=(const Iterator<Any>& iter) const noexcept
|
||||
{
|
||||
return (pos_ != iter.pos_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// Iterators
|
||||
|
||||
//- Forward iterator with non-const access
|
||||
class iterator : public Iterator<false>
|
||||
{
|
||||
public:
|
||||
// using iterator_category = std::forward_iterator_tag;
|
||||
// using difference_type = label;
|
||||
using pointer = T*;
|
||||
using reference = T&;
|
||||
friend class const_iterator;
|
||||
|
||||
//- Construct for a given entry
|
||||
inline iterator(T** ptr) noexcept;
|
||||
|
||||
// Member Functions
|
||||
// Constructors
|
||||
|
||||
//- Return pointer, can be nullptr.
|
||||
pointer get() const { return *ptr_; }
|
||||
//- Default construct - an end iterator
|
||||
constexpr iterator() noexcept = default;
|
||||
|
||||
// Member Operators
|
||||
//- Copy construct from similar access type
|
||||
explicit iterator(const Iterator<false>& iter)
|
||||
:
|
||||
Iterator<false>(iter)
|
||||
{}
|
||||
|
||||
inline pointer operator->() const;
|
||||
inline reference operator*() const;
|
||||
|
||||
// Forward Iteration
|
||||
inline iterator& operator++() noexcept;
|
||||
inline iterator operator++(int) noexcept;
|
||||
// Member Functions/Operators
|
||||
|
||||
inline bool operator==(const iterator& iter) const noexcept;
|
||||
inline bool operator!=(const iterator& iter) const noexcept;
|
||||
//- Pointer to the referenced object (failsafe)
|
||||
inline pointer get() const;
|
||||
|
||||
//- Reference to the object
|
||||
inline reference val() const;
|
||||
|
||||
//- Pointer to the referenced object
|
||||
pointer operator->() const { return this->get(); }
|
||||
|
||||
//- Reference to the object
|
||||
reference operator*() const { return this->val(); }
|
||||
|
||||
//- Legacy call operator: reference to the object
|
||||
reference operator()() const { return this->val(); }
|
||||
|
||||
//- Move to the next non-nullptr entry
|
||||
inline iterator& operator++();
|
||||
inline iterator operator++(int);
|
||||
};
|
||||
|
||||
|
||||
//- Forward iterator with const access
|
||||
class const_iterator
|
||||
class const_iterator : public Iterator<true>
|
||||
{
|
||||
// Pointer to parent
|
||||
const T* const* ptr_;
|
||||
|
||||
public:
|
||||
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = const T;
|
||||
using difference_type = label;
|
||||
// using iterator_category = std::forward_iterator_tag;
|
||||
// using difference_type = label;
|
||||
using pointer = const T*;
|
||||
using reference = const T&;
|
||||
|
||||
//- Construct for a given entry
|
||||
inline const_iterator(const T* const* ptr) noexcept;
|
||||
|
||||
//- Copy construct from non-const iterator
|
||||
inline const_iterator(const iterator& iter) noexcept;
|
||||
// Generated Methods
|
||||
|
||||
// Member Functions
|
||||
//- Default construct (end iterator)
|
||||
const_iterator() = default;
|
||||
|
||||
//- Return pointer, can be nullptr.
|
||||
pointer get() const { return *ptr_; }
|
||||
//- Copy construct
|
||||
const_iterator(const const_iterator&) = default;
|
||||
|
||||
// Member Operators
|
||||
//- Copy assignment
|
||||
const_iterator& operator=(const const_iterator&) = default;
|
||||
|
||||
inline pointer operator->() const;
|
||||
inline reference operator*() const;
|
||||
|
||||
// Forward Iteration
|
||||
inline const_iterator& operator++() noexcept;
|
||||
inline const_iterator operator++(int) noexcept;
|
||||
// Constructors
|
||||
|
||||
inline bool operator==(const const_iterator& iter) const noexcept;
|
||||
inline bool operator!=(const const_iterator& iter) const noexcept;
|
||||
//- Copy construct from any access type
|
||||
template<bool Any>
|
||||
const_iterator(const Iterator<Any>& iter)
|
||||
:
|
||||
Iterator<true>(static_cast<const Iterator<Any>&>(iter))
|
||||
{}
|
||||
|
||||
//- Implicit conversion from dissimilar access type
|
||||
const_iterator(const iterator& iter)
|
||||
:
|
||||
const_iterator(reinterpret_cast<const const_iterator&>(iter))
|
||||
{}
|
||||
|
||||
|
||||
// Member Functions/Operators
|
||||
|
||||
//- Pointer to the referenced object (failsafe)
|
||||
inline pointer get() const;
|
||||
|
||||
//- Reference to the object
|
||||
inline reference val() const;
|
||||
|
||||
//- Pointer to the referenced object
|
||||
pointer operator->() const { return this->get(); }
|
||||
|
||||
//- Reference to the object
|
||||
reference operator*() const { return this->val(); }
|
||||
|
||||
//- Legacy call operator: reference to the object
|
||||
reference operator()() const { return this->val(); }
|
||||
|
||||
//- Move to the next non-nullptr entry
|
||||
inline const_iterator& operator++();
|
||||
inline const_iterator operator++(int);
|
||||
};
|
||||
|
||||
|
||||
@ -429,20 +591,20 @@ public:
|
||||
T** end_ptr() noexcept { return ptrs_.end(); }
|
||||
|
||||
|
||||
//- Return an iterator to begin of UPtrList traversal
|
||||
inline iterator begin() noexcept;
|
||||
//- Return iterator to begin traversal of non-nullptr entries.
|
||||
inline iterator begin();
|
||||
|
||||
//- Return iterator beyond end of UPtrList traversal
|
||||
inline iterator end() noexcept;
|
||||
|
||||
//- Return const_iterator to begin of UPtrList traversal
|
||||
inline const_iterator cbegin() const noexcept;
|
||||
//- Return const_iterator to begin traversal of non-nullptr entries.
|
||||
inline const_iterator cbegin() const;
|
||||
|
||||
//- Return const_iterator beyond end of UPtrList traversal
|
||||
inline const_iterator cend() const noexcept;
|
||||
|
||||
//- Return const_iterator to begin of UPtrList traversal
|
||||
inline const_iterator begin() const noexcept;
|
||||
//- Return const_iterator to begin traversal of non-nullptr entries.
|
||||
inline const_iterator begin() const;
|
||||
|
||||
//- Return const_iterator beyond end of UPtrList traversal
|
||||
inline const_iterator end() const noexcept;
|
||||
|
@ -124,7 +124,7 @@ inline Foam::label Foam::UPtrList<T>::size() const noexcept
|
||||
|
||||
|
||||
template<class T>
|
||||
inline Foam::label Foam::UPtrList<T>::size_nonNull() const noexcept
|
||||
inline Foam::label Foam::UPtrList<T>::count() const noexcept
|
||||
{
|
||||
return ptrs_.count();
|
||||
}
|
||||
@ -292,139 +292,139 @@ inline T& Foam::UPtrList<T>::operator[](const label i)
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * iterator * * * * * * * * * * * * * * * * //
|
||||
// * * * * * * * * * * * * * * * iterator base * * * * * * * * * * * * * * * //
|
||||
|
||||
template<class T>
|
||||
inline Foam::UPtrList<T>::iterator::
|
||||
iterator(T** ptr) noexcept
|
||||
template<bool Const>
|
||||
inline constexpr
|
||||
Foam::UPtrList<T>::Iterator<Const>::Iterator() noexcept
|
||||
:
|
||||
ptr_(ptr)
|
||||
list_(nullptr),
|
||||
pos_(-1)
|
||||
{}
|
||||
|
||||
|
||||
template<class T>
|
||||
inline T* Foam::UPtrList<T>::iterator::operator->() const
|
||||
template<bool Const>
|
||||
inline Foam::UPtrList<T>::Iterator<Const>::Iterator
|
||||
(
|
||||
list_type* list
|
||||
)
|
||||
:
|
||||
list_(list),
|
||||
pos_(-1)
|
||||
{
|
||||
return *ptr_;
|
||||
if (list_)
|
||||
{
|
||||
pos_ = list_->find_next(-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
inline T& Foam::UPtrList<T>::iterator::operator*() const
|
||||
template<bool Const>
|
||||
inline void Foam::UPtrList<T>::Iterator<Const>::increment()
|
||||
{
|
||||
return **ptr_;
|
||||
//TDB: extra safety? if (iter.good())
|
||||
{
|
||||
pos_ = list_->find_next(pos_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * STL iterator * * * * * * * * * * * * * * //
|
||||
|
||||
template<class T>
|
||||
inline T* Foam::UPtrList<T>::iterator::get() const
|
||||
{
|
||||
return this->good() ? this->list_->get(this->pos_) : nullptr;
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
inline T& Foam::UPtrList<T>::iterator::val() const
|
||||
{
|
||||
return this->list_->at(this->pos_);
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
inline typename Foam::UPtrList<T>::iterator&
|
||||
Foam::UPtrList<T>::iterator::operator++() noexcept
|
||||
Foam::UPtrList<T>::iterator::operator++()
|
||||
{
|
||||
++ptr_;
|
||||
this->increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
inline typename Foam::UPtrList<T>::iterator
|
||||
Foam::UPtrList<T>::iterator::operator++(int) noexcept
|
||||
Foam::UPtrList<T>::iterator::operator++(int)
|
||||
{
|
||||
iterator iter(*this);
|
||||
++ptr_;
|
||||
this->increment();
|
||||
return iter;
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * STL const_iterator * * * * * * * * * * * * //
|
||||
|
||||
template<class T>
|
||||
inline bool Foam::UPtrList<T>::iterator::
|
||||
operator==(const iterator& iter) const noexcept
|
||||
inline const T* Foam::UPtrList<T>::const_iterator::get() const
|
||||
{
|
||||
return ptr_ == iter.ptr_;
|
||||
return this->good() ? this->list_->get(this->pos_) : nullptr;
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
inline bool Foam::UPtrList<T>::iterator::
|
||||
operator!=(const iterator& iter) const noexcept
|
||||
inline const T& Foam::UPtrList<T>::const_iterator::val() const
|
||||
{
|
||||
return ptr_ != iter.ptr_;
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * const_iterator * * * * * * * * * * * * * * //
|
||||
|
||||
template<class T>
|
||||
inline Foam::UPtrList<T>::const_iterator::
|
||||
const_iterator(const T* const* ptr) noexcept
|
||||
:
|
||||
ptr_(ptr)
|
||||
{}
|
||||
|
||||
|
||||
template<class T>
|
||||
inline Foam::UPtrList<T>::const_iterator::
|
||||
const_iterator(const iterator& iter) noexcept
|
||||
:
|
||||
ptr_(iter.ptr_)
|
||||
{}
|
||||
|
||||
|
||||
template<class T>
|
||||
inline const T* Foam::UPtrList<T>::const_iterator::operator->() const
|
||||
{
|
||||
return *ptr_;
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
inline const T& Foam::UPtrList<T>::const_iterator::operator*() const
|
||||
{
|
||||
return **ptr_;
|
||||
return this->list_->at(this->pos_);
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
inline typename Foam::UPtrList<T>::const_iterator&
|
||||
Foam::UPtrList<T>::const_iterator::operator++() noexcept
|
||||
Foam::UPtrList<T>::const_iterator::operator++()
|
||||
{
|
||||
++ptr_;
|
||||
this->increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
inline typename Foam::UPtrList<T>::const_iterator
|
||||
Foam::UPtrList<T>::const_iterator::operator++(int) noexcept
|
||||
Foam::UPtrList<T>::const_iterator::operator++(int)
|
||||
{
|
||||
const_iterator iter(*this);
|
||||
++ptr_;
|
||||
this->increment();
|
||||
return iter;
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
inline bool Foam::UPtrList<T>::const_iterator::
|
||||
operator==(const const_iterator& iter) const noexcept
|
||||
{
|
||||
return ptr_ == iter.ptr_;
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
inline bool Foam::UPtrList<T>::const_iterator::
|
||||
operator!=(const const_iterator& iter) const noexcept
|
||||
{
|
||||
return ptr_ != iter.ptr_;
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
template<class T>
|
||||
inline typename Foam::UPtrList<T>::iterator
|
||||
Foam::UPtrList<T>::begin() noexcept
|
||||
Foam::UPtrList<T>::begin()
|
||||
{
|
||||
return ptrs_.begin();
|
||||
return iterator(Iterator<false>(this));
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
inline typename Foam::UPtrList<T>::const_iterator
|
||||
Foam::UPtrList<T>::begin() const
|
||||
{
|
||||
return const_iterator(Iterator<true>(this));
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
inline typename Foam::UPtrList<T>::const_iterator
|
||||
Foam::UPtrList<T>::cbegin() const
|
||||
{
|
||||
return const_iterator(Iterator<true>(this));
|
||||
}
|
||||
|
||||
|
||||
@ -432,31 +432,7 @@ template<class T>
|
||||
inline typename Foam::UPtrList<T>::iterator
|
||||
Foam::UPtrList<T>::end() noexcept
|
||||
{
|
||||
return ptrs_.end();
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
inline typename Foam::UPtrList<T>::const_iterator
|
||||
Foam::UPtrList<T>::cbegin() const noexcept
|
||||
{
|
||||
return ptrs_.cbegin();
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
inline typename Foam::UPtrList<T>::const_iterator
|
||||
Foam::UPtrList<T>::cend() const noexcept
|
||||
{
|
||||
return ptrs_.cend();
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
inline typename Foam::UPtrList<T>::const_iterator
|
||||
Foam::UPtrList<T>::begin() const noexcept
|
||||
{
|
||||
return ptrs_.begin();
|
||||
return iterator();
|
||||
}
|
||||
|
||||
|
||||
@ -464,7 +440,15 @@ template<class T>
|
||||
inline typename Foam::UPtrList<T>::const_iterator
|
||||
Foam::UPtrList<T>::end() const noexcept
|
||||
{
|
||||
return ptrs_.end();
|
||||
return UPtrList<T>::const_iterator();
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
inline typename Foam::UPtrList<T>::const_iterator
|
||||
Foam::UPtrList<T>::cend() const noexcept
|
||||
{
|
||||
return UPtrList<T>::const_iterator();
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user