Skip to content

Commit 4db8147

Browse files
committed
Add raw iteration over queue elements; add buffered UART test double
1 parent aa76a47 commit 4db8147

6 files changed

Lines changed: 260 additions & 14 deletions

File tree

include/tiny_queue.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ typedef struct {
1515
tiny_ring_buffer_t ring_buffer;
1616
} tiny_queue_t;
1717

18+
typedef struct
19+
{
20+
unsigned offset;
21+
const uint8_t* buffer;
22+
unsigned buffer_capacity;
23+
} tiny_queue_raw_element_iterator_t;
24+
1825
/*!
1926
* Initialize a queue with the provided buffer.
2027
*/
@@ -63,4 +70,34 @@ void tiny_queue_peek_size(tiny_queue_t* self, uint16_t* size, uint16_t index);
6370
*/
6471
uint16_t tiny_queue_count(tiny_queue_t* self);
6572

73+
/*!
74+
* Get an iterator over the raw bytes of an element. This allows for optimized access to
75+
* an element that cannot be read out of the queue in its entirety.
76+
*/
77+
tiny_queue_raw_element_iterator_t tiny_queue_raw_element_iterator(
78+
tiny_queue_t* instance,
79+
uint16_t index);
80+
81+
/*!
82+
* View the next byte in the element without advancing the iterator.
83+
*/
84+
static inline uint8_t tiny_queue_raw_element_iterator_peek(
85+
tiny_queue_raw_element_iterator_t* instance)
86+
{
87+
return instance->buffer[instance->offset];
88+
}
89+
90+
/*!
91+
* @warning This does no bounds checking. The client must ensure that the iterator is not
92+
* advanced past the end of the buffer.
93+
*/
94+
static inline void tiny_queue_raw_element_iterator_advance(
95+
tiny_queue_raw_element_iterator_t* instance)
96+
{
97+
instance->offset++;
98+
if(instance->offset >= instance->buffer_capacity) {
99+
instance->offset = 0;
100+
}
101+
}
102+
66103
#endif

library.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"maintainer": true
1414
}
1515
],
16-
"version": "7.0.2",
16+
"version": "7.1.0",
1717
"frameworks": "*",
1818
"platforms": "*",
1919
"export": {

src/tiny_queue.c

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,30 +49,30 @@ static void discard_element(tiny_queue_t* self, uint16_t element_size)
4949
}
5050
}
5151

