//! EBML (Extensible Binary Meta Language) write primitives for Matroska.
//!
//! EBML uses variable-length integers for element IDs and sizes.
//! This module provides low-level writers for constructing MKV files.

use std::io::{self, Read, Seek, SeekFrom, Write};

/// Write an EBML element ID (1-4 bytes, already encoded).
/// Element IDs are predefined constants — we write them verbatim.
pub fn write_id(w: &mut impl Write, id: u32) -> io::Result<()> {
    if id <= 0xFF {
        w.write_all(&[id as u8])
    } else if id <= 0xFFFF {
        w.write_all(&[(id >> 8) as u8, id as u8])
    } else if id <= 0xFF_FFFF {
        w.write_all(&[(id >> 16) as u8, (id >> 8) as u8, id as u8])
    } else {
        w.write_all(&[
            (id >> 24) as u8,
            (id >> 16) as u8,
            (id >> 8) as u8,
            id as u8,
        ])
    }
}

/// Write an EBML variable-length size (1-8 bytes).
/// Uses the EBML VINT encoding: leading bits indicate width.
pub fn write_size(w: &mut impl Write, size: u64) -> io::Result<()> {
    if size < 0x7F {
        w.write_all(&[(size as u8) | 0x80])
    } else if size < 0x3FFF {
        w.write_all(&[((size >> 8) as u8) | 0x40, size as u8])
    } else if size < 0x1F_FFFF {
        w.write_all(&[((size >> 16) as u8) | 0x20, (size >> 8) as u8, size as u8])
    } else if size < 0x0FFF_FFFF {
        w.write_all(&[
            ((size >> 24) as u8) | 0x10,
            (size >> 16) as u8,
            (size >> 8) as u8,
            size as u8,
        ])
    } else {
        // 8-byte size for large elements
        w.write_all(&[
            0x01,
            (size >> 48) as u8,
            (size >> 40) as u8,
            (size >> 32) as u8,
            (size >> 24) as u8,
            (size >> 16) as u8,
            (size >> 8) as u8,
            size as u8,
        ])
    }
}

/// Write an EBML "unknown size" marker (all 1s in VINT, 8 bytes).
/// Used for the Segment element when total size isn't known upfront.
pub fn write_unknown_size(w: &mut impl Write) -> io::Result<()> {
    w.write_all(&[0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])
}

/// Write a complete EBML unsigned integer element.
pub fn write_uint(w: &mut impl Write, id: u32, val: u64) -> io::Result<()> {
    write_id(w, id)?;
    if val <= 0xFF {
        write_size(w, 1)?;
        w.write_all(&[val as u8])
    } else if val <= 0xFFFF {
        write_size(w, 2)?;
        w.write_all(&[(val >> 8) as u8, val as u8])
    } else if val <= 0xFF_FFFF {
        write_size(w, 3)?;
        w.write_all(&[(val >> 16) as u8, (val >> 8) as u8, val as u8])
    } else if val <= 0xFFFF_FFFF {
        write_size(w, 4)?;
        w.write_all(&[
            (val >> 24) as u8,
            (val >> 16) as u8,
            (val >> 8) as u8,
            val as u8,
        ])
    } else {
        write_size(w, 8)?;
        w.write_all(&val.to_be_bytes())
    }
}

/// Write a complete EBML float element (8-byte double).
pub fn write_float(w: &mut impl Write, id: u32, val: f64) -> io::Result<()> {
    write_id(w, id)?;
    write_size(w, 8)?;
    w.write_all(&val.to_be_bytes())
}

/// Write a complete EBML UTF-8 string element.
pub fn write_string(w: &mut impl Write, id: u32, val: &str) -> io::Result<()> {
    write_id(w, id)?;
    write_size(w, val.len() as u64)?;
    w.write_all(val.as_bytes())
}

/// Write a complete EBML binary element.
pub fn write_binary(w: &mut impl Write, id: u32, data: &[u8]) -> io::Result<()> {
    write_id(w, id)?;
    write_size(w, data.len() as u64)?;
    w.write_all(data)
}

/// Start a master element: write ID + placeholder size.
/// Returns the file offset of the size field for later fixup.
pub fn start_master<W: Write + Seek>(w: &mut W, id: u32) -> io::Result<u64> {
    write_id(w, id)?;
    let size_pos = w.stream_position()?;
    // 8-byte size placeholder (will be overwritten by end_master)
    w.write_all(&[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])?;
    Ok(size_pos)
}

