lightstream 0.6.0

Composable, zero-copy Arrow IPC and native data streaming for Rust with SIMD-aligned I/O, async support, and memory-mapping.
Documentation
// Copyright Peter G. Bower 2025-2026.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

//! # TLV Encoders
//!
//! TLV frame encoding and buffered stream writing.

/// Buffered TLV stream writer.
pub mod tlv_stream;

// ---------------------------------------------------------------------------
// TLV Frame Encoder
//
// Encodes Type-Length-Value frames with a 1-byte type and a little-endian
// u32 length, writing into any StreamBuffer.
// ---------------------------------------------------------------------------

use crate::models::frames::tlv_frame::TLVFrame;
use crate::traits::frame_encoder::FrameEncoder;
use crate::traits::stream_buffer::StreamBuffer;
use std::io;

/// Stateless encoder for TLV frames - type `u8`, length `u32` LE, then value.
pub struct TLVEncoder;

impl FrameEncoder for TLVEncoder {
    type Frame<'a> = TLVFrame<'a>;
    type Metadata = ();

    /// Encode a TLV frame as `[type (1 byte)] [length (4 LE bytes)] [value bytes]`.
    fn encode<'a, B: StreamBuffer>(
        global_offset: &mut usize,
        frame: &Self::Frame<'a>,
    ) -> io::Result<(B, Self::Metadata)> {
        let len = frame.value.len();
        if len > u32::MAX as usize {
            return Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                "Value too large for TLV frame",
            ));
        }

        // 1 + 4 + value.len()
        let mut out = B::with_capacity(1 + 4 + len);
        out.push(frame.t);
        out.extend_from_slice(&(len as u32).to_le_bytes());
        *global_offset += 5; // type (1) + length (4)
        out.extend_from_slice(frame.value);
        *global_offset += frame.value.len();

        Ok((out, ()))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use minarrow::Vec64;

    #[test]
    fn test_tlv_encode_basic() {
        let value = [0xAA, 0xBB, 0xCC];
        let frame = TLVFrame {
            t: 42,
            value: &value,
        };

        let (buf, _) = TLVEncoder::encode::<Vec64<u8>>(&mut 0, &frame).unwrap();
        assert_eq!(buf[0], 42);
        assert_eq!(&buf[1..5], &(3u32.to_le_bytes()));
        assert_eq!(&buf[5..8], &value);
        assert_eq!(buf.len(), 8);
    }

    #[test]
    fn test_tlv_encode_empty() {
        let frame = TLVFrame { t: 7, value: &[] };
        let (buf, _) = TLVEncoder::encode::<Vec64<u8>>(&mut 0, &frame).unwrap();
        assert_eq!(buf[0], 7);
        assert_eq!(&buf[1..5], &(0u32.to_le_bytes()));
        assert_eq!(buf.len(), 5);
    }

    #[test]
    fn test_tlv_encode_large() {
        let large = vec![1u8; 65536];
        let frame = TLVFrame {
            t: 1,
            value: &large,
        };
        let (buf, _) = TLVEncoder::encode::<Vec64<u8>>(&mut 0, &frame).unwrap();
        assert_eq!(buf[0], 1);
        assert_eq!(&buf[1..5], &(65536u32.to_le_bytes()));
        assert_eq!(&buf[5..5 + 65536], &large[..]);
        assert_eq!(buf.len(), 5 + 65536);
    }
}