Lists#

pw_containers: Generic collections of objects for embedded devices

A linked list is an ordered collection of items in which each item is associated with pointers to one or more of its adjacent items. Pigweed provides intrusive lists, meaning the pointers are stored within the items themselves. This allows an arbitrary number of items to be added to a list without requiring additional memory beyond that of the items themselves.

pw::IntrusiveList#

pw::IntrusiveList provides an embedded-friendly, double-linked, intrusive list implementation. An intrusive list is a type of linked list that embeds list metadata, such as a “next” pointer, into the list object itself. This allows the construction of a linked list without the need to dynamically allocate list entries.

This class is similar to std::list<T>, except that the type of items to be added must derive from pw::IntrusiveList<T>::Item.

In C, an intrusive list can be made by manually including the “next” pointer as a member of the object’s struct. pw::IntrusiveList uses C++ features to simplify the process of creating an intrusive list. It provides classes that list elements can inherit from, protecting the list metadata from being accessed by the item class.

Note

Originally, pw::IntrusiveList<T> was implemented as a singly linked list. To facilitate migration to pw::IntrusiveForwardList<T>::Item, this original implementation can still be temporarily used by enabling the PW_CONTAINERS_USE_LEGACY_INTRUSIVE_LIST module configuration option.

See also Using items with multiple containers.

Example#

 1using pw::containers::future::IntrusiveList;
 2
 3class IntWrapper : public IntrusiveList<IntWrapper>::Item {
 4 public:
 5  IntWrapper(int value) : value_(value) {}
 6  int value() const { return value_; }
 7
 8 private:
 9  const int value_;
10};
11
12// This example is adapted from https://en.cppreference.com/w/cpp/container/list
13int CreateAndSum() {
14  // Create a list containing integers
15  std::array<IntWrapper, 4> wrappers = {{{6}, {7}, {3}, {0}}};
16  IntrusiveList<IntWrapper> list(wrappers.begin(), wrappers.end());
17
18  // Add an integer to the front of the list
19  IntWrapper eight(8);
20  list.push_front(eight);
21
22  // Add an integer to the back of the list
23  IntWrapper nine(9);
24  list.push_back(nine);
25
26  // Insert an integer before 3 by searching
27  IntWrapper five(5);
28  auto it = list.begin();
29  while (it != list.end()) {
30    if (it->value() == 3) {
31      list.insert(it, five);
32      break;
33    }
34    ++it;
35  }
36
37  // Sum the list
38  int sum = 0;
39  for (const auto& wrapper : list) {
40    sum += wrapper.value();
41  }
42
43  // It is an error for items to go out of scope while still listed, or for a
44  // list to go out of scope while it still has items.
45  list.clear();
46
47  return sum;
48}

If you need to add this item to containers of more than one type, see Using items with multiple containers,

pw::IntrusiveForwardList#

pw::IntrusiveForwardList provides an embedded-friendly, singly linked, intrusive list implementation. It is very similar to pw::IntrusiveList, except that it is singly rather than doubly linked.

This class is similar to std::forward_list<T>. Items to be added must derive from pw::IntrusiveForwardList<T>::Item.

See also Using items with multiple containers.

Example#

 1class Square : public pw::IntrusiveForwardList<Square>::Item {
 2 public:
 3  Square(size_t side_length) : side_length_(side_length) {}
 4  size_t Area() const { return side_length_ * side_length_; }
 5
 6 private:
 7  size_t side_length_;
 8};
 9
10class SquareList {
11 public:
12  // These elements are not copied into the linked list, the original objects
13  // are just chained together and can be accessed via `list_`.
14  SquareList() : list_(squares_.begin(), squares_.end()) {}
15
16  // It is an error for items to go out of scope while still listed, or for a
17  // list to go out of scope while it still has items.
18  ~SquareList() { list_.clear(); }
19
20  // The list can be iterated over normally.
21  size_t SumAreas() const {
22    size_t sum = 0;
23    for (const auto& square : list_) {
24      sum += square.Area();
25    }
26    return sum;
27  }
28
29  // Like `std::forward_list`, an iterator is invalidated when the item it
30  // refers to is removed. It is *NOT* safe to remove items from a list while
31  // iterating over it in a range-based for loop.
32  //
33  // To remove items while iterating, use an iterator to the previous item. If
34  // only removing items, consider using `remove_if` instead.
35  size_t RemoveAndSumAreas(size_t area_to_remove) {
36    size_t sum = 0;
37    auto previous = list_.before_begin();
38    auto current = list_.begin();
39    while (current != list_.end()) {
40      if (current->Area() == area_to_remove) {
41        current = list_.erase_after(previous);
42      } else {
43        sum += current->Area();
44        previous = current;
45        ++current;
46      }
47    }
48    return sum;
49  }
50
51 private:
52  std::array<Square, 3> squares_ = {{{1}, {20}, {400}}};
53  pw::IntrusiveForwardList<Square> list_;
54};

