aboutsummaryrefslogtreecommitdiff
path: root/pw_containers/docs.rst
blob: 83f333402b4454914425a998e2de100fc2e6b0b6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
.. _module-pw_containers:

=============
pw_containers
=============
The ``pw_containers`` module provides embedded-friendly container classes.

----------
pw::Vector
----------
The Vector class is similar to ``std::vector``, except it is backed by a
fixed-size buffer. Vectors must be declared with an explicit maximum size
(e.g. ``Vector<int, 10>``) but vectors can be used and referred to without the
max size template parameter (e.g. ``Vector<int>``).

To allow referring to a ``pw::Vector`` without an explicit maximum size, all
Vector classes inherit from the generic ``Vector<T>``, which stores the maximum
size in a variable. This allows Vectors to be used without having to know
their maximum size at compile time. It also keeps code size small since
function implementations are shared for all maximum sizes.

---------------
pw::InlineDeque
---------------
.. doxygentypedef:: pw::InlineDeque

---------------
pw::InlineQueue
---------------
.. doxygentypedef:: pw::InlineQueue

----------------------------
pw::VariableLengthEntryQueue
----------------------------
.. doxygenfile:: pw_containers/variable_length_entry_queue.h
   :sections: detaileddescription

API Reference
===============
C
-
.. doxygengroup:: variable_length_entry_queue_c_api
   :content-only:

Python
------
.. automodule:: pw_containers.variable_length_entry_queue
   :members:

-----------------
pw::IntrusiveList
-----------------
IntrusiveList provides an embedded-friendly singly-linked intrusive list
implementation. An intrusive list is a type of linked list that embeds the
"next" pointer into the list object itself. This allows the construction of a
linked list without the need to dynamically allocate list entries.

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. ``pw::IntrusiveList``
provides a class that list elements can inherit from. This protects the "next"
pointer from being accessed by the item class, so only the ``pw::IntrusiveList``
class can modify the list.

Usage
=====
While the API of ``pw::IntrusiveList`` is similar to a ``std::forward_list``,
there are extra steps to creating objects that can be stored in this data
structure. Objects that will be added to a ``IntrusiveList<T>`` must inherit
from ``IntrusiveList<T>::Item``. They can inherit directly from it or inherit
from it through another base class. When an item is instantiated and added to a
linked list, the pointer to the object is added to the "next" pointer of
whichever object is the current tail.

That means two key things:

- An instantiated ``IntrusiveList<T>::Item`` will be removed from its
  corresponding ``IntrusiveList`` when it goes out of scope.
- A linked list item CANNOT be included in two lists. Attempting to do so
  results in an assert failure.

.. code-block:: cpp

   class Square
      : public pw::IntrusiveList<Square>::Item {
    public:
     Square(unsigned int side_length) : side_length(side_length) {}
     unsigned long Area() { return side_length * side_length; }

    private:
     unsigned int side_length;
   };

   pw::IntrusiveList<Square> squares;

   Square small(1);
   Square large(4000);
   // These elements are not copied into the linked list, the original objects
   // are just chained together and can be accessed via
   // `IntrusiveList<Square> squares`.
   squares.push_back(small);
   squares.push_back(large);

   {
     // When different_scope goes out of scope, it removes itself from the list.
     Square different_scope = Square(5);
     squares.push_back(&different_scope);
   }

   for (const auto& square : squares) {
     PW_LOG_INFO("Found a square with an area of %lu", square.Area());
   }

   // Like std::forward_list, an iterator is invalidated when the item it refers
   // to is removed. It is *NOT* safe to remove items from a list while iterating
   // over it in a range-based for loop.
   for (const auto& square_bad_example : squares) {
     if (square_bad_example.verticies() != 4) {
       // BAD EXAMPLE of how to remove matching items from a singly linked list.
       squares.remove(square_bad_example);  // NEVER DO THIS! THIS IS A BUG!
     }
   }

   // To remove items while iterating, use an iterator to the previous item.
   auto previous = squares.before_begin();
   auto current = squares.begin();

   while (current != squares.end()) {
     if (current->verticies() != 4) {
       current = squares.erase_after(previous);
     } else {
       previous = current;
       ++current;
     }
   }

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)":

