@@ -16,7 +16,7 @@ struct PaginateContext {
1616 idx : usize ,
1717 remaining_page_size : usize ,
1818 current_packet_len : usize ,
19- last_segment_size : u8 ,
19+ last_segment_size : Option < u8 > ,
2020}
2121
2222impl PaginateContext {
@@ -29,14 +29,14 @@ impl PaginateContext {
2929 flags : PaginateContextFlags {
3030 first_page : true ,
3131 fresh_packet : true ,
32- packet_spans_multiple_pages : false ,
3332 packet_finished_on_page : false ,
33+ need_nil_page : false ,
3434 } ,
3535 pos : 0 ,
3636 idx : 0 ,
3737 remaining_page_size : MAX_WRITTEN_CONTENT_SIZE ,
3838 current_packet_len : 0 ,
39- last_segment_size : 0 ,
39+ last_segment_size : None ,
4040 }
4141 }
4242
@@ -45,7 +45,7 @@ impl PaginateContext {
4545 self . pos = 0 ;
4646
4747 self . current_packet_len = packet. len ( ) ;
48- self . last_segment_size = ( packet. len ( ) % 255 ) as u8 ;
48+ self . last_segment_size = Some ( ( packet. len ( ) % 255 ) as u8 ) ;
4949 }
5050
5151 fn flush_page ( & mut self , content : & mut Vec < u8 > ) {
@@ -61,13 +61,7 @@ impl PaginateContext {
6161 _ => 0 ,
6262 }
6363 } ,
64- abgp : if self . flags . packet_finished_on_page {
65- self . abgp
66- } else {
67- // A special value of '-1' (in two's complement) indicates that no packets
68- // finish on this page.
69- 1_u64 . wrapping_neg ( )
70- } ,
64+ abgp : 0 ,
7165 stream_serial : self . stream_serial ,
7266 sequence_number : self . idx as u32 ,
7367 segments : Vec :: new ( ) ,
@@ -79,27 +73,36 @@ impl PaginateContext {
7973 let content_len = content. len ( ) ;
8074 self . pos += content_len as u64 ;
8175
76+ // Determine how many *full* segments our page content takes up.
77+ // Anything < 255 will be covered by `last_segment_size`
78+ let full_segments_occupied = content_len / 255 ;
79+
8280 // Moving on to a new packet
8381 debug_assert ! ( self . pos <= self . current_packet_len as u64 ) ;
84- if self . pos == self . current_packet_len as u64 {
85- self . flags . packet_spans_multiple_pages = false ;
82+ let at_packet_end = self . pos == self . current_packet_len as u64 ;
83+ if at_packet_end && full_segments_occupied == MAX_WRITTEN_SEGMENT_COUNT {
84+ // See comment on `PaginateContextFlags.need_nil_page`
85+ self . flags . need_nil_page = true ;
86+ self . flags . packet_finished_on_page = false ;
8687 }
8788
88- // We need to determine how many segments our page content takes up.
89- // If it takes up the remainder of the segment table for the entire packet,
90- // we'll just consume it as is.
91- let segments_occupied = if content_len >= 255 {
92- content_len. div_ceil ( 255 )
89+ if self . flags . packet_finished_on_page {
90+ header. abgp = self . abgp ;
9391 } else {
94- 1
95- } ;
92+ // A special value of '-1' (in two's complement) indicates that no packets
93+ // finish on this page.
94+ header. abgp = 1_u64 . wrapping_neg ( )
95+ }
9696
97- debug_assert ! ( segments_occupied <= MAX_WRITTEN_SEGMENT_COUNT ) ;
98- if self . flags . packet_spans_multiple_pages {
99- header. segments = vec ! [ 255 ; segments_occupied] ;
100- } else {
101- header. segments = vec ! [ 255 ; segments_occupied - 1 ] ;
102- header. segments . push ( self . last_segment_size ) ;
97+ debug_assert ! ( full_segments_occupied <= MAX_WRITTEN_SEGMENT_COUNT ) ;
98+ header. segments = vec ! [ 255 ; full_segments_occupied] ;
99+
100+ if full_segments_occupied != MAX_WRITTEN_SEGMENT_COUNT {
101+ // End of the packet
102+ let last_segment_size = self
103+ . last_segment_size
104+ . expect ( "fresh packet should be indicated at this point" ) ;
105+ header. segments . push ( last_segment_size) ;
103106 }
104107
105108 self . pages . push ( Page {
@@ -117,8 +120,16 @@ impl PaginateContext {
117120struct PaginateContextFlags {
118121 first_page : bool ,
119122 fresh_packet : bool ,
120- packet_spans_multiple_pages : bool ,
121123 packet_finished_on_page : bool ,
124+ // A 'nil' page just means it is zero-length. This is used when our packet is perfectly
125+ // divisible by `255 * MAX_SEGMENT_COUNT`. We need a zero-sized segment to mark the end of our
126+ // packet across page boundaries.
127+ //
128+ // Very rare circumstance, but still possible.
129+ //
130+ // From <https://xiph.org/ogg/doc/framing.html>:
131+ // "Note also that a 'nil' (zero length) packet is not an error; it consists of nothing more than a lacing value of zero in the header."
132+ need_nil_page : bool ,
122133}
123134
124135/// Create pages from a list of packets
@@ -162,6 +173,13 @@ fn paginate_packet(ctx: &mut PaginateContext, packet: &[u8]) -> Result<()> {
162173 let mut page_content = Vec :: with_capacity ( MAX_WRITTEN_CONTENT_SIZE ) ;
163174 let mut packet = packet;
164175 loop {
176+ // See comment on `PaginateContextFlags.need_nil_page`
177+ if ctx. flags . need_nil_page {
178+ assert ! ( packet. is_empty( ) ) ;
179+ ctx. flags . packet_finished_on_page = true ;
180+ ctx. flush_page ( & mut page_content) ;
181+ }
182+
165183 if packet. is_empty ( ) {
166184 break ;
167185 }
@@ -175,8 +193,6 @@ fn paginate_packet(ctx: &mut PaginateContext, packet: &[u8]) -> Result<()> {
175193
176194 if bytes_read <= MAX_WRITTEN_CONTENT_SIZE && packet. is_empty ( ) {
177195 ctx. flags . packet_finished_on_page = true ;
178- } else {
179- ctx. flags . packet_spans_multiple_pages = true ;
180196 }
181197
182198 if ctx. remaining_page_size == 0 || packet. is_empty ( ) {
0 commit comments