//! Bounded primitives shared by the generated UDP packet codecs.

use crate::packets::{Header, Packet};
use crate::{Error, Helpers, PacketFrequency};
use libremetaverse_types::{Quaternion, UUID, Vector3, Vector3d, Vector4};

/// A generous protocol safety ceiling. UDP packets normally remain below the
/// 1200-byte MTU, but callers may serialize an unsplit packet for diagnostics.
pub(crate) const MAX_PACKET_BYTES: usize = 16 * 1024 * 1024;

const HEADER_PREFIX_BYTES: usize = 6;

const fn parse(position: usize, context: &'static str) -> Error {
    Error::Parse { position, context }
}

#[derive(Debug)]
pub(crate) struct WireReader<'a> {
    bytes: &'a [u8],
    position: usize,
    end: usize,
}

impl<'a> WireReader<'a> {
    pub(crate) fn new(bytes: &'a [u8], position: usize, end: usize) -> Result<Self, Error> {
        if bytes.len() > MAX_PACKET_BYTES || position > end || end > bytes.len() {
            return Err(parse(position.min(bytes.len()), "packet bounds"));
        }
        Ok(Self {
            bytes,
            position,
            end,
        })
    }

    fn take(&mut self, length: usize, context: &'static str) -> Result<&'a [u8], Error> {
        let end = self
            .position
            .checked_add(length)
            .ok_or_else(|| parse(self.position, context))?;
        if end > self.end {
            return Err(parse(self.position, context));
        }
        let output = &self.bytes[self.position..end];
        self.position = end;
        Ok(output)
    }

    pub(crate) fn finish(self, position: &mut i32) -> Result<(), Error> {
        if self.position != self.end {
            return Err(parse(self.position, "trailing packet payload"));
        }
        *position = i32::try_from(self.position).map_err(|_| Error::Argument)?;
        Ok(())
    }

    pub(crate) fn commit_position(&self, position: &mut i32) -> Result<(), Error> {
        *position = i32::try_from(self.position).map_err(|_| Error::Argument)?;
        Ok(())
    }

    pub(crate) fn read_u8(&mut self) -> Result<u8, Error> {
        Ok(self.take(1, "truncated u8")?[0])
    }

    pub(crate) fn read_i8(&mut self) -> Result<i8, Error> {
        Ok(self.read_u8()?.cast_signed())
    }

    pub(crate) fn read_u16_le(&mut self) -> Result<u16, Error> {
        Ok(u16::from_le_bytes(self.read_array("truncated u16")?))
    }

    pub(crate) fn read_u16_be(&mut self) -> Result<u16, Error> {
        Ok(u16::from_be_bytes(self.read_array("truncated IP port")?))
    }

    pub(crate) fn read_i16_le(&mut self) -> Result<i16, Error> {
        Ok(i16::from_le_bytes(self.read_array("truncated i16")?))
    }

    pub(crate) fn read_u32_le(&mut self) -> Result<u32, Error> {
        Ok(u32::from_le_bytes(self.read_array("truncated u32")?))
    }

    pub(crate) fn read_i32_le(&mut self) -> Result<i32, Error> {
        Ok(i32::from_le_bytes(self.read_array("truncated i32")?))
    }

    pub(crate) fn read_u64_le(&mut self) -> Result<u64, Error> {
        Ok(u64::from_le_bytes(self.read_array("truncated u64")?))
    }

    pub(crate) fn read_f32_le(&mut self) -> Result<f32, Error> {
        Ok(f32::from_le_bytes(self.read_array("truncated f32")?))
    }

    pub(crate) fn read_f64_le(&mut self) -> Result<f64, Error> {
        Ok(f64::from_le_bytes(self.read_array("truncated f64")?))
    }

    fn read_array<const N: usize>(&mut self, context: &'static str) -> Result<[u8; N], Error> {
        self.take(N, context)?
            .try_into()
            .map_err(|_| parse(self.position, context))
    }

    pub(crate) fn read_bytes(
        &mut self,
        length: usize,
        context: &'static str,
    ) -> Result<Vec<u8>, Error> {
        let source = self.take(length, context)?;
        let mut output = Vec::new();
        output
            .try_reserve_exact(length)
            .map_err(|_| Error::InvalidOperation)?;
        output.extend_from_slice(source);
        Ok(output)
    }

    pub(crate) fn read_variable(&mut self, prefix_bytes: usize) -> Result<Vec<u8>, Error> {
        let length = match prefix_bytes {
            1 => usize::from(self.read_u8()?),
            2 => usize::from(self.read_u16_le()?),
            _ => return Err(Error::InvalidOperation),
        };
        self.read_bytes(length, "truncated variable packet field")
    }

    pub(crate) fn read_uuid(&mut self) -> Result<UUID, Error> {
        UUID::from_bytes(self.read_bytes(16, "truncated UUID")?, 0)
    }

    pub(crate) fn read_vector3(&mut self) -> Result<Vector3, Error> {
        Vector3::from_bytes(self.read_bytes(12, "truncated Vector3")?, 0)
    }

    pub(crate) fn read_vector3d(&mut self) -> Result<Vector3d, Error> {
        let mut value = Vector3d::zero();
        value.from_bytes(self.read_bytes(24, "truncated Vector3d")?, 0)?;
        Ok(value)
    }

    pub(crate) fn read_vector4(&mut self) -> Result<Vector4, Error> {
        let mut value = Vector4::zero();
        value.from_bytes(self.read_bytes(16, "truncated Vector4")?, 0)?;
        Ok(value)
    }

    pub(crate) fn read_quaternion(&mut self) -> Result<Quaternion, Error> {
        Quaternion::from_bytes(self.read_bytes(12, "truncated Quaternion")?, 0, true)
    }
}

#[derive(Debug, Default)]
pub(crate) struct WireWriter {
    bytes: Vec<u8>,
}

impl WireWriter {
    pub(crate) fn with_capacity(capacity: usize) -> Result<Self, Error> {
        if capacity > MAX_PACKET_BYTES {
            return Err(Error::Argument);
        }
        let mut bytes = Vec::new();
        bytes
            .try_reserve_exact(capacity)
            .map_err(|_| Error::InvalidOperation)?;
        Ok(Self { bytes })
    }

