#![allow(
unknown_lints,
clippy::too_many_lines,
clippy::cast_precision_loss,
clippy::cast_sign_loss,
clippy::cast_possible_wrap,
clippy::cast_possible_truncation,
clippy::module_name_repetitions,
clippy::must_use_candidate,
clippy::doc_markdown,
let_underscore_drop,
clippy::match_wildcard_for_single_variants,
clippy::semicolon_if_nothing_returned,
clippy::new_without_default,
clippy::from_over_into,
clippy::upper_case_acronyms,
clippy::single_match_else,
clippy::similar_names,
clippy::tabs_in_doc_comments,
clippy::len_without_is_empty,
clippy::needless_late_init,
clippy::type_complexity,
clippy::type_repetition_in_bounds,
unused_qualifications,
clippy::return_self_not_must_use,
clippy::bool_to_int_with_if,
clippy::uninlined_format_args, clippy::manual_let_else,
clippy::struct_excessive_bools,
clippy::match_bool
)]
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::{PageHeader, PAGE_HEADER_SIZE};
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_WRITTEN_CONTENT_SIZE * 4;
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 extend(&mut self, content: &[u8]) -> Option<Page> {
let self_len = self.content.len();
let content_len = content.len();
if self_len + content_len <= MAX_CONTENT_SIZE {
self.content.extend(content.iter());
self.end += content_len as u64;
return None;
}
if content_len <= MAX_CONTENT_SIZE {
let remaining = 65025 - self_len;
self.content.extend(content[0..remaining].iter());
self.header.header_type_flag = 0;
self.header.abgp = 1_u64.wrapping_neg(); self.end += remaining as u64;
let mut p = Page {
content: content[remaining..].to_vec(),
header: PageHeader {
start: self.end,
header_type_flag: 1,
abgp: 0,
stream_serial: self.header.stream_serial,
sequence_number: self.header.sequence_number + 1,
segments: segment_table(remaining),
checksum: 0,
},
end: self.header().start + content.len() as u64,
};
p.gen_crc();
return Some(p);
}
None
}
pub fn content(&self) -> &[u8] {
self.content.as_slice()
}
#[must_use]
pub fn take_content(self) -> Vec<u8> {
self.content
}
#[must_use]
pub fn segment_table(&self) -> Vec<u8> {
segment_table(self.content.len())
}
}
#[must_use]
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
}
#[cfg(test)]
mod tests {
use crate::{paginate, segment_table, Page, PageHeader};
use std::io::Cursor;
#[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: 1759377061,
sequence_number: 0,
segments: vec![19],
checksum: 3579522525,
},
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);
}
}
}
}