- Adding to the end of a list with ``pw::IntrusiveList<T>::push_back(T&)``.
- Accessing the last item in a list with ``pw::IntrusiveList<T>::back()``.
- Destroying an item with ``pw::IntrusiveList<T>::Item::~Item()``.
- Moving an item with either ``pw::IntrusiveList<T>::Item::Item(Item&&)`` or
  ``pw::IntrusiveList<T>::Item::operator=(Item&&)``.
- Removing an item from a list using ``pw::IntrusiveList<T>::remove(const T&)``.
- Getting the list size using ``pw::IntrusiveList<T>::size()``.

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

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

-----------------------
pw::containers::FlatMap
-----------------------
``FlatMap`` provides a simple, fixed-size associative array with `O`\ (log `n`)
lookup by key.

``pw::containers::FlatMap`` contains the same methods and features for looking
up data as ``std::map``. However, modification of the underlying data is limited
to the mapped values, via ``.at()`` (key must exist) and ``mapped_iterator``
objects returned by ``.mapped_begin()`` and ``.mapped_end()``.
``mapped_iterator`` objects are bidirectional iterators that can be dereferenced
to access and mutate the mapped value objects.

The underlying array in ``pw::containers::FlatMap`` does not need to be sorted.
During construction, ``pw::containers::FlatMap`` will perform a constexpr
insertion sort.

----------------------------
pw::containers::FilteredView
----------------------------
.. doxygenclass:: pw::containers::FilteredView

-------------------------------
pw::containers::WrappedIterator
-------------------------------
``pw::containers::WrappedIterator`` is a class that makes it easy to wrap an
existing iterator type. It reduces boilerplate by providing ``operator++``,
``operator--``, ``operator==``, ``operator!=``, and the standard iterator
aliases (``difference_type``, ``value_type``, etc.). It does not provide the
dereference operator; that must be supplied by a derived class.

To use it, create a class that derives from ``WrappedIterator`` and define
``operator*()`` and ``operator->()`` as appropriate. The new iterator might
apply a transformation to or access a member of the values provided by the
original iterator. The following example defines an iterator that multiplies the
values in an array by 2.

.. code-block:: cpp

   // Divides values in a std::array by two.
   class DoubleIterator
       : public pw::containers::WrappedIterator<DoubleIterator, const int*, int> {
    public:
     constexpr DoubleIterator(const int* it) : WrappedIterator(it) {}

     int operator*() const { return value() * 2; }

     // Don't define operator-> since this iterator returns by value.
   };

   constexpr std::array<int, 6> kArray{0, 1, 2, 3, 4, 5};

   void SomeFunction {
     for (DoubleIterator it(kArray.begin()); it != DoubleIterator(kArray.end()); ++it) {
       // The iterator yields 0, 2, 4, 6, 8, 10 instead of the original values.
     }
   };

``WrappedIterator`` may be used in concert with ``FilteredView`` to create a
view that iterates over a matching values in a container and applies a
transformation to the values. For example, it could be used with
``FilteredView`` to filter a list of packets and yield only one field from the
packet.

The combination of ``FilteredView`` and ``WrappedIterator`` provides some basic
functional programming features similar to (though much more cumbersome than)
`generator expressions <https://www.python.org/dev/peps/pep-0289/>`_ (or `filter
<https://docs.python.org/3/library/functions.html#filter>`_/`map
<https://docs.python.org/3/library/functions.html#map>`_) in Python or streams
in Java 8. ``WrappedIterator`` and ``FilteredView`` require no memory
allocation, which is helpful when memory is too constrained to process the items
into a new container.

