BlueToe
an alternative GATT/BLE implementation
Loading...
Searching...
No Matches
ring_buffer.hpp
1#ifndef BLUETOE_LINK_LAYER_RING_BUFFER_HPP
2#define BLUETOE_LINK_LAYER_RING_BUFFER_HPP
3
4#include <cstdint>
5#include <cassert>
6#include <cstdlib>
7
8#include <bluetoe/buffer.hpp>
9#include <bluetoe/default_pdu_layout.hpp>
10
11namespace bluetoe {
12namespace link_layer {
13
26 template < std::size_t Size, typename Buffer = read_buffer, typename Layout = default_pdu_layout >
28 {
29 public:
33 static constexpr std::size_t size = Size;
34
40 explicit pdu_ring_buffer( std::uint8_t* buffer );
41
47 void reset( std::uint8_t* buffer );
48
61 Buffer alloc_front( std::uint8_t* buffer, std::size_t size ) const;
62
73 void push_front( std::uint8_t* buffer, const Buffer& pdu );
74
80 Buffer next_end() const;
81
87 void pop_end( std::uint8_t* buffer );
88
92 bool more_than_one() const;
93
94 private:
95 static constexpr std::size_t ll_header_size = 2;
96 static constexpr std::uint16_t wrap_mark = 0;
97
98 template < class P >
99 static std::uint8_t pdu_length( const P& pdu );
100
101 template < typename P >
102 static std::size_t pdu_length( P* );
103
104 // 1) if end_ == front_, the ring is empty
105 // end_ and front_ can point to everywhere into the buffer
106 // 2) if front_ > end_, the all elements are between front_ and end_
107 // 3) if end_ > front_, -> buffer splited
108 // there are elements from front_ to the end of the buffer
109 // and there are elements from the beginning of the buffer till end_
110 std::uint8_t* end_;
111 std::uint8_t* front_;
112 };
113
114 template < std::size_t Size, typename Buffer, typename Layout >
116 {
117 reset( buffer );
118 }
119
120 template < std::size_t Size, typename Buffer, typename Layout >
122 {
123 assert( buffer );
124 front_ = buffer;
125 end_ = buffer;
126
127 Layout::header( buffer, wrap_mark );
128 }
129
130 template < std::size_t Size, typename Buffer, typename Layout >
131 Buffer pdu_ring_buffer< Size, Buffer, Layout >::alloc_front( std::uint8_t* buffer, std::size_t size ) const
132 {
133 assert( buffer );
134 assert( size >= Layout::data_channel_pdu_memory_size( 0 ) );
135
136 // buffer splited? There must be one byte left to not overflow the ring.
137 if ( end_ > front_ && static_cast< std::ptrdiff_t >( size ) < end_ - front_ )
138 {
139 return Buffer{ front_, size };
140 }
141
142 if ( front_ >= end_ )
143 {
144 const std::uint8_t* end_of_buffer = buffer + Size;
145
146 // allocate at the end?
147 if ( static_cast< std::ptrdiff_t >( size ) <= end_of_buffer - front_ )
148 {
149 return Buffer{ front_, size };
150 }
151
152 // allocate at the begining? Again, there must be one byte left between the end front_ and the end_
153 if ( static_cast< std::ptrdiff_t >( size ) < end_ - buffer )
154 {
155 return Buffer{ buffer, size };
156 }
157 }
158
159 return Buffer{ 0, 0 };
160 }
161
162 template < std::size_t Size, typename Buffer, typename Layout >
163 void pdu_ring_buffer< Size, Buffer, Layout >::push_front( std::uint8_t* buffer, const Buffer& pdu )
164 {
165 assert( pdu.size >= pdu_length( pdu ) );
166
167 const std::uint8_t* end_of_buffer = buffer + Size;
168
169 // set size to 0 to mark force the end_ pointer to wrap here
170 if ( front_ != pdu.buffer && front_ + 1 < end_of_buffer )
171 {
172 Layout::header( front_, wrap_mark );
173 }
174
175 const bool was_empty = front_ == end_;
176
177 front_ = pdu.buffer + pdu_length( pdu );
178
179 // if the ring was empty, the end_ ptr have to wrap too now
180 if ( was_empty )
181 end_ = pdu.buffer;
182 }
183
184 template < std::size_t Size, typename Buffer, typename Layout >
186 {
187 return front_ == end_
188 ? Buffer{ 0, 0 }
189 : Buffer{ end_, pdu_length( end_ ) };
190 }
191
192 template < std::size_t Size, typename Buffer, typename Layout >
194 {
195 end_ += pdu_length( end_ );
196
197 const std::uint8_t* end_of_buffer = buffer + Size;
198
199 // wrap the end_ pointer to the beginning, if the buffer is not empty
200 if ( end_ != front_ && ( end_ + 1 >= end_of_buffer || end_[ 1 ] == wrap_mark ) )
201 end_ = buffer;
202 }
203
204 template < std::size_t Size, typename Buffer, typename Layout >
205 template < class P >
206 std::uint8_t pdu_ring_buffer< Size, Buffer, Layout >::pdu_length( const P& pdu )
207 {
208 return pdu_length( pdu.buffer );
209 }
210
211 template < std::size_t Size, typename Buffer, typename Layout >
212 template < typename P >
213 std::size_t pdu_ring_buffer< Size, Buffer, Layout >::pdu_length( P* p )
214 {
215 return Layout::data_channel_pdu_memory_size( Layout::header( p ) >> 8 );
216 }
217
218 template < std::size_t Size, typename Buffer, typename Layout >
220 {
221 return end_ != front_ && ( end_ + pdu_length( end_) ) != front_;
222 }
223
224}
225}
226
227#endif