diff --git a/src/io/buffer.rs b/src/io/buffer.rs index 01b1f91f..ace99dd4 100644 --- a/src/io/buffer.rs +++ b/src/io/buffer.rs @@ -8,8 +8,8 @@ use std::iter; #[derive(Debug, Default)] pub struct BitBuffer { buffer: Vec, - write_position: usize, - read_position: usize, + pub(crate) write_position: usize, + pub(crate) read_position: usize, } impl BitBuffer { @@ -62,6 +62,20 @@ impl BitBuffer { pub fn byte_len(&self) -> usize { self.buffer.len() } + + /// Changes the write-position to the given position for the closure call. + /// Restores the original write position after the call. + /// + /// # Panics + /// Positions beyond the current buffer length will result in panics. + pub fn with_write_position_at T>(&mut self, position: usize, f: F) -> T { + assert!(position <= self.buffer.len()); + let before = self.write_position; + self.write_position = position; + let result = f(self); + self.write_position = before; + result + } } fn bit_string_copy( diff --git a/src/syn/io/mod.rs b/src/syn/io/mod.rs index d49701e5..b2271a64 100644 --- a/src/syn/io/mod.rs +++ b/src/syn/io/mod.rs @@ -1,3 +1,5 @@ mod println; +mod uper; pub use println::*; +pub use uper::*; diff --git a/src/syn/io/uper.rs b/src/syn/io/uper.rs new file mode 100644 index 00000000..5f4e1d3c --- /dev/null +++ b/src/syn/io/uper.rs @@ -0,0 +1,75 @@ +use crate::io::buffer::BitBuffer; +use crate::io::uper::Error as UperError; +use crate::io::uper::Writer as _; +use crate::prelude::*; +use std::fmt::{Display, Formatter}; + +#[derive(Default)] +pub struct UperWriter { + buffer: BitBuffer, + optional_positions: Vec, +} + +impl UperWriter { + pub fn byte_content(&self) -> &[u8] { + self.buffer.content() + } + + pub const fn bit_len(&self) -> usize { + self.buffer.bit_len() + } +} + +impl Writer for UperWriter { + type Error = UperError; + + fn write_sequence Result<(), Self::Error>>( + &mut self, + f: F, + ) -> Result<(), Self::Error> { + // in UPER the optional flag for all OPTIONAL values is written before any field + // value is written. This reserves the bits, so that on a later call of `write_opt` + // the value can be set to the actual state. + let before = self.optional_positions.len(); + let write_pos = self.buffer.write_position; + for i in (0..C::OPTIONAL_FIELDS).rev() { + // insert in reverse order so that a simple pop() in `write_opt` retrieves + // the relevant position + self.optional_positions.push(write_pos + i); + self.buffer.write_bit(false); + } + f(self)?; + assert_eq!(before, self.optional_positions.len()); + Ok(()) + } + + fn write_opt( + &mut self, + value: Option<&::Type>, + ) -> Result<(), Self::Error> { + self.buffer + .with_write_position_at(self.optional_positions.pop().unwrap(), |buffer| { + buffer.write_bit(value.is_some()) + })?; + if let Some(value) = value { + T::write_value(self, value) + } else { + Ok(()) + } + } + + fn write_int(&mut self, value: i64, range: (i64, i64)) -> Result<(), Self::Error> { + self.buffer.write_int(value, range) + } + + fn write_int_max(&mut self, value: u64) -> Result<(), Self::Error> { + self.buffer.write_int_max(value) + } + + fn write_utf8string( + &mut self, + value: &str, + ) -> Result<(), Self::Error> { + self.buffer.write_utf8_string(value) + } +} diff --git a/tests/basic_proc_macro_attribute.rs b/tests/basic_proc_macro_attribute.rs index 75eb950c..c7d58469 100644 --- a/tests/basic_proc_macro_attribute.rs +++ b/tests/basic_proc_macro_attribute.rs @@ -1,5 +1,6 @@ #![allow(unused)] +use asn1rs::syn::io::UperWriter; use asn1rs_macros::asn; #[asn(sequence)] @@ -24,3 +25,24 @@ fn test_compiles() { string: String::from("where is the content"), }; } + +#[test] +fn test_serialize_with_uper() { + let p = Potato { + size: 123, + size2: 1234, + size3: 128, + string: String::from("where is the content"), + }; + let mut uper = UperWriter::default(); + uper.write(&p).unwrap(); + assert_eq!( + &[ + // https://asn1.io/asn1playground/ + 0x01, 0x7B, 0x02, 0x04, 0xD2, 0xE8, 0x28, 0xEE, 0xD0, 0xCA, 0xE4, 0xCA, 0x40, 0xD2, + 0xE6, 0x40, 0xE8, 0xD0, 0xCA, 0x40, 0xC6, 0xDE, 0xDC, 0xE8, 0xCA, 0xDC, 0xE8 + ], + uper.byte_content() + ); + assert_eq!(26 * 8 + 7, uper.bit_len()); +}