CHERIoT RTOS
A compartmentalised RTOS for CHERIoT hardware
Toggle main menu visibility
Loading...
Searching...
No Matches
sdk
include
ds
ring_buffer.h
Go to the documentation of this file.
1
// Copyright Microsoft and CHERIoT Contributors.
2
// SPDX-License-Identifier: MIT
3
4
/**
5
* @file
6
* Ring/circular buffer state machine.
7
*/
8
9
#pragma once
10
11
#include <cdefs.h>
12
#include <type_traits>
13
14
namespace
ds::ring_buffer
15
{
16
/**
17
* A statically-sized, non-atomic ring buffer state machine. The actual
18
* element storage must be provided externally; all this does is manage the
19
* cursors. This representation uses an explicit empty flag, rather than
20
* considering equal cursors to be empty, so as to not need an extra element
21
* of store Capacity-many items.
22
*
23
* The type of the cursors is parametric, but must be unsigned.
24
*
25
* The consumer workflow is:
26
* - use head_get() to test emptiness and, if nonempty, read the index
27
* of the head element
28
* - make use of the element in the (external!) storage at that index
29
* - call head_advance() to discard the head element
30
*
31
* The producer workflow is:
32
* - use tail_next() to retrieve the index for the next element,
33
* if there is room
34
* - populate the element in the (external!) storage at that index
35
* - call tail_advance() to make that element available to the
36
* consumer
37
*/
38
template
<
typename
Debug, std::
size_t
Capacity,
typename
IxTy = std::
size_t
>
39
class
Cursors
40
{
41
static_assert
(std::is_arithmetic_v<IxTy> && std::is_unsigned_v<IxTy>,
42
"Ring buffer cursor type must be unsigned arithmetic"
);
43
44
template
<
typename
... Args>
45
using
Assert =
typename
Debug::template Assert<Args...>;
46
47
public
:
48
using
Ix = IxTy;
49
50
private
:
51
Ix head;
52
Ix tail;
53
bool
empty;
54
55
Ix advance(Ix v)
56
{
57
if
constexpr
((Capacity & (Capacity - 1)) == 0)
58
{
59
/* For power of two sizes, we can rely on bit masking. */
60
return
(v - 1) & (Capacity - 1);
61
}
62
else
63
{
64
/*
65
* For non-power-of-two, we can use unsigned underflow and
66
* signed arithmetic right shift to avoid branching. mask is
67
* all zeros if next is non-negative and all ones otherwise.
68
*/
69
Ix next = v - 1;
70
using
SIx = std::make_signed_t<Ix>;
71
Ix mask =
static_cast<
SIx
>
(next) >> (8 *
sizeof
(SIx) - 1);
72
return
(mask & (Capacity - 1)) | (~mask & next);
73
}
74
}
75
76
public
:
77
/**
78
* Reset cursors to an empty state.
79
*/
80
void
reset
()
81
{
82
tail = 0;
83
head = advance(tail);
84
empty =
true
;
85
}
86
87
/**
88
* Is this ring empty?
89
*/
90
__always_inline
bool
is_empty
()
91
{
92
return
empty;
93
}
94
95
/**
96
* Try to retrieve the head index; returns true if available and
97
* false otherwise.
98
*/
99
bool
head_get
(Ix &v)
100
{
101
if
(
is_empty
())
102
{
103
return
false
;
104
}
105
106
v = head;
107
return
true
;
108
}
109
110
/**
111
* Retrieve the head index while bypassing the emptiness check. The
112
* caller must have reason to believe that the ring is not empty.
113
*/
114
Ix
head_get_unsafe
()
115
{
116
return
head;
117
}
118
119
/**
120
* Discard the element at the index returned by head_get().
121
*
122
* Do not call this unless there was a matching call to
123
* head_get() that has returned true.
124
*/
125
__always_inline
void
head_advance
()
126
{
127
Assert<>(!empty,
"Cannot advance head of empty ring buffer!"
);
128
if
(head == tail)
129
{
130
empty =
true
;
131
}
132
head = advance(head);
133
}
134
135
/**
136
* Try to retrieve the index of the tail, if nonempty.
137
*/
138
bool
tail_get
(Ix &v)
139
{
140
if
(
is_empty
())
141
{
142
return
false
;
143
}
144
145
v = tail;
146
return
true
;
147
}
148
149
/**
150
* Try to retrieve the index beyond the tail, if there is room.
151
* Returns true and sets the index if so, or returns false if
152
* not.
153
*/
154
155
bool
tail_next
(Ix &v)
156
{
157
Ix nt = advance(tail);
158
if
(!empty && head == nt)
159
{
160
return
false
;
161
}
162
163
v = nt;
164
return
true
;
165
}
166
167
/**
168
* Make the next element available.
169
*
170
* Do not call this unless there was a matching call to
171
* tail_next() that returned true.
172
*/
173
__always_inline
void
tail_advance
()
174
{
175
Assert<>(empty || head != advance(tail),
176
"Cannot advance tail of full ring buffer!"
);
177
tail = advance(tail);
178
empty =
false
;
179
}
180
};
181
}
// namespace ds::ring_buffer
ds::ring_buffer::Cursors
A statically-sized, non-atomic ring buffer state machine.
Definition
ring_buffer.h:40
ds::ring_buffer::Cursors::reset
void reset()
Reset cursors to an empty state.
Definition
ring_buffer.h:80
ds::ring_buffer::Cursors::head_advance
void head_advance()
Discard the element at the index returned by head_get().
Definition
ring_buffer.h:125
ds::ring_buffer::Cursors::is_empty
bool is_empty()
Is this ring empty?
Definition
ring_buffer.h:90
ds::ring_buffer::Cursors::head_get
bool head_get(Ix &v)
Try to retrieve the head index; returns true if available and false otherwise.
Definition
ring_buffer.h:99
ds::ring_buffer::Cursors::tail_get
bool tail_get(Ix &v)
Try to retrieve the index of the tail, if nonempty.
Definition
ring_buffer.h:138
ds::ring_buffer::Cursors::tail_next
bool tail_next(Ix &v)
Try to retrieve the index beyond the tail, if there is room.
Definition
ring_buffer.h:155
ds::ring_buffer::Cursors::tail_advance
void tail_advance()
Make the next element available.
Definition
ring_buffer.h:173
ds::ring_buffer::Cursors::head_get_unsafe
Ix head_get_unsafe()
Retrieve the head index while bypassing the emptiness check.
Definition
ring_buffer.h:114
Generated by
1.18.0