ASCII

[Rust] byte casting 본문

Language/Rust

[Rust] byte casting

규바보 2024. 6. 11. 00:15

추가적인 패키지 없이 pcap 파싱 도전의 첫 관문은 어떻게 header를 파싱하는가였다.

일반적인 경우 use byteorder::{BigEndian, ByteOrder, LittleEndian};에서 정의된 read_u32와 같은 함수를 사용하는 것이다.

u8 타입의 slice 형태로 주어진 값을 이용해서 더 큰 크기의 타입을 만드는 것은 단순한 비트 연산이나 반복으로 정의할 수 있을 것이라 판단하여, 간단히 작성해보았다.

fn convert_u8_to_u32(src: &[u8]) -> u32 {
    // littel endian
    let mut dst: u32 = 0;

    for i in 0..4 {
        dst = dst << 4;
        dst = dst + ((src[i] as u32));
    }

    return dst;
}

작성 후 내부적으로 사용한 mut 키워드나 예외의 부재 등 Rust가 아닌 Rust로 작성한 C언어라는 느낌을 받고 byteorder에서 작성된 소스를 추가로 분석하여, 보완해보고자 하였다.

/// Reads unsigned 32-bit integer from a stream of bytes.
pub fn read_u32(data: &[u8], endianness: ByteOrder) -> EndiannessResult<u32> {
    if data.len() < 4 {
        Err(EndiannessError::ShortSlice)
    } else {
        match endianness {
            ByteOrder::BigEndian => {
                Ok(((data[0] as u32) << 24) + ((data[1] as u32) << 16) + ((data[2] as u32) << 8) +
                   (data[3] as u32))
            }
            ByteOrder::LittleEndian => {
                Ok(((data[3] as u32) << 24) + ((data[2] as u32) << 16) + ((data[1] as u32) << 8) +
                   (data[0] as u32))
            }
        }
    }
}

전달 받은 slice의 길이 체크와 엔디안에 따른 Result 타입 반환 등을 확인할 수 있었다.
추가적으로 단순 연산에 반복을 사용하지 않고 단순 연산결과 반환을 이용하여, mut 사용을 최소화 하는 것의 필요성을 확인할 수 있었다.

fn convert_u8_to_u32(src: &[u8; 4]) -> Option<u32> {
    if src.len() < 4 {
        return None;
    }
    return Some(((src[0] as u32) << 12) + ((src[1] as u32) << 8) + ((src[2] as u32) << 4) + (src[3] as u32));
}

 

Comments