1use core::{marker::PhantomData, mem::size_of};
4use embedded_storage_async::nor_flash::MultiwriteNorFlash;
5
6#[cfg(feature = "postcard")]
7use serde::{Deserialize, Serialize};
8
9use crate::{
10 cache::CacheImpl,
11 item::{Item, ItemHeader, ItemIter},
12};
13
14use self::item::{ItemHeaderIter, ItemUnborrowed};
15
16use super::{
17 Debug, Error, GenericStorage, MAX_WORD_SIZE, NorFlash, NorFlashExt, PageState, Range,
18 calculate_page_address, calculate_page_end_address, calculate_page_index, calculate_page_size,
19 item, run_with_auto_repair,
20};
21
22pub struct MapConfig<S> {
24 flash_range: Range<u32>,
25 _phantom: PhantomData<S>,
26}
27
28impl<S: NorFlash> MapConfig<S> {
29 #[must_use]
32 pub const fn new(flash_range: Range<u32>) -> Self {
33 match Self::try_new(flash_range) {
34 Ok(config) => config,
35 Err(_) => panic!("Map config must be correct"),
36 }
37 }
38
39 pub const fn try_new(flash_range: Range<u32>) -> Result<Self, MapConfigError> {
41 if !flash_range.start.is_multiple_of(S::ERASE_SIZE as u32) {
42 return Err(MapConfigError::StartRangeNotAtPageBoundary);
43 }
44 if !flash_range.end.is_multiple_of(S::ERASE_SIZE as u32) {
45 return Err(MapConfigError::EndRangeNotAtPageBoundary);
46 }
47 if flash_range.end - flash_range.start < S::ERASE_SIZE as u32 * 2 {
49 return Err(MapConfigError::RangeTooSmall);
50 }
51
52 if S::ERASE_SIZE < S::WORD_SIZE * 3 + ItemHeader::data_address::<S>(0) as usize {
54 return Err(MapConfigError::PagesTooSmall);
55 }
56 if S::WORD_SIZE > MAX_WORD_SIZE {
57 return Err(MapConfigError::WordSizeTooLarge);
58 }
59
60 Ok(Self {
61 flash_range,
62 _phantom: PhantomData,
63 })
64 }
65}
66
67#[derive(Debug, Clone, PartialEq, Eq)]
69#[cfg_attr(feature = "defmt", derive(defmt::Format))]
70pub enum MapConfigError {
71 StartRangeNotAtPageBoundary,
73 EndRangeNotAtPageBoundary,
75 RangeTooSmall,
77 PagesTooSmall,
79 WordSizeTooLarge,
81}
82
83pub struct MapStorage<K: Key, S: NorFlash, C: CacheImpl<K>> {
157 inner: GenericStorage<S, C, K>,
158 _phantom: PhantomData<K>,
159}
160
161impl<S: NorFlash, C: CacheImpl<K>, K: Key> MapStorage<K, S, C> {
162 pub const fn new(storage: S, config: MapConfig<S>, cache: C) -> Self {
168 Self {
169 inner: GenericStorage {
170 flash: storage,
171 flash_range: config.flash_range,
172 cache,
173 _phantom: PhantomData,
174 },
175 _phantom: PhantomData,
176 }
177 }
178
179 pub async fn fetch_item<'d, V: Value<'d>>(
188 &mut self,
189 data_buffer: &'d mut [u8],
190 search_key: &K,
191 ) -> Result<Option<V>, Error<S::Error>> {
192 let result = run_with_auto_repair!(
193 function = self.fetch_item_with_location(data_buffer, search_key).await,
194 repair = self.try_repair(data_buffer).await?
195 );
196
197 let Some((item, _, item_key_len)) = result? else {
198 return Ok(None);
199 };
200
201 let data_len = item.header.length as usize;
202 let item_key_len = match item_key_len {
203 Some(item_key_len) => item_key_len,
204 None => K::get_len(&data_buffer[..data_len])?,
205 };
206
207 let (value, _size) =
208 V::deserialize_from(&data_buffer[item_key_len..][..data_len - item_key_len])
209 .map_err(Error::SerializationError)?;
210 Ok(Some(value))
211 }
212
213 #[allow(clippy::type_complexity)]
215 async fn fetch_item_with_location(
216 &mut self,
217 data_buffer: &mut [u8],
218 search_key: &K,
219 ) -> Result<Option<(ItemUnborrowed, u32, Option<usize>)>, Error<S::Error>> {
220 if self.inner.cache.is_dirty() {
221 self.inner.cache.invalidate_cache_state();
222 }
223
224 'cache: {
225 if let Some(cached_location) = self.inner.cache.key_location(search_key) {
226 let page_index = calculate_page_index::<S>(self.flash_range(), cached_location);
227 let page_data_end_address =
228 calculate_page_end_address::<S>(self.flash_range(), page_index)
229 - S::WORD_SIZE as u32;
230
231 let Some(header) = ItemHeader::read_new(
232 &mut self.inner.flash,
233 cached_location,
234 page_data_end_address,
235 )
236 .await?
237 else {
238 #[expect(clippy::assertions_on_constants, reason = "Clippy is wrong here")]
240 {
241 assert!(
242 !cfg!(feature = "_test"),
243 "Wrong cache value. Addr: {cached_location}"
244 );
245 }
246 self.inner.cache.invalidate_cache_state();
247 break 'cache;
248 };
249
250 let item = header
251 .read_item(
252 &mut self.inner.flash,
253 data_buffer,
254 cached_location,
255 page_data_end_address,
256 )
257 .await?;
258
259 match item {
260 item::MaybeItem::Corrupted(_, _) | item::MaybeItem::Erased(_, _) => {
261 #[expect(clippy::assertions_on_constants, reason = "Clippy is wrong here")]
262 {
263 assert!(
264 !cfg!(feature = "_test"),
265 "Wrong cache value. Addr: {cached_location}"
266 );
267 }
268
269 self.inner.cache.invalidate_cache_state();
271 break 'cache;
272 }
273 item::MaybeItem::Present(item) => {
274 return Ok(Some((item.unborrow(), cached_location, None)));
275 }
276 }
277 }
278 }
279
280 let mut last_used_page = self
282 .inner
283 .find_first_page(0, PageState::PartialOpen)
284 .await?;
285
286 if last_used_page.is_none() {
287 if let Some(first_open_page) = self.inner.find_first_page(0, PageState::Open).await? {
290 let previous_page = self.inner.previous_page(first_open_page);
291 if self
292 .inner
293 .get_page_state_cached(previous_page)
294 .await?
295 .is_closed()
296 {
297 last_used_page = Some(previous_page);
298 } else {
299 self.inner.cache.unmark_dirty();
302 return Ok(None);
303 }
304 } else {
305 return Err(Error::Corrupted {
309 #[cfg(feature = "_test")]
310 backtrace: std::backtrace::Backtrace::capture(),
311 });
312 }
313 }
314
315 let mut current_page_to_check = last_used_page.unwrap();
319 let mut newest_found_item_data = None;
320
321 loop {
322 let page_data_start_address =
323 calculate_page_address::<S>(self.flash_range(), current_page_to_check)
324 + S::WORD_SIZE as u32;
325 let page_data_end_address =
326 calculate_page_end_address::<S>(self.flash_range(), current_page_to_check)
327 - S::WORD_SIZE as u32;
328
329 let mut it = ItemIter::new(page_data_start_address, page_data_end_address);
330 while let Some((item, address)) = it.next(&mut self.inner.flash, data_buffer).await? {
331 let (found_key, found_key_len) = K::deserialize_from(item.data())?;
332 if found_key == *search_key {
333 newest_found_item_data = Some((address, found_key_len));
334 }
335 }
336
337 if let Some((newest_found_item_address, _)) = newest_found_item_data.as_ref() {
339 self.inner
340 .cache
341 .notice_key_location(search_key, *newest_found_item_address, false);
342
343 break;
344 }
345
346 let previous_page = self.inner.previous_page(current_page_to_check);
348
349 if self.inner.get_page_state_cached(previous_page).await? != PageState::Closed {
350 self.inner.cache.unmark_dirty();
352 return Ok(None);
353 }
354
355 current_page_to_check = previous_page;
356 }
357
358 self.inner.cache.unmark_dirty();
359
360 if let Some((newest_found_item_address, newest_found_item_key_len)) = newest_found_item_data
363 {
364 let item =
365 ItemHeader::read_new(&mut self.inner.flash, newest_found_item_address, u32::MAX)
366 .await?
367 .ok_or_else(|| {
368 Error::Corrupted {
370 #[cfg(feature = "_test")]
371 backtrace: std::backtrace::Backtrace::capture(),
372 }
373 })?
374 .read_item(
375 &mut self.inner.flash,
376 data_buffer,
377 newest_found_item_address,
378 u32::MAX,
379 )
380 .await?;
381
382 Ok(Some((
383 item.unwrap()?.unborrow(),
384 newest_found_item_address,
385 Some(newest_found_item_key_len),
386 )))
387 } else {
388 Ok(None)
389 }
390 }
391
392 pub async fn store_item<'d, V: Value<'d>>(
398 &mut self,
399 data_buffer: &mut [u8],
400 key: &K,
401 item: &V,
402 ) -> Result<(), Error<S::Error>> {
403 run_with_auto_repair!(
404 function = self
405 .store_item_inner::<dyn Value>(data_buffer, key, item)
406 .await,
407 repair = self.try_repair(data_buffer).await?
408 )
409 }
410
411 pub async fn store_item_generic<'d, V: Value<'d> + ?Sized>(
419 &mut self,
420 data_buffer: &mut [u8],
421 key: &K,
422 item: &V,
423 ) -> Result<(), Error<S::Error>> {
424 run_with_auto_repair!(
425 function = self.store_item_inner::<V>(data_buffer, key, item).await,
426 repair = self.try_repair(data_buffer).await?
427 )
428 }
429
430 async fn store_item_inner<'d, V: Value<'d> + ?Sized>(
431 &mut self,
432 data_buffer: &mut [u8],
433 key: &K,
434 item: &V,
435 ) -> Result<(), Error<S::Error>> {
436 if self.inner.cache.is_dirty() {
437 self.inner.cache.invalidate_cache_state();
438 }
439
440 let mut recursion_level = 0;
441 loop {
442 if recursion_level == self.inner.get_pages(0).count() {
444 self.inner.cache.unmark_dirty();
445 return Err(Error::FullStorage);
446 }
447
448 let next_page_to_use = if let Some(partial_open_page) = self
450 .inner
451 .find_first_page(0, PageState::PartialOpen)
452 .await?
453 {
454 if !self
456 .inner
457 .get_page_state_cached(self.inner.next_page(partial_open_page))
458 .await?
459 .is_open()
460 {
461 return Err(Error::Corrupted {
466 #[cfg(feature = "_test")]
467 backtrace: std::backtrace::Backtrace::capture(),
468 });
469 }
470
471 let page_data_start_address =
474 calculate_page_address::<S>(self.flash_range(), partial_open_page)
475 + S::WORD_SIZE as u32;
476 let page_data_end_address =
477 calculate_page_end_address::<S>(self.flash_range(), partial_open_page)
478 - S::WORD_SIZE as u32;
479
480 let key_len = key.serialize_into(data_buffer)?;
481 let item_data_length = key_len
482 + item
483 .serialize_into(&mut data_buffer[key_len..])
484 .map_err(Error::SerializationError)?;
485
486 if item_data_length > u16::MAX as usize
487 || item_data_length
488 > calculate_page_size::<S>()
489 .saturating_sub(ItemHeader::data_address::<S>(0) as usize)
490 {
491 self.inner.cache.unmark_dirty();
492 return Err(Error::ItemTooBig);
493 }
494
495 let free_spot_address = self
496 .inner
497 .find_next_free_item_spot(
498 page_data_start_address,
499 page_data_end_address,
500 item_data_length as u32,
501 )
502 .await?;
503
504 if let Some(free_spot_address) = free_spot_address {
505 self.inner
506 .cache
507 .notice_key_location(key, free_spot_address, true);
508 Item::write_new(
509 &mut self.inner.flash,
510 self.inner.flash_range.clone(),
511 &mut self.inner.cache,
512 free_spot_address,
513 &data_buffer[..item_data_length],
514 )
515 .await?;
516
517 self.inner.cache.unmark_dirty();
518 return Ok(());
519 }
520
521 self.inner.close_page(partial_open_page).await?;
523 Some(self.inner.next_page(partial_open_page))
524 } else {
525 None
526 };
527
528 if let Some(next_page_to_use) = next_page_to_use {
534 let next_page_state = self.inner.get_page_state_cached(next_page_to_use).await?;
535
536 if !next_page_state.is_open() {
537 return Err(Error::Corrupted {
539 #[cfg(feature = "_test")]
540 backtrace: std::backtrace::Backtrace::capture(),
541 });
542 }
543
544 self.inner.partial_close_page(next_page_to_use).await?;
548
549 let next_buffer_page = self.inner.next_page(next_page_to_use);
550 let next_buffer_page_state =
551 self.inner.get_page_state_cached(next_buffer_page).await?;
552
553 if !next_buffer_page_state.is_open() {
554 self.migrate_items(data_buffer, next_buffer_page, next_page_to_use)
555 .await?;
556 }
557 } else {
558 let Some(first_open_page) = self.inner.find_first_page(0, PageState::Open).await?
560 else {
561 return Err(Error::Corrupted {
565 #[cfg(feature = "_test")]
566 backtrace: std::backtrace::Backtrace::capture(),
567 });
568 };
569
570 self.inner.partial_close_page(first_open_page).await?;
571 }
572
573 recursion_level += 1;
575 }
576 }
577
578 pub async fn remove_item(
591 &mut self,
592 data_buffer: &mut [u8],
593 search_key: &K,
594 ) -> Result<(), Error<S::Error>>
595 where
596 S: MultiwriteNorFlash,
597 {
598 run_with_auto_repair!(
599 function = self.remove_item_inner(data_buffer, Some(search_key)).await,
600 repair = self.try_repair(data_buffer).await?
601 )
602 }
603
604 pub async fn remove_all_items(&mut self, data_buffer: &mut [u8]) -> Result<(), Error<S::Error>>
615 where
616 S: MultiwriteNorFlash,
617 {
618 run_with_auto_repair!(
619 function = self.remove_item_inner(data_buffer, None).await,
620 repair = self.try_repair(data_buffer).await?
621 )
622 }
623
624 async fn remove_item_inner(
626 &mut self,
627 data_buffer: &mut [u8],
628 search_key: Option<&K>,
629 ) -> Result<(), Error<S::Error>>
630 where
631 S: MultiwriteNorFlash,
632 {
633 if let Some(key) = &search_key {
634 self.inner.cache.notice_key_erased(key);
635 } else {
636 self.inner.cache.invalidate_cache_state();
637 }
638
639 let last_used_page = self
643 .inner
644 .find_first_page(0, PageState::PartialOpen)
645 .await?
646 .unwrap_or_default();
647
648 for page_index in self.inner.get_pages(self.inner.next_page(last_used_page)) {
650 if self
651 .inner
652 .get_page_state_cached(page_index)
653 .await?
654 .is_open()
655 {
656 continue;
658 }
659
660 let page_data_start_address =
661 calculate_page_address::<S>(self.flash_range(), page_index) + S::WORD_SIZE as u32;
662 let page_data_end_address =
663 calculate_page_end_address::<S>(self.flash_range(), page_index)
664 - S::WORD_SIZE as u32;
665
666 let mut item_headers =
668 ItemHeaderIter::new(page_data_start_address, page_data_end_address);
669
670 while let (Some(item_header), item_address) =
671 item_headers.next(&mut self.inner.flash).await?
672 {
673 let item = item_header
674 .read_item(
675 &mut self.inner.flash,
676 data_buffer,
677 item_address,
678 page_data_end_address,
679 )
680 .await?;
681
682 match item {
683 item::MaybeItem::Corrupted(_, _) | item::MaybeItem::Erased(_, _) => continue,
684 item::MaybeItem::Present(item) => {
685 let item_match = match search_key {
686 Some(search_key) => K::deserialize_from(item.data())?.0 == *search_key,
687 _ => true,
688 };
689 if item_match {
692 item.header
693 .erase_data(
694 &mut self.inner.flash,
695 self.inner.flash_range.clone(),
696 &mut self.inner.cache,
697 item_address,
698 )
699 .await?;
700 }
701 }
702 }
703 }
704 }
705
706 self.inner.cache.unmark_dirty();
708
709 Ok(())
710 }
711
712 pub async fn fetch_all_items(
773 &mut self,
774 data_buffer: &mut [u8],
775 ) -> Result<MapItemIter<'_, K, S, C>, Error<S::Error>> {
776 let first_page = run_with_auto_repair!(
779 function = {
780 match self
781 .inner
782 .find_first_page(0, PageState::PartialOpen)
783 .await?
784 {
785 Some(last_used_page) => {
786 Ok(self.inner.next_page(last_used_page))
788 }
789 None => {
790 if let Some(first_open_page) =
793 self.inner.find_first_page(0, PageState::Open).await?
794 {
795 let previous_page = self.inner.previous_page(first_open_page);
796 if self
797 .inner
798 .get_page_state_cached(previous_page)
799 .await?
800 .is_closed()
801 {
802 Ok(first_open_page)
804 } else {
805 self.inner.cache.unmark_dirty();
808 Ok(0)
809 }
810 } else {
811 Err(Error::Corrupted {
815 #[cfg(feature = "_test")]
816 backtrace: std::backtrace::Backtrace::capture(),
817 })
818 }
819 }
820 }
821 },
822 repair = self.try_repair(data_buffer).await?
823 )?;
824
825 let start_address =
826 calculate_page_address::<S>(self.flash_range(), first_page) + S::WORD_SIZE as u32;
827 let end_address =
828 calculate_page_end_address::<S>(self.flash_range(), first_page) - S::WORD_SIZE as u32;
829
830 Ok(MapItemIter {
831 storage: self,
832 first_page,
833 current_page_index: first_page,
834 current_iter: ItemIter::new(start_address, end_address),
835 _key: PhantomData,
836 })
837 }
838
839 async fn migrate_items(
840 &mut self,
841 data_buffer: &mut [u8],
842 source_page: usize,
843 target_page: usize,
844 ) -> Result<(), Error<S::Error>> {
845 let mut next_page_write_address =
849 calculate_page_address::<S>(self.flash_range(), target_page) + S::WORD_SIZE as u32;
850
851 let mut it = ItemIter::new(
852 calculate_page_address::<S>(self.flash_range(), source_page) + S::WORD_SIZE as u32,
853 calculate_page_end_address::<S>(self.flash_range(), source_page) - S::WORD_SIZE as u32,
854 );
855 while let Some((item, item_address)) = it.next(&mut self.inner.flash, data_buffer).await? {
856 let (key, _) = K::deserialize_from(item.data())?;
857
858 self.inner.cache.unmark_dirty();
860
861 let Some((found_item, found_address, _)) =
863 self.fetch_item_with_location(data_buffer, &key).await?
864 else {
865 return Err(Error::Corrupted {
867 #[cfg(feature = "_test")]
868 backtrace: std::backtrace::Backtrace::capture(),
869 });
870 };
871
872 let found_item = found_item
873 .reborrow(data_buffer)
874 .ok_or_else(|| Error::LogicBug {
875 #[cfg(feature = "_test")]
876 backtrace: std::backtrace::Backtrace::capture(),
877 })?;
878
879 if found_address == item_address {
880 self.inner
881 .cache
882 .notice_key_location(&key, next_page_write_address, true);
883 found_item
884 .write(
885 &mut self.inner.flash,
886 self.inner.flash_range.clone(),
887 &mut self.inner.cache,
888 next_page_write_address,
889 )
890 .await?;
891 next_page_write_address = found_item
892 .header
893 .next_item_address::<S>(next_page_write_address);
894 }
895 }
896
897 self.inner.open_page(source_page).await?;
898
899 Ok(())
900 }
901
902 async fn try_repair(&mut self, data_buffer: &mut [u8]) -> Result<(), Error<S::Error>> {
913 self.inner.cache.invalidate_cache_state();
914
915 self.inner.try_general_repair().await?;
916
917 if let Some(partial_open_page) = self
919 .inner
920 .find_first_page(0, PageState::PartialOpen)
921 .await?
922 {
923 let buffer_page = self.inner.next_page(partial_open_page);
924 if !self
925 .inner
926 .get_page_state_cached(buffer_page)
927 .await?
928 .is_open()
929 {
930 self.inner.open_page(partial_open_page).await?;
933
934 self.inner.partial_close_page(partial_open_page).await?;
936
937 self.migrate_items(data_buffer, buffer_page, partial_open_page)
938 .await?;
939 }
940 }
941
942 Ok(())
943 }
944
945 pub fn erase_all(&mut self) -> impl Future<Output = Result<(), Error<S::Error>>> {
949 self.inner.erase_all()
950 }
951
952 #[must_use]
957 pub const fn item_overhead_size() -> u32 {
958 GenericStorage::<S, C, K>::item_overhead_size()
959 }
960
961 pub fn destroy(self) -> (S, C) {
965 self.inner.destroy()
966 }
967
968 pub const fn flash(&mut self) -> &mut S {
970 self.inner.flash()
971 }
972
973 pub const fn flash_range(&self) -> Range<u32> {
975 self.inner.flash_range()
976 }
977
978 #[cfg(any(test, fuzzing))]
980 pub const fn cache(&mut self) -> &mut C {
981 &mut self.inner.cache
982 }
983
984 #[cfg(any(test, feature = "std", fuzzing))]
985 pub fn print_items(&mut self) -> impl Future<Output = String> {
989 self.inner.print_items()
990 }
991}
992
993pub struct MapItemIter<'s, K: Key, S: NorFlash, C: CacheImpl<K>> {
1042 storage: &'s mut MapStorage<K, S, C>,
1043 first_page: usize,
1044 current_page_index: usize,
1045 pub(crate) current_iter: ItemIter,
1046 _key: PhantomData<K>,
1047}
1048
1049impl<K: Key, S: NorFlash, C: CacheImpl<K>> MapItemIter<'_, K, S, C> {
1050 pub async fn next<'a, V: Value<'a>>(
1052 &mut self,
1053 data_buffer: &'a mut [u8],
1054 ) -> Result<Option<(K, V)>, Error<S::Error>> {
1055 let item = loop {
1057 if let Some((item, _address)) = self
1058 .current_iter
1059 .next(&mut self.storage.inner.flash, data_buffer)
1060 .await?
1061 {
1062 break item;
1064 }
1065
1066 loop {
1069 self.current_page_index = self.storage.inner.next_page(self.current_page_index);
1070
1071 if self.current_page_index == self.first_page {
1073 return Ok(None);
1074 }
1075
1076 match self
1077 .storage
1078 .inner
1079 .get_page_state_cached(self.current_page_index)
1080 .await
1081 {
1082 Ok(PageState::Closed | PageState::PartialOpen) => {
1083 self.current_iter = ItemIter::new(
1084 calculate_page_address::<S>(
1085 self.storage.inner.flash_range.clone(),
1086 self.current_page_index,
1087 ) + S::WORD_SIZE as u32,
1088 calculate_page_end_address::<S>(
1089 self.storage.inner.flash_range.clone(),
1090 self.current_page_index,
1091 ) - S::WORD_SIZE as u32,
1092 );
1093 break;
1094 }
1095 _ => continue,
1096 }
1097 }
1098 };
1099
1100 let data_len = item.header.length as usize;
1101 let (key, key_len) = K::deserialize_from(item.data())?;
1102 let (value, _value_len) = V::deserialize_from(&data_buffer[..data_len][key_len..])
1103 .map_err(Error::SerializationError)?;
1104
1105 Ok(Some((key, value)))
1106 }
1107}
1108
1109pub trait Key: Eq + Clone + Sized + Debug {
1118 fn serialize_into(&self, buffer: &mut [u8]) -> Result<usize, SerializationError>;
1121 fn deserialize_from(buffer: &[u8]) -> Result<(Self, usize), SerializationError>;
1124 fn get_len(buffer: &[u8]) -> Result<usize, SerializationError> {
1127 Self::deserialize_from(buffer).map(|(_, len)| len)
1128 }
1129}
1130
1131macro_rules! impl_key_num {
1132 ($int:ty) => {
1133 impl Key for $int {
1134 fn serialize_into(&self, buffer: &mut [u8]) -> Result<usize, SerializationError> {
1135 let self_bytes = self.to_le_bytes();
1136
1137 match buffer.get_mut(..self_bytes.len()) {
1138 Some(buffer) => {
1139 buffer.copy_from_slice(&self_bytes);
1140 Ok(buffer.len())
1141 }
1142 None => Err(SerializationError::BufferTooSmall),
1143 }
1144 }
1145
1146 fn deserialize_from(buffer: &[u8]) -> Result<(Self, usize), SerializationError> {
1147 let value = Self::from_le_bytes(
1148 buffer
1149 .get(..size_of::<Self>())
1150 .ok_or(SerializationError::BufferTooSmall)?
1151 .try_into()
1152 .unwrap(),
1153 );
1154 Ok((value, size_of::<Self>()))
1155 }
1156
1157 fn get_len(_buffer: &[u8]) -> Result<usize, SerializationError> {
1158 Ok(size_of::<Self>())
1159 }
1160 }
1161 };
1162}
1163
1164impl_key_num!(u8);
1165impl_key_num!(u16);
1166impl_key_num!(u32);
1167impl_key_num!(u64);
1168impl_key_num!(u128);
1169impl_key_num!(i8);
1170impl_key_num!(i16);
1171impl_key_num!(i32);
1172impl_key_num!(i64);
1173impl_key_num!(i128);
1174
1175impl<const N: usize> Key for [u8; N] {
1176 fn serialize_into(&self, buffer: &mut [u8]) -> Result<usize, SerializationError> {
1177 buffer
1178 .get_mut(..N)
1179 .ok_or(SerializationError::BufferTooSmall)?
1180 .copy_from_slice(self);
1181 Ok(N)
1182 }
1183
1184 fn deserialize_from(buffer: &[u8]) -> Result<(Self, usize), SerializationError> {
1185 Ok((
1186 buffer
1187 .get(..N)
1188 .ok_or(SerializationError::BufferTooSmall)?
1189 .try_into()
1190 .unwrap(),
1191 N,
1192 ))
1193 }
1194
1195 fn get_len(_buffer: &[u8]) -> Result<usize, SerializationError> {
1196 Ok(N)
1197 }
1198}
1199
1200impl Key for () {
1201 fn serialize_into(&self, _buffer: &mut [u8]) -> Result<usize, SerializationError> {
1202 Ok(0)
1203 }
1204
1205 fn deserialize_from(_buffer: &[u8]) -> Result<(Self, usize), SerializationError> {
1206 Ok(((), 0))
1207 }
1208}
1209
1210pub trait Value<'a> {
1215 fn serialize_into(&self, buffer: &mut [u8]) -> Result<usize, SerializationError>;
1218 fn deserialize_from(buffer: &'a [u8]) -> Result<(Self, usize), SerializationError>
1227 where
1228 Self: Sized;
1229}
1230
1231impl<'a> Value<'a> for bool {
1232 fn serialize_into(&self, buffer: &mut [u8]) -> Result<usize, SerializationError> {
1233 <u8 as Value>::serialize_into(&u8::from(*self), buffer)
1234 }
1235
1236 fn deserialize_from(buffer: &'a [u8]) -> Result<(Self, usize), SerializationError>
1237 where
1238 Self: Sized,
1239 {
1240 let (value, size) = <u8 as Value>::deserialize_from(buffer)?;
1241 Ok((value != 0, size))
1242 }
1243}
1244
1245impl<'a, T: Value<'a>> Value<'a> for Option<T> {
1246 fn serialize_into(&self, buffer: &mut [u8]) -> Result<usize, SerializationError> {
1247 if let Some(val) = self {
1248 let mut size = 0;
1249 size += <bool as Value>::serialize_into(&true, buffer)?;
1250 size += <T as Value>::serialize_into(
1251 val,
1252 buffer
1253 .get_mut(size..)
1254 .ok_or(SerializationError::BufferTooSmall)?,
1255 )?;
1256 Ok(size)
1257 } else {
1258 <bool as Value>::serialize_into(&false, buffer)
1259 }
1260 }
1261
1262 fn deserialize_from(buffer: &'a [u8]) -> Result<(Self, usize), SerializationError>
1263 where
1264 Self: Sized,
1265 {
1266 let (is_some, tag_size) = <bool as Value>::deserialize_from(buffer)?;
1267 if is_some {
1268 let (value, value_size) = <T as Value>::deserialize_from(
1269 buffer
1270 .get(tag_size..)
1271 .ok_or(SerializationError::BufferTooSmall)?,
1272 )?;
1273 Ok((Some(value), tag_size + value_size))
1274 } else {
1275 Ok((None, tag_size))
1276 }
1277 }
1278}
1279
1280impl<'a> Value<'a> for &'a [u8] {
1281 fn serialize_into(&self, buffer: &mut [u8]) -> Result<usize, SerializationError> {
1282 buffer
1283 .get_mut(..self.len())
1284 .ok_or(SerializationError::BufferTooSmall)?
1285 .copy_from_slice(self);
1286 Ok(self.len())
1287 }
1288
1289 fn deserialize_from(buffer: &'a [u8]) -> Result<(Self, usize), SerializationError>
1290 where
1291 Self: Sized,
1292 {
1293 Ok((buffer, buffer.len()))
1294 }
1295}
1296
1297#[cfg(feature = "postcard")]
1298pub trait PostcardValue<'a>: Serialize + Deserialize<'a> {}
1305
1306#[cfg(feature = "postcard")]
1307impl<'a, T> Value<'a> for T
1308where
1309 T: PostcardValue<'a>,
1310{
1311 fn serialize_into(&self, buffer: &mut [u8]) -> Result<usize, SerializationError> {
1312 Ok(postcard::to_slice(self, buffer).map(|s| s.len())?)
1313 }
1314
1315 fn deserialize_from(buffer: &'a [u8]) -> Result<(Self, usize), SerializationError>
1316 where
1317 Self: Sized,
1318 {
1319 Ok((postcard::from_bytes(buffer)?, buffer.len()))
1320 }
1321}
1322
1323#[cfg(feature = "postcard")]
1324impl From<postcard::Error> for SerializationError {
1325 fn from(error: postcard::Error) -> SerializationError {
1326 use postcard::Error::*;
1327 match error {
1328 SerializeBufferFull => SerializationError::BufferTooSmall,
1329 SerializeSeqLengthUnknown => SerializationError::InvalidData,
1330 DeserializeUnexpectedEnd
1331 | DeserializeBadVarint
1332 | DeserializeBadBool
1333 | DeserializeBadChar
1334 | DeserializeBadUtf8
1335 | DeserializeBadOption
1336 | DeserializeBadEnum
1337 | DeserializeBadEncoding
1338 | DeserializeBadCrc => SerializationError::InvalidFormat,
1339 _ => SerializationError::Custom(error as i32),
1340 }
1341 }
1342}
1343
1344macro_rules! impl_map_item_num {
1345 ($num:ty) => {
1346 impl<'a> Value<'a> for $num {
1347 fn serialize_into(&self, buffer: &mut [u8]) -> Result<usize, SerializationError> {
1348 let self_bytes = self.to_le_bytes();
1349
1350 match buffer.get_mut(..self_bytes.len()) {
1351 Some(buffer) => {
1352 buffer.copy_from_slice(&self_bytes);
1353 Ok(buffer.len())
1354 }
1355 None => Err(SerializationError::BufferTooSmall),
1356 }
1357 }
1358
1359 fn deserialize_from(buffer: &[u8]) -> Result<(Self, usize), SerializationError> {
1360 let value = Self::from_le_bytes(
1361 buffer
1362 .get(..size_of::<Self>())
1363 .ok_or(SerializationError::BufferTooSmall)?
1364 .try_into()
1365 .unwrap(),
1366 );
1367 Ok((value, size_of::<Self>()))
1368 }
1369 }
1370
1371 impl<'a, const N: usize> Value<'a> for [$num; N] {
1373 fn serialize_into(&self, buffer: &mut [u8]) -> Result<usize, SerializationError> {
1374 let elem_size = size_of::<$num>();
1375
1376 let buffer = buffer
1377 .get_mut(0..elem_size * N)
1378 .ok_or(SerializationError::BufferTooSmall)?;
1379
1380 for (chunk, number) in buffer.chunks_exact_mut(elem_size).zip(self.iter()) {
1381 chunk.copy_from_slice(&number.to_le_bytes())
1382 }
1383 Ok(elem_size * N)
1384 }
1385
1386 fn deserialize_from(buffer: &[u8]) -> Result<(Self, usize), SerializationError> {
1387 let elem_size = size_of::<$num>();
1388 if buffer.len() < elem_size * N {
1389 return Err(SerializationError::BufferTooSmall);
1390 }
1391
1392 let mut array = [0 as $num; N];
1393 for (chunk, number) in buffer.chunks_exact(elem_size).zip(array.iter_mut()) {
1394 *number = <$num>::from_le_bytes(chunk.try_into().unwrap());
1395 }
1396 Ok((array, elem_size * N))
1397 }
1398 }
1399 };
1400}
1401
1402impl_map_item_num!(u8);
1403impl_map_item_num!(u16);
1404impl_map_item_num!(u32);
1405impl_map_item_num!(u64);
1406impl_map_item_num!(u128);
1407impl_map_item_num!(i8);
1408impl_map_item_num!(i16);
1409impl_map_item_num!(i32);
1410impl_map_item_num!(i64);
1411impl_map_item_num!(i128);
1412impl_map_item_num!(f32);
1413impl_map_item_num!(f64);
1414
1415#[non_exhaustive]
1419#[derive(Debug, PartialEq, Eq, Clone)]
1420#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1421pub enum SerializationError {
1422 BufferTooSmall,
1424 InvalidData,
1426 InvalidFormat,
1428 Custom(i32),
1431}
1432
1433impl core::fmt::Display for SerializationError {
1434 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1435 match self {
1436 SerializationError::BufferTooSmall => write!(f, "Buffer too small"),
1437 SerializationError::InvalidData => write!(f, "Invalid data"),
1438 SerializationError::InvalidFormat => write!(f, "Invalid format"),
1439 SerializationError::Custom(val) => write!(f, "Custom error: {val}"),
1440 }
1441 }
1442}
1443
1444#[cfg(test)]
1445mod tests {
1446 use crate::{AlignedBuf, cache::Cache, mock_flash};
1447
1448 use super::*;
1449 use futures_test::test;
1450
1451 type MockFlashBig = mock_flash::MockFlashBase<4, 4, 256>;
1452 type MockFlashTiny = mock_flash::MockFlashBase<2, 1, 32>;
1453
1454 #[test]
1455 async fn store_and_fetch() {
1456 let mut storage = MapStorage::<u8, _, _>::new(
1457 MockFlashBig::default(),
1458 MapConfig::new(0x000..0x1000),
1459 Cache::new_uncached(),
1460 );
1461
1462 let mut data_buffer = AlignedBuf([0; 128]);
1463
1464 let start_snapshot = storage.flash().stats_snapshot();
1465
1466 let item = storage
1467 .fetch_item::<&[u8]>(&mut data_buffer, &0)
1468 .await
1469 .unwrap();
1470 assert_eq!(item, None);
1471
1472 let item = storage
1473 .fetch_item::<&[u8]>(&mut data_buffer, &60)
1474 .await
1475 .unwrap();
1476 assert_eq!(item, None);
1477
1478 let item = storage
1479 .fetch_item::<&[u8]>(&mut data_buffer, &0xFF)
1480 .await
1481 .unwrap();
1482 assert_eq!(item, None);
1483
1484 storage
1485 .store_item(&mut data_buffer, &0u8, &[5u8])
1486 .await
1487 .unwrap();
1488 storage
1489 .store_item(&mut data_buffer, &0u8, &[5u8, 6])
1490 .await
1491 .unwrap();
1492
1493 let item = storage
1494 .fetch_item::<&[u8]>(&mut data_buffer, &0)
1495 .await
1496 .unwrap()
1497 .unwrap();
1498 assert_eq!(item, &[5, 6]);
1499
1500 storage
1501 .store_item(&mut data_buffer, &1u8, &[2u8, 2, 2, 2, 2, 2])
1502 .await
1503 .unwrap();
1504
1505 let item = storage
1506 .fetch_item::<&[u8]>(&mut data_buffer, &0)
1507 .await
1508 .unwrap()
1509 .unwrap();
1510 assert_eq!(item, &[5, 6]);
1511
1512 let item = storage
1513 .fetch_item::<&[u8]>(&mut data_buffer, &1)
1514 .await
1515 .unwrap()
1516 .unwrap();
1517 assert_eq!(item, &[2, 2, 2, 2, 2, 2]);
1518
1519 for index in 0..4000 {
1520 storage
1521 .store_item(
1522 &mut data_buffer,
1523 &((index % 10) as u8),
1524 &vec![(index % 10) as u8 * 2; index % 10].as_slice(),
1525 )
1526 .await
1527 .unwrap();
1528 }
1529
1530 for i in 0..10 {
1531 let item = storage
1532 .fetch_item::<&[u8]>(&mut data_buffer, &i)
1533 .await
1534 .unwrap()
1535 .unwrap();
1536 assert_eq!(item, &vec![(i % 10) * 2; (i % 10) as usize]);
1537 }
1538
1539 for _ in 0..4000 {
1540 storage
1541 .store_item(&mut data_buffer, &11u8, &[0; 10])
1542 .await
1543 .unwrap();
1544 }
1545
1546 for i in 0..10 {
1547 let item = storage
1548 .fetch_item::<&[u8]>(&mut data_buffer, &i)
1549 .await
1550 .unwrap()
1551 .unwrap();
1552 assert_eq!(item, &vec![(i % 10) * 2; (i % 10) as usize]);
1553 }
1554
1555 println!(
1556 "{:?}",
1557 start_snapshot.compare_to(storage.flash().stats_snapshot()),
1558 );
1559 }
1560
1561 #[test]
1562 async fn store_too_many_items() {
1563 const UPPER_BOUND: u8 = 3;
1564
1565 let mut storage = MapStorage::new(
1566 MockFlashTiny::default(),
1567 const { MapConfig::new(0x00..0x40) },
1568 Cache::new_uncached(),
1569 );
1570 let mut data_buffer = AlignedBuf([0; 128]);
1571
1572 for i in 0..UPPER_BOUND {
1573 println!("Storing {i:?}");
1574
1575 storage
1576 .store_item(&mut data_buffer, &i, &vec![i; i as usize].as_slice())
1577 .await
1578 .unwrap();
1579 }
1580
1581 assert_eq!(
1582 storage
1583 .store_item(
1584 &mut data_buffer,
1585 &UPPER_BOUND,
1586 &vec![0; UPPER_BOUND as usize].as_slice(),
1587 )
1588 .await,
1589 Err(Error::FullStorage)
1590 );
1591
1592 for i in 0..UPPER_BOUND {
1593 let item = storage
1594 .fetch_item::<&[u8]>(&mut data_buffer, &i)
1595 .await
1596 .unwrap()
1597 .unwrap();
1598
1599 println!("Fetched {item:?}");
1600
1601 assert_eq!(item, vec![i; i as usize]);
1602 }
1603 }
1604
1605 #[test]
1606 async fn store_too_many_items_big() {
1607 const UPPER_BOUND: u8 = 68;
1608
1609 let mut storage = MapStorage::new(
1610 MockFlashBig::default(),
1611 const { MapConfig::new(0x0000..0x1000) },
1612 Cache::new_uncached(),
1613 );
1614 let mut data_buffer = AlignedBuf([0; 128]);
1615
1616 for i in 0..UPPER_BOUND {
1617 println!("Storing {i:?}");
1618
1619 storage
1620 .store_item(&mut data_buffer, &i, &vec![i; i as usize].as_slice())
1621 .await
1622 .unwrap();
1623 }
1624
1625 assert_eq!(
1626 storage
1627 .store_item(
1628 &mut data_buffer,
1629 &UPPER_BOUND,
1630 &vec![0; UPPER_BOUND as usize].as_slice(),
1631 )
1632 .await,
1633 Err(Error::FullStorage)
1634 );
1635
1636 for i in 0..UPPER_BOUND {
1637 let item = storage
1638 .fetch_item::<&[u8]>(&mut data_buffer, &i)
1639 .await
1640 .unwrap()
1641 .unwrap();
1642
1643 println!("Fetched {item:?}");
1644
1645 assert_eq!(item, vec![i; i as usize]);
1646 }
1647 }
1648
1649 #[test]
1650 async fn store_many_items_big() {
1651 let mut storage = MapStorage::new(
1652 mock_flash::MockFlashBase::<4, 1, 4096>::default(),
1653 const { MapConfig::new(0x0000..0x4000) },
1654 Cache::new_uncached(),
1655 );
1656 let mut data_buffer = AlignedBuf([0; 128]);
1657
1658 const LENGHT_PER_KEY: [usize; 24] = [
1659 11, 13, 6, 13, 13, 10, 2, 3, 5, 36, 1, 65, 4, 6, 1, 15, 10, 7, 3, 15, 9, 3, 4, 5,
1660 ];
1661
1662 for _ in 0..100 {
1663 #[allow(clippy::needless_range_loop)]
1664 for i in 0..24 {
1665 storage
1666 .store_item(
1667 &mut data_buffer,
1668 &(i as u16),
1669 &vec![i as u8; LENGHT_PER_KEY[i]].as_slice(),
1670 )
1671 .await
1672 .unwrap();
1673 }
1674 }
1675
1676 #[allow(clippy::needless_range_loop)]
1677 for i in 0..24 {
1678 let item = storage
1679 .fetch_item::<&[u8]>(&mut data_buffer, &(i as u16))
1680 .await
1681 .unwrap()
1682 .unwrap();
1683
1684 println!("Fetched {item:?}");
1685
1686 assert_eq!(item, vec![i as u8; LENGHT_PER_KEY[i]]);
1687 }
1688 }
1689
1690 #[test]
1691 async fn remove_items() {
1692 let mut storage = MapStorage::new(
1693 mock_flash::MockFlashBase::<4, 1, 4096>::new(
1694 mock_flash::WriteCountCheck::Twice,
1695 None,
1696 true,
1697 ),
1698 const { MapConfig::new(0x0000..0x4000) },
1699 Cache::new_uncached(),
1700 );
1701 let mut data_buffer = AlignedBuf([0; 128]);
1702
1703 for j in 0..10 {
1705 for i in 0..24 {
1706 storage
1707 .store_item(
1708 &mut data_buffer,
1709 &(i as u8),
1710 &vec![i as u8; j + 2].as_slice(),
1711 )
1712 .await
1713 .unwrap();
1714 }
1715 }
1716
1717 for j in (0..24).rev() {
1718 for i in 0..=j {
1720 assert!(
1721 storage
1722 .fetch_item::<&[u8]>(&mut data_buffer, &i)
1723 .await
1724 .unwrap()
1725 .is_some()
1726 );
1727 }
1728
1729 storage.remove_item(&mut data_buffer, &j).await.unwrap();
1731
1732 for i in 0..j {
1734 assert!(
1735 storage
1736 .fetch_item::<&[u8]>(&mut data_buffer, &i)
1737 .await
1738 .unwrap()
1739 .is_some()
1740 );
1741 }
1742
1743 assert!(
1744 storage
1745 .fetch_item::<&[u8]>(&mut data_buffer, &j)
1746 .await
1747 .unwrap()
1748 .is_none()
1749 );
1750 }
1751 }
1752
1753 #[test]
1754 async fn remove_all() {
1755 let mut storage = MapStorage::new(
1756 mock_flash::MockFlashBase::<4, 1, 4096>::new(
1757 mock_flash::WriteCountCheck::Twice,
1758 None,
1759 true,
1760 ),
1761 const { MapConfig::new(0x0000..0x4000) },
1762 Cache::new_uncached(),
1763 );
1764 let mut data_buffer = AlignedBuf([0; 128]);
1765
1766 for value in 0..10 {
1768 for key in 0..24u8 {
1769 storage
1770 .store_item(&mut data_buffer, &key, &vec![key; value + 2].as_slice())
1771 .await
1772 .unwrap();
1773 }
1774 }
1775
1776 for key in 0..24u8 {
1778 assert!(
1779 storage
1780 .fetch_item::<&[u8]>(&mut data_buffer, &key)
1781 .await
1782 .unwrap()
1783 .is_some()
1784 );
1785 }
1786
1787 storage.remove_all_items(&mut data_buffer).await.unwrap();
1789
1790 for key in 0..24 {
1792 assert!(
1793 storage
1794 .fetch_item::<&[u8]>(&mut data_buffer, &key)
1795 .await
1796 .unwrap()
1797 .is_none()
1798 );
1799 }
1800 }
1801
1802 #[test]
1803 async fn store_too_big_item() {
1804 let mut storage = MapStorage::new(
1805 MockFlashBig::new(mock_flash::WriteCountCheck::Twice, None, true),
1806 const { MapConfig::new(0x000..0x1000) },
1807 Cache::new_uncached(),
1808 );
1809
1810 storage
1811 .store_item(&mut [0; 1024], &0u8, &[0u8; 1024 - 4 * 2 - 8 - 1])
1812 .await
1813 .unwrap();
1814
1815 assert_eq!(
1816 storage
1817 .store_item(&mut [0; 1024], &0u8, &[0u8; 1024 - 4 * 2 - 8 - 1 + 1],)
1818 .await,
1819 Err(Error::ItemTooBig)
1820 );
1821 }
1822
1823 #[test]
1824 async fn item_iterator() {
1825 const UPPER_BOUND: u8 = 64;
1826 let mut storage = MapStorage::new(
1827 MockFlashBig::default(),
1828 const { MapConfig::new(0x000..0x1000) },
1829 Cache::new_uncached(),
1830 );
1831
1832 let mut data_buffer = AlignedBuf([0; 128]);
1833
1834 for i in 0..UPPER_BOUND {
1835 storage
1836 .store_item(&mut data_buffer, &i, &vec![i; i as usize].as_slice())
1837 .await
1838 .unwrap();
1839 }
1840
1841 for i in 0..10 {
1843 storage
1844 .store_item(&mut data_buffer, &1u8, &vec![i; i as usize].as_slice())
1845 .await
1846 .unwrap();
1847 }
1848
1849 let mut map_iter = storage.fetch_all_items(&mut data_buffer).await.unwrap();
1850
1851 let mut count = 0;
1852 let mut last_value_buffer = [0u8; 64];
1853 let mut last_value_length = 0;
1854 while let Ok(Some((key, value))) = map_iter.next::<&[u8]>(&mut data_buffer).await {
1855 if key == 1 {
1856 last_value_length = value.len();
1858 last_value_buffer[..value.len()].copy_from_slice(value);
1859 } else {
1860 assert_eq!(value, vec![key; key as usize]);
1861 count += 1;
1862 }
1863 }
1864
1865 assert_eq!(last_value_length, 9);
1866 assert_eq!(
1867 &last_value_buffer[..last_value_length],
1868 vec![9u8; 9].as_slice()
1869 );
1870
1871 assert_eq!(count + 1, UPPER_BOUND);
1873 }
1874
1875 #[test]
1876 async fn store_unit_key() {
1877 let mut storage = MapStorage::new(
1878 MockFlashBig::default(),
1879 const { MapConfig::new(0x000..0x1000) },
1880 Cache::new_uncached(),
1881 );
1882
1883 let mut data_buffer = AlignedBuf([0; 128]);
1884
1885 let item = storage
1886 .fetch_item::<&[u8]>(&mut data_buffer, &())
1887 .await
1888 .unwrap();
1889 assert_eq!(item, None);
1890
1891 storage
1892 .store_item(&mut data_buffer, &(), &[5u8])
1893 .await
1894 .unwrap();
1895 storage
1896 .store_item(&mut data_buffer, &(), &[5u8, 6])
1897 .await
1898 .unwrap();
1899
1900 let item = storage
1901 .fetch_item::<&[u8]>(&mut data_buffer, &())
1902 .await
1903 .unwrap()
1904 .unwrap();
1905 assert_eq!(item, &[5, 6]);
1906 }
1907
1908 #[test]
1909 async fn option_value() {
1910 let mut buffer = [0; 2];
1911
1912 assert_eq!(Some(42u8).serialize_into(&mut buffer), Ok(2));
1913 assert_eq!(Option::<u8>::deserialize_from(&buffer), Ok((Some(42u8), 2)));
1914 assert_eq!(buffer, [1, 42]);
1915
1916 let mut buffer = [0; 1];
1917
1918 assert_eq!(Option::<u8>::None.serialize_into(&mut buffer), Ok(1));
1919 assert_eq!(Option::<u8>::deserialize_from(&buffer), Ok((None, 1)));
1920 assert_eq!(buffer, [0]);
1921 }
1922
1923 #[test]
1924 async fn array_value() {
1925 let mut buffer = [0; 3];
1926 assert_eq!(Value::serialize_into(&[1u8, 2, 3], &mut buffer), Ok(3));
1927 assert_eq!(buffer, [1, 2, 3]);
1928 assert_eq!(
1929 <[u8; 3] as Value>::deserialize_from(&buffer),
1930 Ok(([1, 2, 3], 3))
1931 );
1932
1933 let mut buffer = [0; 4];
1934 assert_eq!(
1935 Value::serialize_into(&[0x1234u16, 0x5678], &mut buffer),
1936 Ok(4)
1937 );
1938 assert_eq!(buffer, [0x34, 0x12, 0x78, 0x56]);
1939 assert_eq!(
1940 <[u16; 2]>::deserialize_from(&buffer),
1941 Ok(([0x1234, 0x5678], 4))
1942 );
1943 }
1944
1945 #[test]
1946 async fn send_sync() {
1947 fn assert_sync<T: Sync + Send>(_: T) {}
1948
1949 let mut storage = MapStorage::new(
1950 MockFlashBig::default(),
1951 const { MapConfig::new(0x000..0x1000) },
1952 Cache::new_uncached(),
1953 );
1954
1955 let mut buffer = [0; 16];
1956
1957 assert_sync(storage.store_item_generic(&mut buffer, &0u8, &0u8));
1959 assert_sync(storage.store_item_generic::<dyn Value + Sync>(&mut buffer, &0u8, &0u8));
1961 assert_sync(storage.fetch_item::<u8>(&mut buffer, &0u8));
1962 }
1963
1964 #[cfg(feature = "postcard")]
1965 #[test]
1966 async fn postcard_value() {
1967 #[derive(PartialEq, Debug, serde::Serialize, serde::Deserialize)]
1968 struct Foo(u32);
1969 impl crate::map::PostcardValue<'_> for Foo {}
1970 let mut buffer = [0; 8];
1971 assert_eq!(Value::serialize_into(&Foo(123), &mut buffer), Ok(1));
1972 assert_eq!(
1973 <Foo as Value>::deserialize_from(&buffer[..1]),
1974 Ok((Foo(123), 1))
1975 );
1976 }
1977}