    pub(crate) fn into_inner(self) -> Vec<u8> {
        self.bytes
    }

    fn reserve(&mut self, additional: usize) -> Result<(), Error> {
        let length = self
            .bytes
            .len()
            .checked_add(additional)
            .ok_or(Error::Argument)?;
        if length > MAX_PACKET_BYTES {
            return Err(Error::Argument);
        }
        self.bytes
            .try_reserve(additional)
            .map_err(|_| Error::InvalidOperation)
    }

    pub(crate) fn write_bytes(&mut self, value: &[u8]) -> Result<(), Error> {
        self.reserve(value.len())?;
        self.bytes.extend_from_slice(value);
        Ok(())
    }

    pub(crate) fn write_u8(&mut self, value: u8) -> Result<(), Error> {
        self.write_bytes(&[value])
    }

    pub(crate) fn write_i8(&mut self, value: i8) -> Result<(), Error> {
        self.write_u8(value.cast_unsigned())
    }

    pub(crate) fn write_u16_le(&mut self, value: u16) -> Result<(), Error> {
        self.write_bytes(&value.to_le_bytes())
    }

    pub(crate) fn write_u16_be(&mut self, value: u16) -> Result<(), Error> {
        self.write_bytes(&value.to_be_bytes())
    }

    pub(crate) fn write_i16_le(&mut self, value: i16) -> Result<(), Error> {
        self.write_bytes(&value.to_le_bytes())
    }

    pub(crate) fn write_u32_le(&mut self, value: u32) -> Result<(), Error> {
        self.write_bytes(&value.to_le_bytes())
    }

    pub(crate) fn write_i32_le(&mut self, value: i32) -> Result<(), Error> {
        self.write_bytes(&value.to_le_bytes())
    }

