#![warn(missing_docs)]
use std::io;
use super::{Numeric, SignedNumeric, BitQueue,
Endianness, BigEndian, LittleEndian};
use huffman::WriteHuffmanTree;
pub struct BitWriter<'a, E: Endianness> {
writer: &'a mut io::Write,
bitqueue: BitQueue<E,u8>
}
impl<'a, E: Endianness> BitWriter<'a, E> {
pub fn new(writer: &mut io::Write) -> BitWriter<E> {
BitWriter{writer: writer, bitqueue: BitQueue::new()}
}
pub fn write_bit(&mut self, bit: bool) -> Result<(), io::Error> {
self.bitqueue.push(1, if bit {1} else {0});
if self.bitqueue.is_full() {
write_byte(self.writer, self.bitqueue.pop(8))
} else {
Ok(())
}
}
pub fn write<U>(&mut self, bits: u32, value: U) -> Result<(), io::Error>
where U: Numeric {
if bits > U::bits_size() {
Err(io::Error::new(io::ErrorKind::InvalidInput,
"excessive bits for type written"))
} else if (bits < U::bits_size()) && (value >= (U::one() << bits)) {
Err(io::Error::new(io::ErrorKind::InvalidInput,
"excessive value for bits written"))
} else if bits < self.bitqueue.remaining_len() {
Ok(self.bitqueue.push(bits, value.to_u8()))
} else {
let mut acc = BitQueue::from_value(value, bits);
write_unaligned(&mut self.writer, &mut acc, &mut self.bitqueue)
.and_then(|()|
write_aligned(&mut self.writer, &mut acc))
.and_then(|()|
Ok(self.bitqueue.push(acc.len(), acc.value().to_u8())))
}
}
pub fn write_bytes(&mut self, buf: &[u8]) -> Result<(), io::Error> {
if self.byte_aligned() {
self.writer.write_all(buf)
} else {
for b in buf {
self.write(8, *b)?;
}
Ok(())
}
}
pub fn write_huffman<T>(&mut self,
tree: &WriteHuffmanTree<E,T>,
symbol: T) ->
Result<(), io::Error> where T: Ord + Copy {
for &(bits, value) in tree.get(symbol) {
self.write(bits, value)?;
}
Ok(())
}
pub fn write_unary0(&mut self, value: u32) -> Result<(), io::Error> {
match value {
0 => {self.write_bit(false)}
bits @ 1...31 => {self.write(value, (1u32 << bits) - 1)
.and_then(|()| self.write_bit(false))}
32 => {self.write(value, 0xFFFFFFFFu32)
.and_then(|()| self.write_bit(false))}
bits @ 32...63 => {self.write(value, (1u64 << bits) - 1)
.and_then(|()| self.write_bit(false))}
64 => {self.write(value, 0xFFFFFFFFFFFFFFFFu64)
.and_then(|()| self.write_bit(false))}
mut bits => {while bits > 64 {
self.write(64, 0xFFFFFFFFFFFFFFFFu64)?;
bits -= 64;
}
self.write_unary0(bits)}
}
}
pub fn write_unary1(&mut self, value: u32) -> Result<(), io::Error> {
match value {
0 => {self.write_bit(true)}
1...32 => {self.write(value, 0u32)
.and_then(|()| self.write_bit(true))}
33...64 => {self.write(value, 0u64)
.and_then(|()| self.write_bit(true))}
mut bits => {while bits > 64 {self.write(64, 0u64)?; bits -= 64;}
self.write_unary1(bits)}
}
}
#[inline(always)]
pub fn byte_aligned(&self) -> bool {self.bitqueue.is_empty()}
pub fn byte_align(&mut self) -> Result<(), io::Error> {
while !self.byte_aligned() {
self.write_bit(false)?;
}
Ok(())
}
#[inline(always)]
pub fn into_unwritten(self) -> (u32, u8) {
(self.bitqueue.len(), self.bitqueue.value())
}
}
impl<'a> BitWriter<'a, BigEndian> {
pub fn write_signed<S>(&mut self, bits: u32, value: S) ->
Result<(), io::Error> where S: SignedNumeric {
if bits > S::bits_size() {
Err(io::Error::new(io::ErrorKind::InvalidInput,
"excessive bits for type written"))
} else if value.is_negative() {
self.write_bit(true)
.and_then(|()| self.write(bits - 1, value.as_unsigned(bits)))
} else {
self.write_bit(false)
.and_then(|()| self.write(bits - 1, value))
}
}
}
impl<'a> BitWriter<'a, LittleEndian> {
pub fn write_signed<S>(&mut self, bits: u32, value: S) ->
Result<(), io::Error> where S: SignedNumeric {
if bits > S::bits_size() {
Err(io::Error::new(io::ErrorKind::InvalidInput,
"excessive bits for type written"))
} else if value.is_negative() {
self.write(bits - 1, value.as_unsigned(bits))
.and_then(|()| self.write_bit(true))
} else {
self.write(bits - 1, value)
.and_then(|()| self.write_bit(false))
}
}
}
#[inline]
fn write_byte(writer: &mut io::Write, byte: u8) -> Result<(),io::Error> {
let buf = [byte];
writer.write_all(&buf)
}
fn write_unaligned<E,N>(writer: &mut io::Write,
acc: &mut BitQueue<E,N>,
rem: &mut BitQueue<E,u8>) -> Result<(), io::Error>
where E:Endianness, N: Numeric {
if rem.is_empty() {
Ok(())
} else {
use std::cmp::min;
let bits_to_transfer = min(8 - rem.len(), acc.len());
rem.push(bits_to_transfer, acc.pop(bits_to_transfer).to_u8());
if rem.len() == 8 {
write_byte(writer, rem.pop(8))
} else {
Ok(())
}
}
}
fn write_aligned<E,N>(writer: &mut io::Write,
acc: &mut BitQueue<E,N>) -> Result<(), io::Error>
where E: Endianness, N: Numeric {
let to_write = (acc.len() / 8) as usize;
if to_write > 0 {
debug_assert!(to_write <= 8);
let mut buf = [0; 8];
for b in buf[0..to_write].iter_mut() {
*b = acc.pop(8).to_u8();
}
writer.write_all(&buf[0..to_write])
} else {
Ok(())
}
}