wgpu/api/
buffer.rs

1use std::{
2    error, fmt,
3    ops::{Bound, Deref, DerefMut, Range, RangeBounds},
4    sync::Arc,
5};
6
7use parking_lot::Mutex;
8
9use crate::*;
10
11/// Handle to a GPU-accessible buffer.
12///
13/// Created with [`Device::create_buffer`] or
14/// [`DeviceExt::create_buffer_init`](util::DeviceExt::create_buffer_init).
15///
16/// Corresponds to [WebGPU `GPUBuffer`](https://gpuweb.github.io/gpuweb/#buffer-interface).
17///
18/// A `Buffer`'s bytes have "interior mutability": functions like
19/// [`Queue::write_buffer`] or [mapping] a buffer for writing only require a
20/// `&Buffer`, not a `&mut Buffer`, even though they modify its contents. `wgpu`
21/// prevents simultaneous reads and writes of buffer contents using run-time
22/// checks.
23///
24/// [mapping]: Buffer#mapping-buffers
25///
26/// # Mapping buffers
27///
28/// If a `Buffer` is created with the appropriate [`usage`], it can be *mapped*:
29/// you can make its contents accessible to the CPU as an ordinary `&[u8]` or
30/// `&mut [u8]` slice of bytes. Buffers created with the
31/// [`mapped_at_creation`][mac] flag set are also mapped initially.
32///
33/// Depending on the hardware, the buffer could be memory shared between CPU and
34/// GPU, so that the CPU has direct access to the same bytes the GPU will
35/// consult; or it may be ordinary CPU memory, whose contents the system must
36/// copy to/from the GPU as needed. This crate's API is designed to work the
37/// same way in either case: at any given time, a buffer is either mapped and
38/// available to the CPU, or unmapped and ready for use by the GPU, but never
39/// both. This makes it impossible for either side to observe changes by the
40/// other immediately, and any necessary transfers can be carried out when the
41/// buffer transitions from one state to the other.
42///
43/// There are two ways to map a buffer:
44///
45/// - If [`BufferDescriptor::mapped_at_creation`] is `true`, then the entire
46///   buffer is mapped when it is created. This is the easiest way to initialize
47///   a new buffer. You can set `mapped_at_creation` on any kind of buffer,
48///   regardless of its [`usage`] flags.
49///
50/// - If the buffer's [`usage`] includes the [`MAP_READ`] or [`MAP_WRITE`]
51///   flags, then you can call `buffer.slice(range).map_async(mode, callback)`
52///   to map the portion of `buffer` given by `range`. This waits for the GPU to
53///   finish using the buffer, and invokes `callback` as soon as the buffer is
54///   safe for the CPU to access.
55///
56/// Once a buffer is mapped:
57///
58/// - You can call `buffer.slice(range).get_mapped_range()` to obtain a
59///   [`BufferView`], which dereferences to a `&[u8]` that you can use to read
60///   the buffer's contents.
61///
62/// - Or, you can call `buffer.slice(range).get_mapped_range_mut()` to obtain a
63///   [`BufferViewMut`], which dereferences to a `&mut [u8]` that you can use to
64///   read and write the buffer's contents.
65///
66/// The given `range` must fall within the mapped portion of the buffer. If you
67/// attempt to access overlapping ranges, even for shared access only, these
68/// methods panic.
69///
70/// While a buffer is mapped, you may not submit any commands to the GPU that
71/// access it. You may record command buffers that use the buffer, but if you
72/// submit them while the buffer is mapped, submission will panic.
73///
74/// When you are done using the buffer on the CPU, you must call
75/// [`Buffer::unmap`] to make it available for use by the GPU again. All
76/// [`BufferView`] and [`BufferViewMut`] views referring to the buffer must be
77/// dropped before you unmap it; otherwise, [`Buffer::unmap`] will panic.
78///
79/// # Example
80///
81/// If `buffer` was created with [`BufferUsages::MAP_WRITE`], we could fill it
82/// with `f32` values like this:
83///
84/// ```no_run
85/// # mod bytemuck {
86/// #     pub fn cast_slice_mut(bytes: &mut [u8]) -> &mut [f32] { todo!() }
87/// # }
88/// # let device: wgpu::Device = todo!();
89/// # let buffer: wgpu::Buffer = todo!();
90/// let buffer = std::sync::Arc::new(buffer);
91/// let capturable = buffer.clone();
92/// buffer.slice(..).map_async(wgpu::MapMode::Write, move |result| {
93///     if result.is_ok() {
94///         let mut view = capturable.slice(..).get_mapped_range_mut();
95///         let floats: &mut [f32] = bytemuck::cast_slice_mut(&mut view);
96///         floats.fill(42.0);
97///         drop(view);
98///         capturable.unmap();
99///     }
100/// });
101/// ```
102///
103/// This code takes the following steps:
104///
105/// - First, it moves `buffer` into an [`Arc`], and makes a clone for capture by
106///   the callback passed to [`map_async`]. Since a [`map_async`] callback may be
107///   invoked from another thread, interaction between the callback and the
108///   thread calling [`map_async`] generally requires some sort of shared heap
109///   data like this. In real code, the [`Arc`] would probably own some larger
110///   structure that itself owns `buffer`.
111///
112/// - Then, it calls [`Buffer::slice`] to make a [`BufferSlice`] referring to
113///   the buffer's entire contents.
114///
115/// - Next, it calls [`BufferSlice::map_async`] to request that the bytes to
116///   which the slice refers be made accessible to the CPU ("mapped"). This may
117///   entail waiting for previously enqueued operations on `buffer` to finish.
118///   Although [`map_async`] itself always returns immediately, it saves the
119///   callback function to be invoked later.
120///
121/// - When some later call to [`Device::poll`] or [`Instance::poll_all`] (not
122///   shown in this example) determines that the buffer is mapped and ready for
123///   the CPU to use, it invokes the callback function.
124///
125/// - The callback function calls [`Buffer::slice`] and then
126///   [`BufferSlice::get_mapped_range_mut`] to obtain a [`BufferViewMut`], which
127///   dereferences to a `&mut [u8]` slice referring to the buffer's bytes.
128///
129/// - It then uses the [`bytemuck`] crate to turn the `&mut [u8]` into a `&mut
130///   [f32]`, and calls the slice [`fill`] method to fill the buffer with a
131///   useful value.
132///
133/// - Finally, the callback drops the view and calls [`Buffer::unmap`] to unmap
134///   the buffer. In real code, the callback would also need to do some sort of
135///   synchronization to let the rest of the program know that it has completed
136///   its work.
137///
138/// If using [`map_async`] directly is awkward, you may find it more convenient to
139/// use [`Queue::write_buffer`] and [`util::DownloadBuffer::read_buffer`].
140/// However, those each have their own tradeoffs; the asynchronous nature of GPU
141/// execution makes it hard to avoid friction altogether.
142///
143/// [`Arc`]: std::sync::Arc
144/// [`map_async`]: BufferSlice::map_async
145/// [`bytemuck`]: https://crates.io/crates/bytemuck
146/// [`fill`]: slice::fill
147///
148/// ## Mapping buffers on the web
149///
150/// When compiled to WebAssembly and running in a browser content process,
151/// `wgpu` implements its API in terms of the browser's WebGPU implementation.
152/// In this context, `wgpu` is further isolated from the GPU:
153///
154/// - Depending on the browser's WebGPU implementation, mapping and unmapping
155///   buffers probably entails copies between WebAssembly linear memory and the
156///   graphics driver's buffers.
157///
158/// - All modern web browsers isolate web content in its own sandboxed process,
159///   which can only interact with the GPU via interprocess communication (IPC).
160///   Although most browsers' IPC systems use shared memory for large data
161///   transfers, there will still probably need to be copies into and out of the
162///   shared memory buffers.
163///
164/// All of these copies contribute to the cost of buffer mapping in this
165/// configuration.
166///
167/// [`usage`]: BufferDescriptor::usage
168/// [mac]: BufferDescriptor::mapped_at_creation
169/// [`MAP_READ`]: BufferUsages::MAP_READ
170/// [`MAP_WRITE`]: BufferUsages::MAP_WRITE
171#[derive(Debug, Clone)]
172pub struct Buffer {
173    pub(crate) inner: dispatch::DispatchBuffer,
174    pub(crate) map_context: Arc<Mutex<MapContext>>,
175    pub(crate) size: wgt::BufferAddress,
176    pub(crate) usage: BufferUsages,
177    // Todo: missing map_state https://www.w3.org/TR/webgpu/#dom-gpubuffer-mapstate
178}
179#[cfg(send_sync)]
180static_assertions::assert_impl_all!(Buffer: Send, Sync);
181
182crate::cmp::impl_eq_ord_hash_proxy!(Buffer => .inner);
183
184impl Buffer {
185    /// Return the binding view of the entire buffer.
186    pub fn as_entire_binding(&self) -> BindingResource<'_> {
187        BindingResource::Buffer(self.as_entire_buffer_binding())
188    }
189
190    /// Return the binding view of the entire buffer.
191    pub fn as_entire_buffer_binding(&self) -> BufferBinding<'_> {
192        BufferBinding {
193            buffer: self,
194            offset: 0,
195            size: None,
196        }
197    }
198
199    /// Returns the inner hal Buffer using a callback. The hal buffer will be `None` if the
200    /// backend type argument does not match with this wgpu Buffer
201    ///
202    /// # Safety
203    ///
204    /// - The raw handle obtained from the hal Buffer must not be manually destroyed
205    #[cfg(wgpu_core)]
206    pub unsafe fn as_hal<A: wgc::hal_api::HalApi, F: FnOnce(Option<&A::Buffer>) -> R, R>(
207        &self,
208        hal_buffer_callback: F,
209    ) -> R {
210        if let Some(buffer) = self.inner.as_core_opt() {
211            unsafe {
212                buffer
213                    .context
214                    .buffer_as_hal::<A, F, R>(buffer, hal_buffer_callback)
215            }
216        } else {
217            hal_buffer_callback(None)
218        }
219    }
220
221    /// Return a slice of a [`Buffer`]'s bytes.
222    ///
223    /// Return a [`BufferSlice`] referring to the portion of `self`'s contents
224    /// indicated by `bounds`. Regardless of what sort of data `self` stores,
225    /// `bounds` start and end are given in bytes.
226    ///
227    /// A [`BufferSlice`] can be used to supply vertex and index data, or to map
228    /// buffer contents for access from the CPU. See the [`BufferSlice`]
229    /// documentation for details.
230    ///
231    /// The `range` argument can be half or fully unbounded: for example,
232    /// `buffer.slice(..)` refers to the entire buffer, and `buffer.slice(n..)`
233    /// refers to the portion starting at the `n`th byte and extending to the
234    /// end of the buffer.
235    pub fn slice<S: RangeBounds<BufferAddress>>(&self, bounds: S) -> BufferSlice<'_> {
236        let (offset, size) = range_to_offset_size(bounds);
237        check_buffer_bounds(self.size, offset, size);
238        BufferSlice {
239            buffer: self,
240            offset,
241            size,
242        }
243    }
244
245    /// Flushes any pending write operations and unmaps the buffer from host memory.
246    pub fn unmap(&self) {
247        self.map_context.lock().reset();
248        self.inner.unmap();
249    }
250
251    /// Destroy the associated native resources as soon as possible.
252    pub fn destroy(&self) {
253        self.inner.destroy();
254    }
255
256    /// Returns the length of the buffer allocation in bytes.
257    ///
258    /// This is always equal to the `size` that was specified when creating the buffer.
259    pub fn size(&self) -> BufferAddress {
260        self.size
261    }
262
263    /// Returns the allowed usages for this `Buffer`.
264    ///
265    /// This is always equal to the `usage` that was specified when creating the buffer.
266    pub fn usage(&self) -> BufferUsages {
267        self.usage
268    }
269}
270
271/// A slice of a [`Buffer`], to be mapped, used for vertex or index data, or the like.
272///
273/// You can create a `BufferSlice` by calling [`Buffer::slice`]:
274///
275/// ```no_run
276/// # let buffer: wgpu::Buffer = todo!();
277/// let slice = buffer.slice(10..20);
278/// ```
279///
280/// This returns a slice referring to the second ten bytes of `buffer`. To get a
281/// slice of the entire `Buffer`:
282///
283/// ```no_run
284/// # let buffer: wgpu::Buffer = todo!();
285/// let whole_buffer_slice = buffer.slice(..);
286/// ```
287///
288/// You can pass buffer slices to methods like [`RenderPass::set_vertex_buffer`]
289/// and [`RenderPass::set_index_buffer`] to indicate which portion of the buffer
290/// a draw call should consult.
291///
292/// To access the slice's contents on the CPU, you must first [map] the buffer,
293/// and then call [`BufferSlice::get_mapped_range`] or
294/// [`BufferSlice::get_mapped_range_mut`] to obtain a view of the slice's
295/// contents. See the documentation on [mapping][map] for more details,
296/// including example code.
297///
298/// Unlike a Rust shared slice `&[T]`, whose existence guarantees that
299/// nobody else is modifying the `T` values to which it refers, a
300/// [`BufferSlice`] doesn't guarantee that the buffer's contents aren't
301/// changing. You can still record and submit commands operating on the
302/// buffer while holding a [`BufferSlice`]. A [`BufferSlice`] simply
303/// represents a certain range of the buffer's bytes.
304///
305/// The `BufferSlice` type is unique to the Rust API of `wgpu`. In the WebGPU
306/// specification, an offset and size are specified as arguments to each call
307/// working with the [`Buffer`], instead.
308///
309/// [map]: Buffer#mapping-buffers
310#[derive(Copy, Clone, Debug)]
311pub struct BufferSlice<'a> {
312    pub(crate) buffer: &'a Buffer,
313    pub(crate) offset: BufferAddress,
314    pub(crate) size: Option<BufferSize>,
315}
316#[cfg(send_sync)]
317static_assertions::assert_impl_all!(BufferSlice<'_>: Send, Sync);
318
319impl<'a> BufferSlice<'a> {
320    /// Map the buffer. Buffer is ready to map once the callback is called.
321    ///
322    /// For the callback to complete, either `queue.submit(..)`, `instance.poll_all(..)`, or `device.poll(..)`
323    /// must be called elsewhere in the runtime, possibly integrated into an event loop or run on a separate thread.
324    ///
325    /// The callback will be called on the thread that first calls the above functions after the gpu work
326    /// has completed. There are no restrictions on the code you can run in the callback, however on native the
327    /// call to the function will not complete until the callback returns, so prefer keeping callbacks short
328    /// and used to set flags, send messages, etc.
329    pub fn map_async(
330        &self,
331        mode: MapMode,
332        callback: impl FnOnce(Result<(), BufferAsyncError>) + WasmNotSend + 'static,
333    ) {
334        let mut mc = self.buffer.map_context.lock();
335        assert_eq!(mc.initial_range, 0..0, "Buffer is already mapped");
336        let end = match self.size {
337            Some(s) => self.offset + s.get(),
338            None => mc.total_size,
339        };
340        mc.initial_range = self.offset..end;
341
342        self.buffer
343            .inner
344            .map_async(mode, self.offset..end, Box::new(callback));
345    }
346
347    /// Gain read-only access to the bytes of a [mapped] [`Buffer`].
348    ///
349    /// Return a [`BufferView`] referring to the buffer range represented by
350    /// `self`. See the documentation for [`BufferView`] for details.
351    ///
352    /// # Panics
353    ///
354    /// - This panics if the buffer to which `self` refers is not currently
355    ///   [mapped].
356    ///
357    /// - If you try to create overlapping views of a buffer, mutable or
358    ///   otherwise, `get_mapped_range` will panic.
359    ///
360    /// [mapped]: Buffer#mapping-buffers
361    pub fn get_mapped_range(&self) -> BufferView<'a> {
362        let end = self.buffer.map_context.lock().add(self.offset, self.size);
363        let range = self.buffer.inner.get_mapped_range(self.offset..end);
364        BufferView {
365            slice: *self,
366            inner: range,
367        }
368    }
369
370    /// Synchronously and immediately map a buffer for reading. If the buffer is not immediately mappable
371    /// through [`BufferDescriptor::mapped_at_creation`] or [`BufferSlice::map_async`], will fail.
372    ///
373    /// This is useful when targeting WebGPU and you want to pass mapped data directly to js.
374    /// Unlike `get_mapped_range` which unconditionally copies mapped data into the wasm heap,
375    /// this function directly hands you the ArrayBuffer that we mapped the data into in js.
376    ///
377    /// This is only available on WebGPU, on any other backends this will return `None`.
378    #[cfg(webgpu)]
379    pub fn get_mapped_range_as_array_buffer(&self) -> Option<js_sys::ArrayBuffer> {
380        let end = self.buffer.map_context.lock().add(self.offset, self.size);
381
382        self.buffer
383            .inner
384            .get_mapped_range_as_array_buffer(self.offset..end)
385    }
386
387    /// Gain write access to the bytes of a [mapped] [`Buffer`].
388    ///
389    /// Return a [`BufferViewMut`] referring to the buffer range represented by
390    /// `self`. See the documentation for [`BufferViewMut`] for more details.
391    ///
392    /// # Panics
393    ///
394    /// - This panics if the buffer to which `self` refers is not currently
395    ///   [mapped].
396    ///
397    /// - If you try to create overlapping views of a buffer, mutable or
398    ///   otherwise, `get_mapped_range_mut` will panic.
399    ///
400    /// [mapped]: Buffer#mapping-buffers
401    pub fn get_mapped_range_mut(&self) -> BufferViewMut<'a> {
402        let end = self.buffer.map_context.lock().add(self.offset, self.size);
403        let range = self.buffer.inner.get_mapped_range(self.offset..end);
404        BufferViewMut {
405            slice: *self,
406            inner: range,
407            readable: self.buffer.usage.contains(BufferUsages::MAP_READ),
408        }
409    }
410}
411
412/// The mapped portion of a buffer, if any, and its outstanding views.
413///
414/// This ensures that views fall within the mapped range and don't overlap, and
415/// also takes care of turning `Option<BufferSize>` sizes into actual buffer
416/// offsets.
417#[derive(Debug)]
418pub(crate) struct MapContext {
419    /// The overall size of the buffer.
420    ///
421    /// This is just a convenient copy of [`Buffer::size`].
422    pub(crate) total_size: BufferAddress,
423
424    /// The range of the buffer that is mapped.
425    ///
426    /// This is `0..0` if the buffer is not mapped. This becomes non-empty when
427    /// the buffer is mapped at creation time, and when you call `map_async` on
428    /// some [`BufferSlice`] (so technically, it indicates the portion that is
429    /// *or has been requested to be* mapped.)
430    ///
431    /// All [`BufferView`]s and [`BufferViewMut`]s must fall within this range.
432    pub(crate) initial_range: Range<BufferAddress>,
433
434    /// The ranges covered by all outstanding [`BufferView`]s and
435    /// [`BufferViewMut`]s. These are non-overlapping, and are all contained
436    /// within `initial_range`.
437    sub_ranges: Vec<Range<BufferAddress>>,
438}
439
440impl MapContext {
441    pub(crate) fn new(total_size: BufferAddress) -> Self {
442        Self {
443            total_size,
444            initial_range: 0..0,
445            sub_ranges: Vec::new(),
446        }
447    }
448
449    /// Record that the buffer is no longer mapped.
450    fn reset(&mut self) {
451        self.initial_range = 0..0;
452
453        assert!(
454            self.sub_ranges.is_empty(),
455            "You cannot unmap a buffer that still has accessible mapped views"
456        );
457    }
458
459    /// Record that the `size` bytes of the buffer at `offset` are now viewed.
460    ///
461    /// Return the byte offset within the buffer of the end of the viewed range.
462    ///
463    /// # Panics
464    ///
465    /// This panics if the given range overlaps with any existing range.
466    fn add(&mut self, offset: BufferAddress, size: Option<BufferSize>) -> BufferAddress {
467        let end = match size {
468            Some(s) => offset + s.get(),
469            None => self.initial_range.end,
470        };
471        assert!(self.initial_range.start <= offset && end <= self.initial_range.end);
472        // This check is essential for avoiding undefined behavior: it is the
473        // only thing that ensures that `&mut` references to the buffer's
474        // contents don't alias anything else.
475        for sub in self.sub_ranges.iter() {
476            assert!(
477                end <= sub.start || offset >= sub.end,
478                "Intersecting map range with {sub:?}"
479            );
480        }
481        self.sub_ranges.push(offset..end);
482        end
483    }
484
485    /// Record that the `size` bytes of the buffer at `offset` are no longer viewed.
486    ///
487    /// # Panics
488    ///
489    /// This panics if the given range does not exactly match one previously
490    /// passed to [`add`].
491    ///
492    /// [`add]`: MapContext::add
493    fn remove(&mut self, offset: BufferAddress, size: Option<BufferSize>) {
494        let end = match size {
495            Some(s) => offset + s.get(),
496            None => self.initial_range.end,
497        };
498
499        let index = self
500            .sub_ranges
501            .iter()
502            .position(|r| *r == (offset..end))
503            .expect("unable to remove range from map context");
504        self.sub_ranges.swap_remove(index);
505    }
506}
507
508/// Describes a [`Buffer`].
509///
510/// For use with [`Device::create_buffer`].
511///
512/// Corresponds to [WebGPU `GPUBufferDescriptor`](
513/// https://gpuweb.github.io/gpuweb/#dictdef-gpubufferdescriptor).
514pub type BufferDescriptor<'a> = wgt::BufferDescriptor<Label<'a>>;
515static_assertions::assert_impl_all!(BufferDescriptor<'_>: Send, Sync);
516
517/// Error occurred when trying to async map a buffer.
518#[derive(Clone, PartialEq, Eq, Debug)]
519pub struct BufferAsyncError;
520static_assertions::assert_impl_all!(BufferAsyncError: Send, Sync);
521
522impl fmt::Display for BufferAsyncError {
523    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
524        write!(f, "Error occurred when trying to async map a buffer")
525    }
526}
527
528impl error::Error for BufferAsyncError {}
529
530/// Type of buffer mapping.
531#[derive(Debug, Clone, Copy, Eq, PartialEq)]
532pub enum MapMode {
533    /// Map only for reading
534    Read,
535    /// Map only for writing
536    Write,
537}
538static_assertions::assert_impl_all!(MapMode: Send, Sync);
539
540/// A read-only view of a mapped buffer's bytes.
541///
542/// To get a `BufferView`, first [map] the buffer, and then
543/// call `buffer.slice(range).get_mapped_range()`.
544///
545/// `BufferView` dereferences to `&[u8]`, so you can use all the usual Rust
546/// slice methods to access the buffer's contents. It also implements
547/// `AsRef<[u8]>`, if that's more convenient.
548///
549/// Before the buffer can be unmapped, all `BufferView`s observing it
550/// must be dropped. Otherwise, the call to [`Buffer::unmap`] will panic.
551///
552/// For example code, see the documentation on [mapping buffers][map].
553///
554/// [map]: Buffer#mapping-buffers
555/// [`map_async`]: BufferSlice::map_async
556#[derive(Debug)]
557pub struct BufferView<'a> {
558    slice: BufferSlice<'a>,
559    inner: dispatch::DispatchBufferMappedRange,
560}
561
562impl std::ops::Deref for BufferView<'_> {
563    type Target = [u8];
564
565    #[inline]
566    fn deref(&self) -> &[u8] {
567        self.inner.slice()
568    }
569}
570
571impl AsRef<[u8]> for BufferView<'_> {
572    #[inline]
573    fn as_ref(&self) -> &[u8] {
574        self.inner.slice()
575    }
576}
577
578/// A write-only view of a mapped buffer's bytes.
579///
580/// To get a `BufferViewMut`, first [map] the buffer, and then
581/// call `buffer.slice(range).get_mapped_range_mut()`.
582///
583/// `BufferViewMut` dereferences to `&mut [u8]`, so you can use all the usual
584/// Rust slice methods to access the buffer's contents. It also implements
585/// `AsMut<[u8]>`, if that's more convenient.
586///
587/// It is possible to read the buffer using this view, but doing so is not
588/// recommended, as it is likely to be slow.
589///
590/// Before the buffer can be unmapped, all `BufferViewMut`s observing it
591/// must be dropped. Otherwise, the call to [`Buffer::unmap`] will panic.
592///
593/// For example code, see the documentation on [mapping buffers][map].
594///
595/// [map]: Buffer#mapping-buffers
596#[derive(Debug)]
597pub struct BufferViewMut<'a> {
598    slice: BufferSlice<'a>,
599    inner: dispatch::DispatchBufferMappedRange,
600    readable: bool,
601}
602
603impl AsMut<[u8]> for BufferViewMut<'_> {
604    #[inline]
605    fn as_mut(&mut self) -> &mut [u8] {
606        self.inner.slice_mut()
607    }
608}
609
610impl Deref for BufferViewMut<'_> {
611    type Target = [u8];
612
613    fn deref(&self) -> &Self::Target {
614        if !self.readable {
615            log::warn!("Reading from a BufferViewMut is slow and not recommended.");
616        }
617
618        self.inner.slice()
619    }
620}
621
622impl DerefMut for BufferViewMut<'_> {
623    fn deref_mut(&mut self) -> &mut Self::Target {
624        self.inner.slice_mut()
625    }
626}
627
628impl Drop for BufferView<'_> {
629    fn drop(&mut self) {
630        self.slice
631            .buffer
632            .map_context
633            .lock()
634            .remove(self.slice.offset, self.slice.size);
635    }
636}
637
638impl Drop for BufferViewMut<'_> {
639    fn drop(&mut self) {
640        self.slice
641            .buffer
642            .map_context
643            .lock()
644            .remove(self.slice.offset, self.slice.size);
645    }
646}
647
648fn check_buffer_bounds(
649    buffer_size: BufferAddress,
650    offset: BufferAddress,
651    size: Option<BufferSize>,
652) {
653    // A slice of length 0 is invalid, so the offset must not be equal to or greater than the buffer size.
654    if offset >= buffer_size {
655        panic!(
656            "slice offset {} is out of range for buffer of size {}",
657            offset, buffer_size
658        );
659    }
660
661    if let Some(size) = size {
662        // Detect integer overflow.
663        let end = offset.checked_add(size.get());
664        if end.map_or(true, |end| end > buffer_size) {
665            panic!(
666                "slice offset {} size {} is out of range for buffer of size {}",
667                offset, size, buffer_size
668            );
669        }
670    }
671}
672
673fn range_to_offset_size<S: RangeBounds<BufferAddress>>(
674    bounds: S,
675) -> (BufferAddress, Option<BufferSize>) {
676    let offset = match bounds.start_bound() {
677        Bound::Included(&bound) => bound,
678        Bound::Excluded(&bound) => bound + 1,
679        Bound::Unbounded => 0,
680    };
681    let size = match bounds.end_bound() {
682        Bound::Included(&bound) => Some(bound + 1 - offset),
683        Bound::Excluded(&bound) => Some(bound - offset),
684        Bound::Unbounded => None,
685    }
686    .map(|size| BufferSize::new(size).expect("Buffer slices can not be empty"));
687
688    (offset, size)
689}
690
691#[cfg(test)]
692mod tests {
693    use super::{check_buffer_bounds, range_to_offset_size, BufferSize};
694
695    #[test]
696    fn range_to_offset_size_works() {
697        assert_eq!(range_to_offset_size(0..2), (0, BufferSize::new(2)));
698        assert_eq!(range_to_offset_size(2..5), (2, BufferSize::new(3)));
699        assert_eq!(range_to_offset_size(..), (0, None));
700        assert_eq!(range_to_offset_size(21..), (21, None));
701        assert_eq!(range_to_offset_size(0..), (0, None));
702        assert_eq!(range_to_offset_size(..21), (0, BufferSize::new(21)));
703    }
704
705    #[test]
706    #[should_panic]
707    fn range_to_offset_size_panics_for_empty_range() {
708        range_to_offset_size(123..123);
709    }
710
711    #[test]
712    #[should_panic]
713    fn range_to_offset_size_panics_for_unbounded_empty_range() {
714        range_to_offset_size(..0);
715    }
716
717    #[test]
718    #[should_panic]
719    fn check_buffer_bounds_panics_for_offset_at_size() {
720        check_buffer_bounds(100, 100, None);
721    }
722
723    #[test]
724    fn check_buffer_bounds_works_for_end_in_range() {
725        check_buffer_bounds(200, 100, BufferSize::new(50));
726        check_buffer_bounds(200, 100, BufferSize::new(100));
727        check_buffer_bounds(u64::MAX, u64::MAX - 100, BufferSize::new(100));
728        check_buffer_bounds(u64::MAX, 0, BufferSize::new(u64::MAX));
729        check_buffer_bounds(u64::MAX, 1, BufferSize::new(u64::MAX - 1));
730    }
731
732    #[test]
733    #[should_panic]
734    fn check_buffer_bounds_panics_for_end_over_size() {
735        check_buffer_bounds(200, 100, BufferSize::new(101));
736    }
737
738    #[test]
739    #[should_panic]
740    fn check_buffer_bounds_panics_for_end_wraparound() {
741        check_buffer_bounds(u64::MAX, 1, BufferSize::new(u64::MAX));
742    }
743}