    pub(crate) fn write_u64_le(&mut self, value: u64) -> Result<(), Error> {
        self.write_bytes(&value.to_le_bytes())
    }

    pub(crate) fn write_f32_le(&mut self, value: f32) -> Result<(), Error> {
        self.write_bytes(&value.to_le_bytes())
    }

    pub(crate) fn write_f64_le(&mut self, value: f64) -> Result<(), Error> {
        self.write_bytes(&value.to_le_bytes())
    }

    pub(crate) fn write_fixed(&mut self, value: &[u8], length: usize) -> Result<(), Error> {
        self.reserve(length)?;
        let copied = value.len().min(length);
        self.bytes.extend_from_slice(&value[..copied]);
        self.bytes.resize(self.bytes.len() + length - copied, 0);
        Ok(())
    }

    pub(crate) fn write_variable(
        &mut self,
        value: &[u8],
        prefix_bytes: usize,
    ) -> Result<(), Error> {
        match prefix_bytes {
            1 => self.write_u8(u8::try_from(value.len()).map_err(|_| Error::Argument)?)?,
            2 => self.write_u16_le(u16::try_from(value.len()).map_err(|_| Error::Argument)?)?,
            _ => return Err(Error::InvalidOperation),
        }
        self.write_bytes(value)
    }

    pub(crate) fn write_uuid(&mut self, value: &UUID) -> Result<(), Error> {
        let mut bytes = [0_u8; 16];
        value.to_bytes(&mut bytes, 0)?;
        self.write_bytes(&bytes)
    }

    pub(crate) fn write_vector3(&mut self, value: &Vector3) -> Result<(), Error> {
        let mut bytes = [0_u8; 12];
        value.to_bytes(&mut bytes, 0)?;
        self.write_bytes(&bytes)
    }

    pub(crate) fn write_vector3d(&mut self, value: &Vector3d) -> Result<(), Error> {
        let mut bytes = [0_u8; 24];
        value.to_bytes(&mut bytes, 0)?;
        self.write_bytes(&bytes)
    }

    pub(crate) fn write_vector4(&mut self, value: &Vector4) -> Result<(), Error> {
        let mut bytes = [0_u8; 16];
        value.to_bytes(&mut bytes, 0)?;
        self.write_bytes(&bytes)
    }

