Module bitstream_io::write

source ·
Expand description

Traits and implementations for writing bits to a stream.

Example

Writing the initial STREAMINFO block to a FLAC file, as documented in its specification.

use std::convert::TryInto;
use std::io::Write;
use bitstream_io::{BigEndian, BitWriter, BitWrite, ByteWriter, ByteWrite, LittleEndian, ToBitStream};

#[derive(Debug, PartialEq, Eq)]
struct BlockHeader {
    last_block: bool,
    block_type: u8,
    block_size: u32,
}

impl ToBitStream for BlockHeader {
    type Error = std::io::Error;

    fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> std::io::Result<()> {
        w.write_bit(self.last_block)?;
        w.write(7, self.block_type)?;
        w.write(24, self.block_size)
    }
}

#[derive(Debug, PartialEq, Eq)]
struct Streaminfo {
    minimum_block_size: u16,
    maximum_block_size: u16,
    minimum_frame_size: u32,
    maximum_frame_size: u32,
    sample_rate: u32,
    channels: u8,
    bits_per_sample: u8,
    total_samples: u64,
    md5: [u8; 16],
}

impl ToBitStream for Streaminfo {
    type Error = std::io::Error;

    fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> std::io::Result<()> {
        w.write_from(self.minimum_block_size)?;
        w.write_from(self.maximum_block_size)?;
        w.write(24, self.minimum_frame_size)?;
        w.write(24, self.maximum_frame_size)?;
        w.write(20, self.sample_rate)?;
        w.write(3, self.channels - 1)?;
        w.write(5, self.bits_per_sample - 1)?;
        w.write(36, self.total_samples)?;
        w.write_bytes(&self.md5)
    }
}

#[derive(Debug, PartialEq, Eq)]
struct VorbisComment {
    vendor: String,
    comment: Vec<String>,
}

impl VorbisComment {
    fn len(&self) -> usize {
        4 + self.vendor.len() + 4 + self.comment.iter().map(|c| 4 + c.len()).sum::<usize>()
    }

    fn write<W: std::io::Write>(&self, w: &mut ByteWriter<W, LittleEndian>) -> std::io::Result<()> {
        use std::convert::TryInto;

        fn write_entry<W: std::io::Write>(
            w: &mut ByteWriter<W, LittleEndian>,
            s: &str,
        ) -> std::io::Result<()> {
            w.write::<u32>(s.len().try_into().unwrap())?;
            w.write_bytes(s.as_bytes())
        }

        write_entry(w, &self.vendor)?;
        w.write::<u32>(self.comment.len().try_into().unwrap())?;
        self.comment.iter().try_for_each(|s| write_entry(w, s))
    }
}

let mut flac: Vec<u8> = Vec::new();

let mut writer = BitWriter::endian(&mut flac, BigEndian);

// stream marker
writer.write_bytes(b"fLaC").unwrap();

// metadata block header
writer.build(&BlockHeader { last_block: false, block_type: 0, block_size: 34 }).unwrap();

// STREAMINFO block
writer.build(&Streaminfo {
    minimum_block_size: 4096,
    maximum_block_size: 4096,
    minimum_frame_size: 1542,
    maximum_frame_size: 8546,
    sample_rate: 44100,
    channels: 2,
    bits_per_sample: 16,
    total_samples: 304844,
    md5: *b"\xFA\xF2\x69\x2F\xFD\xEC\x2D\x5B\x30\x01\x76\xB4\x62\x88\x7D\x92",
}).unwrap();

let comment = VorbisComment {
    vendor: "reference libFLAC 1.1.4 20070213".to_string(),
    comment: vec![
        "title=2ch 44100  16bit".to_string(),
        "album=Test Album".to_string(),
        "artist=Assorted".to_string(),
        "tracknumber=1".to_string(),
    ],
};

// metadata block header
writer.build(
    &BlockHeader {
        last_block: false,
        block_type: 4,
        block_size: comment.len().try_into().unwrap(),
    }
).unwrap();

// VORBIS_COMMENT block (little endian)
comment.write(&mut ByteWriter::new(writer.writer().unwrap())).unwrap();

assert_eq!(flac, vec![0x66,0x4c,0x61,0x43,0x00,0x00,0x00,0x22,
                      0x10,0x00,0x10,0x00,0x00,0x06,0x06,0x00,
                      0x21,0x62,0x0a,0xc4,0x42,0xf0,0x00,0x04,
                      0xa6,0xcc,0xfa,0xf2,0x69,0x2f,0xfd,0xec,
                      0x2d,0x5b,0x30,0x01,0x76,0xb4,0x62,0x88,
                      0x7d,0x92,0x04,0x00,0x00,0x7a,0x20,0x00,
                      0x00,0x00,0x72,0x65,0x66,0x65,0x72,0x65,
                      0x6e,0x63,0x65,0x20,0x6c,0x69,0x62,0x46,
                      0x4c,0x41,0x43,0x20,0x31,0x2e,0x31,0x2e,
                      0x34,0x20,0x32,0x30,0x30,0x37,0x30,0x32,
                      0x31,0x33,0x04,0x00,0x00,0x00,0x16,0x00,
                      0x00,0x00,0x74,0x69,0x74,0x6c,0x65,0x3d,
                      0x32,0x63,0x68,0x20,0x34,0x34,0x31,0x30,
                      0x30,0x20,0x20,0x31,0x36,0x62,0x69,0x74,
                      0x10,0x00,0x00,0x00,0x61,0x6c,0x62,0x75,
                      0x6d,0x3d,0x54,0x65,0x73,0x74,0x20,0x41,
                      0x6c,0x62,0x75,0x6d,0x0f,0x00,0x00,0x00,
                      0x61,0x72,0x74,0x69,0x73,0x74,0x3d,0x41,
                      0x73,0x73,0x6f,0x72,0x74,0x65,0x64,0x0d,
                      0x00,0x00,0x00,0x74,0x72,0x61,0x63,0x6b,
                      0x6e,0x75,0x6d,0x62,0x65,0x72,0x3d,0x31]);

Structs

  • For counting the number of bits written but generating no output.
  • For recording writes in order to play them back on another writer
  • For writing bit values to an underlying stream in a given endianness.
  • For writing aligned bytes to a stream of bytes in a given endianness.
  • A generic signed value for stream recording purposes
  • A generic unsigned value for stream recording purposes

Traits

  • A trait for anything that can write a variable number of potentially un-aligned values to an output stream
  • A trait for anything that can write aligned values to an output stream
  • A trait for anything that can write Huffman codes of a given endianness to an output stream
  • Implemented by complex types that don’t require any additional context to build themselves to a writer
  • Implemented by complex types that require additional context to build themselves to a writer
  • Implemented by complex types that don’t require any additional context to build themselves to a writer
  • Implemented by complex types that require additional context to build themselves to a writer