If you need to add this item to containers of more than one type, see Using items with multiple containers,

API reference#

Moved: pw_containers_lists

Performance considerations#

Items only include pointers to the next item. To reach previous items, the list maintains a cycle of items so that the first item can be reached from the last. This structure means certain operations have linear complexity in terms of the number of items in the list, i.e. they are “O(n)”:

  • Removing an item from a list using pw::IntrusiveForwardList<T>::remove(const T&).

  • Getting the list size using pw::IntrusiveForwardList<T>::size().

When using a pw::IntrusiveForwardList<T> in a performance-critical section or with many items, authors should prefer to avoid these methods. For example, it may be preferable to create items that together with their storage outlive the list.

Notably, pw::IntrusiveForwardList<T>::end() is constant complexity (i.e. “O(1)”). As a result iterating over a list does not incur an additional penalty.

Size reports#

The tables below illustrate the following scenarios:

  • Scenarios related to IntrusiveList:

    • The memory and code size cost incurred by a adding a single IntrusiveList.

    • The memory and code size cost incurred by adding another IntrusiveList with a different type. As IntrusiveList is templated on type, this results in additional code being generated.

  • Scenarios related to IntrusiveForwardList:

    • The memory and code size cost incurred by a adding a single IntrusiveForwardList.

    • The memory and code size cost incurred by adding another IntrusiveForwardList with a different type. As IntrusiveForwardList is templated on type, this results in additional code being generated.

  • The memory and code size cost incurred by a adding both an IntrusiveList and an IntrusiveForwardList of the same type. These types reuse code, so the combined sum is less than the sum of its parts.

Label

Segment

Delta

IntrusiveList

FLASH

+88

[section .rodata]

+34

pw::containers::internal::GenericIntrusiveList<>::insert_after()

+4

pw::containers::size_report::Measure()

+2

pw::allocator::LibCAllocator::DoReallocate()

-4

vClearInterruptMaskFromISR

NEW

+236

_ZN2pw10containers11size_report20MeasureIntrusiveListINS1_8ListItemIjEETpTnRiJENSt3__211__wrap_iterIPS4_EEEEiT1_SA_j

NEW

+124

pw::containers::internal::GenericIntrusiveList<>::merge<>()

NEW

+80

pw::containers::internal::GenericIntrusiveList<>::sort<>()

NEW

+48

pw::containers::internal::GenericIntrusiveList<>::unique<>()

NEW

+48

pw::containers::internal::IntrusiveListItemBase<>::replace()

NEW

+46

pw::containers::size_report::MeasureContainer<>()

NEW

+44

pw::containers::internal::GenericIntrusiveList<>::remove_if<>()

NEW

+44

pw::containers::internal::GenericIntrusiveList<>::swap()

NEW

+40

pw::containers::internal::CheckIntrusiveContainerIsEmpty()

NEW

+36

pw::containers::internal::GenericIntrusiveList<>::splice_after()

NEW

+30

_ZNSt3__214__reverse_implB8nn210000INS_17_ClassicAlgPolicyEN2pw10containers8internal21BidirectionalIteratorINS3_11size_report8ListItemIjEENS4_17IntrusiveListItemEEEEEvT0_SB_NS_26bidirectional_iterator_tagE

NEW

+30

pw::containers::future::IntrusiveList<>::splice()

NEW

+24

_ZNSt3__24swapB8nn210000IN2pw10containers11size_report8ListItemIjEEEENS_9enable_ifIXaasr21is_move_constructibleIT_EE5valuesr18is_move_assignableIS7_EE5valueEvE4typeERS7_SA_

NEW

+24