    pub(crate) fn write_quaternion(&mut self, value: &Quaternion) -> Result<(), Error> {
        let mut bytes = [0_u8; 12];
        value.to_bytes(&mut bytes, 0)?;
        self.write_bytes(&bytes)
    }
}

pub(crate) const fn header_length(frequency: PacketFrequency) -> usize {
    match frequency {
        PacketFrequency::High => 7,
        PacketFrequency::Medium => 8,
        PacketFrequency::Low => 10,
    }
}

pub(crate) fn clone_header(header: &Header) -> Header {
    Header {
        ack_list: header.ack_list.clone(),
        appended_acks: header.appended_acks,
        frequency: header.frequency,
        id: header.id,
        reliable: header.reliable,
        resent: header.resent,
        sequence: header.sequence,
        zerocoded: header.zerocoded,
    }
}

pub(crate) fn encode_header(header: &Header, writer: &mut WireWriter) -> Result<(), Error> {
    let mut flags = 0_u8;
    if header.reliable {
        flags |= Helpers::MSG_RELIABLE;
    }
    if header.resent {
        flags |= Helpers::MSG_RESENT;
    }
    if header.zerocoded {
        flags |= Helpers::MSG_ZEROCODED;
    }
    if header.appended_acks {
        flags |= Helpers::MSG_APPENDED_ACKS;
    }
    writer.write_u8(flags)?;
    writer.write_bytes(&header.sequence.to_be_bytes())?;
    writer.write_u8(0)?;
    match header.frequency {
        PacketFrequency::High => {
            writer.write_u8(u8::try_from(header.id).map_err(|_| Error::Argument)?)?;
        }
        PacketFrequency::Medium => {
            writer.write_u8(0xff)?;
            writer.write_u8(u8::try_from(header.id).map_err(|_| Error::Argument)?)?;
        }
        PacketFrequency::Low => {
            writer.write_bytes(&[0xff, 0xff])?;
            writer.write_bytes(&header.id.to_be_bytes())?;
        }
    }
    Ok(())
}

pub(crate) fn encode_acks(header: &Header, writer: &mut WireWriter) -> Result<(), Error> {
    let Some(acks) = &header.ack_list else {
        return Ok(());
    };
    if acks.is_empty() {
        return Ok(());
    }
    let count = u8::try_from(acks.len()).map_err(|_| Error::Argument)?;
    for ack in acks {
        writer.write_bytes(&ack.to_be_bytes())?;
    }
    writer.write_u8(count)
}

pub(crate) fn ack_length(header: &Header) -> Result<usize, Error> {
    match &header.ack_list {
        Some(acks) if !acks.is_empty() => {
            let _ = u8::try_from(acks.len()).map_err(|_| Error::Argument)?;
            acks.len()
                .checked_mul(4)
                .and_then(|length| length.checked_add(1))
                .ok_or(Error::Argument)
        }
        _ => Ok(0),
    }
}

pub(crate) fn encode_base_packet(packet: &Packet) -> Result<Vec<u8>, Error> {
    let capacity = header_length(packet.header.frequency)
        .checked_add(ack_length(&packet.header)?)
        .ok_or(Error::Argument)?;
    let mut writer = WireWriter::with_capacity(capacity)?;
    encode_header(&packet.header, &mut writer)?;
    encode_acks(&packet.header, &mut writer)?;
    Ok(writer.into_inner())
}

pub(crate) fn decode_header(
    bytes: &[u8],
    position: &mut i32,
    packet_end: &mut i32,
) -> Result<Header, Error> {
    if bytes.len() > MAX_PACKET_BYTES || *position < 0 || *packet_end < 0 {
        return Err(Error::Argument);
    }
    let start = usize::try_from(*position).map_err(|_| Error::Argument)?;
    let mut end = usize::try_from(*packet_end).map_err(|_| Error::Argument)?;
    if end >= bytes.len() || start > end || end - start + 1 < 7 {
        return Err(parse(start.min(bytes.len()), "truncated packet header"));
    }
    let flags = bytes[start];
    let appended_acks = flags & Helpers::MSG_APPENDED_ACKS != 0;
    let reliable = flags & Helpers::MSG_RELIABLE != 0;
    let resent = flags & Helpers::MSG_RESENT != 0;
    let zerocoded = flags & Helpers::MSG_ZEROCODED != 0;
    let sequence = u32::from_be_bytes(
        bytes
            .get(start + 1..start + 5)
            .ok_or_else(|| parse(start, "truncated packet sequence"))?
            .try_into()
            .map_err(|_| parse(start, "truncated packet sequence"))?,
    );

    let marker = *bytes
        .get(start + 6)
        .ok_or_else(|| parse(start, "truncated packet ID"))?;
    let (frequency, id, logical_header_length) = if marker == 0xff {
        let second = *bytes
            .get(start + 7)
            .ok_or_else(|| parse(start + 7, "truncated packet ID"))?;
        if second == 0xff {
            let high = *bytes
                .get(start + 8)
                .ok_or_else(|| parse(start + 8, "truncated packet ID"))?;
            let id = if zerocoded && high == 0 {
                let count = *bytes
                    .get(start + 9)
                    .ok_or_else(|| parse(start + 9, "truncated zerocoded packet ID"))?;
                if count == 0 {
                    return Err(parse(start + 9, "zero-length zerocode run"));
                }
                u16::from(
                    *bytes
                        .get(start + 10)
                        .ok_or_else(|| parse(start + 10, "truncated zerocoded packet ID"))?,
                )
            } else {
                let low = *bytes
                    .get(start + 9)
                    .ok_or_else(|| parse(start + 9, "truncated packet ID"))?;
                (u16::from(high) << 8) | u16::from(low)
            };
            (PacketFrequency::Low, id, 10)
        } else {
            (PacketFrequency::Medium, u16::from(second), 8)
        }
    } else {
        (PacketFrequency::High, u16::from(marker), 7)
    };

    let mut ack_list = None;
    if appended_acks {
        let count = usize::from(bytes[end]);
        let ack_bytes = count.checked_mul(4).ok_or(Error::Argument)?;
        let total = ack_bytes.checked_add(1).ok_or(Error::Argument)?;
        if total > end - start + 1 || end + 1 - total < start + logical_header_length {
            return Err(parse(end, "truncated appended ACK list"));
        }
        let ack_start = end + 1 - total;
        let mut acks = Vec::new();
        acks.try_reserve_exact(count)
            .map_err(|_| Error::InvalidOperation)?;
        for chunk in bytes[ack_start..ack_start + ack_bytes].chunks_exact(4) {
            acks.push(u32::from_be_bytes(
                chunk
                    .try_into()
                    .map_err(|_| parse(ack_start, "truncated appended ACK"))?,
            ));
        }
        ack_list = Some(acks);
        end = ack_start
            .checked_sub(1)
            .ok_or_else(|| parse(ack_start, "invalid appended ACK boundary"))?;
    }

    *position = i32::try_from(start + logical_header_length).map_err(|_| Error::Argument)?;
    *packet_end = i32::try_from(end).map_err(|_| Error::Argument)?;
    Ok(Header {
        ack_list,
        appended_acks,
        frequency,
        id,
        reliable,
        resent,
        sequence,
        zerocoded,
    })
}

pub(crate) fn header_to_slice(
    header: &Header,
    bytes: &mut [u8],
    position: &mut i32,
) -> Result<(), Error> {
    if *position < 0 {
        return Err(Error::Argument);
    }
    let start = usize::try_from(*position).map_err(|_| Error::Argument)?;
    let mut writer = WireWriter::with_capacity(header_length(header.frequency))?;
    encode_header(header, &mut writer)?;
    let encoded = writer.into_inner();
    let end = start.checked_add(encoded.len()).ok_or(Error::Argument)?;
    if end > bytes.len() {
        return Err(Error::IndexOutOfRange);
    }
    bytes[start..end].copy_from_slice(&encoded);
    *position = i32::try_from(end).map_err(|_| Error::Argument)?;
    Ok(())
}

pub(crate) fn acks_to_slice(
    header: &Header,
    bytes: &mut [u8],
    position: &mut i32,
) -> Result<(), Error> {
    if *position < 0 {
        return Err(Error::Argument);
    }
    let start = usize::try_from(*position).map_err(|_| Error::Argument)?;
    let mut writer = WireWriter::with_capacity(ack_length(header)?)?;
    encode_acks(header, &mut writer)?;
    let encoded = writer.into_inner();
    let end = start.checked_add(encoded.len()).ok_or(Error::Argument)?;
    if end > bytes.len() {
        return Err(Error::IndexOutOfRange);
    }
    bytes[start..end].copy_from_slice(&encoded);
    *position = i32::try_from(end).map_err(|_| Error::Argument)?;
    Ok(())
}

pub(crate) fn zero_decode(
    src: Option<&[u8]>,
    source_length: i32,
    dest: Option<&mut [u8]>,
) -> Result<i32, Error> {
    let src = src.ok_or(Error::ArgumentNull)?;
    let dest = dest.ok_or(Error::ArgumentNull)?;
    if source_length < i32::try_from(HEADER_PREFIX_BYTES).unwrap_or(i32::MAX) {
        return Err(Error::Argument);
    }
    let source_length = usize::try_from(source_length).map_err(|_| Error::Argument)?;
    if source_length > src.len() {
        return Err(Error::Argument);
    }
    if dest.len() < HEADER_PREFIX_BYTES {
        return Err(Error::IndexOutOfRange);
    }
    dest[..HEADER_PREFIX_BYTES].copy_from_slice(&src[..HEADER_PREFIX_BYTES]);
    let mut source = HEADER_PREFIX_BYTES;
    let mut target = HEADER_PREFIX_BYTES;
    while source < source_length {
        if src[source] == 0 {
            let trailing = source + 1 >= source_length;
            let count = if trailing {
                1
            } else {
                usize::from(src[source + 1])
            };
            let end = target.checked_add(count).ok_or(Error::IndexOutOfRange)?;
            if end > dest.len() {
                return Err(Error::IndexOutOfRange);
            }
            dest[target..end].fill(0);
            target = end;
            source += if trailing { 1 } else { 2 };
        } else {
            if target >= dest.len() {
                return Err(Error::IndexOutOfRange);
            }
            dest[target] = src[source];
            target += 1;
            source += 1;
        }
    }
    i32::try_from(target).map_err(|_| Error::Argument)
}

pub(crate) fn zero_encode(
    src: Option<&[u8]>,
    source_length: i32,
    dest: Option<&mut [u8]>,
) -> Result<i32, Error> {
    let src = src.ok_or(Error::ArgumentNull)?;
    let dest = dest.ok_or(Error::ArgumentNull)?;
    if source_length < i32::try_from(HEADER_PREFIX_BYTES).unwrap_or(i32::MAX) {
        return Err(Error::Argument);
    }
    let source_length = usize::try_from(source_length).map_err(|_| Error::Argument)?;
    if source_length > src.len() {
        return Err(Error::Argument);
    }
    if dest.len() < HEADER_PREFIX_BYTES {
        return Err(Error::IndexOutOfRange);
    }
    dest[..HEADER_PREFIX_BYTES].copy_from_slice(&src[..HEADER_PREFIX_BYTES]);

    let body_end = if src[0] & Helpers::MSG_APPENDED_ACKS == 0 {
        source_length
    } else {
        let count = usize::from(src[source_length - 1]);
        let suffix = count
            .checked_mul(4)
            .and_then(|length| length.checked_add(1))
            .ok_or(Error::Argument)?;
        if suffix > source_length - HEADER_PREFIX_BYTES {
            return Err(Error::Argument);
        }
        source_length - suffix
    };

    let mut source = HEADER_PREFIX_BYTES;
    let mut target = HEADER_PREFIX_BYTES;
    while source < body_end {
        if src[source] != 0 {
            if target >= dest.len() {
                return Err(Error::IndexOutOfRange);
            }
            dest[target] = src[source];
            target += 1;
            source += 1;
            continue;
        }
        let run_start = source;
        while source < body_end && src[source] == 0 {
            source += 1;
        }
        let mut remaining = source - run_start;
        while remaining != 0 {
            if target.checked_add(2).is_none_or(|end| end > dest.len()) {
                return Err(Error::IndexOutOfRange);
            }
            let count = remaining.min(usize::from(u8::MAX));
            dest[target] = 0;
            dest[target + 1] = u8::try_from(count).map_err(|_| Error::Argument)?;
            target += 2;
            remaining -= count;
        }
    }
    while source < source_length {
        if target >= dest.len() {
            return Err(Error::IndexOutOfRange);
        }
        dest[target] = src[source];
        target += 1;
        source += 1;
    }
    i32::try_from(target).map_err(|_| Error::Argument)
}

pub(crate) fn build_packet_from_bytes(
    packet_buffer: &[u8],
    packet_end: &mut i32,
    zero_buffer: &mut [u8],
) -> Result<Packet, Error> {
    let mut position = 0_i32;
    let header = decode_header(packet_buffer, &mut position, packet_end)?;
    let packet_type = crate::packet_catalog::packet_type(header.id, header.frequency);
    let descriptor = crate::packet_catalog::descriptor_by_type(packet_type)
        .ok_or_else(|| parse(usize::try_from(position).unwrap_or(0), "unknown packet ID"))?;
    if header.zerocoded {
        let decoded_length = zero_decode(
            Some(packet_buffer),
            packet_end.checked_add(1).ok_or(Error::Argument)?,
            Some(zero_buffer),
        )?;
        *packet_end = decoded_length.checked_sub(1).ok_or(Error::Argument)?;
        let decoded_length = usize::try_from(decoded_length).map_err(|_| Error::Argument)?;
        crate::packet_catalog::validate_packet_payload(
            packet_type,
            clone_header(&header),
            &zero_buffer[..decoded_length],
            &mut position,
            packet_end,
        )?;
    } else {
        crate::packet_catalog::validate_packet_payload(
            packet_type,
            clone_header(&header),
            packet_buffer,
            &mut position,
            packet_end,
        )?;
    }
    Ok(Packet {
        has_variable_blocks: descriptor.blocks.iter().any(|block| {
            matches!(
                block.repetition,
                crate::packet_catalog::BlockRepetition::Variable
            )
        }),
        header,
        type_: packet_type,
    })
}

#[cfg(test)]
mod tests {
    use super::{WireReader, WireWriter};

