use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AperError(pub String);
impl fmt::Display for AperError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for AperError {}
pub type AperResult<T> = Result<T, AperError>;
pub fn err<T>(msg: impl Into<String>) -> AperResult<T> {
Err(AperError(msg.into()))
}
pub const MAX_UNFRAGMENTED: usize = 16_383;
pub fn bits_for_range(range: u64) -> u32 {
if range <= 1 {
0
} else {
64 - (range - 1).leading_zeros()
}
}
fn octets_for(value: u64) -> usize {
let bits = 64 - value.leading_zeros() as usize;
bits.div_ceil(8).max(1)
}
pub struct Reader<'a> {
data: &'a [u8],
pos: usize,
}
impl<'a> Reader<'a> {
pub fn new(data: &'a [u8]) -> Self {
Self { data, pos: 0 }
}
fn remaining_bits(&self) -> usize {
self.data.len() * 8 - self.pos
}
pub fn remaining_octets_aligned(&self) -> usize {
let aligned = self.pos.div_ceil(8);
self.data.len().saturating_sub(aligned)
}
pub fn align(&mut self) {
self.pos = self.pos.div_ceil(8) * 8;
}
pub fn bits(&mut self, n: u32, what: &str) -> AperResult<u64> {
if n > 64 {
return err(format!("{what}: {n}-bit field is wider than 64"));
}
if self.remaining_bits() < n as usize {
return err(format!(
"{what}: needs {n} bits, {} remain",
self.remaining_bits()
));
}
let mut value = 0u64;
for _ in 0..n {
let byte = self.data[self.pos / 8];
let bit = (byte >> (7 - (self.pos % 8))) & 1;
value = (value << 1) | bit as u64;
self.pos += 1;
}
Ok(value)
}
pub fn bit(&mut self, what: &str) -> AperResult<bool> {
Ok(self.bits(1, what)? == 1)
}
pub fn octets_aligned(&mut self, n: usize, what: &str) -> AperResult<&'a [u8]> {
self.align();
let start = self.pos / 8;
if self.data.len() - start < n {
return err(format!(
"{what}: declares {n} octets, {} remain",
self.data.len() - start
));
}
self.pos += n * 8;
Ok(&self.data[start..start + n])
}
pub fn octets_unaligned(&mut self, n: usize, what: &str) -> AperResult<Vec<u8>> {
let mut out = Vec::with_capacity(n);
for _ in 0..n {
out.push(self.bits(8, what)? as u8);
}
Ok(out)
}
pub fn constrained(&mut self, lb: u64, ub: u64, what: &str) -> AperResult<u64> {
if ub < lb {
return err(format!("{what}: empty range"));
}
let range = (ub - lb).checked_add(1);
let value = match range {
Some(1) => 0,
Some(r) if r <= 255 => self.bits(bits_for_range(r), what)?,
Some(256) => {
self.align();
self.bits(8, what)?
}
Some(r) if r <= 65_536 => {
self.align();
self.bits(16, what)?
}
_ => {
let max_octets = octets_for(ub - lb) as u64;
let len = self.bits(bits_for_range(max_octets), what)? + 1;
let raw = self.octets_aligned(len as usize, what)?;
raw.iter().fold(0u64, |acc, b| (acc << 8) | *b as u64)
}
};
let v = lb
.checked_add(value)
.ok_or_else(|| AperError(format!("{what}: value overflows")))?;
if v > ub {
return err(format!("{what}: {v} is above the upper bound {ub}"));
}
Ok(v)
}
pub fn general_length(&mut self, what: &str) -> AperResult<usize> {
self.align();
let first = self.bits(8, what)? as usize;
if first & 0x80 == 0 {
return Ok(first);
}
if first & 0xC0 == 0x80 {
let second = self.bits(8, what)? as usize;
return Ok(((first & 0x3F) << 8) | second);
}
err(format!(
"{what}: fragmented length (≥16384) is not accepted"
))
}
pub fn size(&mut self, lb: usize, ub: usize, what: &str) -> AperResult<usize> {
let n = if ub < 65_536 {
self.constrained(lb as u64, ub as u64, what)? as usize
} else {
self.general_length(what)?
};
if n < lb || n > ub {
return err(format!("{what}: size {n} outside {lb}..{ub}"));
}
Ok(n)
}
pub fn normally_small(&mut self, what: &str) -> AperResult<u64> {
if self.bit(what)? {
return err(format!(
"{what}: normally-small value above 63 is not accepted"
));
}
self.bits(6, what)
}
pub fn enumerated(
&mut self,
root: u64,
extensible: bool,
what: &str,
) -> AperResult<(bool, u64)> {
if extensible && self.bit(what)? {
return Ok((true, self.normally_small(what)?));
}
Ok((false, self.constrained(0, root - 1, what)?))
}
pub fn open_type(&mut self, what: &str) -> AperResult<&'a [u8]> {
let len = self.general_length(what)?;
if len > self.remaining_octets_aligned() {
return err(format!(
"{what}: open type declares {len} octets, {} remain",
self.remaining_octets_aligned()
));
}
self.octets_aligned(len, what)
}
pub fn octet_string(&mut self, max: usize, what: &str) -> AperResult<&'a [u8]> {
let len = self.general_length(what)?;
if len > max {
return err(format!(
"{what}: {len} octets exceeds the {max}-octet bound"
));
}
self.octets_aligned(len, what)
}
pub fn fixed_octets(&mut self, n: usize, what: &str) -> AperResult<Vec<u8>> {
if n <= 2 {
self.octets_unaligned(n, what)
} else {
Ok(self.octets_aligned(n, what)?.to_vec())
}
}
pub fn fixed_bits(&mut self, n: u32, what: &str) -> AperResult<u64> {
if n > 16 {
self.align();
}
self.bits(n, what)
}
pub fn varying_bits(&mut self, lb: u32, ub: u32, what: &str) -> AperResult<(u64, u32)> {
let n = self.size(lb as usize, ub as usize, what)? as u32;
if n > 64 {
return err(format!("{what}: {n} bits is wider than 64"));
}
self.align();
Ok((self.bits(n, what)?, n))
}
pub fn extension_bit(&mut self, what: &str) -> AperResult<bool> {
self.bit(what)
}
pub fn skip_extension_additions(&mut self, what: &str) -> AperResult<usize> {
let n = self.normally_small(what)? as u32 + 1;
let mut present = 0usize;
for _ in 0..n {
if self.bit(what)? {
present += 1;
}
}
for _ in 0..present {
self.open_type(what)?;
}
Ok(present)
}
pub fn printable_string(&mut self, lb: usize, ub: usize, what: &str) -> AperResult<String> {
let extended = self.bit(what)?;
let n = if extended {
let n = self.general_length(what)?;
if n > ub {
return err(format!("{what}: {n} characters exceeds {ub}"));
}
n
} else {
self.constrained(lb as u64, ub as u64, what)? as usize
};
let raw = if ub * 8 > 16 || extended {
self.octets_aligned(n, what)?.to_vec()
} else {
self.octets_unaligned(n, what)?
};
if !raw.iter().all(|b| is_printable(*b)) {
return err(format!("{what}: not a PrintableString"));
}
Ok(String::from_utf8_lossy(&raw).into_owned())
}
pub fn at_end(&self) -> bool {
let aligned = self.pos.div_ceil(8);
aligned >= self.data.len()
}
}
pub fn is_printable(b: u8) -> bool {
b.is_ascii_alphanumeric() || b" '()+,-./:=?".contains(&b)
}
#[derive(Default)]
pub struct Writer {
data: Vec<u8>,
bit_len: usize,
}
impl Writer {
pub fn new() -> Self {
Self::default()
}
pub fn bits(&mut self, value: u64, n: u32) {
for i in (0..n).rev() {
let bit = ((value >> i) & 1) as u8;
if self.bit_len.is_multiple_of(8) {
self.data.push(0);
}
if bit == 1 {
let last = self.data.len() - 1;
self.data[last] |= 1 << (7 - (self.bit_len % 8));
}
self.bit_len += 1;
}
}
pub fn bit(&mut self, b: bool) {
self.bits(b as u64, 1);
}
pub fn align(&mut self) {
self.bit_len = self.bit_len.div_ceil(8) * 8;
}
pub fn octets_aligned(&mut self, bytes: &[u8]) {
self.align();
self.data.extend_from_slice(bytes);
self.bit_len += bytes.len() * 8;
}
pub fn octets_unaligned(&mut self, bytes: &[u8]) {
for b in bytes {
self.bits(*b as u64, 8);
}
}
pub fn constrained(&mut self, value: u64, lb: u64, ub: u64, what: &str) -> AperResult<()> {
if value < lb || value > ub {
return err(format!("{what}: {value} outside {lb}..{ub}"));
}
let v = value - lb;
match (ub - lb).checked_add(1) {
Some(1) => {}
Some(r) if r <= 255 => self.bits(v, bits_for_range(r)),
Some(256) => {
self.align();
self.bits(v, 8);
}
Some(r) if r <= 65_536 => {
self.align();
self.bits(v, 16);
}
_ => {
let max_octets = octets_for(ub - lb) as u64;
let len = octets_for(v);
self.bits(len as u64 - 1, bits_for_range(max_octets));
self.align();
for i in (0..len).rev() {
self.bits((v >> (8 * i)) & 0xFF, 8);
}
}
}
Ok(())
}
pub fn general_length(&mut self, n: usize, what: &str) -> AperResult<()> {
self.align();
if n < 128 {
self.bits(n as u64, 8);
} else if n <= MAX_UNFRAGMENTED {
self.bits(0x8000 | n as u64, 16);
} else {
return err(format!(
"{what}: {n} would need a fragmented length, which NetGet does not write"
));
}
Ok(())
}
pub fn size(&mut self, n: usize, lb: usize, ub: usize, what: &str) -> AperResult<()> {
if n < lb || n > ub {
return err(format!("{what}: size {n} outside {lb}..{ub}"));
}
if ub < 65_536 {
self.constrained(n as u64, lb as u64, ub as u64, what)
} else {
self.general_length(n, what)
}
}
pub fn normally_small(&mut self, value: u64, what: &str) -> AperResult<()> {
if value > 63 {
return err(format!("{what}: {value} is not a normally-small number"));
}
self.bit(false);
self.bits(value, 6);
Ok(())
}
pub fn enumerated(
&mut self,
index: u64,
extended: bool,
root: u64,
extensible: bool,
what: &str,
) -> AperResult<()> {
if extensible {
self.bit(extended);
} else if extended {
return err(format!("{what}: not extensible"));
}
if extended {
self.normally_small(index, what)
} else {
self.constrained(index, 0, root - 1, what)
}
}
pub fn open_type(&mut self, inner: &[u8], what: &str) -> AperResult<()> {
self.general_length(inner.len(), what)?;
self.octets_aligned(inner);
Ok(())
}
pub fn octet_string(&mut self, bytes: &[u8], what: &str) -> AperResult<()> {
self.general_length(bytes.len(), what)?;
self.octets_aligned(bytes);
Ok(())
}
pub fn fixed_octets(&mut self, bytes: &[u8]) {
if bytes.len() <= 2 {
self.octets_unaligned(bytes);
} else {
self.octets_aligned(bytes);
}
}
pub fn fixed_bits(&mut self, value: u64, n: u32) {
if n > 16 {
self.align();
}
self.bits(value, n);
}
pub fn varying_bits(
&mut self,
value: u64,
n: u32,
lb: u32,
ub: u32,
what: &str,
) -> AperResult<()> {
if n < 64 && value >> n != 0 {
return err(format!("{what}: {value} does not fit in {n} bits"));
}
self.size(n as usize, lb as usize, ub as usize, what)?;
self.align();
self.bits(value, n);
Ok(())
}
pub fn printable_string(
&mut self,
s: &str,
lb: usize,
ub: usize,
what: &str,
) -> AperResult<()> {
let raw = s.as_bytes();
if !raw.iter().all(|b| is_printable(*b)) {
return err(format!(
"{what}: {s:?} is not a PrintableString (letters, digits, space and '()+,-./:=?)"
));
}
if raw.len() < lb || raw.len() > ub {
return err(format!(
"{what}: {} characters outside {lb}..{ub}",
raw.len()
));
}
self.bit(false);
self.constrained(raw.len() as u64, lb as u64, ub as u64, what)?;
if ub * 8 > 16 {
self.octets_aligned(raw);
} else {
self.octets_unaligned(raw);
}
Ok(())
}
pub fn finish(mut self) -> Vec<u8> {
if self.data.is_empty() {
self.data.push(0);
}
self.data
}
}