Provide a way to iterate over a const AnnotationList

This CL implements a const iterator to allow for iteration over a const
AnnotationList. This way, the annotation list can passed as a const
reference in search only situations.

Change-Id: I53bd7871f3d914e7e7e627b6b464aa7fa79597f4
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/4984053
Reviewed-by: Mark Mentovai <mark@chromium.org>
Commit-Queue: Andre Kempe <andre.kempe@arm.com>
This commit is contained in:
André Kempe 2023-10-27 14:43:48 +01:00 committed by Crashpad LUCI CQ
parent c5e2b0313c
commit c39206f699
4 changed files with 160 additions and 2 deletions

View File

@ -246,6 +246,14 @@ class Annotation {
std::atomic<Annotation*>& link_node() { return link_node_; }
Annotation* GetLinkNode(std::memory_order order = std::memory_order_seq_cst) {
return link_node_.load(order);
}
const Annotation* GetLinkNode(
std::memory_order order = std::memory_order_seq_cst) const {
return link_node_.load(order);
}
private:
//! \brief Linked list next-node pointer. Accessed only by \sa AnnotationList.
//!

View File

@ -77,7 +77,7 @@ Annotation* AnnotationList::Iterator::operator*() const {
AnnotationList::Iterator& AnnotationList::Iterator::operator++() {
CHECK_NE(curr_, tail_);
curr_ = curr_->link_node();
curr_ = curr_->GetLinkNode();
return *this;
}
@ -86,12 +86,42 @@ bool AnnotationList::Iterator::operator==(
return curr_ == other.curr_;
}
AnnotationList::ConstIterator::ConstIterator(const Annotation* head,
const Annotation* tail)
: curr_(head), tail_(tail) {}
AnnotationList::ConstIterator::~ConstIterator() = default;
const Annotation* AnnotationList::ConstIterator::operator*() const {
CHECK_NE(curr_, tail_);
return curr_;
}
AnnotationList::ConstIterator& AnnotationList::ConstIterator::operator++() {
CHECK_NE(curr_, tail_);
curr_ = curr_->GetLinkNode();
return *this;
}
bool AnnotationList::ConstIterator::operator==(
const AnnotationList::ConstIterator& other) const {
return curr_ == other.curr_;
}
AnnotationList::Iterator AnnotationList::begin() {
return Iterator(head_.link_node(), tail_pointer_);
return Iterator(head_.GetLinkNode(), tail_pointer_);
}
AnnotationList::ConstIterator AnnotationList::cbegin() const {
return ConstIterator(head_.GetLinkNode(), tail_pointer_);
}
AnnotationList::Iterator AnnotationList::end() {
return Iterator(&tail_, tail_pointer_);
}
AnnotationList::ConstIterator AnnotationList::cend() const {
return ConstIterator(&tail_, tail_pointer_);
}
} // namespace crashpad

View File

@ -80,11 +80,37 @@ class AnnotationList {
// Copy and assign are required.
};
//! \brief An InputIterator for iterating a const AnnotationList.
class ConstIterator {
public:
~ConstIterator();
const Annotation* operator*() const;
ConstIterator& operator++();
bool operator==(const ConstIterator& other) const;
bool operator!=(const ConstIterator& other) const {
return !(*this == other);
}
private:
friend class AnnotationList;
ConstIterator(const Annotation* head, const Annotation* tail);
const Annotation* curr_;
const Annotation* const tail_;
// Copy and assign are required.
};
//! \brief Returns an iterator to the first element of the annotation list.
Iterator begin();
ConstIterator begin() const { return cbegin(); }
ConstIterator cbegin() const;
//! \brief Returns an iterator past the last element of the annotation list.
Iterator end();
ConstIterator end() const { return cend(); }
ConstIterator cend() const;
protected:
#if BUILDFLAG(IS_IOS)

View File

@ -128,6 +128,100 @@ TEST_F(AnnotationList, DuplicateKeys) {
EXPECT_EQ(1u, annotations.size());
}
TEST_F(AnnotationList, IteratorSingleAnnotation) {
ASSERT_EQ(annotations_.begin(), annotations_.end());
ASSERT_EQ(annotations_.cbegin(), annotations_.cend());
one_.Set("1");
auto iterator = annotations_.begin();
auto const_iterator = annotations_.cbegin();
ASSERT_NE(iterator, annotations_.end());
ASSERT_NE(const_iterator, annotations_.cend());
EXPECT_EQ(*iterator, &one_);
EXPECT_EQ(*const_iterator, &one_);
++iterator;
++const_iterator;
EXPECT_EQ(iterator, annotations_.end());
EXPECT_EQ(const_iterator, annotations_.cend());
}
TEST_F(AnnotationList, IteratorMultipleAnnotationsInserted) {
ASSERT_EQ(annotations_.begin(), annotations_.end());
ASSERT_EQ(annotations_.cbegin(), annotations_.cend());
one_.Set("1");
two_.Set("2");
// New annotations are inserted to the beginning of the list. Hence, |two_|
// must be the first annotation, followed by |one_|.
{
auto iterator = annotations_.begin();
auto const_iterator = annotations_.cbegin();
ASSERT_NE(iterator, annotations_.end());
ASSERT_NE(const_iterator, annotations_.cend());
EXPECT_EQ(*iterator, &two_);
EXPECT_EQ(*const_iterator, &two_);
++iterator;
++const_iterator;
ASSERT_NE(iterator, annotations_.end());
ASSERT_NE(const_iterator, annotations_.cend());
EXPECT_EQ(*iterator, &one_);
EXPECT_EQ(*const_iterator, &one_);
++iterator;
++const_iterator;
EXPECT_EQ(iterator, annotations_.end());
EXPECT_EQ(const_iterator, annotations_.cend());
}
}
TEST_F(AnnotationList, IteratorMultipleAnnotationsInsertedAndRemoved) {
ASSERT_EQ(annotations_.begin(), annotations_.end());
ASSERT_EQ(annotations_.cbegin(), annotations_.cend());
one_.Set("1");
two_.Set("2");
one_.Clear();
two_.Clear();
// Even after clearing, Annotations are still inserted in the list and
// reachable via the iterators.
auto iterator = annotations_.begin();
auto const_iterator = annotations_.cbegin();
ASSERT_NE(iterator, annotations_.end());
ASSERT_NE(const_iterator, annotations_.cend());
EXPECT_EQ(*iterator, &two_);
EXPECT_EQ(*const_iterator, &two_);
++iterator;
++const_iterator;
ASSERT_NE(iterator, annotations_.end());
ASSERT_NE(const_iterator, annotations_.cend());
EXPECT_EQ(*iterator, &one_);
EXPECT_EQ(*const_iterator, &one_);
++iterator;
++const_iterator;
EXPECT_EQ(iterator, annotations_.end());
EXPECT_EQ(const_iterator, annotations_.cend());
}
class RaceThread : public Thread {
public:
explicit RaceThread(test::AnnotationList* test) : Thread(), test_(test) {}