Skip to main content

kacrab_protocol/
bytes_io.rs

1//! Kafka length-prefixed byte payloads.
2//!
3//! This module handles opaque bytes only. UTF-8 validation belongs in
4//! [`crate::string`].
5
6pub mod error;
7
8use bytes::{BufMut, Bytes, BytesMut};
9
10pub use self::error::{BytesError, BytesErrorKind};
11use crate::primitives::{
12    check_remaining, read_i32, read_unsigned_varint, unsigned_varint_len, write_unsigned_varint,
13};
14
15/// Result alias for raw bytes read operations.
16pub type Result<T> = core::result::Result<T, BytesError>;
17
18fn i32_max_len() -> usize {
19    usize::try_from(i32::MAX).unwrap_or(usize::MAX)
20}
21
22fn compact_max_len() -> usize {
23    usize::try_from(u32::MAX)
24        .unwrap_or(usize::MAX)
25        .saturating_sub(1)
26}
27
28fn i32_len_to_usize(len: i32) -> Result<usize> {
29    usize::try_from(len).map_err(|_| {
30        BytesErrorKind::TooLong {
31            length: usize::MAX,
32            max: i32_max_len(),
33        }
34        .into()
35    })
36}
37
38fn compact_len_to_usize(raw: u32) -> Result<usize> {
39    let raw_len = raw.checked_sub(1).ok_or(BytesErrorKind::UnexpectedNull)?;
40    usize::try_from(raw_len).map_err(|_| {
41        BytesErrorKind::TooLong {
42            length: usize::MAX,
43            max: compact_max_len(),
44        }
45        .into()
46    })
47}
48
49fn read_i32_len(buf: &mut Bytes) -> Result<usize> {
50    let len = read_i32(buf)?;
51    if len < 0 {
52        return Err(BytesErrorKind::NegativeLength { length: len }.into());
53    }
54    i32_len_to_usize(len)
55}
56
57fn read_nullable_i32_len(buf: &mut Bytes) -> Result<Option<usize>> {
58    let len = read_i32(buf)?;
59    if len < 0 {
60        return Ok(None);
61    }
62    i32_len_to_usize(len).map(Some)
63}
64
65fn read_compact_len(buf: &mut Bytes) -> Result<usize> {
66    let raw = read_unsigned_varint(buf)?;
67    if raw == 0 {
68        return Err(BytesErrorKind::UnexpectedNull.into());
69    }
70    compact_len_to_usize(raw)
71}
72
73fn read_nullable_compact_len(buf: &mut Bytes) -> Result<Option<usize>> {
74    let raw = read_unsigned_varint(buf)?;
75    if raw == 0 {
76        return Ok(None);
77    }
78    compact_len_to_usize(raw).map(Some)
79}
80
81fn split_bytes(buf: &mut Bytes, len: usize) -> Result<Bytes> {
82    check_remaining(buf, len)?;
83    Ok(buf.split_to(len))
84}
85
86fn compact_len_plus_one(len: usize) -> Result<u32> {
87    len.checked_add(1)
88        .and_then(|len| u32::try_from(len).ok())
89        .ok_or_else(|| {
90            BytesError::new(BytesErrorKind::TooLong {
91                length: len,
92                max: compact_max_len(),
93            })
94        })
95}
96
97/// Read non-flexible bytes (`i32` length prefix).
98pub fn read_bytes(buf: &mut Bytes) -> Result<Bytes> {
99    let len = read_i32_len(buf)?;
100    split_bytes(buf, len)
101}
102
103/// Write non-flexible bytes (`i32` length prefix).
104pub fn write_bytes(buf: &mut BytesMut, value: &[u8]) -> Result<()> {
105    let len = i32::try_from(value.len()).map_err(|_| {
106        BytesError::new(BytesErrorKind::TooLong {
107            length: value.len(),
108            max: i32_max_len(),
109        })
110    })?;
111    buf.put_i32(len);
112    buf.extend_from_slice(value);
113    Ok(())
114}
115
116/// Encoded length of non-flexible bytes (`i32` length prefix).
117pub fn bytes_len(value: &[u8]) -> Result<usize> {
118    let _len = i32::try_from(value.len()).map_err(|_| {
119        BytesError::new(BytesErrorKind::TooLong {
120            length: value.len(),
121            max: i32_max_len(),
122        })
123    })?;
124    value.len().checked_add(4).ok_or_else(|| {
125        BytesError::new(BytesErrorKind::TooLong {
126            length: value.len(),
127            max: i32_max_len(),
128        })
129    })
130}
131
132/// Read compact bytes (unsigned varint of `len + 1`).
133pub fn read_compact_bytes(buf: &mut Bytes) -> Result<Bytes> {
134    let len = read_compact_len(buf)?;
135    split_bytes(buf, len)
136}
137
138/// Write compact bytes (unsigned varint of `len + 1`).
139pub fn write_compact_bytes(buf: &mut BytesMut, value: &[u8]) -> Result<()> {
140    let len_plus_one = compact_len_plus_one(value.len())?;
141    write_unsigned_varint(buf, len_plus_one);
142    buf.extend_from_slice(value);
143    Ok(())
144}
145
146/// Encoded length of compact bytes (unsigned varint of `len + 1`).
147pub fn compact_bytes_len(value: &[u8]) -> Result<usize> {
148    let len_plus_one = compact_len_plus_one(value.len())?;
149    value
150        .len()
151        .checked_add(unsigned_varint_len(len_plus_one))
152        .ok_or_else(|| {
153            BytesError::new(BytesErrorKind::TooLong {
154                length: value.len(),
155                max: compact_max_len(),
156            })
157        })
158}
159
160/// Read nullable non-flexible bytes (`i32`, `-1` = null).
161pub fn read_nullable_bytes(buf: &mut Bytes) -> Result<Option<Bytes>> {
162    let Some(len) = read_nullable_i32_len(buf)? else {
163        return Ok(None);
164    };
165    split_bytes(buf, len).map(Some)
166}
167
168/// Write nullable non-flexible bytes (`i32`, `-1` = null).
169pub fn write_nullable_bytes(buf: &mut BytesMut, value: Option<&[u8]>) -> Result<()> {
170    match value {
171        None => {
172            buf.put_i32(-1);
173            Ok(())
174        },
175        Some(bytes) => write_bytes(buf, bytes),
176    }
177}
178
179/// Encoded length of nullable non-flexible bytes.
180pub fn nullable_bytes_len(value: Option<&[u8]>) -> Result<usize> {
181    value.map_or(Ok(4), bytes_len)
182}
183
184/// Read nullable compact bytes (unsigned varint, `0` = null).
185pub fn read_compact_nullable_bytes(buf: &mut Bytes) -> Result<Option<Bytes>> {
186    let Some(len) = read_nullable_compact_len(buf)? else {
187        return Ok(None);
188    };
189    split_bytes(buf, len).map(Some)
190}
191
192/// Write nullable compact bytes (unsigned varint, `0` = null).
193pub fn write_compact_nullable_bytes(buf: &mut BytesMut, value: Option<&[u8]>) -> Result<()> {
194    match value {
195        None => {
196            write_unsigned_varint(buf, 0);
197            Ok(())
198        },
199        Some(bytes) => write_compact_bytes(buf, bytes),
200    }
201}
202
203/// Encoded length of nullable compact bytes.
204pub fn compact_nullable_bytes_len(value: Option<&[u8]>) -> Result<usize> {
205    value.map_or(Ok(1), compact_bytes_len)
206}