@@ -16,7 +16,7 @@ struct PaginateContext {
16
16
idx : usize ,
17
17
remaining_page_size : usize ,
18
18
current_packet_len : usize ,
19
- last_segment_size : u8 ,
19
+ last_segment_size : Option < u8 > ,
20
20
}
21
21
22
22
impl PaginateContext {
@@ -29,14 +29,14 @@ impl PaginateContext {
29
29
flags : PaginateContextFlags {
30
30
first_page : true ,
31
31
fresh_packet : true ,
32
- packet_spans_multiple_pages : false ,
33
32
packet_finished_on_page : false ,
33
+ need_nil_page : false ,
34
34
} ,
35
35
pos : 0 ,
36
36
idx : 0 ,
37
37
remaining_page_size : MAX_WRITTEN_CONTENT_SIZE ,
38
38
current_packet_len : 0 ,
39
- last_segment_size : 0 ,
39
+ last_segment_size : None ,
40
40
}
41
41
}
42
42
@@ -45,7 +45,7 @@ impl PaginateContext {
45
45
self . pos = 0 ;
46
46
47
47
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 ) ;
49
49
}
50
50
51
51
fn flush_page ( & mut self , content : & mut Vec < u8 > ) {
@@ -61,13 +61,7 @@ impl PaginateContext {
61
61
_ => 0 ,
62
62
}
63
63
} ,
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 ,
71
65
stream_serial : self . stream_serial ,
72
66
sequence_number : self . idx as u32 ,
73
67
segments : Vec :: new ( ) ,
@@ -79,27 +73,36 @@ impl PaginateContext {
79
73
let content_len = content. len ( ) ;
80
74
self . pos += content_len as u64 ;
81
75
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
+
82
80
// Moving on to a new packet
83
81
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 ;
86
87
}
87
88
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 ;
93
91
} 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
+ }
96
96
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) ;
103
106
}
104
107
105
108
self . pages . push ( Page {
@@ -117,8 +120,16 @@ impl PaginateContext {
117
120
struct PaginateContextFlags {
118
121
first_page : bool ,
119
122
fresh_packet : bool ,
120
- packet_spans_multiple_pages : bool ,
121
123
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 ,
122
133
}
123
134
124
135
/// Create pages from a list of packets
@@ -162,6 +173,13 @@ fn paginate_packet(ctx: &mut PaginateContext, packet: &[u8]) -> Result<()> {
162
173
let mut page_content = Vec :: with_capacity ( MAX_WRITTEN_CONTENT_SIZE ) ;
163
174
let mut packet = packet;
164
175
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
+
165
183
if packet. is_empty ( ) {
166
184
break ;
167
185
}
@@ -175,8 +193,6 @@ fn paginate_packet(ctx: &mut PaginateContext, packet: &[u8]) -> Result<()> {
175
193
176
194
if bytes_read <= MAX_WRITTEN_CONTENT_SIZE && packet. is_empty ( ) {
177
195
ctx. flags . packet_finished_on_page = true ;
178
- } else {
179
- ctx. flags . packet_spans_multiple_pages = true ;
180
196
}
181
197
182
198
if ctx. remaining_page_size == 0 || packet. is_empty ( ) {
0 commit comments