use proc_macro::TokenStream;
fn encode_components(components: &[num_bigint::BigUint], relative: bool) -> Vec<u8> {
use num_traits::cast::ToPrimitive;
let mut enc = Vec::new();
let mut dec = components;
if !relative {
if dec.len() < 2 {
if dec.len() == 1 && dec[0] == 0u8.into() {
return vec![0];
}
panic!("Need at least two components for non-relative oid");
}
if dec[0] >= 7u8.into() || dec[1] >= 40u8.into() {
panic!("First components are too big");
}
enc.push(dec[0].to_u8().unwrap() * 40 + dec[1].to_u8().unwrap());
dec = &dec[2..];
}
for int in dec.iter() {
let mut bytes = int.to_bytes_be();
if bytes[0] == 0 {
enc.push(0u8);
continue;
}
let total_bits = (8 - bytes[0].leading_zeros()) as usize + (bytes.len() - 1) * 8;
let octects_needed = ((total_bits + 6) / 7).max(1);
enc.resize_with(enc.len() + octects_needed, Default::default);
let mut pos = enc.len() - 1;
let mut extra = 0u8;
let mut extra_size = 0u8;
bytes.reverse();
let mut bytes_stored = 0;
for byte in bytes.into_iter() {
if extra_size == 7 {
enc[pos] = extra | (1 << 7);
bytes_stored += 1;
pos -= 1;
extra = 0;
extra_size = 0;
}
enc[pos] = (byte << extra_size) | extra | (1 << 7);
bytes_stored += 1;
if pos > 0 {
pos -= 1;
extra_size += 1;
extra = byte >> (8 - extra_size);
}
}
let last = enc.len() - 1;
if bytes_stored != octects_needed {
let first = last + 1 - octects_needed;
enc[first] = extra | (1 << 7);
}
enc[last] ^= 1 << 7;
}
enc
}
#[proc_macro]
pub fn encode_oid(input: TokenStream) -> TokenStream {
let s = input.to_string();
let (rem, relative) = if s.starts_with("rel ") {
(&s[4..], true)
} else {
(s.as_ref(), false)
};
let ints: Vec<num_bigint::BigUint> = rem
.split('.')
.map(|segment| segment.trim())
.map(|s| s.parse().unwrap())
.collect();
let enc = encode_components(&ints, relative);
let mut s = String::with_capacity(2 + 6 * enc.len());
s.push('[');
for byte in enc.iter() {
s.insert_str(s.len(), &format!("0x{:02x}, ", byte));
}
s.push(']');
s.parse().unwrap()
}