use super::*;
#[allow(unused_imports)]
use core::ffi::{c_char};
use core::ptr;
use core::slice;
use core::str;
extern crate alloc;
use alloc::vec::Vec;
#[allow(unused_imports)]
use alloc::string::{String, ToString};
#[allow(unused_imports)]
use alloc::ffi::CString;
#[allow(unused_imports)]
use alloc::format;
#[cfg(target_os = "none")]
pub fn is_valid_storage() -> Result<(), GlobalError> {
let storage = epsilon::storage();
storage.is_valid().map_err(|_| SoftwareError::InvalidStorage.into())
}
pub(crate) fn is_valid_cstring(s: &str) -> bool {
!s.as_bytes().contains(&0) && !s.is_empty()
}
pub(crate) fn strnend(start:*const u8, max:*const u8) -> Result<*const u8, StorageError> {
unsafe {
let len = (max.offset_from(start) as usize).min(epsilon::STORAGE_FILE_MAX_NAME_LEN);
for offset in 0..len {
if *start.add(offset) == 0 {
return Ok(start.add(offset));
}
}
Err(StorageError::NullTerminatorNotFound { start })
}
}
#[cfg(target_os = "none")]
#[doc(hidden)]
pub fn next_free() -> *const u8 {
let storage = epsilon::storage();
let usable_end_addr = storage.usable_end_addr;
let mut offset = storage.usable_start_addr;
while offset < usable_end_addr {
let size = unsafe { ptr::read_unaligned(offset as *const u16) };
if size == 0 {
return offset;
}
offset = unsafe { offset.add(size as usize) };
}
usable_end_addr
}
#[doc(hidden)]
pub struct FileEntry {
size: usize,
ptr: *const u8,
#[allow(unused)]
name: *const u8,
content: *const u8,
content_size: usize,
}
#[doc(hidden)]
#[cfg(target_os = "none")]
pub fn _find_file(filename: &str) -> Result<FileEntry, StorageError> {
let filename_slice = filename.as_bytes();
let filename_len = filename_slice.len();
let storage = epsilon::storage();
let storage_start = storage.usable_start_addr;
let storage_end = storage.usable_end_addr;
let mut offset = storage_start;
unsafe {
while offset < storage_end {
let size = ptr::read_unaligned(offset as *const u16) as usize;
if size == 0 { break; }
let name_ptr = offset.add(2);
let name_candidate = slice::from_raw_parts(name_ptr, filename_len);
let name_null_terminator = name_ptr.add(filename_len);
if name_candidate == filename_slice && *name_null_terminator == 0 {
let content_ptr = name_ptr.add(filename_len + 1);
let content_size = size - 2 - (filename_len + 1);
return Ok(FileEntry { size: size, ptr: offset, name: name_ptr, content: content_ptr, content_size });
}
offset = offset.add(size);
}
}
Err(StorageError::FileNotFound)
}
#[cfg(target_os = "none")]
pub fn find_files_with_suffix(suffix: &str) -> Result<Vec<&str>, GlobalError> {
if !is_valid_cstring(suffix) {
return Err(SoftwareError::InvalidParameter { param_name: "suffix".to_string(), details: "suffix is empty or contains null bytes".to_string() }.into());
}
let suffix_slice = suffix.as_bytes();
let suffix_len = suffix_slice.len();
let storage = epsilon::storage();
let storage_start = storage.usable_start_addr;
let storage_end = storage.usable_end_addr;
let mut offset = storage_start;
let mut matching_files = Vec::new();
unsafe {
while offset < storage_end {
let size = ptr::read_unaligned(offset as *const u16) as usize;
if size == 0 { break; }
let name_ptr = offset.add(2);
let nt_ptr = strnend(name_ptr, offset.add(size))?;
let name_len = nt_ptr.offset_from(name_ptr) as usize;
if name_len >= suffix_len {
let suffix_candidate_ptr = nt_ptr.sub(suffix_len);
let suffix_candidate = slice::from_raw_parts(suffix_candidate_ptr, suffix_len);
if suffix_candidate == suffix_slice {
let name_slice = slice::from_raw_parts(name_ptr, name_len);
let name_str = str::from_utf8_unchecked(name_slice);
matching_files.push(name_str);
}
}
offset = offset.add(size);
}
}
Ok(matching_files)
}
#[cfg(not(target_os = "none"))]
pub fn find_files_with_suffix(_suffix: &str) -> Result<Vec<&str>, GlobalError> {
Ok(Vec::new())
}
#[cfg(target_os = "none")]
pub fn available_space() -> usize {
let free_addr = next_free() as usize; let usable_end = epsilon::storage().usable_end_addr as usize;
usable_end - free_addr
}
#[cfg(target_os = "none")]
pub fn can_store_len(content_len: usize, filename: &str) -> Result<(), GlobalError> {
let filename_size = filename.len() + 1; let total_size = 2 + filename_size + content_len;
if !is_valid_cstring(filename) {
return Err(StorageError::StorageInvalidName { length: filename_size, string: filename.to_string() }.into());
}
if filename_size > u8::MAX as usize {
return Err(StorageError::StorageInvalidName { length: filename_size, string: filename.to_string() }.into());
}
if content_len == 0 {
return Err(StorageError::FileContentEmpty.into());
}
if total_size > u16::MAX as usize {
return Err(StorageError::FileTooLarge { max_size: u16::MAX as usize, actual_size: total_size }.into());
}
if available_space() >= total_size {
Ok(())
} else {
Err(StorageError::StorageOverflow { available: available_space(), needed: total_size }.into())
}
}
#[cfg(not(target_os = "none"))]
pub fn can_store_len(_content_len: usize, _filename: &str) -> Result<(), GlobalError> {
Ok(())
}
#[cfg(target_os = "none")]
pub fn can_store(content: &[u8], filename: &str) -> Result<(), GlobalError> {
can_store_len(content.len(), filename)
}
#[cfg(target_os = "none")]
pub fn file_write_segments(filename: &str, segments: &[&[u8]]) -> Result<(), GlobalError> {
let total_content_size: usize = segments.iter().map(|s| s.len()).sum();
is_valid_storage()?;
can_store_len(total_content_size, filename)?;
let write_pos = next_free() as *mut u8;
let size = (2 + filename.len() + 1 + total_content_size) as u16;
unsafe {
let filename_len = filename.len();
let dest_header_ptr = write_pos as *mut u16;
let dest_name_slice = slice::from_raw_parts_mut((dest_header_ptr as *mut u8).add(2), filename_len);
let dist_nt_ptr = dest_name_slice.as_mut_ptr().add(filename_len);
ptr::write_unaligned(dest_header_ptr, size);
dest_name_slice.copy_from_slice(filename.as_bytes());
*dist_nt_ptr = 0;
let mut content_write_ptr = dist_nt_ptr.add(1);
for segment in segments {
let segment_len = segment.len();
let dest_content_slice = slice::from_raw_parts_mut(content_write_ptr, segment_len);
dest_content_slice.copy_from_slice(segment);
content_write_ptr = content_write_ptr.add(segment_len);
}
ptr::write_unaligned(content_write_ptr as *mut u16, 0);
}
Ok(())
}
#[cfg(not(target_os = "none"))]
pub fn file_write_segments(_filename: &str, _segments: &[&[u8]]) -> Result<(), GlobalError> {
Ok(())
}
#[cfg(target_os = "none")]
pub fn file_write_raw(filename: &str, content: &[u8]) -> Result<(), GlobalError> {
file_write_segments(filename, &[content])?;
Ok(())
}
#[cfg(not(target_os = "none"))]
pub fn file_write_raw(_filename: &str, _content: &[u8]) -> Result<(), GlobalError> {
Err(SoftwareError::SimulatorNotSupported.into())
}
#[cfg(target_os = "none")]
pub unsafe fn file_read_raw(filename: &str) -> Result<&[u8], GlobalError> {
is_valid_storage()?;
let file_view = _find_file(filename)?;
return Ok(slice::from_raw_parts(file_view.content, file_view.content_size));
}
#[cfg(not(target_os = "none"))]
pub unsafe fn file_read_raw(_filename: &str) -> Result<&[u8], GlobalError> {
Err(SoftwareError::SimulatorNotSupported.into())
}
#[cfg(target_os = "none")]
pub fn file_exists(filename: &str) -> Result<bool, GlobalError> {
match unsafe { file_read_raw(filename) } {
Ok(_) => Ok(true),
Err(GlobalError::Storage(StorageError::FileNotFound)) => Ok(false),
Err(e) => Err(e),
}
}
#[cfg(not(target_os = "none"))]
pub fn file_exists(_filename: &str) -> Result<bool, GlobalError> {
Ok(false)
}
#[cfg(target_os = "none")]
pub unsafe fn file_erase(filename: &str) -> Result<(), GlobalError> {
is_valid_storage()?;
let file_to_erase = _find_file(filename)?;
let free_space_before_deletion = next_free();
let next_file_pos = file_to_erase.ptr.add(file_to_erase.size);
ptr::copy(
next_file_pos,
file_to_erase.ptr as *mut u8,
free_space_before_deletion.offset_from(next_file_pos) as usize
);
ptr::write_bytes(
free_space_before_deletion.sub(file_to_erase.size) as *mut u8,
0,
file_to_erase.size
);
Ok(())
}
#[cfg(not(target_os = "none"))]
pub unsafe fn file_erase(_filename: &str) -> Result<(), GlobalError> {
Ok(())
}
#[cfg(target_os = "none")]
pub fn file_write_string(filename: &str, content: &str) -> Result<(), GlobalError> {
let header_slice = [0u8];
let content_slice = content.as_bytes();
let footer_slice = [0u8];
let segments: [&[u8]; 3] = [&header_slice, content_slice, &footer_slice];
file_write_segments(filename, &segments)?;
Ok(())
}
#[cfg(not(target_os = "none"))]
pub fn file_write_string(_filename: &str, _content: &str) -> Result<(), GlobalError> {
Ok(())
}
#[cfg(target_os = "none")]
pub fn file_read_string(filename: &str) -> Result<&'static str, GlobalError> {
unsafe {
let raw_content = file_read_raw(filename)?;
if raw_content.len() < 2 { return Err(StorageError::FileContentTooShort { expected: 2, actual: raw_content.len() }.into());
}
let trimmed_content = &raw_content[1..raw_content.len() - 1];
let static_slice = slice::from_raw_parts(trimmed_content.as_ptr(), trimmed_content.len());
let static_str = str::from_utf8_unchecked(static_slice);
Ok(static_str) }
}
#[cfg(not(target_os = "none"))]
pub fn file_read_string(_filename: &str) -> Result<&'static str, GlobalError> {
Ok("Dummy content")
}