/// End a master element: seek back and write the actual size.
pub fn end_master<W: Write + Seek>(w: &mut W, size_pos: u64) -> io::Result<()> {
    let end_pos = w.stream_position()?;
    let data_size = end_pos - size_pos - 8; // subtract the 8-byte size field itself
    w.seek(SeekFrom::Start(size_pos))?;
    // Write as 8-byte VINT: 0x01 followed by 7 bytes of size
    w.write_all(&[
        0x01,
        (data_size >> 48) as u8,
        (data_size >> 40) as u8,
        (data_size >> 32) as u8,
        (data_size >> 24) as u8,
        (data_size >> 16) as u8,
        (data_size >> 8) as u8,
        data_size as u8,
    ])?;
    w.seek(SeekFrom::Start(end_pos))?;
    Ok(())
}

// ============================================================
// EBML Read primitives
// ============================================================

/// Read an EBML element ID. Returns (id, bytes_consumed).
pub fn read_id(r: &mut impl Read) -> io::Result<(u32, usize)> {
    let mut first = [0u8; 1];
    r.read_exact(&mut first)?;
    let b0 = first[0];

    if b0 & 0x80 != 0 {
        Ok((b0 as u32, 1))
    } else if b0 & 0x40 != 0 {
        let mut b = [0u8; 1];
        r.read_exact(&mut b)?;
        Ok((((b0 as u32) << 8) | b[0] as u32, 2))
    } else if b0 & 0x20 != 0 {
        let mut b = [0u8; 2];
        r.read_exact(&mut b)?;
        Ok((((b0 as u32) << 16) | (b[0] as u32) << 8 | b[1] as u32, 3))
    } else if b0 & 0x10 != 0 {
        let mut b = [0u8; 3];
        r.read_exact(&mut b)?;
        Ok((
            ((b0 as u32) << 24) | (b[0] as u32) << 16 | (b[1] as u32) << 8 | b[2] as u32,
            4,
        ))
    } else {
        Err(crate::error::Error::MkvInvalid.into())
    }
}

/// Read an EBML variable-length size. Returns (size, bytes_consumed).
/// Size of u64::MAX means "unknown size".
pub fn read_size(r: &mut impl Read) -> io::Result<(u64, usize)> {
    let mut first = [0u8; 1];
    r.read_exact(&mut first)?;
    let b0 = first[0];

    if b0 & 0x80 != 0 {
        let val = (b0 & 0x7F) as u64;
        if val == 0x7F {
            return Ok((u64::MAX, 1));
        } // unknown
        Ok((val, 1))
    } else if b0 & 0x40 != 0 {
        let mut b = [0u8; 1];
        r.read_exact(&mut b)?;
        let val = (((b0 & 0x3F) as u64) << 8) | b[0] as u64;
        if val == 0x3FFF {
            return Ok((u64::MAX, 2));
        }
        Ok((val, 2))
    } else if b0 & 0x20 != 0 {
        let mut b = [0u8; 2];
        r.read_exact(&mut b)?;
        let val = (((b0 & 0x1F) as u64) << 16) | (b[0] as u64) << 8 | b[1] as u64;
        if val == 0x1F_FFFF {
            return Ok((u64::MAX, 3));
        }
        Ok((val, 3))
    } else if b0 & 0x10 != 0 {
        let mut b = [0u8; 3];
        r.read_exact(&mut b)?;
        let val =
            (((b0 & 0x0F) as u64) << 24) | (b[0] as u64) << 16 | (b[1] as u64) << 8 | b[2] as u64;
        if val == 0x0FFF_FFFF {
            return Ok((u64::MAX, 4));
        }
        Ok((val, 4))
    } else if b0 & 0x08 != 0 {
        let mut b = [0u8; 4];
        r.read_exact(&mut b)?;
        let val = (((b0 & 0x07) as u64) << 32)
            | (b[0] as u64) << 24
            | (b[1] as u64) << 16
            | (b[2] as u64) << 8
            | b[3] as u64;
        Ok((val, 5))
    } else if b0 & 0x04 != 0 {
        let mut b = [0u8; 5];
        r.read_exact(&mut b)?;
        let val = (((b0 & 0x03) as u64) << 40)
            | (b[0] as u64) << 32
            | (b[1] as u64) << 24
            | (b[2] as u64) << 16
            | (b[3] as u64) << 8
            | b[4] as u64;
        Ok((val, 6))
    } else if b0 & 0x02 != 0 {
        let mut b = [0u8; 6];
        r.read_exact(&mut b)?;
        let val = (((b0 & 0x01) as u64) << 48)
            | (b[0] as u64) << 40
            | (b[1] as u64) << 32
            | (b[2] as u64) << 24
            | (b[3] as u64) << 16
            | (b[4] as u64) << 8
            | b[5] as u64;
        Ok((val, 7))
    } else {
        let mut b = [0u8; 7];
        r.read_exact(&mut b)?;
        let val = (b[0] as u64) << 48
            | (b[1] as u64) << 40
            | (b[2] as u64) << 32
            | (b[3] as u64) << 24
            | (b[4] as u64) << 16
            | (b[5] as u64) << 8
            | b[6] as u64;
        if val == 0x00FF_FFFF_FFFF_FFFF {
            return Ok((u64::MAX, 8));
        }
        Ok((val, 8))
    }
}

