m13o.net

2020-12-05 Sat 19:00
Rustでバイナリを読む その5   AdventCalendar2020 Rust programming

前回まで作ってきたBufferReaderですが, 実はここまで全て一つのlib.rsに記述してきていました. プロジェクトの構成としては以下のような形です.

binary_reader
├── Cargo.lock
├── Cargo.toml
└── src
    └── lib.rs

これくらいの量であれば, 別に良いのかもしれませんが, 関連するとはいえ複数の要素が一つのファイル内に記述されているというのは少々気になるので, 今回はcrateの整理をしてみようと思います.

lib.rsの中に記述されている主な要素はBinaryReaderとBufferOverRunErrorの2つです. これらをそれぞれ異なるrsファイルに異動させます.

まず, error.rsというファイルをsrcディレクトリの中に作ります. そして, BufferOverRunError structとその実装部分をテストも含めて全てerror.rsに異動させます.

// error.rs
use std::error::Error;
use std::fmt;
use std::fmt::{Display, Formatter, Debug};

#[derive(Eq, PartialEq)]
struct BufferOverRunError {
    buffer_size: usize,
    index: usize,
    read_size: usize
}

impl Display for BufferOverRunError {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "読み出し中にエラーが発生しました")
    }
}

impl Debug for BufferOverRunError {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f,
               "バッファオーバーラン バッファサイズ {}, 読み出し位置 {}, 読み出しバイト数 {}",
               self.buffer_size,
               self.index,
               self.read_size)
    }
}

impl Error for BufferOverRunError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn buffer_over_run_error_display() {
        let error = BufferOverRunError{
            buffer_size: 4,
            index: 3,
            read_size: 42
        };
        assert_eq!(format!("{}", error), "読み出し中にエラーが発生しました");
    }

    #[test]
    fn buffer_over_run_error_debug() {
        let error = BufferOverRunError{
            buffer_size: 4,
            index: 3,
            read_size: 42
        };
        assert_eq!(format!("{:?}", error), "バッファオーバーラン バッファサイズ 4, 読み出し位置 3, 読み出しバイト数 42");
    }

    #[test]
    fn buffer_over_run_error_source() {
        let error = BufferOverRunError {
            buffer_size: 4,
            index: 3,
            read_size: 42
        };
        assert!(error.source().is_none())
    }
}

次に, 同じようにbinary_reader.rsというファイルをsrcディレクトリの中に作り, BinaryReaderに関連する物を全てlib.rsから異動させます.

// binary_reader.rs
use std::convert::TryInto;

struct BinaryReader<'a> {
    buffer: &'a Vec<u8>,
    current_index: usize,
}

impl<'a> BinaryReader<'a> {
    fn new(buffer: &'a Vec<u8>) -> Self {
        BinaryReader {
            buffer,
            current_index: 0,
        }
    }

    fn read(&mut self, size: usize) -> Result<(&'a [u8], &'a [u8]), BufferOverRunError> {
        let index = self.current_index;

        let max_buffer_size = self.buffer.len();
        if index > max_buffer_size {
            return Err(BufferOverRunError {
                buffer_size: max_buffer_size,
                index,
                read_size: size
            });
        }

        self.current_index += size;
        if self.current_index > max_buffer_size {
            return Err(BufferOverRunError {
                buffer_size: max_buffer_size,
                index,
                read_size: size,
            });
        }

        Ok((
            &self.buffer[index..self.current_index],
            &self.buffer[self.current_index..],
        ))
    }

    fn read_u8(&mut self) -> Result<(u8, &'a [u8]), BufferOverRunError> {
        match self.read(1) {
            Ok((value, rest)) => Ok((value[0], rest)),
            Err(e) => Err(e),
        }
    }

    fn read_i8(&mut self) -> Result<(i8, &'a [u8]), BufferOverRunError> {
        match self.read(1) {
            Ok((value, rest)) => Ok((value[0] as i8, rest)),
            Err(e) => Err(e),
        }
    }

    fn read_le_u16(&mut self) -> Result<(u16, &'a [u8]), BufferOverRunError> {
        match self.read(2) {
            Ok((u16_bytes, rest)) => Ok((u16::from_le_bytes(u16_bytes.try_into().unwrap()), rest)),
            Err(e) => Err(e),
        }
    }

