use std::result;
use std::io::{self, Cursor, Write, Seek, SeekFrom};
use byteorder::{WriteBytesExt, LittleEndian};
use std::collections::HashMap;
use crc::vorbis_crc32_update;
type IoResult<T> = result::Result<T, io::Error>;
pub struct PacketWriter<T :io::Write> {
wtr :T,
page_vals :HashMap<u32, CurrentPageValues>,
}
struct CurrentPageValues {
first_page :bool,
sequence_num :u32,
segment_cnt :u8,
cur_pg_lacing :[u8; 255],
cur_pg_data :Vec<(Box<[u8]>, u64)>,
pck_this_overflow_idx :Option<usize>,
pck_last_overflow_idx :Option<usize>,
}
#[derive(PartialEq)]
#[derive(Clone, Copy)]
pub enum PacketWriteEndInfo {
NormalPacket,
EndPage,
EndStream,
}
impl <T :io::Write> PacketWriter<T> {
pub fn new(wtr :T) -> Self {
return PacketWriter {
wtr,
page_vals : HashMap::new(),
};
}
pub fn into_inner(self) -> T {
self.wtr
}
pub fn inner(&self) -> &T {
&self.wtr
}
pub fn inner_mut(&mut self) -> &mut T {
&mut self.wtr
}
pub fn write_packet(&mut self, pck_cont :Box<[u8]>, serial :u32,
inf :PacketWriteEndInfo,
absgp :u64) -> IoResult<()> {
let is_end_stream :bool = inf == PacketWriteEndInfo::EndStream;
let pg = self.page_vals.entry(serial).or_insert(
CurrentPageValues {
first_page : true,
sequence_num : 0,
segment_cnt : 0,
cur_pg_lacing :[0; 255],
cur_pg_data :Vec::with_capacity(255),
pck_this_overflow_idx : None,
pck_last_overflow_idx : None,
}
);
let cont_len = pck_cont.len();
pg.cur_pg_data.push((pck_cont, absgp));
let last_data_segment_size = (cont_len % 255) as u8;
let needed_segments :usize = (cont_len / 255) + 1;
let mut segment_in_page_i :u8 = pg.segment_cnt;
let mut at_page_end :bool = false;
for segment_i in 0 .. needed_segments {
at_page_end = false;
if segment_i + 1 < needed_segments {
pg.cur_pg_lacing[segment_in_page_i as usize] = 255;
} else {
pg.cur_pg_lacing[segment_in_page_i as usize] = last_data_segment_size;
}
pg.segment_cnt = segment_in_page_i + 1;
segment_in_page_i = (segment_in_page_i + 1) % 255;
if segment_in_page_i == 0 {
if segment_i + 1 < needed_segments {
pg.pck_this_overflow_idx = Some((segment_i + 1) * 255);
tri!(PacketWriter::write_page(&mut self.wtr, serial, pg,
false));
} else {
tri!(PacketWriter::write_page(&mut self.wtr,
serial, pg, is_end_stream));
pg.pck_this_overflow_idx = None;
pg.pck_last_overflow_idx = None;
}
at_page_end = true;
}
}
if (inf != PacketWriteEndInfo::NormalPacket) && !at_page_end {
tri!(PacketWriter::write_page(&mut self.wtr, serial, pg,
is_end_stream));
pg.pck_last_overflow_idx = None;
}
Ok(())
}
fn write_page(wtr :&mut T, serial :u32, pg :&mut CurrentPageValues,
last_page :bool) -> IoResult<()> {
{
let mut hdr_cur = Cursor::new(Vec::with_capacity(27));
tri!(hdr_cur.write_all(&[0x4f, 0x67, 0x67, 0x53, 0x00]));
let mut flags :u8 = 0;
if pg.pck_last_overflow_idx.is_some() { flags |= 0x01; }
if pg.first_page { flags |= 0x02; }
if last_page { flags |= 0x04; }
tri!(hdr_cur.write_u8(flags));
let pck_data = &pg.cur_pg_data;
let mut last_finishing_pck_absgp = (-1i64) as u64;
for (idx, &(_, absgp)) in pck_data.iter().enumerate() {
if !(idx + 1 == pck_data.len() &&
pg.pck_this_overflow_idx.is_some()) {
last_finishing_pck_absgp = absgp;
}
}
tri!(hdr_cur.write_u64::<LittleEndian>(last_finishing_pck_absgp));
tri!(hdr_cur.write_u32::<LittleEndian>(serial));
tri!(hdr_cur.write_u32::<LittleEndian>(pg.sequence_num));
tri!(hdr_cur.write_u32::<LittleEndian>(0));
tri!(hdr_cur.write_u8(pg.segment_cnt));
let mut hash_calculated :u32;
let pg_lacing = &pg.cur_pg_lacing[0 .. pg.segment_cnt as usize];
hash_calculated = vorbis_crc32_update(0, hdr_cur.get_ref());
hash_calculated = vorbis_crc32_update(hash_calculated, pg_lacing);
for (idx, &(ref pck, _)) in pck_data.iter().enumerate() {
let mut start :usize = 0;
if idx == 0 { if let Some(idx) = pg.pck_last_overflow_idx {
start = idx;
}}
let mut end :usize = pck.len();
if idx + 1 == pck_data.len() {
if let Some(idx) = pg.pck_this_overflow_idx {
end = idx;
}
}
hash_calculated = vorbis_crc32_update(hash_calculated,
&pck[start .. end]);
}
tri!(hdr_cur.seek(SeekFrom::Start(22)));
tri!(hdr_cur.write_u32::<LittleEndian>(hash_calculated));
tri!(wtr.write_all(hdr_cur.get_ref()));
tri!(wtr.write_all(pg_lacing));
for (idx, &(ref pck, _)) in pck_data.iter().enumerate() {
let mut start :usize = 0;
if idx == 0 { if let Some(idx) = pg.pck_last_overflow_idx {
start = idx;
}}
let mut end :usize = pck.len();
if idx + 1 == pck_data.len() {
if let Some(idx) = pg.pck_this_overflow_idx {
end = idx;
}
}
tri!(wtr.write_all(&pck[start .. end]));
}
}
pg.first_page = false;
pg.sequence_num += 1;
pg.segment_cnt = 0;
if pg.pck_this_overflow_idx.is_some() {
let d = pg.cur_pg_data.pop().unwrap();
pg.cur_pg_data.clear();
pg.cur_pg_data.push(d);
} else {
pg.cur_pg_data.clear();
}
pg.pck_last_overflow_idx = pg.pck_this_overflow_idx;
pg.pck_this_overflow_idx = None;
return Ok(());
}
}
impl<T :io::Seek + io::Write> PacketWriter<T> {
pub fn get_current_offs(&mut self) -> Result<u64, io::Error> {
self.wtr.seek(SeekFrom::Current(0))
}
}
#[test]
fn test_recapture() {
use std::io::Write;
use super::PacketReader;
let mut c = Cursor::new(Vec::new());
let test_arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let test_arr_2 = [2, 4, 8, 16, 32, 64, 128, 127, 126, 125, 124];
let test_arr_3 = [3, 5, 9, 17, 33, 65, 129, 129, 127, 126, 125];
{
let np = PacketWriteEndInfo::NormalPacket;
let ep = PacketWriteEndInfo::EndPage;
{
let mut w = PacketWriter::new(&mut c);
w.write_packet(Box::new(test_arr), 0xdeadb33f, ep, 0).unwrap();
w.wtr.write_all(&[0; 38]).unwrap();
w.write_packet(Box::new(test_arr_2), 0xdeadb33f, np, 1).unwrap();
w.write_packet(Box::new(test_arr_3), 0xdeadb33f, ep, 2).unwrap();
}
}
assert_eq!(c.seek(SeekFrom::Start(0)).unwrap(), 0);
{
let mut r = PacketReader::new(c);
let p1 = r.read_packet().unwrap().unwrap();
assert_eq!(test_arr, *p1.data);
let p2 = r.read_packet().unwrap().unwrap();
assert_eq!(test_arr_2, *p2.data);
let p3 = r.read_packet().unwrap().unwrap();
assert_eq!(test_arr_3, *p3.data);
}
}