/// Read an EBML element header (ID + size). Returns (id, data_size, header_bytes).
pub fn read_element_header(r: &mut impl Read) -> io::Result<(u32, u64, usize)> {
    let (id, id_len) = read_id(r)?;
    let (size, size_len) = read_size(r)?;
    Ok((id, size, id_len + size_len))
}

/// Read an unsigned integer value of `len` bytes.
pub fn read_uint_val(r: &mut impl Read, len: usize) -> io::Result<u64> {
    // An EBML unsigned integer is at most 8 bytes. A malformed element
    // claiming `len > 8` would index past this stack buffer and panic
    // (DoS on untrusted input) — reject it at the source so every caller
    // is safe, not just the ones that pre-check.
    if len > 8 {
        return Err(crate::error::Error::MkvInvalid.into());
    }
    let mut buf = [0u8; 8];
    r.read_exact(&mut buf[..len])?;
    let mut val = 0u64;
    for &b in &buf[..len] {
        val = (val << 8) | b as u64;
    }
    Ok(val)
}

/// Read a float value. EBML floats are exactly 0, 4, or 8 bytes.
///
/// The previous `else` branch read a fixed 8 bytes for ANY non-4 length,
/// so a malformed element with `len > 8` left `len - 8` unconsumed bytes
/// (mis-read as the next EBML header → desync of the rest of the parent
/// element) and `len < 4` over-read. Consume exactly `len` bytes and
/// reject anything that isn't a valid float width.
pub fn read_float_val(r: &mut impl Read, len: usize) -> io::Result<f64> {
    match len {
        0 => Ok(0.0),
        4 => {
            let mut buf = [0u8; 4];
            r.read_exact(&mut buf)?;
            Ok(f32::from_be_bytes(buf) as f64)
        }
        8 => {
            let mut buf = [0u8; 8];
            r.read_exact(&mut buf)?;
            Ok(f64::from_be_bytes(buf))
        }
        _ => Err(crate::error::Error::MkvInvalid.into()),
    }
}