    fn read_le_i16(&mut self) -> Result<(i16, &'a [u8]), BufferOverRunError> {
        match self.read(2) {
            Ok((i16_bytes, rest)) => Ok((i16::from_le_bytes(i16_bytes.try_into().unwrap()), rest)),
            Err(e) => Err(e),
        }
    }

    fn read_le_u32(&mut self) -> Result<(u32, &'a [u8]), BufferOverRunError> {
        match self.read(4) {
            Ok((u32_bytes, rest)) => Ok((u32::from_le_bytes(u32_bytes.try_into().unwrap()), rest)),
            Err(e) => Err(e),
        }
    }

    fn read_le_i32(&mut self) -> Result<(i32, &'a [u8]), BufferOverRunError> {
        match self.read(4) {
            Ok((i32_bytes, rest)) => Ok((i32::from_le_bytes(i32_bytes.try_into().unwrap()), rest)),
            Err(e) => Err(e),
        }
    }

    fn read_le_u24(&mut self) -> Result<(u32, &'a [u8]), BufferOverRunError> {
        match self.read(3) {
            Ok((u24_bytes, rest)) => {
                let value =
                    (u24_bytes[2] as u32) << 16 | (u24_bytes[1] as u32) << 8 | u24_bytes[0] as u32;
                Ok((value, rest))
            }
            Err(e) => Err(e),
        }
    }

    fn read_le_i24(&mut self) -> Result<(i32, &'a [u8]), BufferOverRunError> {
        match self.read(3) {
            Ok((i24_bytes, rest)) => {
                let mut value =
                    (i24_bytes[2] as i32) << 16 | (i24_bytes[1] as i32) << 8 | i24_bytes[0] as i32;
                if i24_bytes[2] >= 0x80 {
                    value -= 0x1000000;
                }
                Ok((value, rest))
            }
            Err(e) => Err(e),
        }
    }

