emlabcpp
modern opinionated embedded C++ library
handler.h
Go to the documentation of this file.
1 #pragma once
23 
24 #include "../../algorithm.h"
25 #include "../../protocol/converter.h"
26 #include "../../result.h"
27 #include "./page.h"
28 
29 #include <cstdint>
30 #include <optional>
31 #include <span>
32 
33 namespace emlabcpp::cfg
34 {
35 
36 template < typename T >
37 using opt = std::optional< T >;
38 
39 // each cell in memory is 64b wide, looks like this:
40 // 1b seq | 31b key | 32b val;
41 // if `1b` is true, val represents number of cells forming the value
42 
43 static constexpr uint32_t key_mask = 0x7FFFFFFF;
44 static constexpr uint32_t sin_bit_mask = 0x80000000;
45 
46 static_assert( ~key_mask == sin_bit_mask, "key mask and sin bit mask are not correct" );
47 
48 enum class cell_kind : uint8_t
49 {
50  SINGLE,
51  MULTI,
52 };
53 
54 constexpr uint32_t closest_multiple_of( uint32_t x, uint32_t r ) noexcept
55 {
56  return r * ( ( x + r - 1 ) / r );
57 }
58 
59 inline opt< cell_kind >
60 ser_cell( uint32_t key, std::span< std::byte const > value, std::span< std::byte, cell_size > dest )
61 {
62  if ( value.empty() || key > sin_bit_mask )
63  return {};
64 
65  static_assert( sizeof( uint32_t ) == hcell_size );
66  if ( value.size() <= hcell_size ) {
67  uint32_t val = 0x00;
68  std::memcpy( &val, value.data(), value.size() );
69  uint64_t tmp = key | sin_bit_mask;
70  tmp = ( tmp << 32 ) + val;
71 
72  std::memcpy( dest.data(), &tmp, cell_size );
73  return cell_kind::SINGLE;
74  } else {
75  uint32_t size =
76  closest_multiple_of( static_cast< uint32_t >( value.size() ), cell_size ) /
77  cell_size;
78  uint64_t tmp = key;
79  tmp = ( tmp << 32 ) + size;
80  std::memcpy( dest.data(), &tmp, cell_size );
81  return cell_kind::MULTI;
82  }
83 }
84 
85 struct deser_res
86 {
87  bool is_seq;
88  uint32_t key;
89  uint32_t val;
90 };
91 
92 inline opt< deser_res > deser_cell( std::span< std::byte, cell_size > c )
93 {
94  uint64_t tmp = 0x00;
95  std::memcpy( &tmp, c.data(), c.size() );
96  auto front = static_cast< uint32_t >( tmp >> 32 );
97  deser_res r{
98  .is_seq = !static_cast< bool >( front & sin_bit_mask ),
99  .key = static_cast< uint32_t >( front & key_mask ),
100  .val = static_cast< uint32_t >( tmp & 0xFFFF'FFFF ),
101  };
102 
103  if ( r.is_seq && r.val == 0 )
104  return {};
105  return r;
106 }
107 
108 inline opt< std::span< std::byte > >
109 store_kval_impl( uint32_t key, std::byte* beg, std::byte* val_end, std::byte* end )
110 {
111  if ( end - val_end < static_cast< int >( cell_size ) )
112  return {};
113  std::byte* new_end = val_end + cell_size;
114 
115  auto tmp = ser_cell(
116  key, { beg, val_end }, std::span< std::byte, cell_size >{ val_end, cell_size } );
117  if ( !tmp )
118  return {};
119  switch ( *tmp ) {
120  case cell_kind::MULTI:
121  return std::span{ beg, new_end };
122  case cell_kind::SINGLE:
123  return std::span{ val_end, cell_size };
124  }
125  return {};
126 }
127 
128 template < typename T >
129 opt< std::span< std::byte > > store_val( T const& val, std::span< std::byte > buffer )
130 {
131  using conv = protocol::converter_for< T, std::endian::little >; // XXX: make endianness
132  // configurable?
133  if ( buffer.size() < conv::max_size )
134  return {};
135  std::span< std::byte, conv::max_size > front{ buffer.data(), conv::max_size };
136 
137  bounded const used = conv::serialize_at( front, val );
138 
139  return std::span{ buffer.data(), buffer.data() + *used };
140 }
141 
142 inline opt< std::span< std::byte > >
143 store_val( std::span< std::byte const > val, std::span< std::byte > buffer )
144 {
145  if ( buffer.size() < val.size() )
146  return {};
147  std::span< std::byte > front{ buffer.data(), val.size() };
148  std::memcpy( front.data(), val.data(), val.size() );
149  return front;
150 }
151 
152 inline opt< std::span< std::byte > >
153 store_val( std::span< std::byte > val, std::span< std::byte > buffer )
154 {
155  return store_val( std::span< std::byte const >{ val.data(), val.size() }, buffer );
156 }
157 
158 template < typename T >
159 opt< T > get_val( std::span< std::byte const > data )
160 {
161 
162  using conv = protocol::converter_for< T, std::endian::little >; // XXX: make endianness
163  // configurable?
164  T res{};
165  if ( conv::deserialize( data, res ).has_error() )
166  return {};
167  return res;
168 }
169 
170 enum class cache_res
171 {
172  SEEN,
173  NOT_SEEN,
174 };
175 
176 inline bool is_free_cell( std::span< std::byte, cell_size > cell )
177 {
178  return all_of( cell, [&]( auto x ) {
179  return x == std::byte{ 00 };
180  } );
181 }
182 
183 enum class update_status_e : uint8_t
184 {
185  SUCCESS = 0x00,
186  FULL = 0x01,
187  MISSING_PAGE = 0x02,
188 
190  WRITE_ERROR,
191  READ_ERROR,
192  DESER_ERROR,
194  CLEAR_ERROR,
197 
199 };
200 
201 struct [[nodiscard]] status : emlabcpp::status< status, update_status_e >
202 {
204 
205  using enum update_status_e;
206 };
207 
209 {
210  virtual result read( std::size_t addr, std::span< std::byte, cell_size > data ) = 0;
211 
212  virtual ~read_iface() = default;
213 };
214 
216 {
217  virtual std::span< std::byte > get_buffer() = 0;
218 };
219 
221 {
223  std::size_t addr = 0x00;
224 };
225 
226 inline locate_current_info
227 locate_current_page( std::size_t mem_size, std::size_t page_size, read_iface& iface )
228 {
229  if ( mem_size % page_size != 0 )
230  return { .status = status::MEM_NOT_PAGE_MULTIPLY_ERROR };
231 
232  opt< hdr_state > hdr_st;
233  for ( uint32_t i = 0; i < mem_size / page_size; i++ ) {
234  std::byte data[cell_size] = {};
235  auto addr = i * page_size;
236  if ( iface.read( addr, data ) != result::SUCCESS )
237  return { .status = status::READ_ERROR };
238  auto st = hdr_to_hdr_state( data );
239  if ( !st ) {
240  if ( hdr_st )
241  return { .status = status::SUCCESS, .addr = addr - page_size };
242  continue;
243  } else if ( !hdr_st )
244  hdr_st = st;
245  else if ( *hdr_st != *st )
246  return { .status = status::SUCCESS, .addr = addr - page_size };
247  }
248  if ( !hdr_st )
249  return { .status = status::MISSING_PAGE };
250  return { .status = status::SUCCESS, .addr = mem_size - page_size };
251 }
252 
254 {
256  std::size_t addr = 0x00;
258 };
259 
260 inline locate_next_info
261 locate_next_page( std::size_t mem_size, std::size_t page_size, read_iface& iface )
262 {
263  if ( mem_size % page_size != 0 )
264  return { .status = status::MEM_NOT_PAGE_MULTIPLY_ERROR };
265 
266  opt< hdr_state > hdr_st;
267  for ( uint32_t i = 0; i < mem_size / page_size; i++ ) {
268  std::byte data[cell_size] = {};
269  auto addr = i * page_size;
270  if ( iface.read( addr, data ) != result::SUCCESS )
271  return { .status = status::READ_ERROR };
272  auto st = hdr_to_hdr_state( data );
273  if ( !st )
274  return { .status = status::SUCCESS, .addr = addr, .state = hdr_state::A };
275  else if ( !hdr_st )
276  hdr_st = st;
277  else if ( *hdr_st != *st )
278  return { .status = status::SUCCESS, .addr = addr, .state = *hdr_st };
279  }
280  // XXX: should not be possible at all
281  if ( !hdr_st )
282  return { .status = status::LOCATE_FAILED_ERROR };
283  return { .status = status::SUCCESS, .addr = 0x00, .state = next( *hdr_st ) };
284 }
285 
287 {
288  virtual result write( std::size_t start_addr, std::span< std::byte const > data ) = 0;
289 
290  virtual cache_res check_key_cache( uint32_t key ) = 0;
291  virtual bool value_changed( uint32_t key, std::span< std::byte const > data ) = 0;
292 
294  serialize_value( uint32_t key, std::span< std::byte > buffer ) = 0;
295 
296  virtual result reset_keys() = 0;
298 
299  virtual result clear_page( std::size_t addr ) = 0;
300 };
301 
302 inline bool decr_addr( std::size_t& addr, std::size_t n, std::size_t start_addr )
303 {
304  if ( addr - cell_size * n > addr )
305  return false;
306  addr -= cell_size * n;
307  if ( addr < start_addr )
308  return false;
309  return true;
310 }
311 
312 std::span< std::byte > manifest_value(
313  bool is_seq,
314  auto& cell_val,
315  std::size_t start_addr,
316  auto& addr,
317  auto& iface,
318  auto& buffer )
319 {
320  if ( is_seq ) {
321  for ( std::size_t i = 0; i < cell_val; ++i ) {
322  if ( !decr_addr( addr, 1, start_addr ) )
323  return {};
324  auto buffer_offset = ( cell_val - i - 1 ) * cell_size;
325  if ( buffer_offset + cell_size > buffer.size() )
326  return {};
327  if ( iface.read(
328  addr,
329  std::span< std::byte, cell_size >{
330  buffer.data() + buffer_offset, cell_size } ) !=
331  result::SUCCESS )
332  return {};
333  }
334  return buffer.subspan( 0, cell_val * cell_size );
335  } else {
336  std::memcpy( buffer.data(), &cell_val, sizeof( cell_val ) );
337  return buffer.subspan( 0, sizeof( cell_val ) );
338  }
339 }
340 
341 inline status
342 store_key( std::size_t& addr, std::size_t end_addr, uint32_t key, update_iface& iface )
343 {
344  auto const capacity = end_addr - addr;
345  std::span< std::byte > buffer = iface.get_buffer();
346  opt< std::span< std::byte const > > used = iface.serialize_value( key, buffer );
347  if ( !used )
348  return status::SERIALIZE_VALUE_ERROR;
349  std::span< std::byte const > data = *used;
350 
351  used = store_kval_impl(
352  key, buffer.data(), buffer.data() + data.size(), buffer.data() + buffer.size() );
353  data = *used;
354 
355  if ( capacity < data.size() )
356  return status::FULL;
357 
358  if ( iface.write( addr, data ) != result::SUCCESS )
359  return status::WRITE_ERROR;
360  addr += data.size();
361  return status::SUCCESS;
362 }
363 
364 inline status dump_unseen_keys( std::size_t addr, std::size_t end_addr, update_iface& iface )
365 {
366  while ( auto k = iface.take_unseen_key() ) {
367  auto res = store_key( addr, end_addr, *k, iface );
368  if ( res != status::SUCCESS )
369  return res;
370  }
371  return status::SUCCESS;
372 }
373 
374 inline status
375 update_stored_config( std::size_t start_addr, std::size_t end_addr, update_iface& iface )
376 {
377  std::byte tmp[cell_size];
378  std::size_t addr = end_addr;
379 
380  std::size_t last_free = end_addr;
381  for ( ;; ) {
382  if ( !decr_addr( addr, 1, start_addr ) )
383  break;
384  if ( iface.read( addr, std::span< std::byte, cell_size >{ tmp } ) !=
385  result::SUCCESS )
386  return status::READ_ERROR;
387  if ( is_free_cell( tmp ) ) {
388  last_free = addr;
389  continue;
390  }
391  addr += cell_size;
392  break;
393  }
394 
395  std::span< std::byte > buffer = iface.get_buffer();
396 
397  for ( ;; ) {
398  if ( !decr_addr( addr, 1, start_addr ) )
399  break;
400  if ( iface.read( addr, std::span< std::byte, cell_size >{ tmp } ) !=
401  result::SUCCESS )
402  return status::READ_ERROR;
403  auto c = deser_cell( tmp );
404  if ( !c )
405  return status::DESER_ERROR;
406  auto [is_seq, key, val] = *c;
407  cache_res cr = iface.check_key_cache( key );
408  if ( cr == cache_res::SEEN ) {
409  if ( is_seq )
410  decr_addr( addr, val, start_addr );
411  continue;
412  }
413 
414  std::span< std::byte > val_sp =
415  manifest_value( is_seq, val, start_addr, addr, iface, buffer );
416  bool changed = iface.value_changed( key, val_sp );
417  if ( !changed )
418  continue;
419 
420  if ( auto res = store_key( last_free, end_addr, key, iface );
421  res != status::SUCCESS )
422  return res;
423  }
424 
425  return dump_unseen_keys( last_free, end_addr, iface );
426 }
427 
428 inline status update( std::size_t mem_size, std::size_t page_size, update_iface& iface )
429 {
430  if ( auto [status, addr] = locate_current_page( mem_size, page_size, iface );
431  status != status::MISSING_PAGE ) {
432  if ( status != status::SUCCESS )
433  return status;
434  auto r = update_stored_config( addr + cell_size, addr + page_size, iface );
435  if ( r != status::FULL )
436  return r;
437  }
438  auto [status, addr, page_st] = locate_next_page( mem_size, page_size, iface );
439  if ( status != status::SUCCESS )
440  return status;
441 
442  if ( iface.reset_keys() != result::SUCCESS )
443  return status::RESET_KEYS_ERROR;
444 
445  if ( iface.clear_page( addr ) != result::SUCCESS )
446  return status::CLEAR_ERROR;
447 
448  auto hdr = get_hdr( page_st );
449  if ( iface.write( addr, std::span< std::byte >{ hdr } ) != result::SUCCESS )
450  return status::WRITE_ERROR;
451  addr += cell_size;
452 
453  return dump_unseen_keys( addr, addr + page_size, iface );
454 }
455 
457 {
458 
459  virtual cache_res check_key_cache( uint32_t key ) = 0;
460 
461  virtual result on_kval( uint32_t key, std::span< std::byte > ) = 0;
462 };
463 
464 inline status load_stored_config( std::size_t start_addr, std::size_t end_addr, load_iface& iface )
465 {
466  std::size_t addr = end_addr;
467 
468  std::byte tmp[cell_size];
469  auto buffer = iface.get_buffer();
470  for ( ;; ) {
471  if ( !decr_addr( addr, 1, start_addr ) )
472  break;
473  if ( iface.read( addr, std::span< std::byte, cell_size >{ tmp } ) !=
474  result::SUCCESS )
475  return status::READ_ERROR;
476  // XXX: copy-pasta from other function
477  auto c = deser_cell( tmp );
478  if ( !c )
479  continue;
480  auto [is_seq, key, val] = *c;
481  cache_res cr = iface.check_key_cache( key );
482  if ( cr == cache_res::SEEN ) {
483  if ( is_seq )
484  decr_addr( addr, val, start_addr );
485  continue;
486  }
487  std::span< std::byte > val_sp =
488  manifest_value( is_seq, val, start_addr, addr, iface, buffer );
489 
490  if ( iface.on_kval( key, val_sp ) != result::SUCCESS )
491  return status::ON_KVAL_ERROR;
492  }
493 
494  return status::SUCCESS;
495 }
496 
497 inline status load( std::size_t mem_size, std::size_t page_size, load_iface& iface )
498 {
499  auto [status, addr] = locate_current_page( mem_size, page_size, iface );
500  if ( status == status::MISSING_PAGE )
501  return status::SUCCESS;
502  if ( status != status::SUCCESS )
503  return status;
504  return load_stored_config( addr + cell_size, addr + page_size, iface );
505 }
506 
507 // Util functions usable as callbacks
508 
509 // a is prefix of b with zeros if a.size() <= b.size()
510 inline bool
511 is_prefix_of_with_zeros( std::span< std::byte const > a, std::span< std::byte const > b )
512 {
513  if ( a.size() > b.size() )
514  return false;
515  auto c = b.subspan( 0, a.size() );
516  if ( !std::ranges::equal( a, c ) )
517  return false;
518  return std::ranges::all_of( b.subspan( a.size() ), []( std::byte b ) {
519  return b == std::byte{ 0x00 };
520  } );
521 }
522 
524 {
525  if ( cont.empty() )
526  return {};
527  auto k = cont.back();
528  cont.pop_back();
529  return k;
530 }
531 
532 cache_res key_check_unseen_container( auto& cont, uint32_t key )
533 {
534  auto iter = find( cont, key );
535  if ( iter == cont.end() )
536  return cache_res::SEEN;
537  std::swap( *iter, cont.back() );
538  cont.pop_back();
539  return cache_res::NOT_SEEN;
540 }
541 
542 } // namespace emlabcpp::cfg
The bounded class represents a wrapper over type T constrained between MinVal and MaxVal as compile-t...
Definition: bounded.h:44
MIT License.
Definition: base.h:25
static constexpr uint16_t cell_size
Definition: base.h:28
opt< std::size_t > pop_from_container(auto &cont)
Definition: handler.h:523
update_status_e
Definition: handler.h:184
status update(std::size_t mem_size, std::size_t page_size, update_iface &iface)
Definition: handler.h:428
bool is_prefix_of_with_zeros(std::span< std::byte const > a, std::span< std::byte const > b)
Definition: handler.h:511
static constexpr uint32_t sin_bit_mask
Definition: handler.h:44
cell_kind
Definition: handler.h:49
opt< deser_res > deser_cell(std::span< std::byte, cell_size > c)
Definition: handler.h:92
locate_current_info locate_current_page(std::size_t mem_size, std::size_t page_size, read_iface &iface)
Definition: handler.h:227
locate_next_info locate_next_page(std::size_t mem_size, std::size_t page_size, read_iface &iface)
Definition: handler.h:261
opt< std::span< std::byte > > store_val(T const &val, std::span< std::byte > buffer)
Definition: handler.h:129
bool is_free_cell(std::span< std::byte, cell_size > cell)
Definition: handler.h:176
std::optional< T > opt
Definition: handler.h:37
static constexpr uint32_t key_mask
Definition: handler.h:43
std::array< std::byte, 2 > get_hdr(hdr_state hst) noexcept
Definition: page.h:78
status dump_unseen_keys(std::size_t addr, std::size_t end_addr, update_iface &iface)
Definition: handler.h:364
hdr_state state
Definition: handler.h:257
uint64_t cell
Definition: base.h:27
status load(std::size_t mem_size, std::size_t page_size, load_iface &iface)
Definition: handler.h:497
uint32_t val
Definition: handler.h:89
std::span< std::byte > manifest_value(bool is_seq, auto &cell_val, std::size_t start_addr, auto &addr, auto &iface, auto &buffer)
Definition: handler.h:312
opt< T > get_val(std::span< std::byte const > data)
Definition: handler.h:159
std::size_t addr
Definition: handler.h:223
opt< std::span< std::byte > > store_kval_impl(uint32_t key, std::byte *beg, std::byte *val_end, std::byte *end)
Definition: handler.h:109
status update_stored_config(std::size_t start_addr, std::size_t end_addr, update_iface &iface)
Definition: handler.h:375
bool decr_addr(std::size_t &addr, std::size_t n, std::size_t start_addr)
Definition: handler.h:302
hdr_state
Definition: page.h:37
std::size_t addr
Definition: handler.h:256
hdr_state next(hdr_state cs) noexcept
Definition: page.h:43
bool is_seq
Definition: handler.h:87
status load_stored_config(std::size_t start_addr, std::size_t end_addr, load_iface &iface)
Definition: handler.h:464
cfg::status status
Definition: handler.h:255
opt< hdr_state > hdr_to_hdr_state(std::span< std::byte, cell_size > b) noexcept
Definition: page.h:71
cfg::status status
Definition: handler.h:222
constexpr uint32_t closest_multiple_of(uint32_t x, uint32_t r) noexcept
Definition: handler.h:54
cache_res key_check_unseen_container(auto &cont, uint32_t key)
Definition: handler.h:532
uint32_t key
Definition: handler.h:88
static constexpr uint16_t hcell_size
Definition: base.h:29
opt< cell_kind > ser_cell(uint32_t key, std::span< std::byte const > value, std::span< std::byte, cell_size > dest)
Definition: handler.h:60
cache_res
Definition: handler.h:171
status store_key(std::size_t &addr, std::size_t end_addr, uint32_t key, update_iface &iface)
Definition: handler.h:342
Definition: handler.h:86
Definition: handler.h:221
Definition: handler.h:254
static constexpr T deserialize(std::span< std::byte const, max_size > const &buffer)
Definition: serializer.h:81
static constexpr void serialize_at(std::span< std::byte, max_size > buffer, T item)
Definition: serializer.h:76
static constexpr std::size_t max_size
Definition: serializer.h:73
decltype(converter_for_impl< D, E >()) converter_for
Definition: converter.h:54
constexpr pointer data() noexcept
Returns pointer to first item of the storage.
Definition: static_storage.h:108
constexpr bool all_of(Container &&cont, PredicateCallable &&f=std::identity())
Returns true if call to predicate 'f(x)' returns true for all items in 'cont'.
Definition: algorithm.h:342
constexpr bool equal(LhContainer &&lh, RhContainer &&rh, BinaryPredicateCallable &&f=std::equal_to< void >{})
Returns true if containers 'lh' and 'rh' has same size and calls to predicate f - f(lh[i],...
Definition: algorithm.h:356
void swap(static_vector< T, N > const &lh, static_vector< T, N > const &rh) noexcept
Definition: static_vector.h:294
T res
Definition: algorithm.h:505
constexpr auto find(Container &&cont, T const &item)
Finds first item in container 'cont' that is equal to 'item', returns iterator for container,...
Definition: algorithm.h:140
physical_quantity< 0, 0, 0, 0, 0, 0, 0, 0, 1 > byte
Definition: physical_quantity.h:118
Definition: handler.h:216
virtual std::span< std::byte > get_buffer()=0
Definition: handler.h:457
virtual cache_res check_key_cache(uint32_t key)=0
virtual result on_kval(uint32_t key, std::span< std::byte >)=0
Definition: handler.h:209
virtual result read(std::size_t addr, std::span< std::byte, cell_size > data)=0
virtual ~read_iface()=default
Definition: handler.h:202
Definition: handler.h:287
virtual bool value_changed(uint32_t key, std::span< std::byte const > data)=0
virtual opt< uint32_t > take_unseen_key()=0
virtual opt< std::span< std::byte const > > serialize_value(uint32_t key, std::span< std::byte > buffer)=0
virtual result write(std::size_t start_addr, std::span< std::byte const > data)=0
virtual result clear_page(std::size_t addr)=0
virtual result reset_keys()=0
virtual cache_res check_key_cache(uint32_t key)=0
result represents an result of some operation, as an alternative to returning just bool with true/fal...
Definition: result.h:42
Definition: status.h:31