pw::containers::internal::GenericIntrusiveList<>::assign<>()

NEW

+24

pw::containers::internal::GenericIntrusiveList<>::~GenericIntrusiveList()

NEW

+22

pw::containers::internal::GenericIntrusiveList<>::insert_after<>()

NEW

+20

pw::containers::internal::GenericIntrusiveList<>::clear()

NEW

+20

pw::containers::internal::IntrusiveListItemBase<>::~IntrusiveListItemBase()

NEW

+16

_ZNSt3__28distanceB8nn210000IN2pw10containers8internal21BidirectionalIteratorIKNS2_11size_report8ListItemIjEEKNS3_17IntrusiveListItemEEEEENS_15iterator_traitsIT_E15difference_typeESD_SD_

NEW

+14

pw::containers::internal::GenericIntrusiveList<>::remove()

NEW

+12

pw::containers::future::IntrusiveList<>::reverse()

NEW

+12

pw::containers::future::IntrusiveList<>::size()

NEW

+10

pw::containers::future::IntrusiveList<>::pop_front()

NEW

+8

_ZNSt3__29__reverseB8nn210000INS_17_ClassicAlgPolicyEN2pw10containers8internal21BidirectionalIteratorINS3_11size_report8ListItemIjEENS4_17IntrusiveListItemEEESA_EEvT0_T1_

NEW

+8

pw::containers::future::IntrusiveList<>::merge()

NEW

+8

pw::containers::future::IntrusiveList<>::sort()

NEW

+8

pw::containers::future::IntrusiveList<>::unique()

+1,160

RAM

+1

__Thumbv6MABSLongThunk_best_effort_wfe_or_timeout

NEW

+119

pw::containers::size_report::GetItems<>()::items

NEW

+8

pw::containers::size_report::GetContainer<>()::container

+128

Additional IntrusiveList with different item type

FLASH

+148

pw::containers::internal::GenericIntrusiveList<>::merge<>()

+80

pw::containers::internal::GenericIntrusiveList<>::sort<>()

+56

pw::containers::internal::GenericIntrusiveList<>::unique<>()

+46

pw::containers::size_report::MeasureContainer<>()

+30

pw::containers::future::IntrusiveList<>::splice()

+20

pw::containers::size_report::Measure()

+24

pw::containers::internal::GenericIntrusiveList<>::assign<>()

-2

pw::allocator::LibCAllocator::DoReallocate()

+22

pw::containers::internal::GenericIntrusiveList<>::insert_after<>()

+12

pw::containers::future::IntrusiveList<>::reverse()

+12

pw::containers::future::IntrusiveList<>::size()

+10

pw::containers::future::IntrusiveList<>::pop_front()

+8

pw::containers::future::IntrusiveList<>::merge()

+8

pw::containers::future::IntrusiveList<>::sort()

+8

pw::containers::future::IntrusiveList<>::unique()

+12

vClearInterruptMaskFromISR

NEW

+236

_ZN2pw10containers11size_report20MeasureIntrusiveListINS1_8ListItemIyEETpTnRiJENSt3__211__wrap_iterIPS4_EEEEiT1_SA_j

NEW

+32

_ZNSt3__24swapB8nn210000IN2pw10containers11size_report8ListItemIyEEEENS_9enable_ifIXaasr21is_move_constructibleIT_EE5valuesr18is_move_assignableIS7_EE5valueEvE4typeERS7_SA_

NEW

+30

_ZNSt3__214__reverse_implB8nn210000INS_17_ClassicAlgPolicyEN2pw10containers8internal21BidirectionalIteratorINS3_11size_report8ListItemIyEENS4_17IntrusiveListItemEEEEEvT0_SB_NS_26bidirectional_iterator_tagE

NEW

+16

_ZNSt3__28distanceB8nn210000IN2pw10containers8internal21BidirectionalIteratorIKNS2_11size_report8ListItemIyEEKNS3_17IntrusiveListItemEEEEENS_15iterator_traitsIT_E15difference_typeESD_SD_

NEW

+8

_ZNSt3__29__reverseB8nn210000INS_17_ClassicAlgPolicyEN2pw10containers8internal21BidirectionalIteratorINS3_11size_report8ListItemIyEENS4_17IntrusiveListItemEEESA_EEvT0_T1_

