async_snmp/ber/
encode.rs

1//! BER encoding.
2//!
3//! Uses a reverse buffer approach: writes from end backwards to avoid
4//! needing to pre-calculate lengths.
5
6use super::length::encode_length;
7use super::tag;
8use bytes::Bytes;
9
10/// Buffer for BER encoding that writes backwards.
11///
12/// This approach avoids needing to pre-calculate content lengths:
13/// we write the content first, then prepend the length and tag.
14pub struct EncodeBuf {
15    buf: Vec<u8>,
16}
17
18impl EncodeBuf {
19    /// Create a new encode buffer with default capacity.
20    pub fn new() -> Self {
21        Self::with_capacity(512)
22    }
23
24    /// Create a new encode buffer with specified capacity.
25    pub fn with_capacity(capacity: usize) -> Self {
26        Self {
27            buf: Vec::with_capacity(capacity),
28        }
29    }
30
31    /// Push a single byte (prepends to front).
32    pub fn push_byte(&mut self, byte: u8) {
33        self.buf.push(byte);
34    }
35
36    /// Push multiple bytes (prepends to front, reversed).
37    pub fn push_bytes(&mut self, bytes: &[u8]) {
38        self.buf.extend(bytes.iter().rev());
39    }
40
41    /// Push a BER length encoding.
42    pub fn push_length(&mut self, len: usize) {
43        let (bytes, count) = encode_length(len);
44        // The encode_length returns bytes in reverse order for prepending
45        for byte in bytes.iter().take(count) {
46            self.buf.push(*byte);
47        }
48    }
49
50    /// Push a BER tag.
51    pub fn push_tag(&mut self, tag: u8) {
52        self.buf.push(tag);
53    }
54
55    /// Get the current length of encoded data.
56    pub fn len(&self) -> usize {
57        self.buf.len()
58    }
59
60    /// Check if buffer is empty.
61    pub fn is_empty(&self) -> bool {
62        self.buf.is_empty()
63    }
64
65    /// Encode a constructed type (SEQUENCE, PDU, etc).
66    ///
67    /// Calls the closure to encode contents, then wraps with length and tag.
68    pub fn push_constructed<F>(&mut self, tag: u8, f: F)
69    where
70        F: FnOnce(&mut Self),
71    {
72        let start_len = self.len();
73        f(self);
74        let content_len = self.len() - start_len;
75        self.push_length(content_len);
76        self.push_tag(tag);
77    }
78
79    /// Encode a SEQUENCE.
80    pub fn push_sequence<F>(&mut self, f: F)
81    where
82        F: FnOnce(&mut Self),
83    {
84        self.push_constructed(tag::universal::SEQUENCE, f);
85    }
86
87    /// Encode an INTEGER.
88    pub fn push_integer(&mut self, value: i32) {
89        let (arr, len) = encode_integer_stack(value);
90        // Valid bytes are at the end of the array
91        self.push_bytes(&arr[4 - len..]);
92        self.push_length(len);
93        self.push_tag(tag::universal::INTEGER);
94    }
95
96    /// Encode a 64-bit integer (for Counter64).
97    pub fn push_integer64(&mut self, value: u64) {
98        let (arr, len) = encode_integer64_stack(value);
99        // Valid bytes are at the end of the array
100        self.push_bytes(&arr[9 - len..]);
101        self.push_length(len);
102        self.push_tag(tag::application::COUNTER64);
103    }
104
105    /// Encode an unsigned 32-bit integer with a specific tag.
106    pub fn push_unsigned32(&mut self, tag: u8, value: u32) {
107        let (arr, len) = encode_unsigned32_stack(value);
108        // Valid bytes are at the end of the array
109        self.push_bytes(&arr[5 - len..]);
110        self.push_length(len);
111        self.push_tag(tag);
112    }
113
114    /// Encode an OCTET STRING.
115    pub fn push_octet_string(&mut self, data: &[u8]) {
116        self.push_bytes(data);
117        self.push_length(data.len());
118        self.push_tag(tag::universal::OCTET_STRING);
119    }
120
121    /// Encode a NULL.
122    pub fn push_null(&mut self) {
123        self.push_length(0);
124        self.push_tag(tag::universal::NULL);
125    }
126
127    /// Encode an OBJECT IDENTIFIER.
128    pub fn push_oid(&mut self, oid: &crate::oid::Oid) {
129        let ber = oid.to_ber_smallvec();
130        self.push_bytes(&ber);
131        self.push_length(ber.len());
132        self.push_tag(tag::universal::OBJECT_IDENTIFIER);
133    }
134
135    /// Encode an IP address.
136    pub fn push_ip_address(&mut self, addr: [u8; 4]) {
137        self.push_bytes(&addr);
138        self.push_length(4);
139        self.push_tag(tag::application::IP_ADDRESS);
140    }
141
142    /// Finalize and return the encoded bytes.
143    ///
144    /// The buffer is reversed to produce the correct order.
145    pub fn finish(mut self) -> Bytes {
146        self.buf.reverse();
147        Bytes::from(self.buf)
148    }
149
150    /// Finalize and return as `Vec<u8>`.
151    pub fn finish_vec(mut self) -> Vec<u8> {
152        self.buf.reverse();
153        self.buf
154    }
155}
156
157impl Default for EncodeBuf {
158    fn default() -> Self {
159        Self::new()
160    }
161}
162
163/// Encode a signed 32-bit integer in minimal BER form.
164///
165/// Returns a stack-allocated array and the number of valid bytes.
166/// The valid bytes are at the END of the array (for reverse-buffer compatibility).
167#[inline]
168fn encode_integer_stack(value: i32) -> ([u8; 4], usize) {
169    let bytes = value.to_be_bytes();
170
171    // Find first significant byte
172    let mut start = 0;
173    if value >= 0 {
174        // For positive/zero, skip leading 0x00 bytes (but keep one if needed for sign)
175        while start < 3 && bytes[start] == 0 && bytes[start + 1] & 0x80 == 0 {
176            start += 1;
177        }
178    } else {
179        // For negative, skip leading 0xFF bytes (but keep one if needed for sign)
180        while start < 3 && bytes[start] == 0xFF && bytes[start + 1] & 0x80 != 0 {
181            start += 1;
182        }
183    }
184
185    (bytes, 4 - start)
186}
187
188/// Encode an unsigned 32-bit integer.
189///
190/// Returns a stack-allocated array and the number of valid bytes.
191/// The valid bytes are at the END of the array (for reverse-buffer compatibility).
192#[inline]
193fn encode_unsigned32_stack(value: u32) -> ([u8; 5], usize) {
194    if value == 0 {
195        return ([0, 0, 0, 0, 0], 1);
196    }
197
198    let bytes = value.to_be_bytes();
199    let mut start = 0;
200
201    // Skip leading zeros, but add a 0x00 prefix if MSB is set (to avoid sign extension)
202    while start < 3 && bytes[start] == 0 {
203        start += 1;
204    }
205
206    if bytes[start] & 0x80 != 0 {
207        // Need to add a leading 0x00 to indicate positive
208        let mut result = [0u8; 5];
209        result[1..].copy_from_slice(&bytes);
210        (result, 5 - start)
211    } else {
212        let mut result = [0u8; 5];
213        result[1..].copy_from_slice(&bytes);
214        (result, 4 - start)
215    }
216}
217
218/// Encode an unsigned 64-bit integer.
219///
220/// Returns a stack-allocated array and the number of valid bytes.
221/// The valid bytes are at the END of the array (for reverse-buffer compatibility).
222#[inline]
223fn encode_integer64_stack(value: u64) -> ([u8; 9], usize) {
224    if value == 0 {
225        return ([0; 9], 1);
226    }
227
228    let bytes = value.to_be_bytes();
229    let mut start = 0;
230
231    // Skip leading zeros, but add a 0x00 prefix if MSB is set
232    while start < 7 && bytes[start] == 0 {
233        start += 1;
234    }
235
236    if bytes[start] & 0x80 != 0 {
237        // Need to add a leading 0x00 to indicate positive
238        let mut result = [0u8; 9];
239        result[1..].copy_from_slice(&bytes);
240        (result, 9 - start)
241    } else {
242        let mut result = [0u8; 9];
243        result[1..].copy_from_slice(&bytes);
244        (result, 8 - start)
245    }
246}
247
248#[cfg(test)]
249mod tests {
250    use super::*;
251
252    /// Helper to extract the valid bytes from stack-based integer encoding
253    fn encode_integer(value: i32) -> Vec<u8> {
254        let (arr, len) = encode_integer_stack(value);
255        arr[4 - len..].to_vec()
256    }
257
258    /// Helper to extract the valid bytes from stack-based unsigned32 encoding
259    fn encode_unsigned32(value: u32) -> Vec<u8> {
260        let (arr, len) = encode_unsigned32_stack(value);
261        arr[5 - len..].to_vec()
262    }
263
264    #[test]
265    fn test_encode_integer() {
266        assert_eq!(encode_integer(0), vec![0]);
267        assert_eq!(encode_integer(1), vec![1]);
268        assert_eq!(encode_integer(127), vec![127]);
269        assert_eq!(encode_integer(128), vec![0, 128]);
270        assert_eq!(encode_integer(-1), vec![0xFF]);
271        assert_eq!(encode_integer(-128), vec![0x80]);
272        assert_eq!(encode_integer(-129), vec![0xFF, 0x7F]);
273    }
274
275    #[test]
276    fn test_encode_unsigned32() {
277        assert_eq!(encode_unsigned32(0), vec![0]);
278        assert_eq!(encode_unsigned32(127), vec![127]);
279        assert_eq!(encode_unsigned32(128), vec![0, 128]);
280        assert_eq!(encode_unsigned32(255), vec![0, 255]);
281        assert_eq!(encode_unsigned32(256), vec![1, 0]);
282    }
283
284    #[test]
285    fn test_encode_null() {
286        let mut buf = EncodeBuf::new();
287        buf.push_null();
288        let bytes = buf.finish();
289        assert_eq!(&bytes[..], &[0x05, 0x00]);
290    }
291
292    #[test]
293    fn test_encode_integer_value() {
294        let mut buf = EncodeBuf::new();
295        buf.push_integer(42);
296        let bytes = buf.finish();
297        assert_eq!(&bytes[..], &[0x02, 0x01, 0x2A]);
298    }
299
300    #[test]
301    fn test_encode_sequence() {
302        let mut buf = EncodeBuf::new();
303        buf.push_sequence(|buf| {
304            // Reverse buffer: push in reverse order for forward output
305            buf.push_integer(2);
306            buf.push_integer(1);
307        });
308        let bytes = buf.finish();
309        // SEQUENCE { INTEGER 1, INTEGER 2 }
310        assert_eq!(
311            &bytes[..],
312            &[0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02]
313        );
314    }
315}