@@ -396,9 +396,10 @@ impl Buffer {
396396 /// - If `bounds` has a length less than 1.
397397 /// - If the start and end of `bounds` are not aligned to [`MAP_ALIGNMENT`].
398398 /// - If the buffer to which `self` refers is not currently [mapped].
399- /// - If you try to create overlapping views of a buffer, mutable or otherwise .
399+ /// - If you try to create a view which overlaps an existing [`BufferViewMut`] .
400400 ///
401401 /// [mapped]: Buffer#mapping-buffers
402+ #[ track_caller]
402403 pub fn get_mapped_range < S : RangeBounds < BufferAddress > > ( & self , bounds : S ) -> BufferView {
403404 self . slice ( bounds) . get_mapped_range ( )
404405 }
@@ -419,9 +420,10 @@ impl Buffer {
419420 /// - If `bounds` has a length less than 1.
420421 /// - If the start and end of `bounds` are not aligned to [`MAP_ALIGNMENT`].
421422 /// - If the buffer to which `self` refers is not currently [mapped].
422- /// - If you try to create overlapping views of a buffer, mutable or otherwise .
423+ /// - If you try to create a view which overlaps an existing [`BufferView`] or [`BufferViewMut`] .
423424 ///
424425 /// [mapped]: Buffer#mapping-buffers
426+ #[ track_caller]
425427 pub fn get_mapped_range_mut < S : RangeBounds < BufferAddress > > ( & self , bounds : S ) -> BufferViewMut {
426428 self . slice ( bounds) . get_mapped_range_mut ( )
427429 }
@@ -534,9 +536,9 @@ impl<'a> BufferSlice<'a> {
534536 callback : impl FnOnce ( Result < ( ) , BufferAsyncError > ) + WasmNotSend + ' static ,
535537 ) {
536538 let mut mc = self . buffer . map_context . lock ( ) ;
537- assert_eq ! ( mc. initial_range , 0 ..0 , "Buffer is already mapped" ) ;
539+ assert_eq ! ( mc. mapped_range , 0 ..0 , "Buffer is already mapped" ) ;
538540 let end = self . offset + self . size . get ( ) ;
539- mc. initial_range = self . offset ..end;
541+ mc. mapped_range = self . offset ..end;
540542
541543 self . buffer
542544 . inner
@@ -557,11 +559,16 @@ impl<'a> BufferSlice<'a> {
557559 ///
558560 /// - If the endpoints of this slice are not aligned to [`MAP_ALIGNMENT`] within the buffer.
559561 /// - If the buffer to which `self` refers is not currently [mapped].
560- /// - If you try to create overlapping views of a buffer, mutable or otherwise .
562+ /// - If you try to create a view which overlaps an existing [`BufferViewMut`] .
561563 ///
562564 /// [mapped]: Buffer#mapping-buffers
565+ #[ track_caller]
563566 pub fn get_mapped_range ( & self ) -> BufferView {
564- let end = self . buffer . map_context . lock ( ) . add ( self . offset , self . size ) ;
567+ let end = self
568+ . buffer
569+ . map_context
570+ . lock ( )
571+ . add ( self . offset , self . size , RangeKind :: Immutable ) ;
565572 let range = self . buffer . inner . get_mapped_range ( self . offset ..end) ;
566573 BufferView {
567574 buffer : self . buffer . clone ( ) ,
@@ -585,11 +592,16 @@ impl<'a> BufferSlice<'a> {
585592 ///
586593 /// - If the endpoints of this slice are not aligned to [`MAP_ALIGNMENT`].
587594 /// - If the buffer to which `self` refers is not currently [mapped].
588- /// - If you try to create overlapping views of a buffer, mutable or otherwise .
595+ /// - If you try to create a view which overlaps an existing [`BufferView`] or [`BufferViewMut`] .
589596 ///
590597 /// [mapped]: Buffer#mapping-buffers
598+ #[ track_caller]
591599 pub fn get_mapped_range_mut ( & self ) -> BufferViewMut {
592- let end = self . buffer . map_context . lock ( ) . add ( self . offset , self . size ) ;
600+ let end = self
601+ . buffer
602+ . map_context
603+ . lock ( )
604+ . add ( self . offset , self . size , RangeKind :: Mutable ) ;
593605 let range = self . buffer . inner . get_mapped_range ( self . offset ..end) ;
594606 BufferViewMut {
595607 buffer : self . buffer . clone ( ) ,
@@ -640,6 +652,28 @@ impl<'a> From<BufferSlice<'a>> for crate::BindingResource<'a> {
640652 }
641653}
642654
655+ fn range_overlaps ( a : & Range < BufferAddress > , b : & Range < BufferAddress > ) -> bool {
656+ a. start < b. end && b. start < a. end
657+ }
658+
659+ #[ derive( Debug ) ]
660+ enum RangeKind {
661+ Mutable ,
662+ Immutable ,
663+ }
664+
665+ impl RangeKind {
666+ fn compatible ( & self , other : & Self ) -> bool {
667+ matches ! ( ( self , other) , ( RangeKind :: Immutable , RangeKind :: Immutable ) )
668+ }
669+ }
670+
671+ #[ derive( Debug ) ]
672+ struct Subrange {
673+ index : Range < BufferAddress > ,
674+ kind : RangeKind ,
675+ }
676+
643677/// The mapped portion of a buffer, if any, and its outstanding views.
644678///
645679/// This ensures that views fall within the mapped range and don't overlap.
@@ -653,25 +687,25 @@ pub(crate) struct MapContext {
653687 /// *or has been requested to be* mapped.)
654688 ///
655689 /// All [`BufferView`]s and [`BufferViewMut`]s must fall within this range.
656- pub ( crate ) initial_range : Range < BufferAddress > ,
690+ pub ( crate ) mapped_range : Range < BufferAddress > ,
657691
658692 /// The ranges covered by all outstanding [`BufferView`]s and
659693 /// [`BufferViewMut`]s. These are non-overlapping, and are all contained
660- /// within `initial_range `.
661- sub_ranges : Vec < Range < BufferAddress > > ,
694+ /// within `mapped_range `.
695+ sub_ranges : Vec < Subrange > ,
662696}
663697
664698impl MapContext {
665699 pub ( crate ) fn new ( ) -> Self {
666700 Self {
667- initial_range : 0 ..0 ,
701+ mapped_range : 0 ..0 ,
668702 sub_ranges : Vec :: new ( ) ,
669703 }
670704 }
671705
672706 /// Record that the buffer is no longer mapped.
673707 fn reset ( & mut self ) {
674- self . initial_range = 0 ..0 ;
708+ self . mapped_range = 0 ..0 ;
675709
676710 assert ! (
677711 self . sub_ranges. is_empty( ) ,
@@ -686,19 +720,38 @@ impl MapContext {
686720 /// # Panics
687721 ///
688722 /// This panics if the given range overlaps with any existing range.
689- fn add ( & mut self , offset : BufferAddress , size : BufferSize ) -> BufferAddress {
723+ #[ track_caller]
724+ fn add ( & mut self , offset : BufferAddress , size : BufferSize , kind : RangeKind ) -> BufferAddress {
690725 let end = offset + size. get ( ) ;
691- assert ! ( self . initial_range. start <= offset && end <= self . initial_range. end) ;
726+
727+ if self . mapped_range . is_empty ( ) {
728+ panic ! ( "Tried to call getMappedRange(Mut) on an unmapped buffer" ) ;
729+ }
730+ if !range_overlaps ( & self . mapped_range , & ( offset..end) ) {
731+ panic ! (
732+ "Tried to call getMappedRange(Mut) on a range that is not entirely mapped. \
733+ Attempted to get range {}..{}, but the mapped range is {}..{}",
734+ offset, end, self . mapped_range. start, self . mapped_range. end
735+ ) ;
736+ }
737+
692738 // This check is essential for avoiding undefined behavior: it is the
693739 // only thing that ensures that `&mut` references to the buffer's
694740 // contents don't alias anything else.
695741 for sub in self . sub_ranges . iter ( ) {
696- assert ! (
697- end <= sub. start || offset >= sub. end,
698- "Intersecting map range with {sub:?}"
699- ) ;
742+ if range_overlaps ( & sub. index , & ( offset..end) ) && !sub. kind . compatible ( & kind) {
743+ panic ! (
744+ "Tried to call getMappedRange(Mut) on a range that has already \
745+ been mapped and would break Rust memory aliasing rules. Attempted \
746+ to get range {}..{} ({:?}), and the conflicting range is {}..{} ({:?})",
747+ offset, end, kind, sub. index. start, sub. index. end, sub. kind
748+ ) ;
749+ }
700750 }
701- self . sub_ranges . push ( offset..end) ;
751+ self . sub_ranges . push ( Subrange {
752+ index : offset..end,
753+ kind,
754+ } ) ;
702755 end
703756 }
704757
@@ -716,7 +769,7 @@ impl MapContext {
716769 let index = self
717770 . sub_ranges
718771 . iter ( )
719- . position ( |r| * r == ( offset..end) )
772+ . position ( |r| r . index == ( offset..end) )
720773 . expect ( "unable to remove range from map context" ) ;
721774 self . sub_ranges . swap_remove ( index) ;
722775 }
@@ -922,7 +975,9 @@ fn range_to_offset_size<S: RangeBounds<BufferAddress>>(
922975
923976#[ cfg( test) ]
924977mod tests {
925- use super :: { check_buffer_bounds, range_to_offset_size, BufferAddress , BufferSize } ;
978+ use super :: {
979+ check_buffer_bounds, range_overlaps, range_to_offset_size, BufferAddress , BufferSize ,
980+ } ;
926981
927982 fn bs ( value : BufferAddress ) -> BufferSize {
928983 BufferSize :: new ( value) . unwrap ( )
@@ -972,4 +1027,21 @@ mod tests {
9721027 fn check_buffer_bounds_panics_for_end_wraparound ( ) {
9731028 check_buffer_bounds ( u64:: MAX , 1 , bs ( u64:: MAX ) ) ;
9741029 }
1030+
1031+ #[ test]
1032+ #[ expect( clippy:: bool_assert_comparison) ] // This degrades readability significantly.
1033+ fn range_overlapping ( ) {
1034+ // First range to the left
1035+ assert_eq ! ( range_overlaps( & ( 0 ..1 ) , & ( 1 ..3 ) ) , false ) ;
1036+ // First range overlaps left edge
1037+ assert_eq ! ( range_overlaps( & ( 0 ..2 ) , & ( 1 ..3 ) ) , true ) ;
1038+ // First range completely inside second
1039+ assert_eq ! ( range_overlaps( & ( 1 ..2 ) , & ( 0 ..3 ) ) , true ) ;
1040+ // First range completely surrounds second
1041+ assert_eq ! ( range_overlaps( & ( 0 ..3 ) , & ( 1 ..2 ) ) , true ) ;
1042+ // First range overlaps right edge
1043+ assert_eq ! ( range_overlaps( & ( 1 ..3 ) , & ( 0 ..2 ) ) , true ) ;
1044+ // First range entirely to the right
1045+ assert_eq ! ( range_overlaps( & ( 2 ..3 ) , & ( 0 ..2 ) ) , false ) ;
1046+ }
9751047}
0 commit comments