    fn read_le_f32(&mut self) -> Result<(f32, &'a [u8]), BufferOverRunError> {
        match self.read(4) {
            Ok((f32_bytes, rest)) => {
                let value = f32::from_le_bytes(f32_bytes.try_into().unwrap());
                Ok((value, rest))
            }
            Err(e) => Err(e),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn read_bytes() {
        let buffer = vec![0x02, 0x34, 0x58, 0xFF, 0x80, 0x99, 0x00, 0x42];
        let mut reader = BinaryReader::new(&buffer);
        let (values, rest) = reader.read(5).unwrap();
        assert_eq!(values.len(), 5);
        assert_eq!(values[0], 0x02);
        assert_eq!(values[1], 0x34);
        assert_eq!(values[2], 0x58);
        assert_eq!(values[3], 0xFF);
        assert_eq!(values[4], 0x80);
        assert_eq!(rest.len(), 3);
        assert_eq!(rest[0], 0x99);
        assert_eq!(rest[1], 0x00);
        assert_eq!(rest[2], 0x42);
    }

    #[test]
    fn read_zero_byte() {
        let buffer = vec![0x02, 0x34, 0x58, 0xFF];
        let mut reader = BinaryReader::new(&buffer);
        let (values, rest) = reader.read(0).unwrap();
        assert_eq!(values.len(), 0);
        assert_eq!(rest.len(), 4);
        assert_eq!(rest[0], 0x02);
        assert_eq!(rest[1], 0x34);
        assert_eq!(rest[2], 0x58);
        assert_eq!(rest[3], 0xFF);
    }

    #[test]
    fn read_empty_buffer() {
        let buffer = Vec::new();
        let mut reader = BinaryReader::new(&buffer);
        let result = reader.read(2);
        assert_eq!(
            result,
            Err(BufferOverRunError {
                buffer_size: 0,
                index: 0,
                read_size: 2,
            })
        );
    }

    #[test]
    fn read_buffer_overrun() {
        let buffer = vec![0xDE, 0xAD, 0xBE, 0xEF];
        let mut reader = BinaryReader::new(&buffer);
        let result = reader.read(5);
        assert_eq!(
            result,
            Err(BufferOverRunError {
                buffer_size: 4,
                index: 0,
                read_size: 5,
            })
        );
    }

    #[test]
    fn read_empty_buffer_and_zero_byte() {
        let buffer = Vec::new();
        let mut reader = BinaryReader::new(&buffer);
        let (values, rest) = reader.read(0).unwrap();
        assert_eq!(values.len(), 0);
        assert_eq!(rest.len(), 0);
    }

    #[test]
    fn read_u8() {
        let buffer = vec![0xFE, 0xFF, 0x01, 0x80];
        let mut reader = BinaryReader::new(&buffer);
        let (value, rest) = reader.read_u8().unwrap();
        assert_eq!(value, 254);
        assert_eq!(rest.len(), 3);
        assert_eq!(rest[0], 0xFF);
        assert_eq!(rest[1], 0x01);
        assert_eq!(rest[2], 0x80);
    }

    #[test]
    fn read_i8() {
        let buffer = vec![0xFE, 0xFF, 0x01, 0x80];
        let mut reader = BinaryReader::new(&buffer);
        let (value, rest) = reader.read_i8().unwrap();
        assert_eq!(value, -2);
        assert_eq!(rest.len(), 3);
        assert_eq!(rest[0], 0xFF);
        assert_eq!(rest[1], 0x01);
        assert_eq!(rest[2], 0x80);
    }

    #[test]
    fn read_le_u16() {
        let buffer = vec![0x80, 0xFF, 0x01, 0xFE];
        let mut reader = BinaryReader::new(&buffer);
        let (value, rest) = reader.read_le_u16().unwrap();
        assert_eq!(value, 65408);
        assert_eq!(rest.len(), 2);
        assert_eq!(rest[0], 0x01);
        assert_eq!(rest[1], 0xFE);
    }

    #[test]
    fn read_le_i16_signed_max() {
        let buffer = vec![0xFF, 0x7F, 0x01, 0xFE];
        let mut reader = BinaryReader::new(&buffer);
        let (value, rest) = reader.read_le_i16().unwrap();
        assert_eq!(value, 32767);
        assert_eq!(rest.len(), 2);
        assert_eq!(rest[0], 0x01);
        assert_eq!(rest[1], 0xFE);
    }

    #[test]
    fn read_le_i16_signed_min() {
        let buffer = vec![0x00, 0x80, 0x01, 0xFE];
        let mut reader = BinaryReader::new(&buffer);
        let (value, rest) = reader.read_le_i16().unwrap();
        assert_eq!(value, -32768);
        assert_eq!(rest.len(), 2);
        assert_eq!(rest[0], 0x01);
        assert_eq!(rest[1], 0xFE);
    }

    #[test]
    fn read_le_i16_minus_one() {
        let buffer = vec![0xFF, 0xFF, 0x01, 0xFE];
        let mut reader = BinaryReader::new(&buffer);
        let (value, rest) = reader.read_le_i16().unwrap();
        assert_eq!(value, -1);
        assert_eq!(rest.len(), 2);
        assert_eq!(rest[0], 0x01);
        assert_eq!(rest[1], 0xFE);
    }

    #[test]
    fn read_le_u32() {
        let buffer = vec![0x80, 0x00, 0xF0, 0xFF, 0x42];
        let mut reader = BinaryReader::new(&buffer);
        let (value, rest) = reader.read_le_u32().unwrap();
        assert_eq!(value, 4293918848);
        assert_eq!(rest.len(), 1);
        assert_eq!(rest[0], 0x42);
    }

    #[test]
    fn read_le_i32_signed_max() {
        let buffer = vec![0xFF, 0xFF, 0xFF, 0x7F, 0x42];
        let mut reader = BinaryReader::new(&buffer);
        let (value, rest) = reader.read_le_i32().unwrap();
        assert_eq!(value, 2147483647);
        assert_eq!(rest.len(), 1);
        assert_eq!(rest[0], 0x42);
    }

    #[test]
    fn read_le_i32_signed_min() {
        let buffer = vec![0x00, 0x00, 0x00, 0x80, 0x42];
        let mut reader = BinaryReader::new(&buffer);
        let (value, rest) = reader.read_le_i32().unwrap();
        assert_eq!(value, -2147483648);
        assert_eq!(rest.len(), 1);
        assert_eq!(rest[0], 0x42);
    }

    #[test]
    fn read_le_i32_minus_one() {
        let buffer = vec![0xFF, 0xFF, 0xFF, 0xFF, 0x42];
        let mut reader = BinaryReader::new(&buffer);
        let (value, rest) = reader.read_le_i32().unwrap();
        assert_eq!(value, -1);
        assert_eq!(rest.len(), 1);
        assert_eq!(rest[0], 0x42);
    }

    #[test]
    fn read_le_u24() {
        let buffer = vec![0x56, 0x34, 0x12, 0x80];
        let mut reader = BinaryReader::new(&buffer);
        let (value, rest) = reader.read_le_u24().unwrap();
        assert_eq!(value, 1193046);
        assert_eq!(rest.len(), 1);
        assert_eq!(rest[0], 128);
    }

    #[test]
    fn read_le_i24_signed_max() {
        let buffer = vec![0xFF, 0xFF, 0x7F, 0xFD];
        let mut reader = BinaryReader::new(&buffer);
        let (value, rest) = reader.read_le_i24().unwrap();
        assert_eq!(value, 8388607);
        assert_eq!(rest.len(), 1);
        assert_eq!(rest[0], 253);
    }

    #[test]
    fn read_le_i24_signed_min() {
        let buffer = vec![0x00, 0x00, 0x80, 0xFD];
        let mut reader = BinaryReader::new(&buffer);
        let (value, rest) = reader.read_le_i24().unwrap();
        assert_eq!(value, -8388608);
        assert_eq!(rest.len(), 1);
        assert_eq!(rest[0], 253);
    }

    #[test]
    fn read_le_i24_minus_one() {
        let buffer = vec![0xFF, 0xFF, 0xFF, 0xFD];
        let mut reader = BinaryReader::new(&buffer);
        let (value, rest) = reader.read_le_i24().unwrap();
        assert_eq!(value, -1);
        assert_eq!(rest.len(), 1);
        assert_eq!(rest[0], 253);
    }

    #[test]
    fn read_le_f32_one() {
        let buffer = vec![0x00, 0x00, 0x80, 0x3f, 0xFD];
        let mut reader = BinaryReader::new(&buffer);
        let (value, rest) = reader.read_le_f32().unwrap();
        assert_eq!(value, 1.0);
        assert_eq!(rest.len(), 1);
        assert_eq!(rest[0], 253);
    }

    #[test]
    fn read_le_f32_infinity() {
        let buffer = vec![0x00, 0x00, 0x80, 0x7F, 0xFD];
        let mut reader = BinaryReader::new(&buffer);
        let (value, rest) = reader.read_le_f32().unwrap();
        assert_eq!(value, f32::INFINITY);
        assert_eq!(rest.len(), 1);
        assert_eq!(rest[0], 253);
    }

    #[test]
    fn read_le_f32_neg_infinity() {
        let buffer = vec![0x00, 0x00, 0x80, 0xFF, 0xFD];
        let mut reader = BinaryReader::new(&buffer);
        let (value, rest) = reader.read_le_f32().unwrap();
        assert_eq!(value, f32::NEG_INFINITY);
        assert_eq!(rest.len(), 1);
        assert_eq!(rest[0], 253);
    }

    #[test]
    fn read_le_f32_nan() {
        let buffer = vec![0x01, 0x00, 0x80, 0x7F, 0xFD];
        let mut reader = BinaryReader::new(&buffer);
        let (value, rest) = reader.read_le_f32().unwrap();
        assert!(value.is_nan());
        assert_eq!(rest.len(), 1);
        assert_eq!(rest[0], 253);
    }

    #[test]
    fn read_le_u32_not_enough_byte_count() {
        let buffer = vec![0x80, 0x00, 0xF0];
        let mut reader = BinaryReader::new(&buffer);
        let result = reader.read_le_u32();
        assert_eq!(
            result,
            Err(BufferOverRunError {
                buffer_size: 3,
                index: 0,
                read_size: 4
            })
        );
    }
}

全文載せるとそれだけでこの記事が長くなる事に気付きましたがこのまま参りましょう.

さて, この状態でコンパイルしてみると, BinaryReaderで使われているBufferOverRunErrorがみつからないというエラーが発生します. これはRustが同じcrateの中にある要素であったとしても, ファイルが異なるとそのままでは不可視の状態で扱うようになっている事に起因します. なので, BinaryReaderからBufferOverRunErrorを見えるようにしなければなりません. そのためには, まず, BufferOverRunError structとその各要素に対する可視性をどのレベルにするかを決める必要があります.

Rustには各要素の公開範囲を規定するにあたり, 以下のような可視性の範囲を指定できます.

  • pub: 外部に公開する
  • pub(crate): crate内に公開
  • pub(in [path]): [path]に指定されたmoduleに対してのみ公開
  • pub(super): 親モジュールに公開
  • pub(self): 自身が所属するmoduleにのみ公開(デフォルト)

    BufferOverRunError structそのものは, 最終的にはこのcrate外からも見えるようにした方が良さそうです. ただし, その個別メンバーフィールドに関してはcrate内に閉じておいても良いと思われます. 各メソッドに関しては大元のtraitの可視性に依存するので何かを指定する必要はありません. という処からBufferOverRunErrorの可視性指定は以下のような形にしてみました.

    pub struct BufferOverRunError {
        pub(crate) buffer_size: usize,
        pub(crate) index: usize,
        pub(crate) read_size: usize
    }
    

これでBufferOverRunErrorを外部に公開できるようにはなりました. が, これだけではコンパイルは通りません. binary_reader.rsの最初の行に次の一文を追記します.

use crate::error::BufferOverRunError;

こうする事で, binary_reader.rsに定義, 実装されるstructやメソッドなどからもBufferOverRunErrorが見えるようになり, コンパイル, テスト共に通るようになりました.

BinaryReaderに関する公開範囲についても設定しておきましょう.

まず, BinaryReader structそのものは他のcrateからも利用してもらいたいのでpub, ただしメンバーフィールドは見えて欲しくないので外部からは不可視としてデフォルトのままにします. 各メソッドに関しては, 全て外部から使って欲しいのでpubとします.

pub struct BinaryReader<'a> {
    buffer: &'a Vec<u8>,
    current_index: usize,
}

impl<'a> BinaryReader<'a> {
    pub fn new(buffer: &'a Vec<u8>) -> Self {
        // ...
    }