    #[test]
    fn scalar_endianness_matches_the_packet_template_protocol() {
        let mut writer = WireWriter::with_capacity(35).expect("writer");
        writer.write_u8(0x81).unwrap();
        writer.write_i8(-2).unwrap();
        writer.write_u16_le(0x1234).unwrap();
        writer.write_u16_be(0x5678).unwrap();
        writer.write_i16_le(-0x1234).unwrap();
        writer.write_u32_le(0x1122_3344).unwrap();
        writer.write_i32_le(-0x1122_3344).unwrap();
        writer.write_u64_le(0x0102_0304_0506_0708).unwrap();
        writer.write_f32_le(1.5).unwrap();
        writer.write_f64_le(-2.25).unwrap();
        let bytes = writer.into_inner();
        assert_eq!(
            bytes,
            [
                0x81, 0xfe, 0x34, 0x12, 0x56, 0x78, 0xcc, 0xed, 0x44, 0x33, 0x22, 0x11, 0xbc, 0xcc,
                0xdd, 0xee, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0xc0, 0x3f, 0, 0, 0, 0, 0, 0, 2, 0xc0,
            ]
        );

        let mut reader = WireReader::new(&bytes, 0, bytes.len()).unwrap();
        assert_eq!(reader.read_u8().unwrap(), 0x81);
        assert_eq!(reader.read_i8().unwrap(), -2);
        assert_eq!(reader.read_u16_le().unwrap(), 0x1234);
        assert_eq!(reader.read_u16_be().unwrap(), 0x5678);
        assert_eq!(reader.read_i16_le().unwrap(), -0x1234);
        assert_eq!(reader.read_u32_le().unwrap(), 0x1122_3344);
        assert_eq!(reader.read_i32_le().unwrap(), -0x1122_3344);
        assert_eq!(reader.read_u64_le().unwrap(), 0x0102_0304_0506_0708);
        assert_eq!(reader.read_f32_le().unwrap().to_bits(), 1.5_f32.to_bits());
        assert_eq!(
            reader.read_f64_le().unwrap().to_bits(),
            (-2.25_f64).to_bits()
        );
    }

    #[test]
    fn fixed_and_variable_fields_enforce_reference_lengths() {
        let mut writer = WireWriter::with_capacity(32).expect("writer");
        writer.write_fixed(&[1, 2], 4).unwrap();
        writer.write_fixed(&[3, 4, 5, 6], 2).unwrap();
        writer.write_variable(&[7, 8, 9], 1).unwrap();
        writer.write_variable(&[10, 11], 2).unwrap();
        let bytes = writer.into_inner();
        assert_eq!(bytes, [1, 2, 0, 0, 3, 4, 3, 7, 8, 9, 2, 0, 10, 11]);

        let mut reader = WireReader::new(&bytes, 0, bytes.len()).unwrap();
        assert_eq!(reader.read_bytes(4, "fixed").unwrap(), [1, 2, 0, 0]);
        assert_eq!(reader.read_bytes(2, "fixed").unwrap(), [3, 4]);
        assert_eq!(reader.read_variable(1).unwrap(), [7, 8, 9]);
        assert_eq!(reader.read_variable(2).unwrap(), [10, 11]);

        let mut oversized = WireWriter::with_capacity(300).unwrap();
        assert!(oversized.write_variable(&[0; 256], 1).is_err());
    }
}
