From 12941b436e63de695e76f06cddfec0dca818390f Mon Sep 17 00:00:00 2001 From: Stephen D <webmaster@scd31.com> Date: Sat, 29 Jun 2024 17:05:08 -0300 Subject: [PATCH] refactor storage a bit --- src/controller.rs | 18 ++-- src/gui/chat_list.rs | 8 +- src/{storage.rs => storage/contact.rs} | 125 ++++++++----------------- src/storage/mod.rs | 98 +++++++++++++++++++ 4 files changed, 152 insertions(+), 97 deletions(-) rename src/{storage.rs => storage/contact.rs} (60%) create mode 100644 src/storage/mod.rs diff --git a/src/controller.rs b/src/controller.rs index 23e9f2e..2bf76ae 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -23,7 +23,7 @@ pub enum View { #[allow(clippy::enum_variant_names)] ContactView(ContactView, usize, usize), ChatList(ChatList), - Chat(Chat), + Chat(Chat, usize), } impl View { @@ -49,7 +49,7 @@ impl View { pub fn from_main_menu_id(id: usize, contacts: &ContactIter) -> Option<Self> { match id { 0 => Some(Self::new_contacts(0, contacts)), - 1 => Some(Self::ChatList(ChatList::new())), + 1 => Some(Self::ChatList(ChatList::new(0))), _ => None, } } @@ -118,7 +118,7 @@ impl Element for View { View::ContactOptions(c, _) => c.render(dt), View::ContactView(c, _, _) => c.render(dt), View::ChatList(c) => c.render(dt), - View::Chat(c) => c.render(dt), + View::Chat(c, _) => c.render(dt), } } @@ -131,7 +131,7 @@ impl Element for View { c.key_push(k); } View::ChatList(c) => c.key_push(k), - View::Chat(c) => c.key_push(k), + View::Chat(c, _) => c.key_push(k), } } @@ -142,7 +142,7 @@ impl Element for View { View::ContactOptions(c, _) => c.touchpad_scroll(x, y), View::ContactView(c, _, _) => c.touchpad_scroll(x, y), View::ChatList(c) => c.touchpad_scroll(x, y), - View::Chat(c) => c.touchpad_scroll(x, y), + View::Chat(c, _) => c.touchpad_scroll(x, y), } } } @@ -209,8 +209,8 @@ impl Element for Controller { } } - (View::ChatList(_), KeyCode::Touchpad) => { - self.cur_view = View::Chat(Chat::new()); + (View::ChatList(cl), KeyCode::Touchpad) => { + self.cur_view = View::Chat(Chat::new(), cl.selected()); } (View::MainMenu(_), KeyCode::Back) => {} @@ -226,8 +226,8 @@ impl Element for Controller { self.cur_view = View::new_contact_options(*contact_id, *selected_id); } - (View::Chat(_), KeyCode::Back) => { - self.cur_view = View::ChatList(ChatList::new()); + (View::Chat(_, selected_id), KeyCode::Back) => { + self.cur_view = View::ChatList(ChatList::new(*selected_id)); } (_, KeyCode::Back) => { diff --git a/src/gui/chat_list.rs b/src/gui/chat_list.rs index de2e236..bae3605 100644 --- a/src/gui/chat_list.rs +++ b/src/gui/chat_list.rs @@ -30,7 +30,7 @@ pub struct ChatList { } impl ChatList { - pub fn new() -> Self { + pub fn new(selected: usize) -> Self { Self { groups: [ MessageGroup::default(), @@ -38,9 +38,13 @@ impl ChatList { MessageGroup::default(), ], tracker: ScrollTracker::new(), - selected: 0, + selected, } } + + pub fn selected(&self) -> usize { + self.selected + } } impl Element for ChatList { diff --git a/src/storage.rs b/src/storage/contact.rs similarity index 60% rename from src/storage.rs rename to src/storage/contact.rs index 1215bf8..ce8fd81 100644 --- a/src/storage.rs +++ b/src/storage/contact.rs @@ -1,14 +1,10 @@ -use core::slice::from_raw_parts; +use core::slice; use heapless::{String, Vec}; -use rp2040_flash::flash; -const SECTOR_SIZE: usize = 4096; -const FLASH_START: usize = 0x10000000; -// First 6MB is for code -const DATA_START: usize = FLASH_START + 6144 * 1024; -// 2MB for data -const DATA_END: usize = DATA_START + 2048 * 1024; +use crate::storage::DATA_START; + +use super::{Storage, X25}; const CONTACT_HEADER_START: usize = DATA_START; const CONTACT_HEADER_SIZE: usize = 3; @@ -19,54 +15,16 @@ pub const MAX_CONTACT_CALLSIGN_LENGTH: usize = 10; // 1 byte for each length, 1 byte for SSID, 2 bytes for checksum const CONTACT_LENGTH: usize = MAX_CONTACT_NAME_LENGTH + 1 + 1 + MAX_CONTACT_CALLSIGN_LENGTH + 1 + 2; -const X25: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_IBM_SDLC); - -pub struct Storage { - contact_header: ContactHeader, +#[derive(Copy, Clone)] +pub struct ContactGroup { + header: ContactHeader, } -impl Storage { +impl ContactGroup { pub fn new() -> Self { Self { - // TODO need to invalidate when writing contacts - contact_header: ContactHeader::read(), - } - } - - pub fn save_contact(&mut self, contact: Contact) { - let len = self.contact_header.num_contacts; - - // if id isn't less than the len, we're inserting a new contact - if contact.id >= usize::from(len) { - assert!(usize::from(len) < MAX_CONTACTS); - self.contact_header.num_contacts += 1; - let ch = self.contact_header; - ch.write(self); + header: ContactHeader::read(), } - - contact.write(self); - } - - pub fn delete_contact(&mut self, contact_id: usize) { - assert!(contact_id < self.contacts_len()); - - // Shift every contact down - for mut c in self.contacts().skip(contact_id + 1) { - c.id -= 1; - c.write(self); - } - - self.contact_header.num_contacts -= 1; - let ch = self.contact_header; - ch.write(self); - } - - pub fn contacts(&self) -> ContactIter { - ContactIter::new(self.contact_header.num_contacts.into()) - } - - pub fn contacts_len(&self) -> usize { - self.contact_header.num_contacts.into() } // Note - calling this twice without saving one @@ -77,50 +35,43 @@ impl Storage { callsign: String::new(), ssid: 0, name: String::new(), - id: usize::from(self.contact_header.num_contacts), + id: self.len(), } } - fn write_data(&mut self, start_addr: usize, buf: &[u8]) { - let start_sector = start_addr / SECTOR_SIZE; - let num_sectors = buf.len() / SECTOR_SIZE + 1; - let diff = start_addr % SECTOR_SIZE; - - for s in 0..num_sectors { - let existing_start = if s == 0 { diff } else { 0 }; - let existing_end = (diff + (s + 1) * SECTOR_SIZE).min(existing_start + buf.len()); - let mut existing = self.read_sector((start_sector + s) * SECTOR_SIZE); - let buf_start = (s * SECTOR_SIZE).min(buf.len()); - let buf_end = (buf_start + SECTOR_SIZE).min(buf.len()); - // only write if there's a difference - if existing[existing_start..existing_end] != buf[buf_start..buf_end] { - existing[existing_start..existing_end].copy_from_slice(&buf[buf_start..buf_end]); - self.write_sector((start_sector + s) * SECTOR_SIZE, &existing); - } - } - } + pub fn save_contact(&mut self, storage: &mut Storage, contact: Contact) { + let len = self.header.num_contacts; - fn read_sector(&self, addr: usize) -> [u8; SECTOR_SIZE] { - assert!(addr >= DATA_START); - assert!(addr + SECTOR_SIZE < DATA_END); + // if id isn't less than the len, we're inserting a new contact + if contact.id >= usize::from(len) { + assert!(usize::from(len) < MAX_CONTACTS); + self.header.num_contacts += 1; + let ch = self.header; + ch.write(storage); + } - let sector = unsafe { from_raw_parts(addr as *mut u8, SECTOR_SIZE) }; + contact.write(storage); + } - let mut buf = [0; SECTOR_SIZE]; + pub fn delete_contact(&mut self, storage: &mut Storage, contact_id: usize) { + assert!(contact_id < self.len()); - buf.copy_from_slice(sector); + // Shift every contact down + for mut c in self.contacts().skip(contact_id + 1) { + c.id -= 1; + c.write(storage); + } - buf + self.header.num_contacts -= 1; + self.header.write(storage); } - fn write_sector(&mut self, start_addr: usize, buf: &[u8; 4096]) { - let addr: u32 = (start_addr - FLASH_START).try_into().unwrap(); - let sector_size = u32::try_from(SECTOR_SIZE).unwrap(); - assert!(addr % sector_size == 0); + pub fn contacts(&self) -> ContactIter { + ContactIter::new(self.header.num_contacts.into()) + } - cortex_m::interrupt::free(|_| unsafe { - flash::flash_range_erase_and_program(addr, buf, true); - }); + pub fn len(&self) -> usize { + self.header.num_contacts.into() } } @@ -133,7 +84,8 @@ struct ContactHeader { impl ContactHeader { fn read() -> Self { - let bytes = unsafe { from_raw_parts(CONTACT_HEADER_START as *mut u8, CONTACT_HEADER_SIZE) }; + let bytes = + unsafe { slice::from_raw_parts(CONTACT_HEADER_START as *mut u8, CONTACT_HEADER_SIZE) }; let num_contacts = bytes[0]; let checksum_expected = u16::from_le_bytes([bytes[1], bytes[2]]); // CRC checksum is very overkill right now @@ -170,7 +122,8 @@ impl Contact { let start_addr = (CONTACT_HEADER_START + CONTACT_HEADER_SIZE + idx * CONTACT_LENGTH) as *mut u8; - let bytes = unsafe { from_raw_parts(start_addr, CONTACT_LENGTH) }; + // TODO technically this should take in storage to prevent us from reading and writing at the same time + let bytes = unsafe { slice::from_raw_parts(start_addr, CONTACT_LENGTH) }; let checksum_expected = u16::from_le_bytes([bytes[CONTACT_LENGTH - 2], bytes[CONTACT_LENGTH - 1]]); let checksum_actual = X25.checksum(&bytes[..(CONTACT_LENGTH - 2)]); diff --git a/src/storage/mod.rs b/src/storage/mod.rs new file mode 100644 index 0000000..0b628ea --- /dev/null +++ b/src/storage/mod.rs @@ -0,0 +1,98 @@ +mod contact; + +use contact::ContactGroup; +pub use contact::{ + Contact, ContactIter, MAX_CONTACTS, MAX_CONTACT_CALLSIGN_LENGTH, MAX_CONTACT_NAME_LENGTH, +}; + +use core::slice::from_raw_parts; +use rp2040_flash::flash; + +const SECTOR_SIZE: usize = 4096; +const FLASH_START: usize = 0x10000000; +// First 6MB is for code +const DATA_START: usize = FLASH_START + 6144 * 1024; +// 2MB for data +const DATA_END: usize = DATA_START + 2048 * 1024; + +const X25: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_IBM_SDLC); + +pub struct Storage { + contacts: ContactGroup, +} + +impl Storage { + pub fn new() -> Self { + Self { + contacts: ContactGroup::new(), + } + } + + // Punch through our contact methods + + pub fn new_contact(&self) -> Contact { + self.contacts.new_contact() + } + + pub fn save_contact(&mut self, contact: Contact) { + let mut contacts = self.contacts; + contacts.save_contact(self, contact); + self.contacts = contacts; + } + + pub fn delete_contact(&mut self, contact_id: usize) { + let mut contacts = self.contacts; + contacts.delete_contact(self, contact_id); + self.contacts = contacts; + } + + pub fn contacts(&self) -> ContactIter { + self.contacts.contacts() + } + + pub fn contacts_len(&self) -> usize { + self.contacts.len() + } + + fn write_data(&mut self, start_addr: usize, buf: &[u8]) { + let start_sector = start_addr / SECTOR_SIZE; + let num_sectors = buf.len() / SECTOR_SIZE + 1; + let diff = start_addr % SECTOR_SIZE; + + for s in 0..num_sectors { + let existing_start = if s == 0 { diff } else { 0 }; + let existing_end = (diff + (s + 1) * SECTOR_SIZE).min(existing_start + buf.len()); + let mut existing = self.read_slice((start_sector + s) * SECTOR_SIZE); + let buf_start = (s * SECTOR_SIZE).min(buf.len()); + let buf_end = (buf_start + SECTOR_SIZE).min(buf.len()); + // only write if there's a difference + if existing[existing_start..existing_end] != buf[buf_start..buf_end] { + existing[existing_start..existing_end].copy_from_slice(&buf[buf_start..buf_end]); + self.write_sector((start_sector + s) * SECTOR_SIZE, &existing); + } + } + } + + fn read_slice<const N: usize>(&self, addr: usize) -> [u8; N] { + assert!(addr >= DATA_START); + assert!(addr + N < DATA_END); + + let sector = unsafe { from_raw_parts(addr as *mut u8, N) }; + + let mut buf = [0; N]; + + buf.copy_from_slice(sector); + + buf + } + + fn write_sector(&mut self, start_addr: usize, buf: &[u8; 4096]) { + let addr: u32 = (start_addr - FLASH_START).try_into().unwrap(); + let sector_size = u32::try_from(SECTOR_SIZE).unwrap(); + assert!(addr % sector_size == 0); + + cortex_m::interrupt::free(|_| unsafe { + flash::flash_range_erase_and_program(addr, buf, true); + }); + } +} -- GitLab