memorypack 1.2.1

A rust implementation of Cysharp/MemoryPack
Documentation
use crate::error::MemoryPackError;
use crate::state::MemoryPackReaderOptionalState;

use byteorder::{LittleEndian, ReadBytesExt};
use simdutf8::basic;
use std::io::{Cursor, Read, Seek, SeekFrom};

pub struct MemoryPackReader<'a> {
    pub(crate) cursor: Cursor<&'a [u8]>,
    pub optional_state: Option<MemoryPackReaderOptionalState>,
}

impl<'a> MemoryPackReader<'a> {
    pub fn new(data: &'a [u8]) -> Self {
        Self {
            cursor: Cursor::new(data),
            optional_state: None,
        }
    }

    pub fn new_with_state(data: &'a [u8]) -> Self {
        Self {
            cursor: Cursor::new(data),
            optional_state: Some(MemoryPackReaderOptionalState::new()),
        }
    }

    pub fn read_string(&mut self) -> Result<String, MemoryPackError> {
        let length_or_marker = self.read_i32()?;

        if length_or_marker == -1 {
            return Ok(String::new());
        }

        if length_or_marker < 0 {
            return self.read_utf8_string(!length_or_marker as usize);
        }

        let char_count = length_or_marker as usize;
        if char_count == 0 {
            return Ok(String::new());
        }

        self.read_utf16_string(char_count)
    }

    fn read_utf8_string(&mut self, byte_count: usize) -> Result<String, MemoryPackError> {
        let _char_length = self.read_i32()?;
        let slice = self.read_bytes(byte_count)?;

        Ok(basic::from_utf8(slice)
            .map_err(|_| MemoryPackError::InvalidUtf8)?
            .to_string())
    }

    #[inline]
    pub fn read_str(&mut self) -> Result<&'a str, MemoryPackError> {
        let length_or_marker = self.read_i32()?;

        if length_or_marker == -1 || length_or_marker == 0 {
            return Ok("");
        }

        if length_or_marker < 0 {
            return self.read_utf8_str(!length_or_marker as usize);
        }

