1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/* filesystem.rs
 *
 * Developed by Tim Walls <tim.walls@snowgoons.com>
 * Copyright (c) All Rights Reserved, Tim Walls
 */
//! Doc comment for this file
//!

// Imports ===================================================================
use core::ops::{AddAssign, SubAssign};
use avr_oxide::io::IoError;
use avr_oxide::OxideResult;
use avrox_storage::fs::{File, OpenOptions};
use avrox_storage::{FileAddr, SAddr};

// Declarations ==============================================================
#[derive(Copy,Clone,PartialEq,Eq)]
#[cfg_attr(not(target_arch="avr"), derive(Debug))]
#[cfg_attr(target_arch="avr", derive(ufmt::derive::uDebug))]
pub struct BlockNumber(u16);
pub const BLOCKSIZE : usize = 256;

pub type FileSystemResult<T> = OxideResult<T,IoError>;

pub trait FileSystemRead {
  type FID : Copy;
  const BLOCK_DATA_SIZE: usize;

  /// Read from the given file location into the buffer.  The number of
  /// bytes read, and the new location at the end of the read, are returned.
  ///
  /// This method does not guarantee to read all the bytes requested,
  /// you must pay attention to the number of bytes read returned.
  fn read_from_location(&self, file: Self::FID, block: BlockNumber, offset: usize, buf: &mut [u8]) -> FileSystemResult<(usize,(BlockNumber,usize))>;

  /// Check if the given file exists
  fn check_exists(&self, file: Self::FID) -> bool;

  /// Return the size of the given file
  fn get_file_size(&self, file: Self::FID) -> FileSystemResult<FileAddr>;

  /// Return the approximate amount of free space, in bytes
  fn get_free_space(&self) -> FileSystemResult<u64>;

  /// Return the (block,offset) address for the given absolute file location
  /// (bytes since start of file).
  fn block_and_offset_for_file_location(&self, file: Self::FID, addr: FileAddr) -> FileSystemResult<(BlockNumber,usize)>;

  /// Return the filesystem entry following the given filesystem entry in
  /// the directory listing
  fn get_next_file_in_directory(&self, parent: Option<Self::FID>, previous: Option<Self::FID>) -> Option<Self::FID>;
}

pub trait FileSystemWrite : FileSystemRead {
  /// Open or create the identified file with the options given.
  /// See the [`OpenOptions`] documentation for further details.
  ///
  /// [`OpenOptions`]: avrox_storage::fs::OpenOptions
  fn open<P: Into<Self::FID>>(&self, path: P, options: &OpenOptions) -> OxideResult<File<Self>,IoError> where Self: Sized;

  /// Create the given file, with zero length
  fn create_file(&self, file: Self::FID) -> OxideResult<(),IoError>;

  /// Write from the given buffer to the given file location.  The number
  /// of bytes written, and the new location at the end of the last write,
  /// are returned.
  ///
  /// This method does not guarantee to write all the bytes requested,
  /// you must pay attention to the number of bytes written returned.
  fn write_at_location(&self, file: Self::FID, block: BlockNumber, offset: usize, buf: &[u8])
                       -> FileSystemResult<(usize,(BlockNumber,usize))>;

  /// Truncate the given file so the location given points to the end of
  /// the file.  All remaining space is freed for re-allocation.
  fn truncate_at(&self, file: Self::FID, block: BlockNumber, offset: usize) -> FileSystemResult<()>;

  /// Delete the given file.  The directory entry and all space associated
  /// with the file is freed for reallocation.
  fn remove_file(&self, file: Self::FID) -> FileSystemResult<()>;

  /// Sync all data to the underlying storage
  fn sync(&self) -> FileSystemResult<()>;
}


// Code ======================================================================
impl BlockNumber {
  pub const INVALID : BlockNumber = BlockNumber(0);
  pub const ZERO    : BlockNumber = BlockNumber(0);

  pub(crate) fn as_bytes(&self) -> [u8; 2] {
    [
      (self.0 >> 8) as u8,
      self.0 as u8
    ]
  }

  pub(crate) fn from_bytes(buf: &[u8]) -> BlockNumber {
    BlockNumber(((buf[0] as u16) << 8) | buf[1] as u16)
  }

  pub(crate) fn offset_addr(&self) -> SAddr {
    BLOCKSIZE as SAddr * self.0 as SAddr
  }

  pub fn value(&self) -> u16 {
    self.0
  }

  pub fn is_invalid(&self) -> bool {
    self.0 == 0
  }

  pub fn is_valid(&self) -> bool {
    !self.is_invalid()
  }
}

impl AddAssign<u16> for BlockNumber {
  fn add_assign(&mut self, rhs: u16) {
    self.0 += rhs
  }
}

impl SubAssign<u16> for BlockNumber {
  fn sub_assign(&mut self, rhs: u16) {
    self.0 -= rhs
  }
}

impl From<u32> for BlockNumber {
  fn from(val: u32) -> Self {
    Self(val as u16)
  }
}

// Tests =====================================================================