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 "../../error_code.h"
26 #include "../../protocol/converter.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 status : 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 status_category : error_category< status >
202 {
203  [[nodiscard]] char const* message( error_value_type code ) const noexcept override
204  {
205  auto s = static_cast< status >( code );
206  switch ( s ) {
207  case status::SUCCESS:
208  return "success";
209  case status::FULL:
210  return "not enough space to store the value";
212  return "no page header found in memory";
214  return "failed to serialize the value";
215  case status::WRITE_ERROR:
216  return "failed to write to memory";
217  case status::READ_ERROR:
218  return "failed to read from memory";
219  case status::DESER_ERROR:
220  return "failed to deserialize the value";
222  return "failed to reset keys";
223  case status::CLEAR_ERROR:
224  return "failed to clear page";
226  return "failed to locate page";
228  return "error in on_kval callback";
230  return "memory size is not a multiple of page size";
231  default:
232  return "unknown error";
233  }
234  }
235 };
236 } // namespace emlabcpp::cfg
237 
238 namespace emlabcpp
239 {
240 template <>
241 inline constexpr cfg::status_category error_category_v< cfg::status > = {};
242 }
243 
244 namespace emlabcpp::cfg
245 {
247 {
248  virtual error_code read( std::size_t addr, std::span< std::byte, cell_size > data ) = 0;
249 
250  virtual ~read_iface() = default;
251 };
252 
254 {
255  virtual std::span< std::byte > get_buffer() = 0;
256 };
257 
259 {
261  std::size_t addr = 0x00;
262 };
263 
264 inline locate_current_info
265 locate_current_page( std::size_t mem_size, std::size_t page_size, read_iface& iface )
266 {
267  if ( mem_size % page_size != 0 )
269 
270  opt< hdr_state > hdr_st;
271  for ( uint32_t i = 0; i < mem_size / page_size; i++ ) {
272  std::byte data[cell_size] = {};
273  auto addr = i * page_size;
274  if ( !iface.read( addr, data ) )
275  return { .status = status::READ_ERROR };
276  auto st = hdr_to_hdr_state( data );
277  if ( !st ) {
278  if ( hdr_st )
279  return { .status = status::SUCCESS, .addr = addr - page_size };
280  continue;
281  } else if ( !hdr_st )
282  hdr_st = st;
283  else if ( *hdr_st != *st )
284  return { .status = status::SUCCESS, .addr = addr - page_size };
285  }
286  if ( !hdr_st )
287  return { .status = status::MISSING_PAGE };
288  return { .status = status::SUCCESS, .addr = mem_size - page_size };
289 }
290 
292 {
294  std::size_t addr = 0x00;
296 };
297 
298 inline locate_next_info
299 locate_next_page( std::size_t mem_size, std::size_t page_size, read_iface& iface )
300 {
301  if ( mem_size % page_size != 0 )
303 
304  opt< hdr_state > hdr_st;
305  for ( uint32_t i = 0; i < mem_size / page_size; i++ ) {
306  std::byte data[cell_size] = {};
307  auto addr = i * page_size;
308  if ( !iface.read( addr, data ) )
309  return { .status = status::READ_ERROR };
310  auto st = hdr_to_hdr_state( data );
311  if ( !st )
312  return { .status = status::SUCCESS, .addr = addr, .state = hdr_state::A };
313  else if ( !hdr_st )
314  hdr_st = st;
315  else if ( *hdr_st != *st )
316  return { .status = status::SUCCESS, .addr = addr, .state = *hdr_st };
317  }
318  // XXX: should not be possible at all
319  if ( !hdr_st )
320  return { .status = status::LOCATE_FAILED_ERROR };
321  return { .status = status::SUCCESS, .addr = 0x00, .state = next( *hdr_st ) };
322 }
323 
325 {
326  virtual error_code write( std::size_t start_addr, std::span< std::byte const > data ) = 0;
327 
328  virtual cache_res check_key_cache( uint32_t key ) = 0;
329  virtual bool value_changed( uint32_t key, std::span< std::byte const > data ) = 0;
330 
332  serialize_value( uint32_t key, std::span< std::byte > buffer ) = 0;
333 
334  virtual error_code reset_keys() = 0;
336 
337  virtual error_code clear_page( std::size_t addr ) = 0;
338 };
339 
340 inline bool decr_addr( std::size_t& addr, std::size_t n, std::size_t start_addr )
341 {
342  if ( addr - cell_size * n > addr )
343  return false;
344  addr -= cell_size * n;
345  if ( addr < start_addr )
346  return false;
347  return true;
348 }
349 
350 std::span< std::byte > manifest_value(
351  bool is_seq,
352  auto& cell_val,
353  std::size_t start_addr,
354  auto& addr,
355  auto& iface,
356  auto& buffer )
357 {
358  if ( is_seq ) {
359  for ( std::size_t i = 0; i < cell_val; ++i ) {
360  if ( !decr_addr( addr, 1, start_addr ) )
361  return {};
362  auto buffer_offset = ( cell_val - i - 1 ) * cell_size;
363  if ( buffer_offset + cell_size > buffer.size() )
364  return {};
365  if ( !iface.read(
366  addr,
367  std::span< std::byte, cell_size >{
368  buffer.data() + buffer_offset, cell_size } ) )
369  return {};
370  }
371  return buffer.subspan( 0, cell_val * cell_size );
372  } else {
373  std::memcpy( buffer.data(), &cell_val, sizeof( cell_val ) );
374  return buffer.subspan( 0, sizeof( cell_val ) );
375  }
376 }
377 
378 inline status
379 store_key( std::size_t& addr, std::size_t end_addr, uint32_t key, update_iface& iface )
380 {
381  auto const capacity = end_addr - addr;
382  std::span< std::byte > buffer = iface.get_buffer();
383  opt< std::span< std::byte const > > used = iface.serialize_value( key, buffer );
384  if ( !used )
386  std::span< std::byte const > data = *used;
387 
388  used = store_kval_impl(
389  key, buffer.data(), buffer.data() + data.size(), buffer.data() + buffer.size() );
390  data = *used;
391 
392  if ( capacity < data.size() )
393  return status::FULL;
394 
395  if ( !iface.write( addr, data ) )
396  return status::WRITE_ERROR;
397  addr += data.size();
398  return status::SUCCESS;
399 }
400 
401 inline status dump_unseen_keys( std::size_t addr, std::size_t end_addr, update_iface& iface )
402 {
403  while ( auto k = iface.take_unseen_key() ) {
404  auto res = store_key( addr, end_addr, *k, iface );
405  if ( res != status::SUCCESS )
406  return res;
407  }
408  return status::SUCCESS;
409 }
410 
411 inline status
412 update_stored_config( std::size_t start_addr, std::size_t end_addr, update_iface& iface )
413 {
414  std::byte tmp[cell_size];
415  std::size_t addr = end_addr;
416 
417  std::size_t last_free = end_addr;
418  for ( ;; ) {
419  if ( !decr_addr( addr, 1, start_addr ) )
420  break;
421  if ( !iface.read( addr, std::span< std::byte, cell_size >{ tmp } ) )
422  return status::READ_ERROR;
423  if ( is_free_cell( tmp ) ) {
424  last_free = addr;
425  continue;
426  }
427  addr += cell_size;
428  break;
429  }
430 
431  std::span< std::byte > buffer = iface.get_buffer();
432 
433  for ( ;; ) {
434  if ( !decr_addr( addr, 1, start_addr ) )
435  break;
436  if ( !iface.read( addr, std::span< std::byte, cell_size >{ tmp } ) )
437  return status::READ_ERROR;
438  auto c = deser_cell( tmp );
439  if ( !c )
440  return status::DESER_ERROR;
441  auto [is_seq, key, val] = *c;
442  cache_res cr = iface.check_key_cache( key );
443  if ( cr == cache_res::SEEN ) {
444  if ( is_seq )
445  decr_addr( addr, val, start_addr );
446  continue;
447  }
448 
449  std::span< std::byte > val_sp =
450  manifest_value( is_seq, val, start_addr, addr, iface, buffer );
451  bool changed = iface.value_changed( key, val_sp );
452  if ( !changed )
453  continue;
454 
455  if ( auto res = store_key( last_free, end_addr, key, iface );
456  res != status::SUCCESS )
457  return res;
458  }
459 
460  return dump_unseen_keys( last_free, end_addr, iface );
461 }
462 
463 inline status update( std::size_t mem_size, std::size_t page_size, update_iface& iface )
464 {
465  if ( auto [status, addr] = locate_current_page( mem_size, page_size, iface );
467  if ( status != status::SUCCESS )
468  return status;
469  auto r = update_stored_config( addr + cell_size, addr + page_size, iface );
470  if ( r != status::FULL )
471  return r;
472  }
473  auto [status, addr, page_st] = locate_next_page( mem_size, page_size, iface );
474  if ( status != status::SUCCESS )
475  return status;
476 
477  if ( !iface.reset_keys() )
479 
480  if ( !iface.clear_page( addr ) )
481  return status::CLEAR_ERROR;
482 
483  auto hdr = get_hdr( page_st );
484  if ( !iface.write( addr, std::span< std::byte >{ hdr } ) )
485  return status::WRITE_ERROR;
486  addr += cell_size;
487 
488  return dump_unseen_keys( addr, addr + page_size, iface );
489 }
490 
492 {
493 
494  virtual cache_res check_key_cache( uint32_t key ) = 0;
495 
496  virtual error_code on_kval( uint32_t key, std::span< std::byte > ) = 0;
497 };
498 
499 inline status load_stored_config( std::size_t start_addr, std::size_t end_addr, load_iface& iface )
500 {
501  std::size_t addr = end_addr;
502 
503  std::byte tmp[cell_size];
504  auto buffer = iface.get_buffer();
505  for ( ;; ) {
506  if ( !decr_addr( addr, 1, start_addr ) )
507  break;
508  if ( !iface.read( addr, std::span< std::byte, cell_size >{ tmp } ) )
509  return status::READ_ERROR;
510  // XXX: copy-pasta from other function
511  auto c = deser_cell( tmp );
512  if ( !c )
513  continue;
514  auto [is_seq, key, val] = *c;
515  cache_res cr = iface.check_key_cache( key );
516  if ( cr == cache_res::SEEN ) {
517  if ( is_seq )
518  decr_addr( addr, val, start_addr );
519  continue;
520  }
521  std::span< std::byte > val_sp =
522  manifest_value( is_seq, val, start_addr, addr, iface, buffer );
523 
524  if ( !iface.on_kval( key, val_sp ) )
525  return status::ON_KVAL_ERROR;
526  }
527 
528  return status::SUCCESS;
529 }
530 
531 inline status load( std::size_t mem_size, std::size_t page_size, load_iface& iface )
532 {
533  auto [status, addr] = locate_current_page( mem_size, page_size, iface );
534  if ( status == status::MISSING_PAGE )
535  return status::SUCCESS;
536  if ( status != status::SUCCESS )
537  return status;
538  return load_stored_config( addr + cell_size, addr + page_size, iface );
539 }
540 
541 // Util functions usable as callbacks
542 
543 // a is prefix of b with zeros if a.size() <= b.size()
544 inline bool
545 is_prefix_of_with_zeros( std::span< std::byte const > a, std::span< std::byte const > b )
546 {
547  if ( a.size() > b.size() )
548  return false;
549  auto c = b.subspan( 0, a.size() );
550  if ( !std::ranges::equal( a, c ) )
551  return false;
552  return std::ranges::all_of( b.subspan( a.size() ), []( std::byte b ) {
553  return b == std::byte{ 0x00 };
554  } );
555 }
556 
558 {
559  if ( cont.empty() )
560  return {};
561  auto k = cont.back();
562  cont.pop_back();
563  return k;
564 }
565 
566 cache_res key_check_unseen_container( auto& cont, uint32_t key )
567 {
568  auto iter = find( cont, key );
569  if ( iter == cont.end() )
570  return cache_res::SEEN;
571  std::swap( *iter, cont.back() );
572  cont.pop_back();
573  return cache_res::NOT_SEEN;
574 }
575 
576 } // 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:557
status update(std::size_t mem_size, std::size_t page_size, update_iface &iface)
Definition: handler.h:463
bool is_prefix_of_with_zeros(std::span< std::byte const > a, std::span< std::byte const > b)
Definition: handler.h:545
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:265
locate_next_info locate_next_page(std::size_t mem_size, std::size_t page_size, read_iface &iface)
Definition: handler.h:299
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:77
status dump_unseen_keys(std::size_t addr, std::size_t end_addr, update_iface &iface)
Definition: handler.h:401
hdr_state state
Definition: handler.h:295
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:531
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:350
opt< T > get_val(std::span< std::byte const > data)
Definition: handler.h:159
std::size_t addr
Definition: handler.h:261
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:412
bool decr_addr(std::size_t &addr, std::size_t n, std::size_t start_addr)
Definition: handler.h:340
hdr_state
Definition: page.h:36
std::size_t addr
Definition: handler.h:294
hdr_state next(hdr_state cs) noexcept
Definition: page.h:42
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:499
cfg::status status
Definition: handler.h:293
opt< hdr_state > hdr_to_hdr_state(std::span< std::byte, cell_size > b) noexcept
Definition: page.h:70
cfg::status status
Definition: handler.h:260
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:566
uint32_t key
Definition: handler.h:88
static constexpr uint16_t hcell_size
Definition: base.h:29
status
Definition: handler.h:184
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:379
Definition: handler.h:86
Definition: handler.h:259
Definition: handler.h:292
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
MIT License.
Definition: impl.h:31
constexpr pointer data() noexcept
Returns pointer to first item of the storage.
Definition: static_storage.h:168
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
uint32_t error_value_type
Definition: error_code.h:31
physical_quantity< 0, 0, 0, 0, 0, 0, 0, 0, 1 > byte
Definition: physical_quantity.h:118
Definition: handler.h:254
virtual std::span< std::byte > get_buffer()=0
Definition: handler.h:492
virtual cache_res check_key_cache(uint32_t key)=0
virtual error_code on_kval(uint32_t key, std::span< std::byte >)=0
Definition: handler.h:247
virtual ~read_iface()=default
virtual error_code read(std::size_t addr, std::span< std::byte, cell_size > data)=0
Definition: handler.h:202
char const * message(error_value_type code) const noexcept override
Definition: handler.h:203
Definition: handler.h:325
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 cache_res check_key_cache(uint32_t key)=0
virtual error_code clear_page(std::size_t addr)=0
virtual error_code write(std::size_t start_addr, std::span< std::byte const > data)=0
virtual error_code reset_keys()=0
Definition: error_code.h:42
Definition: error_code.h:80