@@ -437,35 +437,43 @@ impl BitBufferMut {
437437 ///
438438 /// Unlike bytes, if the split position is not on a byte-boundary this operation will copy
439439 /// data into the result type, and mutate self.
440+ #[ must_use = "consider BitBufferMut::truncate if you don't need the other half" ]
440441 pub fn split_off ( & mut self , at : usize ) -> Self {
441- assert ! ( at <= self . len, "index {at} exceeds len {}" , self . len) ;
442+ assert ! (
443+ at <= self . capacity( ) ,
444+ "index {at} exceeds capacity {}" ,
445+ self . capacity( )
446+ ) ;
442447
443- let new_offset = self . offset ;
444- let new_len = self . len - at;
448+ // The length of the tail is any bits after `at`
449+ let tail_len = self . len . saturating_sub ( at) ;
450+ let byte_pos = ( self . offset + at) . div_ceil ( 8 ) ;
445451
446452 // If we are splitting on a byte boundary, we can just slice the buffer
447- if ( self . offset + at) % 8 == 0 {
448- let byte_pos = ( self . offset + at) / 8 ;
449- let new_buffer = self . buffer . split_off ( byte_pos) ;
450- self . len = at;
453+ // Or if `at > self.len`, then the tail is empty anyway and we can just return as much
454+ // of the existing capacity as possible.
455+ if at > self . len ( ) || ( ( self . offset + at) % 8 == 0 ) {
456+ let tail_buffer = self . buffer . split_off ( byte_pos) ;
457+ self . len = self . len . min ( at) ;
458+
459+ // Return the tail buffer
451460 return Self {
452- buffer : new_buffer ,
453- offset : new_offset ,
454- len : new_len ,
461+ buffer : tail_buffer ,
462+ offset : 0 ,
463+ len : tail_len ,
455464 } ;
456465 }
457466
458- // Otherwise, we need to copy bits into a new buffer
459- let mut new_buffer = BitBufferMut :: with_capacity ( new_len) ;
460- for i in 0 ..new_len {
461- let value = self . value ( at + i) ;
462- new_buffer. append ( value) ;
463- }
467+ // Otherwise, we truncate ourselves, and copy any bits into a new tail buffer.
468+ // Note that in this case we do not preserve the capacity.
469+ let u64_cap = tail_len. div_ceil ( 8 ) ;
470+ let mut tail_buffer_u64 = BufferMut :: < u64 > :: with_capacity ( u64_cap) ;
471+ tail_buffer_u64. extend (
472+ BitChunks :: new ( self . buffer . as_slice ( ) , self . offset + at, tail_len) . iter_padded ( ) ,
473+ ) ;
464474
465- // Truncate self to the split position
466475 self . truncate ( at) ;
467-
468- new_buffer
476+ BitBufferMut :: from_buffer ( tail_buffer_u64. into_byte_buffer ( ) , 0 , tail_len)
469477 }
470478
471479 /// Absorbs a mutable buffer that was previously split off.
@@ -1058,4 +1066,40 @@ mod tests {
10581066 assert_eq ! ( bit_buf. value( i) , i % 2 == 0 ) ;
10591067 }
10601068 }
1069+
1070+ #[ test]
1071+ fn test_split_off ( ) {
1072+ // Test splitting at various positions and across a byte boundary
1073+ for i in 0 ..10 {
1074+ let buf = bitbuffer ! [ 0 1 0 1 0 1 0 1 0 1 ] ;
1075+
1076+ let mut buf_mut = buf. clone ( ) . into_mut ( ) ;
1077+ assert_eq ! ( buf_mut. len( ) , 10 ) ;
1078+
1079+ let tail = buf_mut. split_off ( i) ;
1080+ assert_eq ! ( buf_mut. len( ) , i) ;
1081+ assert_eq ! ( buf_mut. freeze( ) , buf. slice( 0 ..i) ) ;
1082+
1083+ assert_eq ! ( tail. len( ) , 10 - i) ;
1084+ assert_eq ! ( tail. freeze( ) , buf. slice( i..10 ) ) ;
1085+ }
1086+ }
1087+
1088+ #[ test]
1089+ fn test_split_off_with_offset ( ) {
1090+ // Test splitting at various positions and across a byte boundary
1091+ for i in 0 ..10 {
1092+ let buf = bitbuffer ! [ 0 1 0 1 0 1 0 1 0 1 0 1 ] . slice ( 2 ..) ;
1093+
1094+ let mut buf_mut = buf. clone ( ) . into_mut ( ) ;
1095+ assert_eq ! ( buf_mut. len( ) , 10 ) ;
1096+
1097+ let tail = buf_mut. split_off ( i) ;
1098+ assert_eq ! ( buf_mut. len( ) , i) ;
1099+ assert_eq ! ( buf_mut. freeze( ) , buf. slice( 0 ..i) ) ;
1100+
1101+ assert_eq ! ( tail. len( ) , 10 - i) ;
1102+ assert_eq ! ( tail. freeze( ) , buf. slice( i..10 ) ) ;
1103+ }
1104+ }
10611105}
0 commit comments