+816

RAM

+160

pw::containers::size_report::GetItems<>()::items

+8

[section .data]

+8

pw::containers::size_report::GetContainer<>()::container

+176

IntrusiveForwardList

FLASH

+88

[section .rodata]

+4

pw::containers::size_report::Measure()

-2

pw::containers::internal::IntrusiveForwardListItem::DoGetPrevious()

+4

vClearInterruptMaskFromISR

NEW

+212

_ZN2pw10containers11size_report27MeasureIntrusiveForwardListINS1_15ForwardListItemIjEETpTnRiJENSt3__211__wrap_iterIPS4_EEEEiT1_SA_j

NEW

+116

pw::containers::internal::GenericIntrusiveList<>::merge<>()

NEW

+78

pw::containers::internal::GenericIntrusiveList<>::sort<>()

NEW

+50

pw::containers::internal::GenericIntrusiveList<>::reverse()

NEW

+50

pw::containers::internal::IntrusiveListItemBase<>::replace()

NEW

+44

pw::containers::internal::GenericIntrusiveList<>::swap()

NEW

+44

pw::containers::internal::GenericIntrusiveList<>::unique<>()

NEW

+40

pw::containers::internal::CheckIntrusiveContainerIsEmpty()

NEW

+40

pw::containers::internal::GenericIntrusiveList<>::remove_if<>()

NEW

+40

pw::containers::internal::GenericIntrusiveList<>::splice_after()

NEW

+24

pw::containers::internal::GenericIntrusiveList<>::assign<>()

NEW

+24

pw::containers::internal::GenericIntrusiveList<>::~GenericIntrusiveList()

NEW

+24

pw::containers::internal::IntrusiveListItemBase<>::unlist()

NEW

+22

pw::containers::internal::GenericIntrusiveList<>::insert_after<>()

NEW

+20

pw::containers::internal::IntrusiveListItemBase<>::~IntrusiveListItemBase()

NEW

+16

pw::containers::internal::GenericIntrusiveList<>::clear()

NEW

+14

pw::IntrusiveForwardList<>::splice_after()

NEW

+14

pw::containers::internal::GenericIntrusiveList<>::remove()

NEW

+10

pw::IntrusiveForwardList<>::pop_front()

NEW

+8

pw::IntrusiveForwardList<>::merge()

NEW

+8

pw::IntrusiveForwardList<>::sort()

NEW

+8

pw::IntrusiveForwardList<>::unique()

+1,000

RAM

+12

[section .data]

+1

__Thumbv6MABSLongThunk_best_effort_wfe_or_timeout

NEW

+79

pw::containers::size_report::GetItems<>()::items

NEW

+4

pw::containers::size_report::GetContainer<>()::container

+96

Additional IntrusiveForwardList with different item type

FLASH

+140

pw::containers::internal::GenericIntrusiveList<>::merge<>()

+78

pw::containers::internal::GenericIntrusiveList<>::sort<>()

+52

pw::containers::internal::GenericIntrusiveList<>::unique<>()

+20

pw::containers::size_report::Measure()

+24

pw::containers::internal::GenericIntrusiveList<>::assign<>()

+22

pw::containers::internal::GenericIntrusiveList<>::insert_after<>()

-4

vClearInterruptMaskFromISR

+14

pw::IntrusiveForwardList<>::splice_after()

+10

pw::IntrusiveForwardList<>::pop_front()

+8

pw::IntrusiveForwardList<>::merge()

+8

pw::IntrusiveForwardList<>::sort()

+8

pw::IntrusiveForwardList<>::unique()

NEW

+212

_ZN2pw10containers11size_report27MeasureIntrusiveForwardListINS1_15ForwardListItemIyEETpTnRiJENSt3__211__wrap_iterIPS4_EEEEiT1_SA_j

+592

RAM

+160

pw::containers::size_report::GetItems<>()::items

-8

[section .data]

+8

pw::containers::size_report::GetContainer<>()::container

+160

IntrusiveForwardList and IntrusiveList

FLASH

+88

[section .rodata]

+36

pw::containers::internal::GenericIntrusiveList<>::insert_after()

+24

pw::containers::size_report::Measure()

-2

pw::containers::internal::IntrusiveForwardListItem::DoGetPrevious()

+8

