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
138
139
140
141
142
143
144
145
146
147
148
//! ストレージやNVMのブロック(読み書きの際の最小単位)関連の構成要素.
use {ErrorKind, Result};

pub(crate) use self::aligned_bytes::AlignedBytes;

mod aligned_bytes;

/// [`Storage`]や[`NonVolatileMemory`]のブロックサイズを表現するための構造体.
///
/// "ブロック"は、I/Oの最小単位であり、読み書き対象の領域およびその際に使用するバッファは、
/// `BlockSize`によって指定された境界にアライメントされている必要がある.
///
/// 指定されたサイズのブロック境界にアライメントを行うための補助メソッド群も提供している.
///
/// [`Storage`]: ../storage/struct.Storage.html
/// [`NonVolatileMemory`]: ../nvm/trait.NonVolatileMemory.html
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BlockSize(u16);
impl BlockSize {
    /// 許容されるブロックサイズの最小値.
    ///
    /// 全てのブロックサイズは、この値の倍数である必要がある.
    ///
    /// また`BlockSize::default()`で使われる値でもある.
    pub const MIN: u16 = 512;

    /// 許容可能な最小のブロックサイズを持つ`BlockSize`インスタンスを返す.
    ///
    /// # Examples
    ///
    /// ```
    /// use cannyls::block::BlockSize;
    ///
    /// assert_eq!(BlockSize::min().as_u16(), BlockSize::MIN);
    /// ```
    pub fn min() -> Self {
        BlockSize(Self::MIN)
    }

    /// 指定された値のブロックサイズを表現する`BlockSize`インスタンスを生成する.
    ///
    /// # Errors
    ///
    /// 以下の場合には、種類が`ErrorKind::InvalidInput`のエラーが返される:
    ///
    /// - `block_size`が`BlockSize::MIN`未満
    /// - `block_size`が`BlockSize::MIN`の倍数ではない
    ///
    /// # Examples
    ///
    /// ```
    /// use cannyls::ErrorKind;
    /// use cannyls::block::BlockSize;
    ///
    /// assert_eq!(BlockSize::new(512).ok().map(|a| a.as_u16()), Some(512));
    /// assert_eq!(BlockSize::new(4096).ok().map(|a| a.as_u16()), Some(4096));
    ///
    /// assert_eq!(BlockSize::new(256).err().map(|e| *e.kind()), Some(ErrorKind::InvalidInput));
    /// assert_eq!(BlockSize::new(513).err().map(|e| *e.kind()), Some(ErrorKind::InvalidInput));
    /// ```
    #[allow(clippy::new_ret_no_self)]
    pub fn new(block_size: u16) -> Result<Self> {
        track_assert!(block_size >= Self::MIN, ErrorKind::InvalidInput);
        track_assert_eq!(block_size % Self::MIN, 0, ErrorKind::InvalidInput);
        Ok(BlockSize(block_size))
    }

    /// 指定位置より後方の最初のブロックサイズ位置を返す.
    ///
    /// # Examples
    ///
    /// ```
    /// use cannyls::block::BlockSize;
    ///
    /// let block_size = BlockSize::new(512).unwrap();
    /// assert_eq!(block_size.ceil_align(0), 0);
    /// assert_eq!(block_size.ceil_align(1), 512);
    /// assert_eq!(block_size.ceil_align(512), 512);
    /// ```
    pub fn ceil_align(self, position: u64) -> u64 {
        let block_size = u64::from(self.0);
        (position + block_size - 1) / block_size * block_size
    }

    /// 指定位置より前方の最初のブロックサイズ位置を返す.
    ///
    /// # Examples
    ///
    /// ```
    /// use cannyls::block::BlockSize;
    ///
    /// let block_size = BlockSize::new(512).unwrap();
    /// assert_eq!(block_size.floor_align(0), 0);
    /// assert_eq!(block_size.floor_align(1), 0);
    /// assert_eq!(block_size.floor_align(512), 512);
    /// ```
    pub fn floor_align(self, position: u64) -> u64 {
        let block_size = u64::from(self.0);
        (position / block_size) * block_size
    }

    /// ブロックサイズ値を`u16`に変換して返す.
    pub fn as_u16(self) -> u16 {
        self.0
    }

    /// このブロックサイズが`other`を包含しているかを確認する.
    ///
    /// "包含している"とは「`self`のブロックサイズが`other`のブロックサイズの倍数」であることを意味する.
    ///
    /// # Examples
    ///
    /// ```
    /// use cannyls::block::BlockSize;
    ///
    /// let block_size = BlockSize::new(2048).unwrap();
    /// assert!(block_size.contains(BlockSize::new(512).unwrap()));
    /// assert!(block_size.contains(BlockSize::new(1024).unwrap()));
    /// assert!(!block_size.contains(BlockSize::new(1536).unwrap()));
    /// ```
    pub fn contains(self, other: BlockSize) -> bool {
        self.0 >= other.0 && self.0 % other.0 == 0
    }

    /// 指定位置がブロックサイズ境界に沿っているかどうかを判定する.
    ///
    /// # Examples
    ///
    /// ```
    /// use cannyls::block::BlockSize;
    ///
    /// let block_size = BlockSize::new(512).unwrap();
    /// assert!(block_size.is_aligned(0));
    /// assert!(block_size.is_aligned(512));
    /// assert!(block_size.is_aligned(1024));
    ///
    /// assert!(!block_size.is_aligned(511));
    /// assert!(!block_size.is_aligned(513));
    /// ```
    pub fn is_aligned(self, position: u64) -> bool {
        (position % u64::from(self.0)) == 0
    }
}
impl Default for BlockSize {
    fn default() -> Self {
        Self::min()
    }
}