use crate::error::Result;
use crate::{
segment_table, Page, PageHeader, CONTAINS_FIRST_PAGE_OF_BITSTREAM,
CONTAINS_LAST_PAGE_OF_BITSTREAM, CONTINUED_PACKET, MAX_WRITTEN_CONTENT_SIZE,
MAX_WRITTEN_SEGMENT_COUNT,
};
use std::io::Read;
struct PaginateContext {
pages: Vec<Page>,
abgp: u64,
stream_serial: u32,
header_flags: u8,
flags: PaginateContextFlags,
pos: u64,
idx: usize,
remaining_page_size: usize,
current_packet_len: usize,
}
impl PaginateContext {
fn new(abgp: u64, stream_serial: u32, header_flags: u8) -> Self {
Self {
pages: Vec::new(),
abgp,
stream_serial,
header_flags,
flags: PaginateContextFlags {
first_page: true,
fresh_packet: true,
packet_spans_multiple_pages: false,
packet_finished_on_page: false,
},
pos: 0,
idx: 0,
remaining_page_size: MAX_WRITTEN_CONTENT_SIZE,
current_packet_len: 0,
}
}
fn flush(&mut self, content: &mut Vec<u8>, segment_table: &mut Vec<u8>) {
let mut header = PageHeader {
start: self.pos,
header_type_flag: {
match self.flags.first_page {
true if self.header_flags & CONTAINS_FIRST_PAGE_OF_BITSTREAM != 0 => {
CONTAINS_FIRST_PAGE_OF_BITSTREAM
},
false if !self.flags.fresh_packet => CONTINUED_PACKET,
_ => 0,
}
},
abgp: if self.flags.packet_finished_on_page {
self.abgp
} else {
1_u64.wrapping_neg()
},
stream_serial: self.stream_serial,
sequence_number: self.idx as u32,
segments: Vec::new(),
checksum: 0,
};
let content = core::mem::take(content);
let content_len = content.len();
self.pos += content_len as u64;
if self.pos > self.current_packet_len as u64 {
self.flags.packet_spans_multiple_pages = false;
}
let segments_occupied = if content_len >= 255 {
content_len / 255
} else {
1
};
if self.flags.packet_spans_multiple_pages {
header.segments = segment_table.drain(..segments_occupied).collect();
} else {
header.segments = core::mem::take(segment_table);
}
self.pages.push(Page {
content,
header,
end: self.pos,
});
self.idx += 1;
self.flags.packet_finished_on_page = false;
self.remaining_page_size = MAX_WRITTEN_CONTENT_SIZE;
}
}
struct PaginateContextFlags {
first_page: bool,
fresh_packet: bool,
packet_spans_multiple_pages: bool,
packet_finished_on_page: bool,
}
pub fn paginate<'a, I: 'a>(
packets: I,
stream_serial: u32,
abgp: u64,
flags: u8,
) -> Result<Vec<Page>>
where
I: IntoIterator<Item = &'a [u8]>,
{
let mut ctx = PaginateContext::new(abgp, stream_serial, flags);
let mut packets_iter = packets.into_iter();
let mut packet = match packets_iter.next() {
Some(packet) => packet,
None => return Ok(ctx.pages),
};
ctx.current_packet_len = packet.len();
let mut segments = segment_table(packet.len());
let mut page_content = Vec::new();
loop {
if !ctx.flags.packet_spans_multiple_pages && !ctx.flags.first_page {
match packets_iter.next() {
Some(packet_) => {
packet = packet_;
segments.append(&mut segment_table(packet.len()));
ctx.current_packet_len = packet.len();
ctx.flags.fresh_packet = true;
},
None => break,
};
}
let bytes_read = packet
.take(ctx.remaining_page_size as u64)
.read_to_end(&mut page_content)?;
ctx.remaining_page_size -= bytes_read;
packet = &packet[bytes_read..];
if packet.is_empty() {
ctx.flags.packet_finished_on_page = true;
}
let first_page_of_bitstream = ctx.header_flags & CONTAINS_FIRST_PAGE_OF_BITSTREAM != 0;
let first_packet_finished_on_page =
ctx.flags.first_page && first_page_of_bitstream && ctx.flags.packet_finished_on_page;
let segments_required = (packet.len() / MAX_WRITTEN_SEGMENT_COUNT) + 1;
let remaining_segments = MAX_WRITTEN_SEGMENT_COUNT.saturating_sub(segments.len());
ctx.flags.packet_spans_multiple_pages = segments_required > remaining_segments;
if first_packet_finished_on_page
|| (ctx.remaining_page_size == 0 || remaining_segments == 0)
|| packet.is_empty()
{
ctx.flush(&mut page_content, &mut segments);
}
ctx.flags.first_page = false;
ctx.flags.fresh_packet = false;
}
if !page_content.is_empty() {
ctx.flush(&mut page_content, &mut segments);
}
if flags & CONTAINS_LAST_PAGE_OF_BITSTREAM == 0x04 {
if let Some(last) = ctx.pages.last_mut() {
last.header.header_type_flag |= CONTAINS_LAST_PAGE_OF_BITSTREAM;
}
}
Ok(ctx.pages)
}