@@ -32,10 +32,20 @@ pub struct Buffer<T> {
3232 pub ( crate ) _marker : PhantomData < T > ,
3333}
3434
35+ /// Zero-length backing for empty buffers, "aligned" to [`Alignment::MAX`] so it satisfies any
36+ /// valid alignment without allocating. A zero-length slice never reads memory, so it may use a
37+ /// dangling pointer as long as it is non-null and aligned.
38+ const EMPTY_BACKING : & [ u8 ] = {
39+ let addr = 1usize << 20 ;
40+ assert ! ( Alignment :: MAX . is_offset_aligned( addr) ) ;
41+ // SAFETY: the pointer is non-null and aligned, and the slice is zero-length.
42+ unsafe { std:: slice:: from_raw_parts ( std:: ptr:: without_provenance ( addr) , 0 ) }
43+ } ;
44+
3545impl < T > Default for Buffer < T > {
3646 fn default ( ) -> Self {
3747 Self {
38- bytes : Default :: default ( ) ,
48+ bytes : Bytes :: from_static ( EMPTY_BACKING ) ,
3949 length : 0 ,
4050 alignment : Alignment :: of :: < T > ( ) ,
4151 _marker : PhantomData ,
@@ -101,12 +111,27 @@ impl<T> Buffer<T> {
101111
102112 /// Create a new empty `ByteBuffer` with the provided alignment.
103113 pub fn empty ( ) -> Self {
104- BufferMut :: empty ( ) . freeze ( )
114+ Self :: empty_aligned ( Alignment :: of :: < T > ( ) )
105115 }
106116
107117 /// Create a new empty `ByteBuffer` with the provided alignment.
118+ ///
119+ /// This does not allocate: empty buffers are backed by a zero-length `Bytes` that is
120+ /// aligned to [`Alignment::MAX`].
108121 pub fn empty_aligned ( alignment : Alignment ) -> Self {
109- BufferMut :: empty_aligned ( alignment) . freeze ( )
122+ if !alignment. is_aligned_to ( Alignment :: of :: < T > ( ) ) {
123+ vortex_panic ! (
124+ "Alignment {} must align to the scalar type's alignment {}" ,
125+ alignment,
126+ Alignment :: of:: <T >( ) ,
127+ ) ;
128+ }
129+ Self {
130+ bytes : Bytes :: from_static ( EMPTY_BACKING ) ,
131+ length : 0 ,
132+ alignment,
133+ _marker : PhantomData ,
134+ }
110135 }
111136
112137 /// Create a new full `ByteBuffer` with the given value.
@@ -152,7 +177,7 @@ impl<T> Buffer<T> {
152177 Alignment :: of:: <T >( ) ,
153178 ) ;
154179 }
155- if bytes. as_ptr ( ) . align_offset ( * alignment ) != 0 {
180+ if !alignment . is_ptr_aligned ( bytes. as_ptr ( ) ) {
156181 vortex_panic ! (
157182 "Bytes alignment must align to the requested alignment {}" ,
158183 alignment,
@@ -320,7 +345,7 @@ impl<T> Buffer<T> {
320345 let begin_byte = begin * size_of :: < T > ( ) ;
321346 let end_byte = end * size_of :: < T > ( ) ;
322347
323- if !begin_byte . is_multiple_of ( * alignment ) {
348+ if !alignment . is_offset_aligned ( begin_byte ) {
324349 vortex_panic ! (
325350 "range start must be aligned to {alignment:?}, byte {}" ,
326351 begin_byte
@@ -369,7 +394,7 @@ impl<T> Buffer<T> {
369394 vortex_panic ! ( "slice_ref subset alignment must at least align to the buffer alignment" )
370395 }
371396
372- if subset. as_ptr ( ) . align_offset ( * alignment ) != 0 {
397+ if !alignment . is_ptr_aligned ( subset. as_ptr ( ) ) {
373398 vortex_panic ! ( "slice_ref subset must be aligned to {:?}" , alignment) ;
374399 }
375400
@@ -435,17 +460,17 @@ impl<T> Buffer<T> {
435460 /// Convert self into `BufferMut<T>`, cloning the data if there are multiple strong references.
436461 pub fn into_mut ( self ) -> BufferMut < T > {
437462 self . try_into_mut ( )
438- . unwrap_or_else ( |buffer| BufferMut :: < T > :: copy_from ( & buffer) )
463+ . unwrap_or_else ( |buffer| BufferMut :: < T > :: copy_from_aligned ( & buffer, buffer . alignment ) )
439464 }
440465
441466 /// Returns whether a `Buffer<T>` is aligned to the given alignment.
442467 pub fn is_aligned ( & self , alignment : Alignment ) -> bool {
443- self . bytes . as_ptr ( ) . align_offset ( * alignment ) == 0
468+ alignment . is_ptr_aligned ( self . bytes . as_ptr ( ) )
444469 }
445470
446471 /// Return a `Buffer<T>` with the given alignment. Where possible, this will be zero-copy.
447472 pub fn aligned ( mut self , alignment : Alignment ) -> Self {
448- if self . as_ptr ( ) . align_offset ( * alignment ) == 0 {
473+ if alignment . is_ptr_aligned ( self . as_ptr ( ) ) {
449474 self . alignment = alignment;
450475 self
451476 } else {
@@ -462,7 +487,7 @@ impl<T> Buffer<T> {
462487
463488 /// Return a `Buffer<T>` with the given alignment. Panics if the buffer is not aligned.
464489 pub fn ensure_aligned ( mut self , alignment : Alignment ) -> Self {
465- if self . as_ptr ( ) . align_offset ( * alignment ) == 0 {
490+ if alignment . is_ptr_aligned ( self . as_ptr ( ) ) {
466491 self . alignment = alignment;
467492 self
468493 } else {
@@ -634,7 +659,7 @@ impl Buf for ByteBuffer {
634659
635660 #[ inline]
636661 fn advance ( & mut self , cnt : usize ) {
637- if !cnt . is_multiple_of ( * self . alignment ) {
662+ if !self . alignment . is_offset_aligned ( cnt ) {
638663 vortex_panic ! (
639664 "Cannot advance buffer by {} items, resulting alignment is not {}" ,
640665 cnt,
@@ -786,6 +811,31 @@ mod test {
786811 assert_eq ! ( vec, buff. as_ref( ) ) ;
787812 }
788813
814+ #[ test]
815+ fn empty_aligned_max_alignment ( ) {
816+ // Empty buffers are backed by a static and must satisfy any valid alignment.
817+ let buf = Buffer :: < u8 > :: empty_aligned ( Alignment :: MAX ) ;
818+ assert ! ( buf. is_empty( ) ) ;
819+ assert ! ( buf. is_aligned( Alignment :: MAX ) ) ;
820+ }
821+
822+ #[ test]
823+ fn empty_slice_preserves_alignment ( ) {
824+ let buf = Buffer :: < u64 > :: zeroed_aligned ( 8 , Alignment :: new ( 64 ) ) ;
825+ let sliced = buf. slice ( 0 ..0 ) ;
826+ assert ! ( sliced. is_empty( ) ) ;
827+ assert_eq ! ( sliced. alignment( ) , Alignment :: new( 64 ) ) ;
828+ assert ! ( sliced. is_aligned( Alignment :: new( 64 ) ) ) ;
829+ }
830+
831+ #[ test]
832+ fn empty_into_mut_preserves_alignment ( ) {
833+ let buf = Buffer :: < u8 > :: empty_aligned ( Alignment :: new ( 64 ) ) ;
834+ let buf_mut = buf. into_mut ( ) ;
835+ assert_eq ! ( buf_mut. alignment( ) , Alignment :: new( 64 ) ) ;
836+ assert ! ( buf_mut. is_empty( ) ) ;
837+ }
838+
789839 #[ test]
790840 fn test_slice_unaligned_end_pos ( ) {
791841 let data = vec ! [ 0u8 ; 2 ] ;
@@ -797,4 +847,12 @@ mod test {
797847 // to be aligned.
798848 aligned_buffer. slice ( 0 ..1 ) ;
799849 }
850+
851+ #[ test]
852+ fn test_empty_equality ( ) {
853+ let a = Buffer :: < u16 > :: empty ( ) ;
854+ let b = Buffer :: < u16 > :: empty ( ) ;
855+
856+ assert_eq ! ( a, b) ;
857+ }
800858}
0 commit comments