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 pub fn encode_bytes(values: &[&[u8]]) -> Result<Vec<u8>> {
146 if values.is_empty() {
147 return Err(EncodingError::EmptyInput {
148 context: "plain bytes",
149 });
150 }
151 let count = checked_count(values.len())?;
152 let total_bytes: usize = values.iter().map(|b| 4 + b.len()).sum();
153 let mut buf = Vec::with_capacity(4 + total_bytes);
154 buf.extend_from_slice(&count.to_le_bytes());
155 for &b in values {
156 let len = u32::try_from(b.len()).map_err(|_| EncodingError::CorruptData {
157 detail: format!("byte string length {} exceeds u32::MAX", b.len()),
158 })?;
159 buf.extend_from_slice(&len.to_le_bytes());
160 buf.extend_from_slice(b);
161 }
162 Ok(buf)
163 }
164}
165
166impl PlainDecoder {
167 pub fn decode_f64(data: &[u8]) -> Result<Vec<f64>> {
173 let count = Self::read_count(data, "f64")?;
174 let expected = 4 + count * 8;
175 Self::check_len(data, expected, "f64")?;
176
177 let mut values = Vec::with_capacity(count);
178 for i in 0..count {
179 let offset = 4 + i * 8;
180 let bytes: [u8; 8] =
181 data[offset..offset + 8]
182 .try_into()
183 .map_err(|_| EncodingError::CorruptData {
184 detail: "plain f64 slice conversion failed".to_string(),
185 })?;
186 values.push(f64::from_le_bytes(bytes));
187 }
188 Ok(values)
189 }
190
191 pub fn decode_bytes(data: &[u8]) -> Result<Vec<Vec<u8>>> {
200 let count = Self::read_count(data, "bytes")?;
201 let mut offset = 4;
202 let mut values = Vec::with_capacity(count.min(1024));
203 let mut decoded_total = 0usize;
204
205 for _ in 0..count {
206 if offset + 4 > data.len() {
207 return Err(EncodingError::CorruptData {
208 detail: "plain bytes data truncated at length prefix".to_string(),
209 });
210 }
211 let len = u32::from_le_bytes([
212 data[offset],
213 data[offset + 1],
214 data[offset + 2],
215 data[offset + 3],
216 ]) as usize;
217 offset += 4;
218
219 if offset + len > data.len() {
220 return Err(EncodingError::CorruptData {
221 detail: "plain bytes data truncated".to_string(),
222 });
223 }
224 decoded_total = decoded_total.saturating_add(len);
227 if decoded_total > crate::coding::MAX_DECODED_BYTES {
228 return Err(EncodingError::CorruptData {
229 detail: format!(
230 "plain bytes would decode to {decoded_total} bytes, above the {} byte ceiling",
231 crate::coding::MAX_DECODED_BYTES
232 ),
233 });
234 }
235 values.push(data[offset..offset + len].to_vec());
236 offset += len;
237 }
238
239 Ok(values)
240 }
241
242 pub fn decode_i64(data: &[u8]) -> Result<Vec<i64>> {
248 let count = Self::read_count(data, "i64")?;
249 let expected = 4 + count * 8;
250 Self::check_len(data, expected, "i64")?;
251
252 let mut values = Vec::with_capacity(count);
253 for i in 0..count {
254 let offset = 4 + i * 8;
255 let bytes: [u8; 8] =
256 data[offset..offset + 8]
257 .try_into()
258 .map_err(|_| EncodingError::CorruptData {
259 detail: "plain i64 slice conversion failed".to_string(),
260 })?;
261 values.push(i64::from_le_bytes(bytes));
262 }
263 Ok(values)
264 }
265
266 pub fn decode_u64(data: &[u8]) -> Result<Vec<u64>> {
272 let count = Self::read_count(data, "u64")?;
273 let expected = 4 + count * 8;
274 Self::check_len(data, expected, "u64")?;
275
276 let mut values = Vec::with_capacity(count);
277 for i in 0..count {
278 let offset = 4 + i * 8;
279 let bytes: [u8; 8] =
280 data[offset..offset + 8]
281 .try_into()
282 .map_err(|_| EncodingError::CorruptData {
283 detail: "plain u64 slice conversion failed".to_string(),
284 })?;
285 values.push(u64::from_le_bytes(bytes));
286 }
287 Ok(values)
288 }
289
290 pub fn decode_bool(data: &[u8]) -> Result<Vec<bool>> {
296 let count = Self::read_count(data, "bool")?;
297 let expected = 4 + count;
298 Self::check_len(data, expected, "bool")?;
299
300 let mut values = Vec::with_capacity(count);
301 for i in 0..count {
302 values.push(data[4 + i] != 0);
303 }
304 Ok(values)
305 }
306
307 pub fn decode_string(data: &[u8]) -> Result<Vec<String>> {
313 let count = Self::read_count(data, "string")?;
314 let mut offset = 4;
315 let mut values = Vec::with_capacity(count);
316
317 for _ in 0..count {
318 if offset + 4 > data.len() {
319 return Err(EncodingError::CorruptData {
320 detail: "plain string data truncated at length prefix".to_string(),
321 });
322 }
323 let len = u32::from_le_bytes([
324 data[offset],
325 data[offset + 1],
326 data[offset + 2],
327 data[offset + 3],
328 ]) as usize;
329 offset += 4;
330
331 if offset + len > data.len() {
332 return Err(EncodingError::CorruptData {
333 detail: "plain string data truncated".to_string(),
334 });
335 }
336 let s = std::str::from_utf8(&data[offset..offset + len]).map_err(|e| {
337 EncodingError::CorruptData {
338 detail: format!("invalid UTF-8 in plain string: {e}"),
339 }
340 })?;
341 values.push(s.to_owned());
342 offset += len;
343 }
344
345 Ok(values)
346 }
347
348 fn read_count(data: &[u8], label: &str) -> Result<usize> {
349 if data.len() < 4 {
350 return Err(EncodingError::CorruptData {
351 detail: format!("plain {label} data too short for header"),
352 });
353 }
354 checked_decode_count(
355 u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize,
356 "plain",
357 )
358 }
359
360 fn check_len(data: &[u8], expected: usize, label: &str) -> Result<()> {
361 if data.len() < expected {
362 return Err(EncodingError::CorruptData {
363 detail: format!(
364 "plain {label} data truncated: expected {expected}, got {}",
365 data.len()
366 ),
367 });
368 }
369 Ok(())
370 }
371}
372
373#[cfg(test)]
374mod tests {
375 use super::*;
376
377 #[test]
378 fn f64_roundtrip() {
379 let values = vec![1.0, -2.5, 0.0, f64::INFINITY, f64::NEG_INFINITY];
380 let encoded = PlainEncoder::encode_f64(&values).unwrap();
381 let decoded = PlainDecoder::decode_f64(&encoded).unwrap();
382 assert_eq!(values, decoded);
383 }
384
385 #[test]
386 fn f64_nan_roundtrip() {
387 let values = vec![f64::NAN];
388 let encoded = PlainEncoder::encode_f64(&values).unwrap();
389 let decoded = PlainDecoder::decode_f64(&encoded).unwrap();
390 assert!(decoded[0].is_nan());
391 }
392
393 #[test]
394 fn i64_roundtrip() {
395 let values = vec![i64::MIN, -1, 0, 1, i64::MAX];
396 let encoded = PlainEncoder::encode_i64(&values).unwrap();
397 let decoded = PlainDecoder::decode_i64(&encoded).unwrap();
398 assert_eq!(values, decoded);
399 }
400
401 #[test]
402 fn u64_roundtrip() {
403 let values = vec![0, 1, u64::MAX, 42, 1_000_000];
404 let encoded = PlainEncoder::encode_u64(&values).unwrap();
405 let decoded = PlainDecoder::decode_u64(&encoded).unwrap();
406 assert_eq!(values, decoded);
407 }
408
409 #[test]
410 fn bool_roundtrip() {
411 let values = vec![true, false, true, true, false];
412 let encoded = PlainEncoder::encode_bool(&values).unwrap();
413 let decoded = PlainDecoder::decode_bool(&encoded).unwrap();
414 assert_eq!(values, decoded);
415 }
416
417 #[test]
418 fn string_roundtrip() {
419 let values = vec!["hello", "world", "", "café", "日本語"];
420 let encoded = PlainEncoder::encode_string(&values).unwrap();
421 let decoded = PlainDecoder::decode_string(&encoded).unwrap();
422 let expected: Vec<String> = values.iter().map(|s| (*s).to_owned()).collect();
423 assert_eq!(expected, decoded);
424 }
425
426 #[test]
427 fn empty_errors() {
428 assert!(PlainEncoder::encode_f64(&[]).is_err());
429 assert!(PlainEncoder::encode_i64(&[]).is_err());
430 assert!(PlainEncoder::encode_u64(&[]).is_err());
431 assert!(PlainEncoder::encode_bool(&[]).is_err());
432 let empty: &[&str] = &[];
433 assert!(PlainEncoder::encode_string(empty).is_err());
434 }
435
436 #[test]
437 fn single_values() {
438 let e = PlainEncoder::encode_f64(&[std::f64::consts::PI]).unwrap();
439 assert_eq!(
440 vec![std::f64::consts::PI],
441 PlainDecoder::decode_f64(&e).unwrap()
442 );
443
444 let e = PlainEncoder::encode_i64(&[42]).unwrap();
445 assert_eq!(vec![42i64], PlainDecoder::decode_i64(&e).unwrap());
446
447 let e = PlainEncoder::encode_u64(&[99]).unwrap();
448 assert_eq!(vec![99u64], PlainDecoder::decode_u64(&e).unwrap());
449
450 let e = PlainEncoder::encode_bool(&[true]).unwrap();
451 assert_eq!(vec![true], PlainDecoder::decode_bool(&e).unwrap());
452
453 let e = PlainEncoder::encode_string(&["x"]).unwrap();
454 assert_eq!(
455 vec!["x".to_owned()],
456 PlainDecoder::decode_string(&e).unwrap()
457 );
458 }
459
460 #[test]
461 fn corrupt_data_detected() {
462 assert!(PlainDecoder::decode_f64(&[0, 0]).is_err());
464 let mut bad = PlainEncoder::encode_f64(&[1.0, 2.0]).unwrap();
466 bad.truncate(4 + 8); assert!(PlainDecoder::decode_f64(&bad).is_err());
468 }
469
470 #[test]
471 fn no_compression_overhead() {
472 let values: Vec<f64> = (0..1000).map(|i| i as f64).collect();
474 let encoded = PlainEncoder::encode_f64(&values).unwrap();
475 assert_eq!(encoded.len(), 4 + 1000 * 8);
476 }
477}
478
479#[cfg(test)]
480mod proptests {
481 use super::*;
482 use proptest::prelude::*;
483
484 proptest! {
485 #[test]
486 fn roundtrip_f64(values in proptest::collection::vec(any::<f64>(), 1..500)) {
487 let encoded = PlainEncoder::encode_f64(&values).unwrap();
488 let decoded = PlainDecoder::decode_f64(&encoded).unwrap();
489 prop_assert_eq!(values.len(), decoded.len());
490 for (a, b) in values.iter().zip(decoded.iter()) {
491 prop_assert_eq!(a.to_bits(), b.to_bits());
492 }
493 }
494
495 #[test]
496 fn roundtrip_i64(values in proptest::collection::vec(any::<i64>(), 1..500)) {
497 let encoded = PlainEncoder::encode_i64(&values).unwrap();
498 let decoded = PlainDecoder::decode_i64(&encoded).unwrap();
499 prop_assert_eq!(&values, &decoded);
500 }
501
502 #[test]
503 fn roundtrip_u64(values in proptest::collection::vec(any::<u64>(), 1..500)) {
504 let encoded = PlainEncoder::encode_u64(&values).unwrap();
505 let decoded = PlainDecoder::decode_u64(&encoded).unwrap();
506 prop_assert_eq!(&values, &decoded);
507 }
508
509 #[test]
510 fn roundtrip_bool(values in proptest::collection::vec(any::<bool>(), 1..500)) {
511 let encoded = PlainEncoder::encode_bool(&values).unwrap();
512 let decoded = PlainDecoder::decode_bool(&encoded).unwrap();
513 prop_assert_eq!(&values, &decoded);
514 }
515
516 #[test]
517 fn roundtrip_string(values in proptest::collection::vec("[a-z]{0,8}", 1..200)) {
518 let refs: Vec<&str> = values.iter().map(String::as_str).collect();
519 let encoded = PlainEncoder::encode_string(&refs).unwrap();
520 let decoded = PlainDecoder::decode_string(&encoded).unwrap();
521 prop_assert_eq!(&values, &decoded);
522 }
523 }
524}