CHERIoT RTOS
A compartmentalised RTOS for CHERIoT hardware
Loading...
Searching...
No Matches
linked_list.h
Go to the documentation of this file.
1// Copyright Microsoft and CHERIoT Contributors.
2// SPDX-License-Identifier: MIT
3
4/**
5 * @file
6 * A (circular) doubly linked list, abstracted over cons cell
7 * representations.
8 *
9 * See `tests/list-test.cc` for additional information on how to use it.
10 */
11
12#pragma once
13
14#include <concepts>
15#include <ds/pointer.h>
16
17namespace ds::linked_list
18{
19
20 namespace cell
21 {
22 /**
23 * The primitive, required, abstract interface to our cons cells.
24 *
25 * All methods are "namespaced" with `cell_` to support the case where
26 * the encoded forms are also representing other state (for example,
27 * bit-packed flags in pointer address bits).
28 */
29 template<typename T>
30 concept HasCellOperations = requires(T &t) {
31 /** Proxies for list linkages */
32 { t.cell_next() } -> ds::pointer::proxy::Proxies<T>;
33 { t.cell_prev() } -> ds::pointer::proxy::Proxies<T>;
34 };
35
36 /**
37 * Reset a cell to a singleton ring. Not all cons cells are required to
38 * be able to do this, though if you're sticking to rings and not
39 * (ab)using the machinery here in interesting ways, this should be easy
40 * to specify.
41 *
42 * This is a method, and not a constructor, to handle cases where the
43 * cell is also packing other state into its representation.
44 */
45 template<typename T>
46 concept HasReset = requires(T &t) {
47 { t.cell_reset() } -> std::same_as<void>;
48 };
49
50 template<typename T>
52
53 /**
54 * Additional, optional overrides available within implementation of
55 * cons cells. It may be useful to static_assert() these in
56 * implementations to make sure we are not falling back to the defaults
57 * in terms of the above primops.
58 *
59 * @{
60 */
61 template<typename T>
62 concept HasIsSingleton = requires(T &t) {
63 { t.cell_is_singleton() } -> std::same_as<bool>;
64 };
65
66 template<typename T>
67 concept HasIsSingletonCheck = requires(T &t) {
68 { t.cell_is_singleton_check() } -> std::same_as<bool>;
69 };
70
71 template<typename T>
72 concept HasIsDoubleton = requires(T &t) {
73 { t.cell_is_doubleton() } -> std::same_as<bool>;
74 };
75
76 /** @} */
77
78 } // namespace cell
79
80 /**
81 * Self-loops indicate either the sentinels of an empty list or,
82 * less often, singletons without their sentinels; it's up to
83 * the caller to know which is being tested for, here.
84 *
85 * The default implementation decodes and compares one link;
86 * implementations may have more efficient mechanisms.
87 *
88 * @{
89 */
90 template<cell::HasCellOperations T>
92 __always_inline bool is_singleton(T *e)
93 {
94 return e == e->cell_prev();
95 }
96
97 template<cell::HasCellOperations T>
98 requires(cell::HasIsSingleton<T>)
99 __always_inline bool is_singleton(T *e)
100 {
101 return e->cell_is_singleton();
102 }
103 /** @} */
104
105 /**
106 * Like is_singleton(), but checks both edges. Useful only for
107 * testing invariants.
108 *
109 * The default implementation decodes and compares both links.
110 */
111 template<cell::HasCellOperations T>
112 requires(!cell::HasIsSingletonCheck<T>)
113 __always_inline bool is_singleton_check(T *e)
114 {
115 return (e == e->cell_next()) && (e == e->cell_prev());
116 }
117
118 template<cell::HasCellOperations T>
119 requires(cell::HasIsSingletonCheck<T>)
120 __always_inline bool is_singleton_check(T *e)
121 {
122 return e->is_singleton_check();
123 }
124 /** @} */
125
126 /**
127 * Doubletons are either singleton collections (with both the sentinel
128 * and the single element satisfying this test) or, less often, a pair
129 * of elements without a sentinel. The caller is expected to know
130 * what's meant by this test.
131 *
132 * The default implementation decodes and compares both links.
133 * @{
134 */
135 template<cell::HasCellOperations T>
136 requires(!cell::HasIsDoubleton<T>)
137 __always_inline bool is_doubleton(T *e)
138 {
139 return e->cell_prev() == e->cell_next();
140 }
141
142 template<cell::HasCellOperations T>
143 requires(cell::HasIsDoubleton<T>)
144 __always_inline bool is_doubleton(T *e)
145 {
146 return e->cell_is_doubleton();
147 }
148 /** @} */
149
150 /**
151 * Verify linkage invariants. Again, useful only for testing.
152 *
153 * The default implementation decodes all four relevant links.
154 */
155 template<cell::HasCellOperations T>
156 __always_inline bool is_well_formed(T *e)
157 {
158 return (e == e->cell_prev()->cell_next()) &&
159 (e == e->cell_next()->cell_prev());
160 }
161
162 /**
163 * Insert a ring of `elem`-ents (typically, a singleton ring) before the
164 * `curr`-ent element (or sentinel) in the ring. In general, you will
165 * probably want to make sure that at most one of `elem` or `curr`
166 * points to a ring with a sentinel node.
167 *
168 * If `curr` is the sentinel, this is appending to the list, in the
169 * sense that the element(s) occupy (or span) the next-most and
170 * prev-least position from the sentinel.
171 *
172 * By symmetry, if `elem` is, instead, the sentinel, then `curr` is
173 * prepended to the list in the same sense.
174 */
175 template<cell::HasCellOperations Cell>
176 __always_inline void insert_before(Cell *curr, Cell *elem)
177 {
178 curr->cell_prev()->cell_next() = elem->cell_next();
179 elem->cell_next()->cell_prev() = curr->cell_prev();
180 curr->cell_prev() = elem;
181 elem->cell_next() = curr;
182 }
183
184 /**
185 * Emplacement before. This fuses initialization and insertion, so that
186 *
187 * emplace_before(c, e);
188 *
189 * is semantically equivalent to
190 *
191 * e->cell_reset(); insert_before(c, e);
192 *
193 * but spelled in a way that the compiler can understand a bit better, with
194 * less effort spent in provenance and/or alias analysis.
195 */
196 template<cell::HasCellOperations Cell, typename P>
197 requires std::same_as<P, Cell *> || ds::pointer::proxy::Proxies<P, Cell>
198 __always_inline void emplace_before(P curr, Cell *elem)
199 {
200 auto prev = curr->cell_prev();
201 elem->cell_next() = curr;
202 elem->cell_prev() = prev;
203 prev->cell_next() = elem;
204 prev = elem;
205 }
206
207 /**
208 * Emplacement after. This fuses initialization and insertion, so that
209 *
210 * emplace_after(c, e);
211 *
212 * is semantically equivalent to
213 *
214 * e->cell_reset(); insert_before(e, c);
215 *
216 * but spelled in a way that the compiler can understand a bit better, with
217 * less effort spent in provenance and/or alias analysis.
218 */
219 template<cell::HasCellOperations Cell, typename P>
220 requires std::same_as<P, Cell *> || ds::pointer::proxy::Proxies<P, Cell>
221 __always_inline void emplace_after(P curr, Cell *elem)
222 {
223 auto next = curr->cell_next();
224 elem->cell_prev() = curr;
225 elem->cell_next() = next;
226 next->cell_prev() = elem;
227 next = elem;
228 }
229
230 /**
231 * Remove from the list without turning the removed span into a
232 * well-formed ring. This is useful only if that invariant will be
233 * restored later (prior to insertion, at the very least).
234 *
235 * The removed element or span instead retains links into the ring
236 * whence it was removed, but is no longer well-formed, since that ring
237 * no longer references the removed element or span.
238 *
239 * This can be used to remove...
240 *
241 * - a single element (`el == er`)
242 *
243 * - the sentinel (`el == er`), leaving the rest of the ring, if any,
244 * as a sentinel-free ring
245 *
246 * - a span of elements from `el` to `er` via the `next` links; the
247 * removed span is damaged and must be corrected, while the residual
248 * ring remains well-formed.
249 *
250 * In all cases, `el`'s previous element is returned as a handle to the
251 * residual ring.
252 */
253 template<cell::HasCellOperations Cell>
254 __always_inline Cell *unsafe_remove(Cell *el, Cell *er)
255 {
256 auto p = el->cell_prev();
257 auto n = er->cell_next();
258 n->cell_prev() = p;
259 p->cell_next() = n;
260 return p;
261 }
262
263 template<cell::HasCellOperations Cell>
264 __always_inline Cell *unsafe_remove(Cell *e)
265 {
266 return unsafe_remove(e, e);
267 }
268
269 /**
270 * Remove a particular element `rem` from the ring, already knowing its
271 * adjacent, previous link `prev`. `prev` remains connected to the ring
272 * but `rem` will no longer be well-formed. Returns a proxy to prev's
273 * next field.
274 */
275 template<cell::HasCellOperations Cell>
276 __always_inline auto unsafe_remove_link(Cell *prev, Cell *rem)
277 {
278 auto next = rem->cell_next();
279 auto prevnext = prev->cell_next();
280 prevnext = next;
281 next->cell_prev() = prev;
282 return prevnext;
283 }
284
285 /**
286 * Remove from the ring, cleaving the ring into two well-formed rings.
287 *
288 * This can be used to remove...
289 *
290 * - a single element (`el == er`)
291 *
292 * - the sentinel (`el == er`), leaving the rest of the ring, if any,
293 * as a sentinel-free collection
294 *
295 * - a span of elements from `el` to `er` via `next` links; the
296 * removed span is made into a ring and the residual ring is left
297 * well-formed.
298 *
299 * In all cases, `el`'s previous element is returned as a handle to the
300 * residual ring. (The caller must already have a reference to the span
301 * being removed). This is especially useful when `remove`-ing elements
302 * during a `search`, below: overwriting the callback's Cell pointer
303 * (passed by *reference*) will continue the iteration, calling back at
304 * the removed node's successor.
305 *
306 * Removing a singleton from its ring from itself causes no change, as
307 * any would-be residual ring is empty. This corner case requires some
308 * care on occasion.
309 */
310 template<cell::HasCellOperations Cell>
311 __always_inline Cell *remove(Cell *el, Cell *er)
312 {
313 Cell *p = unsafe_remove(el, er);
314 el->cell_prev() = er;
315 er->cell_next() = el;
316 return p;
317 }
318
319 template<cell::HasCellOperations Cell>
320 __always_inline Cell *remove(Cell *e)
321 {
322 return remove(e, e);
323 }
324
325 /**
326 * Search through a span of a ring, inclusively from `from` through
327 * exclusively to `to`, applying `f` to each cons cell in turn. If `f`
328 * returns `true`, the search stops early and returns `true`; otherwise,
329 * search returns `false`. To (side-effectfully) visit every node in the
330 * span, have `f` always return false.
331 */
332 template<cell::HasCellOperations Cell, typename F>
333 __always_inline bool search(Cell *from, Cell *to, F f)
334 {
335 Cell *elem;
336 for (elem = from; elem != to; elem = elem->cell_next())
337 {
338 if (f(elem))
339 {
340 return true;
341 }
342 }
343 return false;
344 }
345
346 /**
347 * Search through all elements of a ring *except* `elem`. If `elem` is the
348 * sentinel of a ring, then this is, as one expects, a `search` over all
349 * non-sentinel members of the ring.
350 */
351 template<cell::HasCellOperations Cell, typename F>
352 __always_inline bool search(Cell *elem, F f)
353 {
354 return search(static_cast<Cell *>(elem->cell_next()), elem, f);
355 }
356
357 /**
358 * Convenience wrapper for a sentinel cons cell, encapsulating some common
359 * patterns.
360 */
361 template<cell::HasCellOperationsReset CellTemplateArg>
362 struct Sentinel
363 {
364 using Cell = CellTemplateArg;
365
366 /**
367 * The sentinel node itself. Viewing the ring as a list, this
368 * effectively serves as pointers to the head (next) and to the tail
369 * (prev) of the list. Unlike more traditional nullptr-terminated
370 * lists, though, here, the sentinel participates in the ring.
371 *
372 * This is marked `cheri_no_subobject_bounds` because some of our cons
373 * cell implementations use pointer proxies that rely on the bounds
374 * provided by `this` (which, in turn, is likely to be
375 * `cheri_no_subobject_bounds`)
376 */
377 Cell sentinel __attribute__((__cheri_no_subobject_bounds__)) = {};
378
379 __always_inline void reset()
380 {
381 sentinel.cell_reset();
382 }
383
384 __always_inline bool is_empty()
385 {
387 }
388
389 __always_inline void append(Cell *elem)
390 {
391 linked_list::insert_before(&sentinel, elem);
392 }
393
394 __always_inline void append_emplace(Cell *elem)
395 {
396 linked_list::emplace_before(&sentinel, elem);
397 }
398
399 __always_inline void prepend(Cell *elem)
400 {
401 linked_list::insert_before(elem, &sentinel);
402 }
403
404 __always_inline Cell *first()
405 {
406 return sentinel.cell_next();
407 }
408
409 __always_inline Cell *last()
410 {
411 return sentinel.cell_prev();
412 }
413
414 __always_inline Cell *unsafe_take_first()
415 {
416 Cell *f = sentinel.cell_next();
417 linked_list::unsafe_remove_link(&sentinel, f);
418 return f;
419 }
420
421 __always_inline Cell *take_all()
422 {
423 auto p = linked_list::unsafe_remove(&sentinel);
424 sentinel.cell_reset();
425 return p;
426 }
427
428 template<typename F>
429 __always_inline bool search(F f)
430 {
431 return linked_list::search(&sentinel, f);
432 }
433 };
434
435 namespace cell
436 {
437
438 /** Cons cell using two pointers */
439 class Pointer
440 {
441 Pointer *prev, *next;
442
443 public:
444 Pointer()
445 {
446 this->cell_reset();
447 }
448
449 __always_inline void cell_reset()
450 {
451 prev = next = this;
452 }
453
454 __always_inline auto cell_next()
455 {
456 return ds::pointer::proxy::Pointer(next);
457 }
458
459 __always_inline auto cell_prev()
460 {
461 return ds::pointer::proxy::Pointer(prev);
462 }
463 };
464 static_assert(HasCellOperationsReset<Pointer>);
465
466 /**
467 * Encode a linked list cons cell as a pair of addresses (but present an
468 * interface in terms of pointers). CHERI bounds on the returned
469 * pointers are inherited from the pointer to `this` cons cell.
470 */
471 class __cheri_no_subobject_bounds PtrAddr
472 {
473 ptraddr_t prev, next;
474
475 public:
476 PtrAddr()
477 {
478 this->cell_reset();
479 }
480 /* Primops */
481
482 __always_inline void cell_reset()
483 {
484 prev = next = CHERI::Capability{this}.address();
485 }
486
487 __always_inline auto cell_next()
488 {
489 return ds::pointer::proxy::PtrAddr(this, next);
490 }
491
492 __always_inline auto cell_prev()
493 {
494 return ds::pointer::proxy::PtrAddr(this, prev);
495 }
496
497 /*
498 * Specialized implementations that may be slightly fewer
499 * instructions than the generic approaches in terms of the primops.
500 */
501
502 __always_inline bool cell_is_singleton()
503 {
504 return prev == CHERI::Capability{this}.address();
505 }
506
507 __always_inline bool cell_is_doubleton()
508 {
509 return prev == next;
510 }
511 };
512 static_assert(HasCellOperationsReset<PtrAddr>);
513 static_assert(HasIsSingleton<PtrAddr>);
514 static_assert(HasIsDoubleton<PtrAddr>);
515
516 /**
517 * Encode a linked list cons cell as a pair of addresses (but present an
518 * interface in terms of pointers). CHERI bounds on the returned
519 * pointers are inherited from the pointer to `this` cons cell.
520 */
521 template<ptrdiff_t Offset>
522 class OffsetPtrAddr
523 {
524 ptraddr_t prev, next;
525
526 public:
527 OffsetPtrAddr()
528 {
529 this->cell_reset();
530 }
531
532 /* Primops */
533
534 __always_inline void cell_reset()
535 {
536 prev = next = CHERI::Capability{this}.address() - Offset;
537 }
538
539 __always_inline auto cell_next()
540 {
542 this, next);
543 }
544
545 __always_inline auto cell_prev()
546 {
548 this, prev);
549 }
550
551 /*
552 * Specialized implementations that may be slightly fewer
553 * instructions than the generic approaches in terms of the primops.
554 */
555
556 __always_inline bool cell_is_singleton()
557 {
558 return prev == CHERI::Capability{this}.address() - Offset;
559 }
560
561 __always_inline bool cell_is_doubleton()
562 {
563 return prev == next;
564 }
565 };
567 static_assert(HasIsSingleton<OffsetPtrAddr<0>>);
568 static_assert(HasIsDoubleton<OffsetPtrAddr<0>>);
569
570 } // namespace cell
571
572} // namespace ds::linked_list
Helper class for accessing capability properties on pointers.
Definition cheri.hh:482
AddressProxy address()
Access the address of the capability.
Definition cheri.hh:851
Like the above, but with a constant offset on the interpretation of its addresss fields.
Definition pointer.h:198
Pointer references are pointer proxies, shockingly enough.
Definition pointer.h:82
Equipped with a context for bounds, an address reference can be a proxy for a pointer.
Definition pointer.h:130
The primitive, required, abstract interface to our cons cells.
Definition linked_list.h:30
Additional, optional overrides available within implementation of cons cells.
Definition linked_list.h:62
Reset a cell to a singleton ring.
Definition linked_list.h:46
Proxies<P,T> if P is a proxy object for T*-s.
Definition pointer.h:50
bool is_singleton_check(T *e)
Like is_singleton(), but checks both edges.
bool is_singleton(T *e)
Self-loops indicate either the sentinels of an empty list or, less often, singletons without their se...
Definition linked_list.h:92
void emplace_before(P curr, Cell *elem)
Emplacement before.
bool is_doubleton(T *e)
Doubletons are either singleton collections (with both the sentinel and the single element satisfying...
Cell * unsafe_remove(Cell *el, Cell *er)
Remove from the list without turning the removed span into a well-formed ring.
bool is_well_formed(T *e)
Verify linkage invariants.
void emplace_after(P curr, Cell *elem)
Emplacement after.
Cell * remove(Cell *el, Cell *er)
Remove from the ring, cleaving the ring into two well-formed rings.
void insert_before(Cell *curr, Cell *elem)
Insert a ring of elem-ents (typically, a singleton ring) before the curr-ent element (or sentinel) in...
bool search(Cell *from, Cell *to, F f)
Search through a span of a ring, inclusively from from through exclusively to to, applying f to each ...
auto unsafe_remove_link(Cell *prev, Cell *rem)
Remove a particular element rem from the ring, already knowing its adjacent, previous link prev.
Pointer utilities.
Convenience wrapper for a sentinel cons cell, encapsulating some common patterns.
Cell sentinel
The sentinel node itself.