appcore_dnt/
compression.rs1use crate::flags::{validate_flags, DNT_FLAG_PAYLOAD_DEFLATE};
14use crate::{DntError, DntResult};
15use flate2::read::ZlibDecoder;
16use flate2::write::ZlibEncoder;
17use flate2::Compression;
18use std::io::{Read, Write};
19use zeroize::Zeroize;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum DntCompression {
24 None,
26 Deflate,
28}
29
30impl DntCompression {
31 pub(crate) fn from_flags(flags: u32) -> DntResult<Self> {
32 validate_flags(flags)?;
33 if flags & DNT_FLAG_PAYLOAD_DEFLATE != 0 {
34 return Ok(Self::Deflate);
35 }
36 Ok(Self::None)
37 }
38
39 pub fn is_compacted(self) -> bool {
41 self != Self::None
42 }
43}
44
45pub(crate) fn encode_payload(
46 flags: u32,
47 mut encoded_payload: Vec<u8>,
48 max_payload_bytes: Option<u64>,
49) -> DntResult<Vec<u8>> {
50 match DntCompression::from_flags(flags)? {
51 DntCompression::None => Ok(encoded_payload),
52 DntCompression::Deflate => {
53 let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
54 let write_result = encoder.write_all(&encoded_payload);
55 encoded_payload.zeroize();
56 write_result.map_err(|_| DntError::CodecFailed)?;
57 let compacted = encoder.finish().map_err(|_| DntError::CodecFailed)?;
58 enforce_max(compacted.len() as u64, max_payload_bytes)?;
59 Ok(compacted)
60 }
61 }
62}
63
64pub(crate) fn decode_payload(
65 flags: u32,
66 stored_payload: &[u8],
67 max_payload_bytes: Option<u64>,
68) -> DntResult<Vec<u8>> {
69 match DntCompression::from_flags(flags)? {
70 DntCompression::None => Ok(stored_payload.to_vec()),
71 DntCompression::Deflate => inflate_bounded(stored_payload, max_payload_bytes),
72 }
73}
74
75fn inflate_bounded(input: &[u8], max_payload_bytes: Option<u64>) -> DntResult<Vec<u8>> {
76 let Some(max_payload_bytes) = max_payload_bytes else {
77 return Err(DntError::PayloadTooLarge);
78 };
79 let read_limit = max_payload_bytes.saturating_add(1);
80 let mut decoder = ZlibDecoder::new(input).take(read_limit);
81 let mut output = Vec::new();
82 if decoder.read_to_end(&mut output).is_err() {
83 output.zeroize();
84 return Err(DntError::CodecFailed);
85 }
86 if let Err(error) = enforce_max(output.len() as u64, Some(max_payload_bytes)) {
87 output.zeroize();
88 return Err(error);
89 }
90 Ok(output)
91}
92
93fn enforce_max(actual: u64, max: Option<u64>) -> DntResult<()> {
94 if max.is_some_and(|max| actual > max) {
95 return Err(DntError::PayloadTooLarge);
96 }
97 Ok(())
98}