use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use bytes::{Buf, BufMut, Bytes, IntoBuf};
use num_bigint::BigUint;
use std::fmt::Debug;
use std::io::{self, Read, Result, Write};
const HIGH_BIT: u8 = 0x80;
const LOWER_SEVEN_BITS: u8 = 0x7f;
pub trait ReadOerExt: Read + ReadBytesExt + Debug {
#[inline]
fn read_var_octet_string(&mut self) -> Result<Vec<u8>> {
let length: u8 = self.read_u8()?;
if length == 0 {
return Ok(vec![]);
}
let actual_length: u64 = if length & HIGH_BIT != 0 {
let length_prefix_length = length & LOWER_SEVEN_BITS;
self.read_uint::<BigEndian>(length_prefix_length as usize)? as u64
} else {
u64::from(length)
};
let mut buf = Vec::with_capacity(actual_length as usize);
self.take(actual_length).read_to_end(&mut buf)?;
Ok(buf)
}
#[inline]
fn read_var_uint(&mut self) -> Result<BigUint> {
let contents = self.read_var_octet_string()?;
Ok(BigUint::from_bytes_be(&contents))
}
}
impl<R: io::Read + ?Sized + Debug> ReadOerExt for R {}
pub trait WriteOerExt: Write + WriteBytesExt + Debug {
#[inline]
fn write_var_octet_string(&mut self, string: &[u8]) -> Result<()> {
let length = string.len();
if length < 127 {
self.write_u8(length as u8)?;
} else {
let bit_length_of_length = format!("{:b}", length).chars().count();
let length_of_length = { bit_length_of_length as f32 / 8.0 }.ceil() as u8;
self.write_u8(HIGH_BIT | length_of_length)?;
self.write_uint::<BigEndian>(length as u64, length_of_length as usize)?;
}
self.write_all(string)?;
Ok(())
}
#[inline]
fn write_var_uint(&mut self, uint: &BigUint) -> Result<()> {
self.write_var_octet_string(&uint.to_bytes_be())?;
Ok(())
}
}
impl<W: io::Write + ?Sized + Debug> WriteOerExt for W {}
pub trait BufOerExt: Buf + Sized {
#[inline]
fn get_var_octet_string(&mut self) -> Bytes {
let length: u8 = self.get_u8();
if length == 0 {
return Bytes::new();
}
let actual_length: usize = if length & HIGH_BIT != 0 {
let length_prefix_length = length & LOWER_SEVEN_BITS;
self.get_uint_be(length_prefix_length as usize) as usize
} else {
length as usize
};
let buf = Bytes::from(self.bytes()).slice_to(actual_length);
self.advance(actual_length);
buf
}
#[inline]
fn get_var_uint(&mut self) -> BigUint {
let contents = self.get_var_octet_string();
BigUint::from_bytes_be(&contents[..])
}
}
impl<B: Buf + Sized> BufOerExt for B {}
pub trait MutBufOerExt: BufMut + Sized {
#[inline]
fn put_var_octet_string<B>(&mut self, buf: B)
where
B: IntoBuf,
{
let buf = buf.into_buf();
let length = buf.remaining();
if length < 127 {
self.put_u8(length as u8);
} else {
let bit_length_of_length = format!("{:b}", length).chars().count();
let length_of_length = { bit_length_of_length as f32 / 8.0 }.ceil() as u8;
self.put_u8(HIGH_BIT | length_of_length);
self.put_uint_be(length as u64, length_of_length as usize);
}
self.put(buf);
}
#[inline]
fn put_var_uint(&mut self, uint: &BigUint) {
self.put_var_octet_string(uint.to_bytes_be());
}
}
impl<B: BufMut + Sized> MutBufOerExt for B {}
#[cfg(test)]
mod writer_ext {
use super::*;
#[test]
fn it_writes_var_octet_strings() {
let mut empty = vec![];
empty.write_var_octet_string(&[]).unwrap();
assert_eq!(empty, vec![0]);
let mut one = vec![];
one.write_var_octet_string(&[0xb0]).unwrap();
assert_eq!(one, vec![0x01, 0xb0]);
let mut larger = vec![];
let mut larger_string: Vec<u8> = Vec::with_capacity(256 as usize);
for _ in 0..256 {
larger_string.push(0xb0);
}
larger.write_var_octet_string(&larger_string).unwrap();
let mut expected = vec![0x82, 0x01, 0x00];
expected.extend(larger_string);
assert_eq!(larger.len(), 259);
assert_eq!(larger, expected);
}
}
#[cfg(test)]
mod reader_ext {
use super::*;
use std::io::Cursor;
#[test]
fn it_reads_var_octet_strings() {
let nothing = vec![0];
assert_eq!(
Cursor::new(nothing).read_var_octet_string().unwrap().len(),
0
);
let two_bytes = vec![0x01, 0xb0];
assert_eq!(
Cursor::new(two_bytes).read_var_octet_string().unwrap(),
&[0xb0]
);
let mut larger = vec![0x82, 0x01, 0x00];
let mut larger_string: Vec<u8> = Vec::with_capacity(256 as usize);
for _ in 0..256 {
larger_string.push(0xb0);
}
larger.extend(&larger_string);
assert_eq!(
Cursor::new(larger).read_var_octet_string().unwrap(),
&larger_string[..]
);
}
}