------------------------
pw::containers::to_array
------------------------
``pw::containers::to_array`` is a C++14-compatible implementation of C++20's
`std::to_array <https://en.cppreference.com/w/cpp/container/array/to_array>`_.
In C++20, it is an alias for ``std::to_array``. It converts a C array to a
``std::array``.

-------------------------
pw_containers/algorithm.h
-------------------------
Pigweed provides a set of Container-based versions of algorithmic functions
within the C++ standard library, based on a subset of
``absl/algorithm/container.h``.

.. cpp:function:: bool pw::containers::AllOf()

   Container-based version of the <algorithm> ``std::all_of()`` function to
   test if all elements within a container satisfy a condition.

.. cpp:function:: bool pw::containers::AnyOf()

   Container-based version of the <algorithm> ``std::any_of()`` function to
   test if any element in a container fulfills a condition.

.. cpp:function:: bool pw::containers::NoneOf()

   Container-based version of the <algorithm> ``std::none_of()`` function to
   test if no elements in a container fulfill a condition.

.. cpp:function:: pw::containers::ForEach()

   Container-based version of the <algorithm> ``std::for_each()`` function to
   apply a function to a container's elements.

.. cpp:function:: pw::containers::Find()

   Container-based version of the <algorithm> ``std::find()`` function to find
   the first element containing the passed value within a container value.

.. cpp:function:: pw::containers::FindIf()

   Container-based version of the <algorithm> ``std::find_if()`` function to find
   the first element in a container matching the given condition.

.. cpp:function:: pw::containers::FindIfNot()

   Container-based version of the <algorithm> ``std::find_if_not()`` function to
   find the first element in a container not matching the given condition.

.. cpp:function:: pw::containers::FindEnd()

   Container-based version of the <algorithm> ``std::find_end()`` function to
   find the last subsequence within a container.

.. cpp:function:: pw::containers::FindFirstOf()

   Container-based version of the <algorithm> ``std::find_first_of()`` function
   to find the first element within the container that is also within the options
   container.

.. cpp:function:: pw::containers::AdjacentFind()

   Container-based version of the <algorithm> ``std::adjacent_find()`` function
   to find equal adjacent elements within a container.

.. cpp:function:: pw::containers::Count()

   Container-based version of the <algorithm> ``std::count()`` function to count
   values that match within a container.

.. cpp:function:: pw::containers::CountIf()

   Container-based version of the <algorithm> ``std::count_if()`` function to
   count values matching a condition within a container.

.. cpp:function:: pw::containers::Mismatch()

   Container-based version of the <algorithm> ``std::mismatch()`` function to
   return the first element where two ordered containers differ. Applies ``==``
   to the first ``N`` elements of ``c1`` and ``c2``, where
   ``N = min(size(c1), size(c2)).`` the function's test condition. Applies
   ``pred`` to the first N elements of ``c1``  and ``c2``, where
   ``N = min(size(c1), size(c2))``.

.. cpp:function:: bool pw::containers::Equal()

   Container-based version of the <algorithm> ``std::equal()`` function to
   test whether two containers are equal.

   .. note::

      The semantics of ``Equal()`` are slightly different than those of
      ``std::equal()``: while the latter iterates over the second container only
      up to the size of the first container, ``Equal()`` also checks whether the
      container sizes are equal.  This better matches expectations about
      ``Equal()`` based on its signature.

.. cpp:function:: bool pw::containers::IsPermutation()

   Container-based version of the <algorithm> ``std::is_permutation()`` function
   to test whether a container is a permutation of another.

.. cpp:function:: pw::containers::Search()

   Container-based version of the <algorithm> ``std::search()`` function to
   search a container for a subsequence.

.. cpp:function:: pw::containers::SearchN()

   Container-based version of the <algorithm> ``std::search_n()`` function to
   search a container for the first sequence of N elements.

-------------
Compatibility
-------------
- C++17

------
Zephyr
------
To enable ``pw_containers`` for Zephyr add ``CONFIG_PIGWEED_CONTAINERS=y`` to
the project's configuration.