aws_smithy_eventstream/
message_size_hint.rs1use aws_smithy_types::event_stream::{HeaderValue, Message};
9use std::mem::size_of;
10
11pub trait MessageSizeHint {
16    fn size_hint(&self) -> usize;
37}
38
39impl MessageSizeHint for Message {
40    fn size_hint(&self) -> usize {
41        const PRELUDE_LENGTH_BYTES: usize = 3 * size_of::<u32>();
43        const MESSAGE_CRC_LENGTH_BYTES: usize = size_of::<u32>();
44        const MAX_HEADER_NAME_LEN: usize = 255;
45
46        let mut headers_len = 0;
48        for header in self.headers() {
49            let name_len = header.name().as_bytes().len().min(MAX_HEADER_NAME_LEN);
50            headers_len += 1 + name_len; headers_len += match header.value() {
54                HeaderValue::Bool(_) => 1,                            HeaderValue::Byte(_) => 2,                            HeaderValue::Int16(_) => 3,                           HeaderValue::Int32(_) => 5,                           HeaderValue::Int64(_) => 9,                           HeaderValue::ByteArray(val) => 3 + val.len(),         HeaderValue::String(val) => 3 + val.as_bytes().len(), HeaderValue::Timestamp(_) => 9,                       HeaderValue::Uuid(_) => 17,                           _ => 0, };
65        }
66
67        let payload_len = self.payload().len();
68
69        PRELUDE_LENGTH_BYTES + headers_len + payload_len + MESSAGE_CRC_LENGTH_BYTES
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77    use crate::frame::write_message_to;
78    use aws_smithy_types::event_stream::{Header, HeaderValue, Message};
79    use bytes::Bytes;
80
81    #[test]
82    fn test_size_hint_accuracy() {
83        let test_cases = vec![
85            Message::new(Bytes::from(b"hello world".to_vec())),
87            Message::new(Bytes::from(b"test payload".to_vec())).add_header(Header::new(
89                "content-type",
90                HeaderValue::String("application/json".into()),
91            )),
92            Message::new(Bytes::from(b"complex test".to_vec()))
94                .add_header(Header::new("bool-true", HeaderValue::Bool(true)))
95                .add_header(Header::new("bool-false", HeaderValue::Bool(false)))
96                .add_header(Header::new("byte-val", HeaderValue::Byte(42)))
97                .add_header(Header::new("int16-val", HeaderValue::Int16(12345)))
98                .add_header(Header::new("int32-val", HeaderValue::Int32(987654321)))
99                .add_header(Header::new(
100                    "int64-val",
101                    HeaderValue::Int64(1234567890123456789),
102                ))
103                .add_header(Header::new(
104                    "string-val",
105                    HeaderValue::String("hello world".into()),
106                ))
107                .add_header(Header::new(
108                    "bytes-val",
109                    HeaderValue::ByteArray(Bytes::from(b"binary data".to_vec())),
110                )),
111            Message::new(Bytes::new()),
113            Message::new(Bytes::from(vec![0u8; 1024])).add_header(Header::new(
115                "large-content",
116                HeaderValue::String("large payload test".into()),
117            )),
118        ];
119
120        for (i, message) in test_cases.iter().enumerate() {
121            let size_hint = message.size_hint();
122
123            let mut buffer = Vec::new();
125            write_message_to(message, &mut buffer)
126                .expect(&format!("Failed to serialize test case {}", i));
127            let actual_size = buffer.len();
128
129            assert_eq!(
131                size_hint, actual_size,
132                "Size hint mismatch for test case {}: hint={}, actual={}",
133                i, size_hint, actual_size
134            );
135        }
136    }
137
138    #[test]
139    fn test_size_hint_with_long_header_name() {
140        let long_name = "x".repeat(200); let message = Message::new(Bytes::from(b"payload".to_vec())).add_header(Header::new(
143            long_name,
144            HeaderValue::String("long header name test".into()),
145        ));
146
147        let size_hint = message.size_hint();
148
149        let mut buffer = Vec::new();
150        write_message_to(&message, &mut buffer)
151            .expect("Failed to serialize message with long header name");
152        let actual_size = buffer.len();
153
154        assert_eq!(
155            size_hint, actual_size,
156            "Size hint should match actual size for long header names"
157        );
158    }
159
160    #[test]
161    fn test_size_hint_performance_benefit() {
162        let message = Message::new(Bytes::from(vec![0u8; 1024])).add_header(Header::new(
164            "content-type",
165            HeaderValue::String("application/json".into()),
166        ));
167
168        let size_hint = message.size_hint();
169
170        let mut buffer = Vec::with_capacity(size_hint);
172        write_message_to(&message, &mut buffer).expect("Failed to serialize message");
173
174        assert!(
176            buffer.capacity() >= buffer.len(),
177            "Buffer should have sufficient capacity"
178        );
179
180        assert_eq!(
182            size_hint,
183            buffer.len(),
184            "Size hint should exactly match serialized size"
185        );
186    }
187}