52-
static void peek_element_size_at_ring_buffer_index(tiny_queue_t* self, uint16_t* element_size, uint16_t location)
52+
static void peek_element_size_at_ring_buffer_index(tiny_queue_t* self, uint16_t* element_size, unsigned location)
5353
{
5454
uint8_t* destination = (uint8_t*)element_size;
55-
for(uint16_t i = location; i < location + sizeof(uint16_t); i++) {
55+
for(unsigned i = location; i < location + sizeof(uint16_t); i++) {
5656
tiny_ring_buffer_at(&self->ring_buffer, i, destination);
5757
destination++;
5858
}
5959
}
6060

61-
static uint16_t ring_buffer_index_for_element_index(tiny_queue_t* self, uint16_t element_index)
61+
static unsigned ring_buffer_index_for_element_index(tiny_queue_t* self, uint16_t element_index)
6262
{
6363
uint16_t element_size;
64-
uint16_t location = 0;
64+
unsigned location = 0;
6565
for(uint16_t i = 0; i < element_index; i++) {
6666
peek_element_size_at_ring_buffer_index(self, &element_size, location);
6767
location += (uint16_t)(element_size + sizeof(uint16_t));
6868
}
6969
return location;
7070
}
7171

72-
static void peek_element_at_ring_buffer_index(tiny_queue_t* self, void* element, uint16_t element_size, uint16_t location)
72+
static void peek_element_at_ring_buffer_index(tiny_queue_t* self, void* element, uint16_t element_size, unsigned location)
7373
{
7474
uint8_t* destination = element;
75-
for(uint16_t i = location; i < location + element_size; i++) {
75+
for(unsigned i = location; i < location + element_size; i++) {
7676
tiny_ring_buffer_at(&self->ring_buffer, i, destination);
7777
destination++;
7878
}
@@ -111,20 +111,20 @@ void tiny_queue_discard(tiny_queue_t* self)
111111

112112
void tiny_queue_peek(tiny_queue_t* self, void* element, uint16_t* size_storage, uint16_t element_index)
113113
{
114-
uint16_t i = ring_buffer_index_for_element_index(self, element_index);
114+
unsigned i = ring_buffer_index_for_element_index(self, element_index);
115115
peek_element_size_at_ring_buffer_index(self, size_storage, i);
116116
peek_element_at_ring_buffer_index(self, element, *size_storage, i + sizeof(uint16_t));
117117
}
118118

119119
void tiny_queue_peek_partial(tiny_queue_t* self, void* element, uint16_t size, uint16_t offset, uint16_t element_index)
120120
{
121-
uint16_t i = ring_buffer_index_for_element_index(self, element_index);
121+
unsigned i = ring_buffer_index_for_element_index(self, element_index);
122122
peek_element_at_ring_buffer_index(self, element, size, (uint16_t)(i + offset + sizeof(uint16_t)));
123123
}
124124

125125
void tiny_queue_peek_size(tiny_queue_t* self, uint16_t* size_storage, uint16_t element_index)
126126
{
127-
uint16_t i = ring_buffer_index_for_element_index(self, element_index);
127+
unsigned i = ring_buffer_index_for_element_index(self, element_index);
128128
peek_element_size_at_ring_buffer_index(self, size_storage, i);
129129
}
130130

@@ -141,3 +141,20 @@ void tiny_queue_init(
141141
self->element_count = 0;
142142
tiny_ring_buffer_init(&self->ring_buffer, storage, sizeof(uint8_t), storage_size);
143143
}
144+
145+
tiny_queue_raw_element_iterator_t tiny_queue_raw_element_iterator(
146+
tiny_queue_t* instance,
147+
uint16_t index)
148+
{
149+
unsigned ring_buffer_index = ring_buffer_index_for_element_index(instance, index);
150+
unsigned offset = ring_buffer_index + (unsigned)sizeof(uint16_t) + instance->ring_buffer.tail;
151+
if(offset >= tiny_ring_buffer_capacity(&instance->ring_buffer)) {
152+
offset -= tiny_ring_buffer_capacity(&instance->ring_buffer);
153+
}
154+
155+
return (tiny_queue_raw_element_iterator_t){
156+
.offset = offset,
157+
.buffer = instance->ring_buffer.buffer,
158+
.buffer_capacity = tiny_ring_buffer_capacity(&instance->ring_buffer),
159+
};
160+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*!
2+
* @file
3+
* @brief UART test double.
4+
*/
5+
6+
#ifndef tiny_buffered_uart_double_hpp
7+
#define tiny_buffered_uart_double_hpp
8+
9+
extern "C" {
10+
#include "hal/i_tiny_buffered_uart.h"
11+
#include "tiny_event.h"
12+
}
13+
14+
typedef struct {
15+
i_tiny_buffered_uart_t interface;
16+
tiny_event_t receive;
17+
tiny_event_t send_complete;
18+
bool automatic_send_complete;
19+
bool sending;
20+
bool send_completed;
21+
bool receive_pending;
22+
const void* receive_buffer;
23+
uint16_t receive_buffer_size;
24+
} tiny_buffered_uart_double_t;
25+
26+
/*!
27+
* Initializes a buffered UART double.
28+
*/
29+
void tiny_buffered_uart_double_init(tiny_buffered_uart_double_t* self);
30+
31+
/*!
32+
* True if a byte was sent without send completing.
33+
*/
34+
bool tiny_buffered_uart_double_sending(tiny_buffered_uart_double_t* self);
35+
36+
/*!
37+
* Causes the double to raise a send complete event.
38+
*/
39+
void tiny_buffered_uart_double_trigger_send_complete(tiny_buffered_uart_double_t* self);
40+
41+
/*!
42+
* Causes the double to raise a receive event.
43+
*/
44+
void tiny_buffered_uart_double_trigger_receive(tiny_buffered_uart_double_t* self, const void* buffer, uint16_t buffer_size);
45+
46+
/*!
47+
* When enabled, the double will raise a send complete event when a buffer is sent. Defaults to disabled.
48+
*/
49+
void tiny_buffered_uart_double_configure_automatic_send_complete(tiny_buffered_uart_double_t* self, bool enabled);
50+
51+
#endif
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*!
2+
* @file
3+
* @brief
4+
*/
5+
6+
#include "CppUTestExt/MockSupport.h"
7+
#include "double/tiny_buffered_uart_double.hpp"
8+
9+
static void send(i_tiny_buffered_uart_t* _self, const void* buffer, uint16_t buffer_size)
10+
{
11+
auto self = reinterpret_cast<tiny_buffered_uart_double_t*>(_self);
12+
self->sending = true;
13+
14+
if(self->automatic_send_complete) {
15+
self->send_completed = true;
16+
}
17+
18+
mock()
19+
.actualCall("send")
20+
.onObject(self)
21+
.withMemoryBufferParameter("buffer", reinterpret_cast<const uint8_t*>(buffer), buffer_size);
22+
}
23+
24+
static i_tiny_event_t* on_send_complete(i_tiny_buffered_uart_t* _self)
25+
{
26+
auto self = reinterpret_cast<tiny_buffered_uart_double_t*>(_self);
27+
return &self->send_complete.interface;
28+
}
29+
30+
static i_tiny_event_t* on_receive(i_tiny_buffered_uart_t* _self)
31+
{
32+
auto self = reinterpret_cast<tiny_buffered_uart_double_t*>(_self);
33+
return &self->receive.interface;
34+
}
35+
36+
static void run(i_tiny_buffered_uart_t* _self)
37+
{
38+
auto self = reinterpret_cast<tiny_buffered_uart_double_t*>(_self);
39+
40+
if(self->send_completed) {
41+
self->sending = false;
42+
self->send_completed = false;
43+
tiny_event_publish(&self->send_complete, nullptr);
44+
}
45+
46+
if(self->receive_pending) {
47+
self->receive_pending = false;
48+
tiny_buffered_uart_on_receive_args_t args = { self->receive_buffer, self->receive_buffer_size };
49+
tiny_event_publish(&self->receive, &args);
50+
}
51+
}
52+
53+
static const i_tiny_buffered_uart_api_t api = { send, on_send_complete, on_receive, run };
54+
55+
void tiny_buffered_uart_double_init(tiny_buffered_uart_double_t* self)
56+
{
57+
self->interface.api = &api;
58+
self->sending = false;
59+
self->automatic_send_complete = false;
60+
61+
tiny_event_init(&self->send_complete);
62+
tiny_event_init(&self->receive);
63+
}
64+
65+
bool tiny_buffered_uart_double_sending(tiny_buffered_uart_double_t* self)
66+
{
67+
return self->sending;
68+
}
69+
70+
void tiny_buffered_uart_double_trigger_send_complete(tiny_buffered_uart_double_t* self)
71+
{
72+
self->sending = false;
73+
self->send_completed = true;
74+
}
75+
76+
void tiny_buffered_uart_double_trigger_receive(tiny_buffered_uart_double_t* self, const void* buffer, uint16_t buffer_size)
77+
{
78+
self->receive_buffer = buffer;
79+
self->receive_buffer_size = buffer_size;
80+
self->receive_pending = true;
81+
}
82+
83+
void tiny_buffered_uart_double_configure_automatic_send_complete(tiny_buffered_uart_double_t* self, bool enabled)
84+
{
85+
self->automatic_send_complete = enabled;
86+
}

test/tests/tiny_queue_test.cpp

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ TEST_GROUP(tiny_queue)
9898
CHECK_EQUAL(element, dequeued_element_storage);
9999
}
100100

101-
bool given_that_large_element_has_been_enqueued(uint8_t * element)
101+
bool given_that_large_element_has_been_enqueued(uint8_t* element)
102102
{
103103
return tiny_queue_enqueue(&self, (void*)element, large_element_size);
104104
}
@@ -115,7 +115,7 @@ TEST_GROUP(tiny_queue)
115115
CHECK_FALSE(given_that_large_element_has_been_enqueued(some_large_element_1));
116116
}
117117

118-
void dequeueing_should_yield(uint8_t * element)
118+
void dequeueing_should_yield(uint8_t* element)
119119
{
120120
uint8_t dequeued_element_storage[large_element_size];
121121
memset(dequeued_element_storage, 0, sizeof(dequeued_element_storage));
@@ -134,7 +134,7 @@ TEST_GROUP(tiny_queue)
134134
tiny_queue_discard(&self);
135135
}
136136

137-
void peeking_should_yield(uint8_t * element, size_t element_size, uint16_t element_index)
137+
void peeking_should_yield(uint8_t* element, size_t element_size, uint16_t element_index)
138138
{
139139
uint8_t peekedElementStorage[std::numeric_limits<uint8_t>::max()];
140140
memset(peekedElementStorage, 0, sizeof(peekedElementStorage));
@@ -148,7 +148,7 @@ TEST_GROUP(tiny_queue)
148148
}
149149
}
150150

151-
void peeking_from_the_front_of_the_queue_should_yield(uint8_t * element)
151+
void peeking_from_the_front_of_the_queue_should_yield(uint8_t* element)
152152
{
153153
peeking_should_yield(element, large_element_size, 0);
154154
}
@@ -226,6 +226,41 @@ TEST_GROUP(tiny_queue)
226226
{
227227
CHECK_EQUAL(expected, tiny_queue_count(&self));
228228
}
229+
230+
void given_that_the_queue_has_been_initialized_with_storage_size(uint16_t size)
231+
{
232+
CHECK(size <= sizeof(buffer));
233+
234+
tiny_queue_init(
235+
&self,
236+
buffer,
237+
size);
238+
}
239+
240+
template <typename T>
241+
void given_that_an_element_has_been_enqueued(T element)
242+
{
243+
CHECK(tiny_queue_enqueue(&self, &element, sizeof(element)));
244+
}
245+
246+
void should_be_able_to_iterate_over_element_bytes(uint16_t index)
247+
{
248+
uint8_t expected[UINT8_MAX];
249+
uint16_t size;
250+
tiny_queue_peek(&self, expected, &size, index);
251+
252+
tiny_queue_raw_element_iterator_t iterator = tiny_queue_raw_element_iterator(&self, index);
253+
for(uint16_t i = 0; i < size; i++) {
254+
uint8_t byte = tiny_queue_raw_element_iterator_peek(&iterator);
255+
tiny_queue_raw_element_iterator_advance(&iterator);
256+
CHECK_EQUAL(expected[i], byte);
257+
}
258+
}
259+
260+
void given_that_an_element_has_been_discarded()
261+
{
262+
tiny_queue_discard(&self);
263+
}
229264
};
230265

