A custom OS for the xteink x4 ebook reader
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

kernel: migrate _PULP dir/subdir ops to sd()

* delete 7 remaining KernelHandle forwarding methods:
read_app_data_start, write_app_data, ensure_app_subdir,
read_app_subdir_chunk, write_app_subdir, append_app_subdir,
file_size_app_subdir
* migrate ~30 app callsites across home, settings, reader,
images, cover_cache, stats to k.sd().method() form
* PULP_DIR is now explicit at callsites that previously hid
it behind read_app_data_start/write_app_data wrappers

Co-authored-by: Claude <noreply@anthropic.com>

tymek.me f6e9551b 3192387d

verified
+34 -77
+1 -47
kernel/src/kernel/handle.rs
··· 8 8 // rather than through dedicated handle methods 9 9 10 10 use crate::drivers::sdcard::SdStorage; 11 - use crate::drivers::storage::{self, DirEntry, DirPage}; 11 + use crate::drivers::storage::{DirEntry, DirPage}; 12 12 use crate::error::{Error, Result}; 13 13 use crate::kernel::bookmarks::BookmarkCache; 14 14 use crate::kernel::dir_cache::DirCache; ··· 65 65 .map_err(|e: Error| -> &'static str { e.into() }) 66 66 }; 67 67 f(&mut reader) 68 - } 69 - 70 - // storage primitives 71 - // 72 - // each calls a single storage::* function; return type is 73 - // Result<T> (unified Error) throughout 74 - 75 - #[inline] 76 - pub fn read_app_data_start(&mut self, name: &str, buf: &mut [u8]) -> Result<(u32, usize)> { 77 - self.kernel.sd.read_file_start_in_dir(storage::PULP_DIR, name, buf) 78 - } 79 - 80 - #[inline] 81 - pub fn write_app_data(&mut self, name: &str, data: &[u8]) -> Result<()> { 82 - self.kernel.sd.write_file_in_dir(storage::PULP_DIR, name, data) 83 - } 84 - 85 - #[inline] 86 - pub fn ensure_app_subdir(&mut self, dir: &str) -> Result<()> { 87 - self.kernel.sd.ensure_pulp_subdir(dir) 88 - } 89 - 90 - #[inline] 91 - pub fn read_app_subdir_chunk( 92 - &mut self, 93 - dir: &str, 94 - name: &str, 95 - offset: u32, 96 - buf: &mut [u8], 97 - ) -> Result<usize> { 98 - self.kernel.sd.read_chunk_in_pulp_subdir(dir, name, offset, buf) 99 - } 100 - 101 - #[inline] 102 - pub fn write_app_subdir(&mut self, dir: &str, name: &str, data: &[u8]) -> Result<()> { 103 - self.kernel.sd.write_in_pulp_subdir(dir, name, data) 104 - } 105 - 106 - #[inline] 107 - pub fn append_app_subdir(&mut self, dir: &str, name: &str, data: &[u8]) -> Result<()> { 108 - self.kernel.sd.append_in_pulp_subdir(dir, name, data) 109 - } 110 - 111 - #[inline] 112 - pub fn file_size_app_subdir(&mut self, dir: &str, name: &str) -> Result<u32> { 113 - self.kernel.sd.file_size_in_pulp_subdir(dir, name) 114 68 } 115 69 116 70 pub fn dir_page(&mut self, offset: usize, buf: &mut [DirEntry]) -> Result<DirPage> {
+6 -6
src/apps/cover_cache.rs
··· 42 42 let mut header = [0u8; 4]; 43 43 header[0..2].copy_from_slice(&img.width.to_le_bytes()); 44 44 header[2..4].copy_from_slice(&img.height.to_le_bytes()); 45 - k.write_app_subdir(dir, COVER_THUMB_FILE, &header)?; 46 - k.append_app_subdir(dir, COVER_THUMB_FILE, &img.data)?; 45 + k.sd().write_in_pulp_subdir(dir, COVER_THUMB_FILE, &header)?; 46 + k.sd().append_in_pulp_subdir(dir, COVER_THUMB_FILE, &img.data)?; 47 47 Ok(()) 48 48 } 49 49 ··· 51 51 /// 52 52 /// Returns `None` if the file doesn't exist or is invalid. 53 53 pub fn load_cover_thumb(k: &mut KernelHandle<'_>, dir: &str) -> Option<DecodedImage> { 54 - let size = k.file_size_app_subdir(dir, COVER_THUMB_FILE).ok()?; 54 + let size = k.sd().file_size_in_pulp_subdir(dir, COVER_THUMB_FILE).ok()?; 55 55 if size < 5 { 56 56 return None; 57 57 } 58 58 let mut header = [0u8; 4]; 59 - k.read_app_subdir_chunk(dir, COVER_THUMB_FILE, 0, &mut header) 59 + k.sd().read_chunk_in_pulp_subdir(dir, COVER_THUMB_FILE, 0, &mut header) 60 60 .ok()?; 61 61 let width = u16::from_le_bytes([header[0], header[1]]); 62 62 let height = u16::from_le_bytes([header[2], header[3]]); ··· 71 71 let mut data = Vec::new(); 72 72 data.try_reserve_exact(data_len).ok()?; 73 73 data.resize(data_len, 0); 74 - k.read_app_subdir_chunk(dir, COVER_THUMB_FILE, 4, &mut data) 74 + k.sd().read_chunk_in_pulp_subdir(dir, COVER_THUMB_FILE, 4, &mut data) 75 75 .ok()?; 76 76 Some(DecodedImage { 77 77 width, ··· 83 83 84 84 /// Check whether a cover thumbnail already exists for the given cache dir. 85 85 pub fn has_cover_thumb(k: &mut KernelHandle<'_>, dir: &str) -> bool { 86 - k.file_size_app_subdir(dir, COVER_THUMB_FILE) 86 + k.sd().file_size_in_pulp_subdir(dir, COVER_THUMB_FILE) 87 87 .map(|s| s >= 5) 88 88 .unwrap_or(false) 89 89 }
+3 -2
src/apps/home.rs
··· 6 6 use crate::board::action::{Action, ActionEvent}; 7 7 use crate::board::{SCREEN_H, SCREEN_W}; 8 8 use crate::drivers::battery; 9 + use crate::drivers::storage::PULP_DIR; 9 10 use crate::drivers::strip::StripBuffer; 10 11 use crate::fonts; 11 12 use crate::kernel::KernelHandle; ··· 209 210 // This is intentional for now — see TODO-960fb375 for context. 210 211 pub fn load_recent(&mut self, k: &mut KernelHandle<'_>) { 211 212 let mut buf = [0u8; 196]; 212 - match k.read_app_data_start(RECENT_FILE, &mut buf) { 213 + match k.sd().read_file_start_in_dir(PULP_DIR, RECENT_FILE, &mut buf) { 213 214 Ok((_, n)) if n > 0 => self.parse_recent(&buf[..n]), 214 215 _ => self.recent_book_len = 0, 215 216 } ··· 418 419 if self.needs_load_recent { 419 420 let old_count = self.item_count; 420 421 let mut buf = [0u8; 196]; 421 - match k.read_app_data_start(RECENT_FILE, &mut buf) { 422 + match k.sd().read_file_start_in_dir(PULP_DIR, RECENT_FILE, &mut buf) { 422 423 Ok((_, n)) if n > 0 => self.parse_recent(&buf[..n]), 423 424 _ => self.recent_book_len = 0, 424 425 }
+3 -3
src/apps/reader/epubs.rs
··· 138 138 // ensure image subdir exists for skip markers 139 139 let dir_buf = self.cache_dir; 140 140 let dir = cache::dir_name_str(&dir_buf); 141 - let _ = k.ensure_app_subdir(dir); 141 + let _ = k.sd().ensure_pulp_subdir(dir); 142 142 log::info!("epub: v3 cache hit ({} chapters)", count); 143 143 return Ok(true); 144 144 } ··· 152 152 // ensure image subdir exists (images stay in _PULP/_XXXXXXX/) 153 153 let dir_buf = self.cache_dir; 154 154 let dir = cache::dir_name_str(&dir_buf); 155 - k.ensure_app_subdir(dir)?; 155 + k.sd().ensure_pulp_subdir(dir)?; 156 156 self.cache_chapter = 0; 157 157 Ok(false) 158 158 } ··· 471 471 472 472 // ensure the per-book cache directory exists (cover generation 473 473 // runs before NeedCache which normally creates it) 474 - if k.ensure_app_subdir(dir).is_err() { 474 + if k.sd().ensure_pulp_subdir(dir).is_err() { 475 475 log::warn!("epub: failed to create cache dir for cover thumb"); 476 476 return; 477 477 }
+12 -12
src/apps/reader/images.rs
··· 489 489 let resume = (offset + path_start + path_len) as u32; 490 490 491 491 // already cached or skip-marked 492 - if k.file_size_app_subdir(dir, img_file).is_ok() { 492 + if k.sd().file_size_in_pulp_subdir(dir, img_file).is_ok() { 493 493 self.epub.img_found_count = self.epub.img_found_count.saturating_add(1); 494 494 self.epub.img_cached_count = self.epub.img_cached_count.saturating_add(1); 495 495 i = path_start + path_len; ··· 501 501 502 502 if !is_jpeg && !is_png { 503 503 log::info!("precache: skip unsupported: {}", full_path); 504 - let _ = k.write_app_subdir(dir, img_file, &[]); 504 + let _ = k.sd().write_in_pulp_subdir(dir, img_file, &[]); 505 505 self.epub.img_found_count = self.epub.img_found_count.saturating_add(1); 506 506 self.epub.img_cached_count = self.epub.img_cached_count.saturating_add(1); 507 507 i = path_start + path_len; ··· 531 531 // remaining large images so the device stays responsive 532 532 if entry.uncomp_size > PRECACHE_IMG_MAX { 533 533 if self.epub.skip_large_img { 534 - let _ = k.write_app_subdir(dir, img_file, &[]); 534 + let _ = k.sd().write_in_pulp_subdir(dir, img_file, &[]); 535 535 self.epub.img_found_count = self.epub.img_found_count.saturating_add(1); 536 536 self.epub.img_cached_count = self.epub.img_cached_count.saturating_add(1); 537 537 i = path_start + path_len; ··· 561 561 } 562 562 Err(e) => { 563 563 log::warn!("precache: streaming failed: {}", e); 564 - let _ = k.write_app_subdir(dir, img_file, &[]); 564 + let _ = k.sd().write_in_pulp_subdir(dir, img_file, &[]); 565 565 // stop trying large images this session 566 566 self.epub.skip_large_img = true; 567 567 } ··· 588 588 Ok(d) => d, 589 589 Err(e) => { 590 590 log::warn!("precache: extract failed: {}", e); 591 - let _ = k.write_app_subdir(dir, img_file, &[]); 591 + let _ = k.sd().write_in_pulp_subdir(dir, img_file, &[]); 592 592 self.epub.img_found_count = self.epub.img_found_count.saturating_add(1); 593 593 self.epub.img_cached_count = self.epub.img_cached_count.saturating_add(1); 594 594 i = path_start + path_len; ··· 840 840 dir: &str, 841 841 name: &str, 842 842 ) -> crate::error::Result<DecodedImage> { 843 - let size = k.file_size_app_subdir(dir, name)?; 843 + let size = k.sd().file_size_in_pulp_subdir(dir, name)?; 844 844 if size < 5 { 845 845 return Err(Error::new( 846 846 ErrorKind::InvalidData, ··· 848 848 )); 849 849 } 850 850 let mut header = [0u8; 4]; 851 - k.read_app_subdir_chunk(dir, name, 0, &mut header)?; 851 + k.sd().read_chunk_in_pulp_subdir(dir, name, 0, &mut header)?; 852 852 let width = u16::from_le_bytes([header[0], header[1]]); 853 853 let height = u16::from_le_bytes([header[2], header[3]]); 854 854 if width == 0 || height == 0 { ··· 869 869 data.try_reserve_exact(data_len) 870 870 .map_err(|_| Error::new(ErrorKind::OutOfMemory, "load_cached_image"))?; 871 871 data.resize(data_len, 0); 872 - k.read_app_subdir_chunk(dir, name, 4, &mut data)?; 872 + k.sd().read_chunk_in_pulp_subdir(dir, name, 4, &mut data)?; 873 873 Ok(DecodedImage { 874 874 width, 875 875 height, ··· 883 883 // returns None if the file doesn't exist, is too small, or has 884 884 // zero dimensions. 885 885 fn peek_cached_image_size(k: &mut KernelHandle<'_>, dir: &str, name: &str) -> Option<(u16, u16)> { 886 - let size = k.file_size_app_subdir(dir, name).ok()?; 886 + let size = k.sd().file_size_in_pulp_subdir(dir, name).ok()?; 887 887 if size < 5 { 888 888 return None; 889 889 } 890 890 let mut hdr = [0u8; 4]; 891 - k.read_app_subdir_chunk(dir, name, 0, &mut hdr).ok()?; 891 + k.sd().read_chunk_in_pulp_subdir(dir, name, 0, &mut hdr).ok()?; 892 892 let w = u16::from_le_bytes([hdr[0], hdr[1]]); 893 893 let h = u16::from_le_bytes([hdr[2], hdr[3]]); 894 894 if w == 0 || h == 0 { ··· 999 999 let mut header = [0u8; 4]; 1000 1000 header[0..2].copy_from_slice(&img.width.to_le_bytes()); 1001 1001 header[2..4].copy_from_slice(&img.height.to_le_bytes()); 1002 - k.write_app_subdir(dir, name, &header)?; 1003 - k.append_app_subdir(dir, name, &img.data)?; 1002 + k.sd().write_in_pulp_subdir(dir, name, &header)?; 1003 + k.sd().append_in_pulp_subdir(dir, name, &img.data)?; 1004 1004 Ok(()) 1005 1005 }
+2 -1
src/apps/reader/mod.rs
··· 19 19 use embedded_graphics::text::Text; 20 20 21 21 use crate::apps::{App, AppContext, AppId, DeferredPersistenceReason, RECENT_FILE, Transition}; 22 + use crate::drivers::storage::PULP_DIR; 22 23 use crate::board::action::{Action, ActionEvent}; 23 24 use crate::board::{SCREEN_H, SCREEN_W}; 24 25 use crate::drivers::strip::{GrayMode, StripBuffer}; ··· 1454 1455 buf[pos] = self.progress_pct(); 1455 1456 pos += 1; 1456 1457 1457 - k.write_app_data(RECENT_FILE, &buf[..pos])?; 1458 + k.sd().write_file_in_dir(PULP_DIR, RECENT_FILE, &buf[..pos])?; 1458 1459 self.recent_dirty = false; 1459 1460 pulp_kernel::perf_event!( 1460 1461 "reader",
+3 -2
src/apps/settings.rs
··· 15 15 use crate::apps::{App, AppContext, AppId, Transition}; 16 16 use crate::board::action::{Action, ActionEvent}; 17 17 use crate::board::{SCREEN_H, SCREEN_W}; 18 + use crate::drivers::storage::PULP_DIR; 18 19 use crate::drivers::strip::StripBuffer; 19 20 use crate::fonts; 20 21 use crate::fonts::max_size_idx; ··· 111 112 self.settings = SystemSettings::defaults(); 112 113 self.wifi = WifiConfig::empty(); 113 114 114 - match k.read_app_data_start(config::SETTINGS_FILE, &mut buf) { 115 + match k.sd().read_file_start_in_dir(PULP_DIR, config::SETTINGS_FILE, &mut buf) { 115 116 Ok((_size, n)) if n > 0 => { 116 117 parse_settings_txt(&buf[..n], &mut self.settings, &mut self.wifi); 117 118 self.settings.sanitize(); ··· 128 129 fn save(&self, k: &mut KernelHandle<'_>) -> bool { 129 130 let mut buf = [0u8; 512]; 130 131 let len = write_settings_txt(&self.settings, &self.wifi, &mut buf); 131 - match k.write_app_data(config::SETTINGS_FILE, &buf[..len]) { 132 + match k.sd().write_file_in_dir(PULP_DIR, config::SETTINGS_FILE, &buf[..len]) { 132 133 Ok(_) => { 133 134 log::info!("settings: saved to {}", config::SETTINGS_FILE); 134 135 true
+4 -4
src/apps/stats.rs
··· 147 147 let s = fmt.as_str().as_bytes(); 148 148 let len = s.len().min(buf.len()); 149 149 buf[..len].copy_from_slice(&s[..len]); 150 - k.ensure_app_subdir(STATS_DIR)?; 151 - k.write_app_subdir(STATS_DIR, filename, &buf[..len]) 150 + k.sd().ensure_pulp_subdir(STATS_DIR)?; 151 + k.sd().write_in_pulp_subdir(STATS_DIR, filename, &buf[..len]) 152 152 } 153 153 154 154 pub fn load_book_stats(k: &mut KernelHandle<'_>, filename: &str) -> Option<(u32, u32, u16)> { 155 155 let mut buf = [0u8; 128]; 156 156 let n = k 157 - .read_app_subdir_chunk(STATS_DIR, filename, 0, &mut buf) 157 + .sd().read_chunk_in_pulp_subdir(STATS_DIR, filename, 0, &mut buf) 158 158 .ok()?; 159 159 if n == 0 { 160 160 return None; ··· 275 275 let fname = entry.name_str(); 276 276 277 277 // try to load stats for this file 278 - let n = match k.read_app_subdir_chunk(STATS_DIR, fname, 0, &mut buf) { 278 + let n = match k.sd().read_chunk_in_pulp_subdir(STATS_DIR, fname, 0, &mut buf) { 279 279 Ok(n) if n > 0 => n, 280 280 _ => continue, 281 281 };