        Err(MemoryPackError::Utf16NotSupportedForZeroCopy)
    }

    #[inline]
    fn read_utf8_str(&mut self, byte_count: usize) -> Result<&'a str, MemoryPackError> {
        let _char_length = self.read_i32()?;
        let slice = self.read_bytes(byte_count)?;
        
        let str_slice = basic::from_utf8(slice)
            .map_err(|_| MemoryPackError::InvalidUtf8)?;
        
        Ok(str_slice)
    }

    #[inline]
    pub fn read_bytes(&mut self, length: usize) -> Result<&'a [u8], MemoryPackError> {
        let pos = self.cursor.position() as usize;
        let buffer = self.cursor.get_ref();

        if pos + length > buffer.len() {
            return Err(MemoryPackError::UnexpectedEndOfBuffer);
        }

        let slice = &buffer[pos..pos + length];
        self.cursor.set_position((pos + length) as u64);
        Ok(slice)
    }

    #[inline]
    pub fn read_bytes_vec(&mut self, length: usize) -> Result<Vec<u8>, MemoryPackError> {
        Ok(self.read_bytes(length)?.to_vec())
    }

    #[inline]
    pub fn read_fixed_bytes<const N: usize>(&mut self) -> Result<[u8; N], MemoryPackError> {
        let mut buffer = [0u8; N];
        self.cursor.read_exact(&mut buffer)?;
        Ok(buffer)
    }

    #[inline]
    fn read_utf16_string(&mut self, char_count: usize) -> Result<String, MemoryPackError> {
        let byte_count = char_count * 2;
        let slice = self.read_bytes(byte_count)?;

        let mut result = String::with_capacity(char_count * 3);
        let mut i = 0;
        while i < byte_count {
            let code_unit = u16::from_le_bytes([slice[i], slice[i + 1]]);
            i += 2;

            if !(0xD800..=0xDFFF).contains(&code_unit) {
                if let Some(c) = char::from_u32(code_unit as u32) {
                    result.push(c);
                } else {
                    return Err(MemoryPackError::InvalidUtf8);
                }
            } else if (0xD800..=0xDBFF).contains(&code_unit) {
                if i + 2 > byte_count {
                    return Err(MemoryPackError::InvalidUtf8);
                }

                let low = u16::from_le_bytes([slice[i], slice[i + 1]]);

                i += 2;
                if !(0xDC00..=0xDFFF).contains(&low) {
                    return Err(MemoryPackError::InvalidUtf8);
                }

                let code_point = 0x10000 + ((code_unit as u32 - 0xD800) << 10) + (low as u32 - 0xDC00);

                if let Some(c) = char::from_u32(code_point) {
                    result.push(c);
                } else {
                    return Err(MemoryPackError::InvalidUtf8);
                }
            } else {
                return Err(MemoryPackError::InvalidUtf8);
            }
        }
        Ok(result)
    }

    #[inline(always)]
    pub fn read_bool(&mut self) -> Result<bool, MemoryPackError> {
        Ok(self.cursor.read_u8()? == 1)
    }

    #[inline(always)]
    pub fn read_i8(&mut self) -> Result<i8, MemoryPackError> {
        Ok(self.cursor.read_i8()?)
    }

    #[inline(always)]
    pub fn read_u8(&mut self) -> Result<u8, MemoryPackError> {
        Ok(self.cursor.read_u8()?)
    }

    #[inline(always)]
    pub fn read_i16(&mut self) -> Result<i16, MemoryPackError> {
        Ok(self.cursor.read_i16::<LittleEndian>()?)
    }

    #[inline(always)]
    pub fn read_u16(&mut self) -> Result<u16, MemoryPackError> {
        Ok(self.cursor.read_u16::<LittleEndian>()?)
    }

    #[inline(always)]
    pub fn read_i32(&mut self) -> Result<i32, MemoryPackError> {
        Ok(self.cursor.read_i32::<LittleEndian>()?)
    }

    #[inline(always)]
    pub fn read_u32(&mut self) -> Result<u32, MemoryPackError> {
        Ok(self.cursor.read_u32::<LittleEndian>()?)
    }

    #[inline(always)]
    pub fn read_i64(&mut self) -> Result<i64, MemoryPackError> {
        Ok(self.cursor.read_i64::<LittleEndian>()?)
    }

    #[inline(always)]
    pub fn read_u64(&mut self) -> Result<u64, MemoryPackError> {
        Ok(self.cursor.read_u64::<LittleEndian>()?)
    }

    #[inline(always)]
    pub fn read_f32(&mut self) -> Result<f32, MemoryPackError> {
        Ok(self.cursor.read_f32::<LittleEndian>()?)
    }

    #[inline(always)]
    pub fn read_f64(&mut self) -> Result<f64, MemoryPackError> {
        Ok(self.cursor.read_f64::<LittleEndian>()?)
    }

    #[inline(always)]
    pub fn read_i128(&mut self) -> Result<i128, MemoryPackError> {
        Ok(self.cursor.read_i128::<LittleEndian>()?)
    }

    #[inline(always)]
    pub fn read_u128(&mut self) -> Result<u128, MemoryPackError> {
        Ok(self.cursor.read_u128::<LittleEndian>()?)
    }

    #[inline(always)]
    pub fn read_char(&mut self) -> Result<char, MemoryPackError> {
        let code_unit = self.read_u16()?;

        if !(0xD800..=0xDFFF).contains(&code_unit) {
            return char::from_u32(code_unit as u32).ok_or({
                MemoryPackError::InvalidCodePoint
            });
        }

        Err(MemoryPackError::DeserializationError(
            "Surrogate code unit cannot be converted to Rust char".into(),
        ))
    }

    #[inline]
    pub fn skip(&mut self, n: usize) -> Result<(), MemoryPackError> {
        self.cursor.seek(SeekFrom::Current(n as i64))?;
        Ok(())
    }

    #[inline]
    pub fn rewind(&mut self, n: usize) -> Result<(), MemoryPackError> {
        self.cursor.seek(SeekFrom::Current(-(n as i64)))?;
        Ok(())
    }

    #[inline]
    pub fn position(&self) -> u64 {
        self.cursor.position()
    }
}