@@ -396,9 +396,10 @@ impl Buffer {
396
396
/// - If `bounds` has a length less than 1.
397
397
/// - If the start and end of `bounds` are not aligned to [`MAP_ALIGNMENT`].
398
398
/// - 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`] .
400
400
///
401
401
/// [mapped]: Buffer#mapping-buffers
402
+ #[ track_caller]
402
403
pub fn get_mapped_range < S : RangeBounds < BufferAddress > > ( & self , bounds : S ) -> BufferView {
403
404
self . slice ( bounds) . get_mapped_range ( )
404
405
}
@@ -419,9 +420,10 @@ impl Buffer {
419
420
/// - If `bounds` has a length less than 1.
420
421
/// - If the start and end of `bounds` are not aligned to [`MAP_ALIGNMENT`].
421
422
/// - 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`] .
423
424
///
424
425
/// [mapped]: Buffer#mapping-buffers
426
+ #[ track_caller]
425
427
pub fn get_mapped_range_mut < S : RangeBounds < BufferAddress > > ( & self , bounds : S ) -> BufferViewMut {
426
428
self . slice ( bounds) . get_mapped_range_mut ( )
427
429
}
@@ -534,9 +536,9 @@ impl<'a> BufferSlice<'a> {
534
536
callback : impl FnOnce ( Result < ( ) , BufferAsyncError > ) + WasmNotSend + ' static ,
535
537
) {
536
538
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" ) ;
538
540
let end = self . offset + self . size . get ( ) ;
539
- mc. initial_range = self . offset ..end;
541
+ mc. mapped_range = self . offset ..end;
540
542
541
543
self . buffer
542
544
. inner
@@ -557,11 +559,16 @@ impl<'a> BufferSlice<'a> {
557
559
///
558
560
/// - If the endpoints of this slice are not aligned to [`MAP_ALIGNMENT`] within the buffer.
559
561
/// - 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`] .
561
563
///
562
564
/// [mapped]: Buffer#mapping-buffers
565
+ #[ track_caller]
563
566
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 ) ;
565
572
let range = self . buffer . inner . get_mapped_range ( self . offset ..end) ;
566
573
BufferView {
567
574
buffer : self . buffer . clone ( ) ,
@@ -585,11 +592,16 @@ impl<'a> BufferSlice<'a> {
585
592
///
586
593
/// - If the endpoints of this slice are not aligned to [`MAP_ALIGNMENT`].
587
594
/// - 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`] .
589
596
///
590
597
/// [mapped]: Buffer#mapping-buffers
598
+ #[ track_caller]
591
599
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 ) ;
593
605
let range = self . buffer . inner . get_mapped_range ( self . offset ..end) ;
594
606
BufferViewMut {
595
607
buffer : self . buffer . clone ( ) ,
@@ -640,6 +652,28 @@ impl<'a> From<BufferSlice<'a>> for crate::BindingResource<'a> {
640
652
}
641
653
}
642
654
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
+
643
677
/// The mapped portion of a buffer, if any, and its outstanding views.
644
678
///
645
679
/// This ensures that views fall within the mapped range and don't overlap.
@@ -653,25 +687,25 @@ pub(crate) struct MapContext {
653
687
/// *or has been requested to be* mapped.)
654
688
///
655
689
/// 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 > ,
657
691
658
692
/// The ranges covered by all outstanding [`BufferView`]s and
659
693
/// [`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 > ,
662
696
}
663
697
664
698
impl MapContext {
665
699
pub ( crate ) fn new ( ) -> Self {
666
700
Self {
667
- initial_range : 0 ..0 ,
701
+ mapped_range : 0 ..0 ,
668
702
sub_ranges : Vec :: new ( ) ,
669
703
}
670
704
}
671
705
672
706
/// Record that the buffer is no longer mapped.
673
707
fn reset ( & mut self ) {
674
- self . initial_range = 0 ..0 ;
708
+ self . mapped_range = 0 ..0 ;
675
709
676
710
assert ! (
677
711
self . sub_ranges. is_empty( ) ,
@@ -686,19 +720,38 @@ impl MapContext {
686
720
/// # Panics
687
721
///
688
722
/// 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 {
690
725
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
+
692
738
// This check is essential for avoiding undefined behavior: it is the
693
739
// only thing that ensures that `&mut` references to the buffer's
694
740
// contents don't alias anything else.
695
741
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
+ }
700
750
}
701
- self . sub_ranges . push ( offset..end) ;
751
+ self . sub_ranges . push ( Subrange {
752
+ index : offset..end,
753
+ kind,
754
+ } ) ;
702
755
end
703
756
}
704
757
@@ -716,7 +769,7 @@ impl MapContext {
716
769
let index = self
717
770
. sub_ranges
718
771
. iter ( )
719
- . position ( |r| * r == ( offset..end) )
772
+ . position ( |r| r . index == ( offset..end) )
720
773
. expect ( "unable to remove range from map context" ) ;
721
774
self . sub_ranges . swap_remove ( index) ;
722
775
}
@@ -922,7 +975,9 @@ fn range_to_offset_size<S: RangeBounds<BufferAddress>>(
922
975
923
976
#[ cfg( test) ]
924
977
mod 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
+ } ;
926
981
927
982
fn bs ( value : BufferAddress ) -> BufferSize {
928
983
BufferSize :: new ( value) . unwrap ( )
@@ -972,4 +1027,21 @@ mod tests {
972
1027
fn check_buffer_bounds_panics_for_end_wraparound ( ) {
973
1028
check_buffer_bounds ( u64:: MAX , 1 , bs ( u64:: MAX ) ) ;
974
1029
}
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
+ }
975
1047
}
0 commit comments