1use crate::coding::{checked_count, checked_decode_count};
18use crate::error::{EncodingError, Result};
19
20#[derive(Debug, Clone, Copy)]
22pub struct PlainEncoder;
23
24#[derive(Debug, Clone, Copy)]
26pub struct PlainDecoder;
27
28impl PlainEncoder {
29 pub fn encode_f64(values: &[f64]) -> Result<Vec<u8>> {
35 if values.is_empty() {
36 return Err(EncodingError::EmptyInput {
37 context: "plain f64",
38 });
39 }
40 let count = checked_count(values.len())?;
41 let mut buf = Vec::with_capacity(4 + values.len() * 8);
42 buf.extend_from_slice(&count.to_le_bytes());
43 for &v in values {
44 buf.extend_from_slice(&v.to_le_bytes());
45 }
46 Ok(buf)
47 }
48
49 pub fn encode_i64(values: &[i64]) -> Result<Vec<u8>> {
55 if values.is_empty() {
56 return Err(EncodingError::EmptyInput {
57 context: "plain i64",
58 });
59 }
60 let count = checked_count(values.len())?;
61 let mut buf = Vec::with_capacity(4 + values.len() * 8);
62 buf.extend_from_slice(&count.to_le_bytes());
63 for &v in values {
64 buf.extend_from_slice(&v.to_le_bytes());
65 }
66 Ok(buf)
67 }
68
69 pub fn encode_u64(values: &[u64]) -> Result<Vec<u8>> {
75 if values.is_empty() {
76 return Err(EncodingError::EmptyInput {
77 context: "plain u64",
78 });
79 }
80 let count = checked_count(values.len())?;
81 let mut buf = Vec::with_capacity(4 + values.len() * 8);
82 buf.extend_from_slice(&count.to_le_bytes());
83 for &v in values {
84 buf.extend_from_slice(&v.to_le_bytes());
85 }
86 Ok(buf)
87 }
88
89 pub fn encode_bool(values: &[bool]) -> Result<Vec<u8>> {
95 if values.is_empty() {
96 return Err(EncodingError::EmptyInput {
97 context: "plain bool",
98 });
99 }
100 let count = checked_count(values.len())?;
101 let mut buf = Vec::with_capacity(4 + values.len());
102 buf.extend_from_slice(&count.to_le_bytes());
103 for &v in values {
104 buf.push(u8::from(v));
105 }
106 Ok(buf)
107 }
108
109 pub fn encode_string(values: &[&str]) -> Result<Vec<u8>> {
115 if values.is_empty() {
116 return Err(EncodingError::EmptyInput {
117 context: "plain string",
118 });
119 }
120 let count = checked_count(values.len())?;
121 let total_bytes: usize = values.iter().map(|s| 4 + s.len()).sum();
122 let mut buf = Vec::with_capacity(4 + total_bytes);
123 buf.extend_from_slice(&count.to_le_bytes());
124 for &s in values {
125 let len = u32::try_from(s.len()).map_err(|_| EncodingError::CorruptData {
126 detail: format!("string length {} exceeds u32::MAX", s.len()),
127 })?;
128 buf.extend_from_slice(&len.to_le_bytes());
129 buf.extend_from_slice(s.as_bytes());
130 }
131 Ok(buf)
132 }
133}
134
135impl PlainDecoder {
136 pub fn decode_f64(data: &[u8]) -> Result<Vec<f64>> {
142 let count = Self::read_count(data, "f64")?;
143 let expected = 4 + count * 8;
144 Self::check_len(data, expected, "f64")?;
145
146 let mut values = Vec::with_capacity(count);
147 for i in 0..count {
148 let offset = 4 + i * 8;
149 let bytes: [u8; 8] =
150 data[offset..offset + 8]
151 .try_into()
152 .map_err(|_| EncodingError::CorruptData {
153 detail: "plain f64 slice conversion failed".to_string(),
154 })?;
155 values.push(f64::from_le_bytes(bytes));
156 }
157 Ok(values)
158 }
159
160 pub fn decode_i64(data: &[u8]) -> Result<Vec<i64>> {
166 let count = Self::read_count(data, "i64")?;
167 let expected = 4 + count * 8;
168 Self::check_len(data, expected, "i64")?;
169
170 let mut values = Vec::with_capacity(count);
171 for i in 0..count {
172 let offset = 4 + i * 8;
173 let bytes: [u8; 8] =
174 data[offset..offset + 8]
175 .try_into()
176 .map_err(|_| EncodingError::CorruptData {
177 detail: "plain i64 slice conversion failed".to_string(),
178 })?;
179 values.push(i64::from_le_bytes(bytes));
180 }
181 Ok(values)
182 }
183
184 pub fn decode_u64(data: &[u8]) -> Result<Vec<u64>> {
190 let count = Self::read_count(data, "u64")?;
191 let expected = 4 + count * 8;
192 Self::check_len(data, expected, "u64")?;
193
194 let mut values = Vec::with_capacity(count);
195 for i in 0..count {
196 let offset = 4 + i * 8;
197 let bytes: [u8; 8] =
198 data[offset..offset + 8]
199 .try_into()
200 .map_err(|_| EncodingError::CorruptData {
201 detail: "plain u64 slice conversion failed".to_string(),
202 })?;
203 values.push(u64::from_le_bytes(bytes));
204 }
205 Ok(values)
206 }
207
208 pub fn decode_bool(data: &[u8]) -> Result<Vec<bool>> {
214 let count = Self::read_count(data, "bool")?;
215 let expected = 4 + count;
216 Self::check_len(data, expected, "bool")?;
217
218 let mut values = Vec::with_capacity(count);
219 for i in 0..count {
220 values.push(data[4 + i] != 0);
221 }
222 Ok(values)
223 }
224
225 pub fn decode_string(data: &[u8]) -> Result<Vec<String>> {
231 let count = Self::read_count(data, "string")?;
232 let mut offset = 4;
233 let mut values = Vec::with_capacity(count);
234
235 for _ in 0..count {
236 if offset + 4 > data.len() {
237 return Err(EncodingError::CorruptData {
238 detail: "plain string data truncated at length prefix".to_string(),
239 });
240 }
241 let len = u32::from_le_bytes([
242 data[offset],
243 data[offset + 1],
244 data[offset + 2],
245 data[offset + 3],
246 ]) as usize;
247 offset += 4;
248
249 if offset + len > data.len() {
250 return Err(EncodingError::CorruptData {
251 detail: "plain string data truncated".to_string(),
252 });
253 }
254 let s = std::str::from_utf8(&data[offset..offset + len]).map_err(|e| {
255 EncodingError::CorruptData {
256 detail: format!("invalid UTF-8 in plain string: {e}"),
257 }
258 })?;
259 values.push(s.to_owned());
260 offset += len;
261 }
262
263 Ok(values)
264 }
265
266 fn read_count(data: &[u8], label: &str) -> Result<usize> {
267 if data.len() < 4 {
268 return Err(EncodingError::CorruptData {
269 detail: format!("plain {label} data too short for header"),
270 });
271 }
272 checked_decode_count(
273 u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize,
274 "plain",
275 )
276 }
277
278 fn check_len(data: &[u8], expected: usize, label: &str) -> Result<()> {
279 if data.len() < expected {
280 return Err(EncodingError::CorruptData {
281 detail: format!(
282 "plain {label} data truncated: expected {expected}, got {}",
283 data.len()
284 ),
285 });
286 }
287 Ok(())
288 }
289}
290
291#[cfg(test)]
292mod tests {
293 use super::*;
294
295 #[test]
296 fn f64_roundtrip() {
297 let values = vec![1.0, -2.5, 0.0, f64::INFINITY, f64::NEG_INFINITY];
298 let encoded = PlainEncoder::encode_f64(&values).unwrap();
299 let decoded = PlainDecoder::decode_f64(&encoded).unwrap();
300 assert_eq!(values, decoded);
301 }
302
303 #[test]
304 fn f64_nan_roundtrip() {
305 let values = vec![f64::NAN];
306 let encoded = PlainEncoder::encode_f64(&values).unwrap();
307 let decoded = PlainDecoder::decode_f64(&encoded).unwrap();
308 assert!(decoded[0].is_nan());
309 }
310
311 #[test]
312 fn i64_roundtrip() {
313 let values = vec![i64::MIN, -1, 0, 1, i64::MAX];
314 let encoded = PlainEncoder::encode_i64(&values).unwrap();
315 let decoded = PlainDecoder::decode_i64(&encoded).unwrap();
316 assert_eq!(values, decoded);
317 }
318
319 #[test]
320 fn u64_roundtrip() {
321 let values = vec![0, 1, u64::MAX, 42, 1_000_000];
322 let encoded = PlainEncoder::encode_u64(&values).unwrap();
323 let decoded = PlainDecoder::decode_u64(&encoded).unwrap();
324 assert_eq!(values, decoded);
325 }
326
327 #[test]
328 fn bool_roundtrip() {
329 let values = vec![true, false, true, true, false];
330 let encoded = PlainEncoder::encode_bool(&values).unwrap();
331 let decoded = PlainDecoder::decode_bool(&encoded).unwrap();
332 assert_eq!(values, decoded);
333 }
334
335 #[test]
336 fn string_roundtrip() {
337 let values = vec!["hello", "world", "", "café", "日本語"];
338 let encoded = PlainEncoder::encode_string(&values).unwrap();
339 let decoded = PlainDecoder::decode_string(&encoded).unwrap();
340 let expected: Vec<String> = values.iter().map(|s| (*s).to_owned()).collect();
341 assert_eq!(expected, decoded);
342 }
343
344 #[test]
345 fn empty_errors() {
346 assert!(PlainEncoder::encode_f64(&[]).is_err());
347 assert!(PlainEncoder::encode_i64(&[]).is_err());
348 assert!(PlainEncoder::encode_u64(&[]).is_err());
349 assert!(PlainEncoder::encode_bool(&[]).is_err());
350 let empty: &[&str] = &[];
351 assert!(PlainEncoder::encode_string(empty).is_err());
352 }
353
354 #[test]
355 fn single_values() {
356 let e = PlainEncoder::encode_f64(&[std::f64::consts::PI]).unwrap();
357 assert_eq!(
358 vec![std::f64::consts::PI],
359 PlainDecoder::decode_f64(&e).unwrap()
360 );
361
362 let e = PlainEncoder::encode_i64(&[42]).unwrap();
363 assert_eq!(vec![42i64], PlainDecoder::decode_i64(&e).unwrap());
364
365 let e = PlainEncoder::encode_u64(&[99]).unwrap();
366 assert_eq!(vec![99u64], PlainDecoder::decode_u64(&e).unwrap());
367
368 let e = PlainEncoder::encode_bool(&[true]).unwrap();
369 assert_eq!(vec![true], PlainDecoder::decode_bool(&e).unwrap());
370
371 let e = PlainEncoder::encode_string(&["x"]).unwrap();
372 assert_eq!(
373 vec!["x".to_owned()],
374 PlainDecoder::decode_string(&e).unwrap()
375 );
376 }
377
378 #[test]
379 fn corrupt_data_detected() {
380 assert!(PlainDecoder::decode_f64(&[0, 0]).is_err());
382 let mut bad = PlainEncoder::encode_f64(&[1.0, 2.0]).unwrap();
384 bad.truncate(4 + 8); assert!(PlainDecoder::decode_f64(&bad).is_err());
386 }
387
388 #[test]
389 fn no_compression_overhead() {
390 let values: Vec<f64> = (0..1000).map(|i| i as f64).collect();
392 let encoded = PlainEncoder::encode_f64(&values).unwrap();
393 assert_eq!(encoded.len(), 4 + 1000 * 8);
394 }
395}
396
397#[cfg(test)]
398mod proptests {
399 use super::*;
400 use proptest::prelude::*;
401
402 proptest! {
403 #[test]
404 fn roundtrip_f64(values in proptest::collection::vec(any::<f64>(), 1..500)) {
405 let encoded = PlainEncoder::encode_f64(&values).unwrap();
406 let decoded = PlainDecoder::decode_f64(&encoded).unwrap();
407 prop_assert_eq!(values.len(), decoded.len());
408 for (a, b) in values.iter().zip(decoded.iter()) {
409 prop_assert_eq!(a.to_bits(), b.to_bits());
410 }
411 }
412
413 #[test]
414 fn roundtrip_i64(values in proptest::collection::vec(any::<i64>(), 1..500)) {
415 let encoded = PlainEncoder::encode_i64(&values).unwrap();
416 let decoded = PlainDecoder::decode_i64(&encoded).unwrap();
417 prop_assert_eq!(&values, &decoded);
418 }
419
420 #[test]
421 fn roundtrip_u64(values in proptest::collection::vec(any::<u64>(), 1..500)) {
422 let encoded = PlainEncoder::encode_u64(&values).unwrap();
423 let decoded = PlainDecoder::decode_u64(&encoded).unwrap();
424 prop_assert_eq!(&values, &decoded);
425 }
426
427 #[test]
428 fn roundtrip_bool(values in proptest::collection::vec(any::<bool>(), 1..500)) {
429 let encoded = PlainEncoder::encode_bool(&values).unwrap();
430 let decoded = PlainDecoder::decode_bool(&encoded).unwrap();
431 prop_assert_eq!(&values, &decoded);
432 }
433
434 #[test]
435 fn roundtrip_string(values in proptest::collection::vec("[a-z]{0,8}", 1..200)) {
436 let refs: Vec<&str> = values.iter().map(String::as_str).collect();
437 let encoded = PlainEncoder::encode_string(&refs).unwrap();
438 let decoded = PlainDecoder::decode_string(&encoded).unwrap();
439 prop_assert_eq!(&values, &decoded);
440 }
441 }
442}