231266
TEST(tiny_queue, should_be_able_to_enqueue_and_dequeue_a_small_element)
@@ -371,3 +406,23 @@ TEST(tiny_queue, should_know_how_many_elements_are_queued_after_some_have_been_d
371406
given_that_n_elements_have_been_discarded(2);
372407
the_element_count_should_be(2);
373408
}
409+
410+
TEST(tiny_queue, should_allow_the_bytes_of_an_element_to_be_iterated_over)
411+
{
412+
given_that_the_queue_has_been_initialized();
413+
given_that_an_element_has_been_enqueued((uint32_t)0x12345678);
414+
given_that_an_element_has_been_enqueued((uint16_t)0xABCD);
415+
should_be_able_to_iterate_over_element_bytes(0);
416+
should_be_able_to_iterate_over_element_bytes(1);
417+
}
418+
419+
TEST(tiny_queue, should_allow_the_bytes_of_an_element_to_be_iterated_over_even_if_the_element_wraps)
420+
{
421+
given_that_the_queue_has_been_initialized_with_storage_size(12);
422+
given_that_an_element_has_been_enqueued((uint16_t)0xABCD);
423+
given_that_an_element_has_been_enqueued((uint32_t)0x12345678);
424+
given_that_an_element_has_been_discarded();
425+
given_that_an_element_has_been_enqueued((uint32_t)0x87654321);
426+
427+
should_be_able_to_iterate_over_element_bytes(1);
428+
}

0 commit comments

Comments
 (0)