vClearInterruptMaskFromISR

NEW

+240

pw::containers::internal::GenericIntrusiveList<>::merge<>()

NEW

+236

_ZN2pw10containers11size_report20MeasureIntrusiveListINS1_8ListItemIjEETpTnRiJENSt3__211__wrap_iterIPS4_EEEEiT1_SA_j

NEW

+212

_ZN2pw10containers11size_report27MeasureIntrusiveForwardListINS1_15ForwardListItemIjEETpTnRiJENSt3__211__wrap_iterIPS4_EEEEiT1_SA_j

NEW

+158

pw::containers::internal::GenericIntrusiveList<>::sort<>()

NEW

+98

pw::containers::internal::IntrusiveListItemBase<>::replace()

NEW

+92

pw::containers::internal::GenericIntrusiveList<>::unique<>()

NEW

+88

pw::containers::internal::GenericIntrusiveList<>::swap()

NEW

+84

pw::containers::internal::GenericIntrusiveList<>::remove_if<>()

NEW

+76

pw::containers::internal::GenericIntrusiveList<>::splice_after()

NEW

+50

pw::containers::internal::GenericIntrusiveList<>::reverse()

NEW

+48

pw::containers::internal::GenericIntrusiveList<>::assign<>()

NEW

+48

pw::containers::internal::GenericIntrusiveList<>::~GenericIntrusiveList()

NEW

+46

pw::containers::size_report::MeasureContainer<>()

NEW

+44

pw::containers::internal::GenericIntrusiveList<>::insert_after<>()

NEW

+40

pw::containers::internal::CheckIntrusiveContainerIsEmpty()

NEW

+40

pw::containers::internal::IntrusiveListItemBase<>::~IntrusiveListItemBase()

NEW

+36

pw::containers::internal::GenericIntrusiveList<>::clear()

NEW

+30

_ZNSt3__214__reverse_implB8nn210000INS_17_ClassicAlgPolicyEN2pw10containers8internal21BidirectionalIteratorINS3_11size_report8ListItemIjEENS4_17IntrusiveListItemEEEEEvT0_SB_NS_26bidirectional_iterator_tagE

NEW

+30

pw::containers::future::IntrusiveList<>::splice()

NEW

+28

pw::containers::internal::GenericIntrusiveList<>::remove()

NEW

+24

_ZNSt3__24swapB8nn210000IN2pw10containers11size_report8ListItemIjEEEENS_9enable_ifIXaasr21is_move_constructibleIT_EE5valuesr18is_move_assignableIS7_EE5valueEvE4typeERS7_SA_

NEW

+24

pw::containers::internal::IntrusiveListItemBase<>::unlist()

NEW

+16

_ZNSt3__28distanceB8nn210000IN2pw10containers8internal21BidirectionalIteratorIKNS2_11size_report8ListItemIjEEKNS3_17IntrusiveListItemEEEEENS_15iterator_traitsIT_E15difference_typeESD_SD_

NEW

+14

pw::IntrusiveForwardList<>::splice_after()

NEW

+12

pw::containers::future::IntrusiveList<>::reverse()

NEW

+12

pw::containers::future::IntrusiveList<>::size()

NEW

+10

pw::IntrusiveForwardList<>::pop_front()

NEW

+10

pw::containers::future::IntrusiveList<>::pop_front()

NEW

+8

_ZNSt3__29__reverseB8nn210000INS_17_ClassicAlgPolicyEN2pw10containers8internal21BidirectionalIteratorINS3_11size_report8ListItemIjEENS4_17IntrusiveListItemEEESA_EEvT0_T1_

NEW

+8

pw::IntrusiveForwardList<>::merge()

NEW

+8

pw::IntrusiveForwardList<>::sort()

NEW

+8

pw::IntrusiveForwardList<>::unique()

NEW

+8

pw::containers::future::IntrusiveList<>::merge()

NEW

+8

pw::containers::future::IntrusiveList<>::sort()

NEW

+8

pw::containers::future::IntrusiveList<>::unique()

+2,056

RAM

+12

[section .data]

+1

__Thumbv6MABSLongThunk_best_effort_wfe_or_timeout

NEW

+199

pw::containers::size_report::GetItems<>()::items

NEW

+12

pw::containers::size_report::GetContainer<>()::container

+224