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 aws_smithy_types::DateTime;
80 use bytes::Bytes;
81
82 #[test]
83 fn test_size_hint_accuracy() {
84 let test_cases = vec![
86 Message::new(Bytes::from(b"hello world".to_vec())),
88 Message::new(Bytes::from(b"test payload".to_vec())).add_header(Header::new(
90 "content-type",
91 HeaderValue::String("application/json".into()),
92 )),
93 Message::new(Bytes::from(b"complex test".to_vec()))
95 .add_header(Header::new("bool-true", HeaderValue::Bool(true)))
96 .add_header(Header::new("bool-false", HeaderValue::Bool(false)))
97 .add_header(Header::new("byte-val", HeaderValue::Byte(42)))
98 .add_header(Header::new("int16-val", HeaderValue::Int16(12345)))
99 .add_header(Header::new("int32-val", HeaderValue::Int32(987654321)))
100 .add_header(Header::new(
101 "int64-val",
102 HeaderValue::Int64(1234567890123456789),
103 ))
104 .add_header(Header::new(
105 "string-val",
106 HeaderValue::String("hello world".into()),
107 ))
108 .add_header(Header::new(
109 "bytes-val",
110 HeaderValue::ByteArray(Bytes::from(b"binary data".to_vec())),
111 ))
112 .add_header(Header::new(
113 "timestamp-val",
114 HeaderValue::Timestamp(DateTime::from_secs(5_000_000)),
115 ))
116 .add_header(Header::new("uuid-val", HeaderValue::Uuid(u128::MAX))),
117 Message::new(Bytes::new()),
119 Message::new(Bytes::from(vec![0u8; 1024])).add_header(Header::new(
121 "large-content",
122 HeaderValue::String("large payload test".into()),
123 )),
124 ];
125
126 for (i, message) in test_cases.iter().enumerate() {
127 let size_hint = message.size_hint();
128
129 let mut buffer = Vec::new();
131 write_message_to(message, &mut buffer)
132 .unwrap_or_else(|_| panic!("Failed to serialize test case {i}"));
133 let actual_size = buffer.len();
134
135 assert_eq!(
137 size_hint, actual_size,
138 "Size hint mismatch for test case {i}: hint={size_hint}, actual={actual_size}"
139 );
140 }
141 }
142
143 #[test]
144 fn test_size_hint_with_long_header_name() {
145 let long_name = "x".repeat(200); let message = Message::new(Bytes::from(b"payload".to_vec())).add_header(Header::new(
148 long_name,
149 HeaderValue::String("long header name test".into()),
150 ));
151
152 let size_hint = message.size_hint();
153
154 let mut buffer = Vec::new();
155 write_message_to(&message, &mut buffer)
156 .expect("Failed to serialize message with long header name");
157 let actual_size = buffer.len();
158
159 assert_eq!(
160 size_hint, actual_size,
161 "Size hint should match actual size for long header names"
162 );
163 }
164
165 #[test]
166 fn test_size_hint_performance_benefit() {
167 let message = Message::new(Bytes::from(vec![0u8; 1024])).add_header(Header::new(
169 "content-type",
170 HeaderValue::String("application/json".into()),
171 ));
172
173 let size_hint = message.size_hint();
174
175 let mut buffer = Vec::with_capacity(size_hint);
177 write_message_to(&message, &mut buffer).expect("Failed to serialize message");
178
179 assert!(
181 buffer.capacity() >= buffer.len(),
182 "Buffer should have sufficient capacity"
183 );
184
185 assert_eq!(
187 size_hint,
188 buffer.len(),
189 "Size hint should exactly match serialized size"
190 );
191 }
192}