Slide 1

Slide 1 text

バイト列から整数を得る fukuoka.rs vol.3 pixiv Inc. 森内 建太 2019.4.23

Slide 2

Slide 2 text

2 自己紹介 ● 主にフロントエンドエンジニア ● Ruby on Rails、iOS アプリ開発もやる ● ECMAScript とか DOM API とかの仕様まわりを調 べるのが好き 福岡オフィスエンジニア

Slide 3

Slide 3 text

におけるバイト列 3

Slide 4

Slide 4 text

4 ● &[u8], &mut [u8], Vec でバイト列を扱う ○ 読み込む場合は &mut [u8] にしてから Read の実装を使う ○ Rustでバイト列を扱う時のtips - κeenのHappy Hacκing Blog https://keens.github.io/blog/2016/12/01/rustdebaitoretsuwoatsukautokinotips/ におけるバイト列

Slide 5

Slide 5 text

バイト列の読み込み 5 use std::io::{Read as _, Result}; fn main() -> Result<()> { let mut bytes: &[u8] = &[0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc]; let mut buf = [0; 4]; bytes.read_exact(&mut buf)?; println!("{:#x?}", buf); // [0x12, 0x34, 0x56, 0x78] assert_eq!(bytes.len(), 2); Ok(()) }

Slide 6

Slide 6 text

マルチバイト型への変換 6

Slide 7

Slide 7 text

7 ● &mut [u8] でバイト列を扱うため、u32 にしたい場合は変換しなければならない ○ std::mem::transmute を使って [u8; 4] を u32 にする マルチバイト型への変換 use std::io::{Read as _, Result}; use std::mem::transmute; fn main() -> Result<()> { let mut bytes: &[u8] = &[0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc]; let mut buf = [0; 4]; bytes.read_exact(&mut buf)?; let val = unsafe { transmute::<[u8; 4], u32>(buf) }; println!("{}", val); Ok(()) }

Slide 8

Slide 8 text

ところで 8 use std::io::{Read as _, Result}; use std::mem::transmute; fn main() -> Result<()> { let mut bytes: &[u8] = &[0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc]; let mut buf = [0; 4]; bytes.read_exact(&mut buf)?; let val = unsafe { transmute::<[u8; 4], u32>(buf) }; assert_eq!(val, 0x78563412); // ??? Ok(()) }

Slide 9

Slide 9 text

9 ● CPU によってマルチバイトな型において メモリに置かれる順序が異なる ○ ビッグエンディアンなら 0x12345678 ○ リトルエンディアンなら 0x78563412 ● つまりビッグエンディアンなバイト列をリトルエンディアン環境で読み込むなら 順序を並び替える必要がある ○ Rust の条件付きコンパイルで環境のエンディアンを判定できる ○ cfg(target_endianess = “little”) エンディアン

Slide 10

Slide 10 text

エンディアンの変換 10 let raw = unsafe { transmute::<[u8; 4], u32>([0x12, 0x34, 0x56, 0x78]) }; let val = if cfg!(target_endianess = "little") { raw.swap_bytes() } else { raw }; assert_eq!(val, 0x12345678); let raw = unsafe { transmute::<[u8; 4], u32>([0x12, 0x34, 0x56, 0x78]) }; let val = u32::from_be(raw); assert_eq!(val, 0x12345678);

Slide 11

Slide 11 text

まとめると 11 use std::io::{Read as _, Result}; use std::mem::transmute; fn main() -> Result<()> { let mut bytes: &[u8] = &[0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc]; let mut buf = [0; 4]; bytes.read_exact(&mut buf)?; let raw = unsafe { transmute::<[u8; 4], u32>(buf) }; let val = u32::from_be(raw); assert_eq!(val, 0x12345678); Ok(()) }

Slide 12

Slide 12 text

12

Slide 13

Slide 13 text

13 ● 今まで説明したやつをまるまるやってくれる便利なクレート ○ use byteorder::ReadBytesExt で Read を拡張する とは use std::io::Result; use byteorder::{BigEndian, ReadBytesExt as _}; fn main() -> Result<()> { let mut bytes: &[u8] = &[0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc]; let val = bytes.read_u32::()?; assert_eq!(val, 0x12345678); Ok(()) }

Slide 14

Slide 14 text

14 ● Rust でバイト列を読み込むには &mut [u8] の Read を使う ○ Rustでバイト列を扱う時のtips - κeenのHappy Hacκing Blog https://keens.github.io/blog/2016/12/01/rustdebaitoretsuwoatsukautokinotips/ ● マルチバイト型への変換時にエンディアンを考慮する必要がある ● byteorder クレートが便利! まとめ