/// Read a UTF-8 string value of `len` bytes.
pub fn read_string_val(r: &mut impl Read, len: usize) -> io::Result<String> {
    let mut buf = read_exact_bounded(r, len)?;
    // Strip trailing nulls
    while buf.last() == Some(&0) {
        buf.pop();
    }
    String::from_utf8(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
}

/// Read binary data of `len` bytes.
pub fn read_binary_val(r: &mut impl Read, len: usize) -> io::Result<Vec<u8>> {
    read_exact_bounded(r, len)
}

/// Read exactly `len` bytes WITHOUT trusting `len` to size the allocation.
///
/// `vec![0u8; len]` on an attacker-controlled EBML size would allocate
/// gigabytes before the read fails. Instead we cap the reader to `len`
/// and grow the buffer as bytes actually arrive: a malformed element that
/// claims a huge length but supplies few bytes allocates only what it
/// delivers, then errors on the short read.
fn read_exact_bounded(r: &mut impl Read, len: usize) -> io::Result<Vec<u8>> {
    let mut buf = Vec::new();
    let got = r.take(len as u64).read_to_end(&mut buf)?;
    if got != len {
        return Err(io::ErrorKind::UnexpectedEof.into());
    }
    Ok(buf)
}

// ============================================================
// Matroska Element IDs
// ============================================================

// EBML Header
pub const EBML: u32 = 0x1A45_DFA3;
pub const EBML_VERSION: u32 = 0x4286;
pub const EBML_READ_VERSION: u32 = 0x42F7;
pub const EBML_MAX_ID_LENGTH: u32 = 0x42F2;
pub const EBML_MAX_SIZE_LENGTH: u32 = 0x42F3;
pub const EBML_DOC_TYPE: u32 = 0x4282;
pub const EBML_DOC_TYPE_VERSION: u32 = 0x4287;
pub const EBML_DOC_TYPE_READ_VERSION: u32 = 0x4285;

// Segment
pub const SEGMENT: u32 = 0x1853_8067;

// SeekHead
pub const SEEK_HEAD: u32 = 0x114D_9B74;
pub const SEEK: u32 = 0x4DBB;
pub const SEEK_ID: u32 = 0x53AB;
pub const SEEK_POSITION: u32 = 0x53AC;

// Segment Info
pub const INFO: u32 = 0x1549_A966;
pub const TIMESTAMP_SCALE: u32 = 0x2A_D7B1;
pub const DURATION: u32 = 0x4489;
pub const MUXING_APP: u32 = 0x4D80;
pub const WRITING_APP: u32 = 0x5741;
pub const TITLE: u32 = 0x7BA9;

// Tracks
pub const TRACKS: u32 = 0x1654_AE6B;
pub const TRACK_ENTRY: u32 = 0xAE;
pub const TRACK_NUMBER: u32 = 0xD7;
pub const TRACK_UID: u32 = 0x73C5;
pub const TRACK_TYPE: u32 = 0x83;
pub const FLAG_LACING: u32 = 0x9C;
pub const FLAG_DEFAULT: u32 = 0x88;
pub const FLAG_FORCED: u32 = 0x55AA;
pub const LANGUAGE: u32 = 0x22_B59C;
pub const CODEC_ID: u32 = 0x86;
pub const CODEC_PRIVATE: u32 = 0x63A2;
pub const TRACK_NAME: u32 = 0x536E;
pub const DEFAULT_DURATION: u32 = 0x23_E383;

// Video
pub const VIDEO: u32 = 0xE0;
pub const PIXEL_WIDTH: u32 = 0xB0;
pub const PIXEL_HEIGHT: u32 = 0xBA;
pub const DISPLAY_WIDTH: u32 = 0x54B0;
pub const DISPLAY_HEIGHT: u32 = 0x54BA;
pub const COLOUR: u32 = 0x55B0;
pub const TRANSFER_CHARACTERISTICS: u32 = 0x55BA;
pub const MATRIX_COEFFICIENTS: u32 = 0x55B1;
pub const PRIMARIES: u32 = 0x55BB;
pub const RANGE: u32 = 0x55B9;

// Dolby Vision — BlockAdditionMapping carries the DOVIDecoderConfigurationRecord
// (dvcC) so players / mediainfo recognise the track as Dolby Vision.
pub const BLOCK_ADDITION_MAPPING: u32 = 0x41E4;
pub const BLOCK_ADD_ID_TYPE: u32 = 0x41E7;
pub const BLOCK_ADD_ID_EXTRA_DATA: u32 = 0x41ED;

// Audio
pub const AUDIO: u32 = 0xE1;
pub const SAMPLING_FREQUENCY: u32 = 0xB5;
pub const CHANNELS: u32 = 0x9F;
pub const BIT_DEPTH: u32 = 0x6264;

// Cluster
pub const CLUSTER: u32 = 0x1F43_B675;
pub const CLUSTER_TIMESTAMP: u32 = 0xE7;
pub const SIMPLE_BLOCK: u32 = 0xA3;
pub const BLOCK_GROUP: u32 = 0xA0;
pub const BLOCK: u32 = 0xA1;
pub const BLOCK_DURATION: u32 = 0x9B;

// Cues
pub const CUES: u32 = 0x1C53_BB6B;
pub const CUE_POINT: u32 = 0xBB;
pub const CUE_TIME: u32 = 0xB3;
pub const CUE_TRACK_POSITIONS: u32 = 0xB7;
pub const CUE_TRACK: u32 = 0xF7;
pub const CUE_CLUSTER_POSITION: u32 = 0xF1;

// Chapters
pub const CHAPTERS: u32 = 0x1043_A770;
pub const EDITION_ENTRY: u32 = 0x45B9;
pub const CHAPTER_ATOM: u32 = 0xB6;
pub const CHAPTER_UID: u32 = 0x73C4;
pub const CHAPTER_TIME_START: u32 = 0x91;
pub const CHAPTER_DISPLAY: u32 = 0x80;
pub const CHAP_STRING: u32 = 0x85;
pub const CHAP_LANGUAGE: u32 = 0x437C;

// Track types
pub const TRACK_TYPE_VIDEO: u64 = 1;
pub const TRACK_TYPE_AUDIO: u64 = 2;
pub const TRACK_TYPE_SUBTITLE: u64 = 17;

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

    #[test]
    fn test_write_size() {
        let mut buf = Vec::new();
        write_size(&mut buf, 0).unwrap();
        assert_eq!(buf, [0x80]);

        buf.clear();
        write_size(&mut buf, 127).unwrap();
        assert_eq!(buf, [0x40, 127]); // 127 >= 0x7F, uses 2 bytes: (0>>8)|0x40, 127

        buf.clear();
        write_size(&mut buf, 126).unwrap();
        assert_eq!(buf, [126 | 0x80]); // 126 < 0x7F, uses 1 byte
    }

    #[test]
    fn test_write_uint() {
        let mut buf = Vec::new();
        write_uint(&mut buf, 0x4286, 1).unwrap(); // EBML_VERSION = 1
        // ID: 42 86, Size: 81 (1 byte), Data: 01
        assert_eq!(buf, [0x42, 0x86, 0x81, 0x01]);
    }

    #[test]
    fn test_write_string() {
        let mut buf = Vec::new();
        write_string(&mut buf, 0x4282, "matroska").unwrap();
        // ID: 42 82, Size: 88 (8 bytes), Data: "matroska"
        assert_eq!(&buf[0..2], &[0x42, 0x82]);
        assert_eq!(buf[2], 0x88); // size = 8
        assert_eq!(&buf[3..], b"matroska");
    }

    #[test]
    fn test_master_element() {
        let mut buf = Cursor::new(Vec::new());
        let pos = start_master(&mut buf, EBML).unwrap();
        write_uint(&mut buf, EBML_VERSION, 1).unwrap();
        end_master(&mut buf, pos).unwrap();
        let data = buf.into_inner();
        // EBML header: 1A 45 DF A3, then 8-byte size, then content
        assert_eq!(&data[0..4], &[0x1A, 0x45, 0xDF, 0xA3]);
    }

    #[test]
    fn write_read_id_roundtrip() {
        // 1-byte IDs have high bit set (0x80..=0xFF)
        for &id in &[0x80u32, 0xA3, 0xFF] {
            let mut buf = Vec::new();
            write_id(&mut buf, id).unwrap();
            assert_eq!(buf.len(), 1);
            let mut cursor = Cursor::new(&buf);
            let (read_back, consumed) = read_id(&mut cursor).unwrap();
            assert_eq!(read_back, id, "1-byte ID roundtrip failed for 0x{:X}", id);
            assert_eq!(consumed, 1);
        }
        // 2-byte IDs (0x4000..=0x7FFF)
        for &id in &[0x4286u32, 0x4282, 0x7FFF] {
            let mut buf = Vec::new();
            write_id(&mut buf, id).unwrap();
            assert_eq!(buf.len(), 2);
            let mut cursor = Cursor::new(&buf);
            let (read_back, consumed) = read_id(&mut cursor).unwrap();
            assert_eq!(read_back, id, "2-byte ID roundtrip failed for 0x{:X}", id);
            assert_eq!(consumed, 2);
        }
        // 3-byte IDs (0x200000..=0x3FFFFF)
        for &id in &[0x22B59Cu32, 0x23E383] {
            let mut buf = Vec::new();
            write_id(&mut buf, id).unwrap();
            assert_eq!(buf.len(), 3);
            let mut cursor = Cursor::new(&buf);
            let (read_back, consumed) = read_id(&mut cursor).unwrap();
            assert_eq!(read_back, id, "3-byte ID roundtrip failed for 0x{:X}", id);
            assert_eq!(consumed, 3);
        }
        // 4-byte IDs (0x10000000..=0x1FFFFFFF)
        for &id in &[EBML, SEGMENT, TRACKS, CLUSTER] {
            let mut buf = Vec::new();
            write_id(&mut buf, id).unwrap();
            assert_eq!(buf.len(), 4);
            let mut cursor = Cursor::new(&buf);
            let (read_back, consumed) = read_id(&mut cursor).unwrap();
            assert_eq!(read_back, id, "4-byte ID roundtrip failed for 0x{:X}", id);
            assert_eq!(consumed, 4);
        }
    }

    #[test]
    fn write_read_size_roundtrip() {
        let test_sizes: &[u64] = &[
            0,
            1,
            0x7E,
            127,
            128,
            0x3FFE,
            16383,
            16384,
            0x1FFFFE,
            0x0FFFFFFE,
            0x1_0000_0000,
        ];
        for &size in test_sizes {
            let mut buf = Vec::new();
            write_size(&mut buf, size).unwrap();
            let mut cursor = Cursor::new(&buf);
            let (read_back, _consumed) = read_size(&mut cursor).unwrap();
            assert_eq!(read_back, size, "size roundtrip failed for {}", size);
        }
    }

    #[test]
    fn write_read_uint_roundtrip() {
        let test_vals: &[u64] = &[
            0,
            1,
            127,
            255,
            256,
            0xFFFF,
            0xFF_FFFF,
            0xFFFF_FFFF,
            1_000_000_000_000,
        ];
        let test_id = EBML_VERSION;
        for &val in test_vals {
            let mut buf = Vec::new();
            write_uint(&mut buf, test_id, val).unwrap();
            let mut cursor = Cursor::new(&buf);
            let (id, _id_len) = read_id(&mut cursor).unwrap();
            assert_eq!(id, test_id);
            let (size, _) = read_size(&mut cursor).unwrap();
            let read_val = read_uint_val(&mut cursor, size as usize).unwrap();
            assert_eq!(read_val, val, "uint roundtrip failed for {}", val);
        }
    }

    #[test]
    fn write_read_string_roundtrip() {
        let test_strings = &[
            "",
            "matroska",
            "freemkv",
            "Hello, World!",
            "unicode: \u{1F600}",
        ];
        let test_id = EBML_DOC_TYPE;
        for &s in test_strings {
            let mut buf = Vec::new();
            write_string(&mut buf, test_id, s).unwrap();
            let mut cursor = Cursor::new(&buf);
            let (id, _) = read_id(&mut cursor).unwrap();
            assert_eq!(id, test_id);
            let (size, _) = read_size(&mut cursor).unwrap();
            let read_s = read_string_val(&mut cursor, size as usize).unwrap();
            assert_eq!(read_s, s, "string roundtrip failed for {:?}", s);
        }
    }

    #[test]
    fn write_read_float_roundtrip() {
        let test_vals: &[f64] = &[
            0.0,
            1.0,
            -1.0,
            std::f64::consts::PI,
            48000.0,
            7200000.0,
            f64::MIN,
            f64::MAX,
        ];
        let test_id = DURATION;
        for &val in test_vals {
            let mut buf = Vec::new();
            write_float(&mut buf, test_id, val).unwrap();
            let mut cursor = Cursor::new(&buf);
            let (id, _) = read_id(&mut cursor).unwrap();
            assert_eq!(id, test_id);
            let (size, _) = read_size(&mut cursor).unwrap();
            assert_eq!(size, 8);
            let read_val = read_float_val(&mut cursor, size as usize).unwrap();
            assert_eq!(
                read_val.to_bits(),
                val.to_bits(),
                "float roundtrip failed for {}",
                val
            );
        }
    }

    #[test]
    fn unknown_size() {
        let mut buf = Vec::new();
        write_unknown_size(&mut buf).unwrap();
        assert_eq!(buf.len(), 8);
        assert_eq!(buf[0], 0x01);
        for &b in &buf[1..] {
            assert_eq!(
                b, 0xFF,
                "unknown size bytes should all be 0xFF after first byte"
            );
        }
        // Reading it back should yield u64::MAX
        let mut cursor = Cursor::new(&buf);
        let (size, consumed) = read_size(&mut cursor).unwrap();
        assert_eq!(size, u64::MAX);
        assert_eq!(consumed, 8);
    }
}
