kacrab_protocol/
string.rs1pub mod error;
16
17use std::fmt;
18
19use bytes::{BufMut, Bytes, BytesMut};
20
21pub use self::error::{StringError, StringErrorKind};
22use crate::primitives::{
23 check_remaining, read_i16, read_unsigned_varint, unsigned_varint_len, write_unsigned_varint,
24};
25
26pub type Result<T> = core::result::Result<T, StringError>;
28
29#[derive(Debug, Clone, PartialEq, Eq, Hash)]
35pub struct KafkaString {
36 inner: Bytes,
37}
38
39impl KafkaString {
40 pub fn new(b: Bytes) -> Result<Self> {
42 if let Err(source) = std::str::from_utf8(&b) {
43 return Err(StringErrorKind::InvalidUtf8 { source }.into());
44 }
45 Ok(Self { inner: b })
46 }
47
48 #[must_use]
50 pub const fn from_static(s: &'static str) -> Self {
51 Self {
52 inner: Bytes::from_static(s.as_bytes()),
53 }
54 }
55
56 #[must_use]
58 pub fn as_str(&self) -> &str {
59 std::str::from_utf8(&self.inner).unwrap_or_else(|_| {
60 debug_assert!(false, "KafkaString contains invalid UTF-8");
61 ""
62 })
63 }
64
65 #[must_use]
67 pub fn as_bytes(&self) -> &[u8] {
68 &self.inner
69 }
70
71 #[must_use]
73 pub const fn len(&self) -> usize {
74 self.inner.len()
75 }
76
77 #[must_use]
79 pub const fn is_empty(&self) -> bool {
80 self.inner.is_empty()
81 }
82}
83
84impl fmt::Display for KafkaString {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 f.write_str(self.as_str())
87 }
88}
89
90impl Default for KafkaString {
91 fn default() -> Self {
92 Self {
93 inner: Bytes::new(),
94 }
95 }
96}
97
98impl From<String> for KafkaString {
99 fn from(s: String) -> Self {
100 Self {
101 inner: Bytes::from(s.into_bytes()),
102 }
103 }
104}
105
106fn i16_max_len() -> usize {
107 usize::try_from(i16::MAX).unwrap_or(usize::MAX)
108}
109
110fn compact_max_len() -> usize {
111 usize::try_from(u32::MAX)
112 .unwrap_or(usize::MAX)
113 .saturating_sub(1)
114}
115
116fn i16_len_to_usize(len: i16) -> Result<usize> {
117 usize::try_from(len).map_err(|_| {
118 StringErrorKind::TooLong {
119 length: usize::MAX,
120 max: i16_max_len(),
121 }
122 .into()
123 })
124}
125
126fn compact_len_to_usize(raw: u32) -> Result<usize> {
127 let raw_len = raw.checked_sub(1).ok_or(StringErrorKind::UnexpectedNull)?;
128 usize::try_from(raw_len).map_err(|_| {
129 StringErrorKind::TooLong {
130 length: usize::MAX,
131 max: compact_max_len(),
132 }
133 .into()
134 })
135}
136
137fn read_i16_len(buf: &mut Bytes) -> Result<usize> {
138 let len = read_i16(buf)?;
139 if len < 0 {
140 return Err(StringErrorKind::NegativeLength {
141 length: i32::from(len),
142 }
143 .into());
144 }
145 i16_len_to_usize(len)
146}
147
148fn read_nullable_i16_len(buf: &mut Bytes) -> Result<Option<usize>> {
149 let len = read_i16(buf)?;
150 if len < 0 {
151 return Ok(None);
152 }
153 i16_len_to_usize(len).map(Some)
154}
155
156fn read_compact_len(buf: &mut Bytes) -> Result<usize> {
157 let raw = read_unsigned_varint(buf)?;
158 if raw == 0 {
159 return Err(StringErrorKind::UnexpectedNull.into());
160 }
161 compact_len_to_usize(raw)
162}
163
164fn read_nullable_compact_len(buf: &mut Bytes) -> Result<Option<usize>> {
165 let raw = read_unsigned_varint(buf)?;
166 if raw == 0 {
167 return Ok(None);
168 }
169 compact_len_to_usize(raw).map(Some)
170}
171
172fn split_string(buf: &mut Bytes, len: usize) -> Result<KafkaString> {
173 check_remaining(buf, len)?;
174 KafkaString::new(buf.split_to(len))
175}
176
177fn compact_len_plus_one(len: usize) -> Result<u32> {
178 len.checked_add(1)
179 .and_then(|len| u32::try_from(len).ok())
180 .ok_or_else(|| {
181 StringError::new(StringErrorKind::TooLong {
182 length: len,
183 max: compact_max_len(),
184 })
185 })
186}
187
188pub fn read_string(buf: &mut Bytes) -> Result<KafkaString> {
190 let len = read_i16_len(buf)?;
191 split_string(buf, len)
192}
193
194pub fn write_string(buf: &mut BytesMut, value: &KafkaString) -> Result<()> {
196 let len = i16::try_from(value.len()).map_err(|_| {
197 StringError::new(StringErrorKind::TooLong {
198 length: value.len(),
199 max: i16_max_len(),
200 })
201 })?;
202 buf.put_i16(len);
203 buf.extend_from_slice(value.as_bytes());
204 Ok(())
205}
206
207pub fn string_len(value: &KafkaString) -> Result<usize> {
209 let _len = i16::try_from(value.len()).map_err(|_| {
210 StringError::new(StringErrorKind::TooLong {
211 length: value.len(),
212 max: i16_max_len(),
213 })
214 })?;
215 value.len().checked_add(2).ok_or_else(|| {
216 StringError::new(StringErrorKind::TooLong {
217 length: value.len(),
218 max: i16_max_len(),
219 })
220 })
221}
222
223pub fn read_compact_string(buf: &mut Bytes) -> Result<KafkaString> {
225 let len = read_compact_len(buf)?;
226 split_string(buf, len)
227}
228
229pub fn write_compact_string(buf: &mut BytesMut, value: &KafkaString) -> Result<()> {
231 write_unsigned_varint(buf, compact_len_plus_one(value.len())?);
232 buf.extend_from_slice(value.as_bytes());
233 Ok(())
234}
235
236pub fn compact_string_len(value: &KafkaString) -> Result<usize> {
238 let len_plus_one = compact_len_plus_one(value.len())?;
239 value
240 .len()
241 .checked_add(unsigned_varint_len(len_plus_one))
242 .ok_or_else(|| {
243 StringError::new(StringErrorKind::TooLong {
244 length: value.len(),
245 max: compact_max_len(),
246 })
247 })
248}
249
250pub fn read_nullable_string(buf: &mut Bytes) -> Result<Option<KafkaString>> {
252 let Some(len) = read_nullable_i16_len(buf)? else {
253 return Ok(None);
254 };
255 split_string(buf, len).map(Some)
256}
257
258pub fn write_nullable_string(buf: &mut BytesMut, value: Option<&KafkaString>) -> Result<()> {
260 match value {
261 None => {
262 buf.put_i16(-1);
263 Ok(())
264 },
265 Some(s) => write_string(buf, s),
266 }
267}
268
269pub fn nullable_string_len(value: Option<&KafkaString>) -> Result<usize> {
271 value.map_or(Ok(2), string_len)
272}
273
274pub fn read_compact_nullable_string(buf: &mut Bytes) -> Result<Option<KafkaString>> {
276 let Some(len) = read_nullable_compact_len(buf)? else {
277 return Ok(None);
278 };
279 split_string(buf, len).map(Some)
280}
281
282pub fn write_compact_nullable_string(
284 buf: &mut BytesMut,
285 value: Option<&KafkaString>,
286) -> Result<()> {
287 match value {
288 None => {
289 write_unsigned_varint(buf, 0);
290 Ok(())
291 },
292 Some(s) => write_compact_string(buf, s),
293 }
294}
295
296pub fn compact_nullable_string_len(value: Option<&KafkaString>) -> Result<usize> {
298 value.map_or(Ok(1), compact_string_len)
299}