mod crc;
mod error;
mod header;
mod packets;
mod paginate;
use std::io::{Read, Seek};
pub use crc::crc32;
pub use error::{PageError, Result};
pub use header::{PAGE_HEADER_SIZE, PageHeader};
pub use packets::{Packets, PacketsIter};
pub use paginate::paginate;
const CONTINUED_PACKET: u8 = 0x01;
pub(crate) const MAX_WRITTEN_SEGMENT_COUNT: usize = 32;
pub(crate) const MAX_WRITTEN_CONTENT_SIZE: usize = MAX_WRITTEN_SEGMENT_COUNT * 255;
pub const MAX_CONTENT_SIZE: usize = MAX_SEGMENT_COUNT * 255;
pub const MAX_SEGMENT_COUNT: usize = 255;
pub const CONTAINS_FIRST_PAGE_OF_BITSTREAM: u8 = 0x02;
pub const CONTAINS_LAST_PAGE_OF_BITSTREAM: u8 = 0x04;
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Page {
content: Vec<u8>,
header: PageHeader,
pub end: u64,
}
impl Page {
pub fn header(&self) -> &PageHeader {
&self.header
}
pub fn header_mut(&mut self) -> &mut PageHeader {
&mut self.header
}
#[must_use]
pub fn as_bytes(&self) -> Vec<u8> {
let segment_table = &self.header.segments;
let num_segments = segment_table.len();
let mut bytes =
Vec::with_capacity(PAGE_HEADER_SIZE + num_segments + self.header.content_size());
bytes.extend(b"OggS");
bytes.push(0); bytes.push(self.header.header_type_flag);
bytes.extend(self.header.abgp.to_le_bytes());
bytes.extend(self.header.stream_serial.to_le_bytes());
bytes.extend(self.header.sequence_number.to_le_bytes());
bytes.extend(self.header.checksum.to_le_bytes());
bytes.push(num_segments as u8);
bytes.extend(segment_table);
bytes.extend(self.content.iter());
bytes
}
pub fn read<V>(data: &mut V) -> Result<Self>
where
V: Read + Seek,
{
let header = PageHeader::read(data)?;
let mut content = vec![0; header.content_size()];
data.read_exact(&mut content)?;
let end = data.stream_position()?;
Ok(Page {
content,
header,
end,
})
}
pub fn gen_crc(&mut self) {
self.header.checksum = 0;
self.header.checksum = crc::crc32(&self.as_bytes());
}
pub fn content(&self) -> &[u8] {
self.content.as_slice()
}
#[must_use]
pub fn take_content(self) -> Vec<u8> {
self.content
}
}
#[cfg(test)]
mod tests {
use crate::{Page, PageHeader, paginate};
use std::io::Cursor;
pub fn segment_table(length: usize) -> Vec<u8> {
if length == 0 {
return vec![1, 0];
}
let last_len = (length % 255) as u8;
let needed = (length / 255) + 1;
let mut segments = Vec::with_capacity(needed);
for i in 0..needed {
if i + 1 < needed {
segments.push(255);
} else {
segments.push(last_len);
}
}
segments
}
#[test]
fn opus_ident_header() {
let expected = Page {
content: vec![
0x4F, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64, 0x01, 0x02, 0x38, 0x01, 0x80, 0xBB,
0, 0, 0, 0, 0,
],
header: PageHeader {
start: 0,
header_type_flag: 2,
abgp: 0,
stream_serial: 1_759_377_061,
sequence_number: 0,
segments: vec![19],
checksum: 3_579_522_525,
},
end: 47,
};
let content = std::fs::read("test_assets/opus_ident_header.page").unwrap();
let page = Page::read(&mut Cursor::new(content)).unwrap();
assert_eq!(expected, page);
}
#[test]
fn paginate_large() {
let packet = std::fs::read("test_assets/large_comment_packet.page").unwrap();
let pages = paginate([packet.as_slice()], 1234, 0, 0).unwrap();
let len = pages.len();
assert_eq!(len, 130);
let last_page_content = pages.last().unwrap().content();
assert_eq!(
last_page_content.len() % 255,
*segment_table(last_page_content.len()).last().unwrap() as usize
);
for (i, page) in pages.into_iter().enumerate() {
let header = page.header();
assert_eq!(header.stream_serial, 1234);
if i + 1 == len {
assert_eq!(header.abgp, 0);
} else {
assert_eq!(header.abgp, u64::MAX);
}
assert_eq!(header.sequence_number, i as u32);
if i == 0 {
assert_eq!(header.header_type_flag, 0);
} else {
assert_eq!(header.header_type_flag, 1);
}
if i + 1 == len {
let segments = &header.segments[..header.segments.len()];
for s in &segments[..segments.len() - 1] {
assert_eq!(*s, 255);
}
assert_eq!(segments.last(), Some(&171));
} else {
assert_eq!(header.segments, vec![255; super::MAX_WRITTEN_SEGMENT_COUNT]);
}
}
}
#[test]
fn paginate_large_perfectly_divisible() {
const PAGES_TO_WRITE: usize = 20;
const PACKET_SIZE: usize = (super::MAX_WRITTEN_SEGMENT_COUNT * 255) * PAGES_TO_WRITE;
let packet = vec![0; PACKET_SIZE];
let pages = paginate([packet.as_slice()], 1234, 0, 0).unwrap();
let len = pages.len();
assert_eq!(len, PAGES_TO_WRITE + 1);
for (i, page) in pages.iter().enumerate() {
if i + 1 == len {
break;
}
assert!(page.header.segments.iter().all(|c| *c == 255));
}
let last = pages.last().unwrap();
assert_eq!(last.header.segments.len(), 1);
assert_eq!(*last.header.segments.first().unwrap(), 0);
let mut total_size = 0;
for page in pages {
total_size += page
.header
.segments
.iter()
.map(|&b| usize::from(b))
.sum::<usize>();
}
assert_eq!(total_size, PACKET_SIZE);
}
#[test]
fn paginate_perfectly_divisible_terminate() {
const SEGMENTS: usize = 17;
const PACKET_SIZE: usize = SEGMENTS * 255;
let packet = vec![0; PACKET_SIZE];
let pages = paginate([packet.as_slice()], 1234, 0, 0).unwrap();
let len = pages.len();
assert_eq!(len, 1);
let page = &pages[0];
assert_eq!(page.header.segments.len(), SEGMENTS + 1);
let correct_number_of_segments = page
.header
.segments
.iter()
.take(SEGMENTS)
.all(|&b| b == 255);
assert!(correct_number_of_segments);
assert_eq!(*page.header.segments.last().unwrap(), 0);
}
}