Control Surface main
MIDI Control Surface library for Arduino
Loading...
Searching...
No Matches
BLERingBuf.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cstdint>
4#include <cstring>
5
6#include <Settings/NamespaceSettings.hpp>
7
9
11
12template <class T>
16 constexpr static size_t alignment = alignof(T);
17 T load_acquire() const { return value; }
18 void add_release(T t) { value += t; }
19 void sub_release(T t) { value -= t; }
20};
21
32template <uint_fast16_t Capacity,
35 private:
36 struct Header {
37 uint16_t size : 14;
38 uint8_t type : 2;
39 Header() = default;
41 : size {size}, type {static_cast<uint8_t>(type)} {}
42 BLEDataType getType() const { return static_cast<BLEDataType>(type); }
43 };
44 constexpr static uint_fast16_t header_size = sizeof(Header);
45 static_assert(header_size == 2, "");
46
47 constexpr static uint_fast16_t capacity = Capacity;
48 unsigned char buffer[capacity];
49 alignas(SizeT::alignment) uint_fast16_t read_p = 0;
50 alignas(SizeT::alignment) uint_fast16_t write_p = header_size;
51 alignas(SizeT::alignment) SizeT size {header_size};
52
53 constexpr static uint_fast16_t ceil_h(uint_fast16_t i) {
54 return ((i + header_size - 1) / header_size) * header_size;
55 }
56
57 public:
59 Header header {0, BLEDataType::None};
60 static_assert(capacity % header_size == 0, "");
61 std::memcpy(buffer, &header, header_size);
62 }
63
69 if (type == BLEDataType::None)
70 return false;
71 uint_fast16_t loc_size = size.load_acquire();
72 uint_fast16_t add_size = 0;
73 assert(loc_size <= capacity);
74 assert(write_p < capacity);
75 assert(write_p % header_size == 0);
76 // We need to write at least one header
77 uint_fast16_t expected_req_size = loc_size + data.length + header_size;
78 if (expected_req_size > capacity)
79 return false; // not enough space
80 // Contiguous size remaining (excluding header)
81 uint_fast16_t contig_size = capacity - write_p - header_size;
82 assert(contig_size < capacity);
83 // We may need to write a second header
84 uint_fast16_t worst_case_req_size = expected_req_size + header_size;
85 if (data.length > contig_size && worst_case_req_size > capacity)
86 return false; // not enough space
87 // Write the first header for the packet
88 uint16_t size_1 = std::min<uint_fast16_t>(contig_size, data.length);
89 Header header {size_1, type};
90 std::memcpy(buffer + write_p, &header, sizeof(header));
92 add_size += header_size;
93 // Write first data
94 if (size_1 > 0) // avoid memcpy with nullptr
95 std::memcpy(buffer + write_p, data.data, size_1);
96 write_p += ceil_h(size_1);
97 add_size += ceil_h(size_1);
98 if (write_p == capacity)
99 write_p = 0;
100 // Now write the remainder at the beginning of the circular buffer
101 uint16_t size_2 = data.length - size_1;
102 if (size_2 > 0) {
103 // Write the continuation header
104 Header header {size_2, BLEDataType::Continuation};
105 std::memcpy(buffer + write_p, &header, sizeof(header));
107 add_size += header_size;
108 // Write the remainder of the data
109 std::memcpy(buffer + write_p, data.data + size_1, size_2);
110 write_p += ceil_h(size_2);
111 add_size += ceil_h(size_2);
112 }
113 size.add_release(add_size);
114 return true;
115 }
116
128 uint_fast16_t loc_size = size.load_acquire();
129 assert(loc_size >= header_size);
130 assert(read_p < capacity);
131 assert(read_p % header_size == 0);
132 // Read the old header
133 Header old_header;
134 std::memcpy(&old_header, buffer + read_p, sizeof(old_header));
135 // If the previous chunk is the only thing left in the buffer
136 if (loc_size - header_size == ceil_h(old_header.size)) {
137 // If there is still actual data left in the buffer
138 if (old_header.getType() != BLEDataType::None) {
139 // Free the old data, preserving space for a header
140 read_p += ceil_h(old_header.size);
141 size.sub_release(ceil_h(old_header.size));
142 // Write an empty header
143 Header header {0, BLEDataType::None};
144 std::memcpy(buffer + read_p, &header, sizeof(header));
145 }
146 data = {nullptr, 0};
147 return BLEDataType::None;
148 } else {
149 // Otherwise, discard the old data and header
150 read_p += header_size + ceil_h(old_header.size);
151 size.sub_release(header_size + ceil_h(old_header.size));
152 if (read_p == capacity)
153 read_p = 0;
154 // Read the next header
155 Header header;
156 std::memcpy(&header, buffer + read_p, sizeof(header));
157 data = {buffer + read_p + header_size, header.size};
158 return header.getType();
159 }
160 }
161};
162
Type definitions and callback interfaces for communication between the low-level BLE stacks and highe...
BLEDataType
Describes a byte buffer containing (part of) a BLE packet.
Definition BLEAPI.hpp:58
@ None
No buffers available.
@ Continuation
Buffer contains a chunk of a BLE packet.
@ Packet
Buffer contains the start of a BLE packet.
#define END_CS_NAMESPACE
#define BEGIN_CS_NAMESPACE
Circular FIFO buffer for buffering BLE packet data.
static constexpr uint_fast16_t ceil_h(uint_fast16_t i)
unsigned char buffer[capacity]
BLEDataType pop(BLEDataView &data)
Get a view to the next chunk of data.
bool push(BLEDataView data, BLEDataType type=BLEDataType::Packet)
Copy the given data into the buffer.
uint_fast16_t read_p
static constexpr uint_fast16_t header_size
uint_fast16_t write_p
static constexpr uint_fast16_t capacity
Non-owning, std::span-style read-only view of BLE data.
Definition BLEAPI.hpp:42
uint16_t length
Definition BLEAPI.hpp:44
const uint8_t * data
Definition BLEAPI.hpp:43
BLEDataType getType() const
Header(uint16_t size, BLEDataType type)
static constexpr size_t alignment
Alignment for size, and read/write pointers to avoid false sharing.