spectrusty/memory/multi_page/
serde.rs

1/*
2    Copyright (C) 2022  Rafal Michalski
3
4    This file is part of SPECTRUSTY, a Rust library for building emulators.
5
6    For the full copyright notice, see the lib.rs file.
7*/
8use core::convert::TryFrom;
9use serde::{Serialize, Deserialize};
10use serde::ser::{self, Serializer, SerializeStruct};
11use serde::de::{self, Deserializer};
12use super::super::arrays;
13use super::{
14    ExRom, MemoryConfig, MemoryBox, MemoryPages, MemPageableRomRamExRom, MemSerExt,
15    cast_slice_as_bank_ptr,
16    ro_flag_mask, serialize_mem, deserialize_mem};
17
18impl<const MEM_SIZE: usize, const PAGE_SIZE: usize, const NUM_PAGES: usize
19    > Serialize for MemPageableRomRamExRom<MEM_SIZE, PAGE_SIZE, NUM_PAGES>
20    where Self: MemoryConfig
21{
22    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
23
24        #[derive(Serialize)]
25        #[serde(transparent)]
26        struct MemSerWrap<'a, M>(
27            #[serde(serialize_with = "serialize_mem")] &'a M
28        ) where &'a M: MemSerExt;
29
30        #[derive(Serialize)]
31        #[serde(transparent)]
32        struct BanksWrap<const NUM_PAGES: usize>(
33            #[serde(with = "arrays")]
34            [u8; NUM_PAGES]
35        );
36
37        let mut pages = self.pages;
38        if let Some(exrom) = &self.ex_rom {
39            *pages.page_mut(exrom.page) = exrom.ptr;
40        }
41        let mem_ptr: *const u8 = self.mem.as_ptr();
42        let mut banks = BanksWrap([0u8; NUM_PAGES]);
43        for (p, b) in pages.0.iter().zip(banks.0.iter_mut()) {
44            let offset = unsafe { (p.as_ptr() as *const u8).offset_from(mem_ptr) };
45            assert_eq!(offset % PAGE_SIZE as isize, 0);
46            *b = u8::try_from(offset / PAGE_SIZE as isize)
47                    .map_err(|err| ser::Error::custom(err.to_string()))?;
48        }
49        let mut state = serializer.serialize_struct("MemPageableRomRamExRom", 3)?;
50        state.serialize_field("mem", &MemSerWrap(&self.mem))?;
51        state.serialize_field("pages", &banks)?;
52        state.serialize_field("exrom", &self.ex_rom)?;
53        state.end()
54    }
55}
56
57impl<'de,
58     const MEM_SIZE: usize, const PAGE_SIZE: usize, const NUM_PAGES: usize
59     > Deserialize<'de> for MemPageableRomRamExRom<MEM_SIZE, PAGE_SIZE, NUM_PAGES>
60    where Self: MemoryConfig
61{
62    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
63        where D: Deserializer<'de>
64    {
65        #[derive(Deserialize)]
66        struct MemTemp<const MEM_SIZE: usize, const NUM_PAGES: usize> {
67            #[serde(deserialize_with = "deserialize_mem")]
68            mem: MemoryBox<MEM_SIZE>,
69            #[serde(with = "arrays",rename(deserialize = "pages"))]
70            banks: [u8;NUM_PAGES],
71            exrom: Option<ExRomTemp>
72        }
73
74        #[derive(Deserialize)]
75        struct ExRomTemp {
76            page: u8,
77            #[serde(deserialize_with = "deserialize_mem")]
78            rom: ExRom
79        }
80
81        let MemTemp::<MEM_SIZE, NUM_PAGES> { mem, banks, exrom } = Deserialize::deserialize(deserializer)?;
82
83        let mut pages = MemoryPages::<PAGE_SIZE, NUM_PAGES>::new();
84        let mut ro_pages = 0;
85        let offset_max = mem.len() - PAGE_SIZE;
86        for ((p, bank), page) in pages.0.iter_mut().zip(banks).zip(0u8..) {
87            let offset =  bank as usize * PAGE_SIZE;
88            if offset > offset_max {
89                return Err(de::Error::custom(format!("memory bank: {} larger than max: {}",
90                                                        bank, offset_max / PAGE_SIZE)));
91            }
92            if (bank as usize) < Self::ROM_BANKS {
93                ro_pages |= ro_flag_mask(page);
94            }
95            *p = cast_slice_as_bank_ptr(&mem[offset..offset + PAGE_SIZE]);
96        }
97
98        let mut res = MemPageableRomRamExRom { mem, pages, ro_pages, ex_rom: None};
99        if let Some(ExRomTemp { page, rom }) = exrom {
100            if rom.len() != PAGE_SIZE {
101                return Err(de::Error::custom(format!("attached ex-rom size incorrect: {} != {}",
102                                                        rom.len(), PAGE_SIZE)));
103            }
104            if page > Self::PAGES_MASK {
105                return Err(de::Error::custom(format!("attached ex-rom page: {}  larger than max: {}",
106                                                        page, Self::PAGES_MASK)));
107            }
108            res.attach_exrom(rom, page);
109        }
110        Ok(res)
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use std::rc::Rc;
117    use crate::memory::multi_page::MEM16K_SIZE;
118    use crate::memory::*;
119    use core::ptr::NonNull;
120
121    fn page_mem_offset(mem: &[u8], ptr: NonNull<[u8; MEM16K_SIZE]>) -> usize {
122        let mem_ptr = mem.as_ptr() as usize;
123        ptr.as_ptr() as usize - mem_ptr
124    }
125
126    fn serde_compare(mem: &Memory128k, mem_de: &Memory128k, must_have_exrom: bool) {
127        for (a, b) in mem.mem.iter().zip(mem_de.mem.iter()) {
128            assert_eq!(*a, *b);
129        }
130        let mut pages = mem.pages;
131        let mut pages_de = mem_de.pages;
132        if let Some(ex_rom) = &mem.ex_rom {
133            *pages.page_mut(ex_rom.page) = ex_rom.ptr;
134        }
135        if let Some(ex_rom) = &mem_de.ex_rom {
136            *pages_de.page_mut(ex_rom.page) = ex_rom.ptr;
137        }
138        for (&a, &b) in pages.0.iter().zip(pages_de.0.iter()) {
139            page_mem_offset(&mem.mem_ref(), a);
140            page_mem_offset(&mem_de.mem_ref(), b);
141        }
142        assert_eq!(mem.ro_pages, mem_de.ro_pages);
143        if must_have_exrom {
144            assert!(mem.ex_rom.is_some());
145            assert!(mem_de.ex_rom.is_some());
146            assert_eq!(mem.ex_rom.as_ref().unwrap().page, mem_de.ex_rom.as_ref().unwrap().page);
147            assert_eq!(mem.ex_rom.as_ref().unwrap().ro, mem_de.ex_rom.as_ref().unwrap().ro);
148            assert_eq!(
149                page_mem_offset(&mem.ex_rom.as_ref().unwrap().rom[..], mem.pages.page(mem.ex_rom.as_ref().unwrap().page)),
150                page_mem_offset(&mem_de.ex_rom.as_ref().unwrap().rom[..], mem_de.pages.page(mem_de.ex_rom.as_ref().unwrap().page))
151            );
152        }
153        else {
154            assert!(mem.ex_rom.is_none());
155            assert!(mem_de.ex_rom.is_none());
156        }
157    }
158
159    #[test]
160    fn memory_serde_works() {
161        let mut mem = Memory128k::default();
162        let sermem = serde_json::to_string(&mem).unwrap();
163        let mem_de: Memory128k = serde_json::from_str(&sermem).unwrap();
164        serde_compare(&mem, &mem_de, false);
165
166        let encoded: Vec<u8> = bincode::serialize(&mem).unwrap();
167        let mem_de: Memory128k = bincode::deserialize(&encoded).unwrap();
168        serde_compare(&mem, &mem_de, false);
169
170        let exrom: Rc<[u8]> = Rc::from(vec![0u8;Memory128k::PAGE_SIZE]);
171        mem.map_exrom(Rc::clone(&exrom), 3).unwrap();
172        let sermem = serde_json::to_string(&mem).unwrap();
173        let mem_de: Memory128k = serde_json::from_str(&sermem).unwrap();
174        serde_compare(&mem, &mem_de, true);
175
176        let encoded: Vec<u8> = bincode::serialize(&mem).unwrap();
177        let mem_de: Memory128k = bincode::deserialize(&encoded).unwrap();
178        serde_compare(&mem, &mem_de, true);
179    }
180}