    pub fn read(&mut self, size: usize) -> Result<(&'a [u8], &'a [u8]), BufferOverRunError> {
        // ...
    }

    pub fn read_u8(&mut self) -> Result<(u8, &'a [u8]), BufferOverRunError> {
        // ...
    }

    pub fn read_i8(&mut self) -> Result<(i8, &'a [u8]), BufferOverRunError> {
        // ...
    }

    pub fn read_le_u16(&mut self) -> Result<(u16, &'a [u8]), BufferOverRunError> {
        // ...
    }

    pub fn read_le_i16(&mut self) -> Result<(i16, &'a [u8]), BufferOverRunError> {
        // ...
    }

    pub fn read_le_u32(&mut self) -> Result<(u32, &'a [u8]), BufferOverRunError> {
        // ...
    }

    pub fn read_le_i32(&mut self) -> Result<(i32, &'a [u8]), BufferOverRunError> {
        // ...
    }

    pub fn read_le_u24(&mut self) -> Result<(u32, &'a [u8]), BufferOverRunError> {
        // ...
    }

    pub fn read_le_i24(&mut self) -> Result<(i32, &'a [u8]), BufferOverRunError> {
        // ...
    }

    pub fn read_le_f32(&mut self) -> Result<(f32, &'a [u8]), BufferOverRunError> {
        // ...
}

最後に, lib.rsですが, ここにはこのcrate内のモジュールについて記述する事とします. 今作業しているbinary_rader crateには, binary_readerとerrorという2つのモジュールがあります. これらは共にこのcrate外から利用して欲しいものですので, pub可視性を持ったモジュールとして設定します.

// lib.rs
pub mod binary_reader;
pub mod error;

これでこのbinary_readerというcrateを外部から使ってもらうための準備が整いましたので, 次回以降, 別のcrateからこのbinary_reader crateを利用して何かしらのバイナリデータを読み取る物を実装してみようと思います.