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