Skip to main content

deno_core/io/
buffers.rs

1// Copyright 2018-2026 the Deno authors. MIT license.
2
3use std::ops::Deref;
4use std::ops::DerefMut;
5
6use bytes::Buf;
7use bytes::BytesMut;
8use serde_v8::JsBuffer;
9use serde_v8::V8Slice;
10
11/// BufView is a wrapper around an underlying contiguous chunk of bytes. It can
12/// be created from a [JsBuffer], [bytes::Bytes], or [Vec<u8>] and implements
13/// `Deref<[u8]>` and `AsRef<[u8]>`.
14///
15/// The wrapper has the ability to constrain the exposed view to a sub-region of
16/// the underlying buffer. This is useful for write operations, because they may
17/// have to be called multiple times, with different views onto the buffer to be
18/// able to write it entirely.
19#[derive(Debug)]
20pub struct BufView {
21  inner: BufViewInner,
22  cursor: usize,
23}
24
25#[derive(Debug)]
26enum BufViewInner {
27  Empty,
28  Bytes(bytes::Bytes),
29  JsBuffer(V8Slice<u8>),
30}
31
32impl BufView {
33  const fn from_inner(inner: BufViewInner) -> Self {
34    Self { inner, cursor: 0 }
35  }
36
37  pub const fn empty() -> Self {
38    Self::from_inner(BufViewInner::Empty)
39  }
40
41  /// Get the length of the buffer view. This is the length of the underlying
42  /// buffer minus the cursor position.
43  pub fn len(&self) -> usize {
44    match &self.inner {
45      BufViewInner::Empty => 0,
46      BufViewInner::Bytes(bytes) => bytes.len() - self.cursor,
47      BufViewInner::JsBuffer(js_buf) => js_buf.len() - self.cursor,
48    }
49  }
50
51  /// Is the buffer view empty?
52  pub fn is_empty(&self) -> bool {
53    self.len() == 0
54  }
55
56  /// Advance the internal cursor of the buffer view by `n` bytes.
57  pub fn advance_cursor(&mut self, n: usize) {
58    assert!(self.len() >= n);
59    self.cursor += n;
60  }
61
62  /// Reset the internal cursor of the buffer view to the beginning of the
63  /// buffer. Returns the old cursor position.
64  pub fn reset_cursor(&mut self) -> usize {
65    let old = self.cursor;
66    self.cursor = 0;
67    old
68  }
69
70  /// Adjust the length of the remaining buffer. If the requested size is greater than the current
71  /// length, no changes are made.
72  pub fn truncate(&mut self, size: usize) {
73    match &mut self.inner {
74      BufViewInner::Empty => {}
75      BufViewInner::Bytes(bytes) => bytes.truncate(size + self.cursor),
76      BufViewInner::JsBuffer(buffer) => buffer.truncate(size + self.cursor),
77    }
78  }
79
80  /// Split the underlying buffer. The other piece will maintain the current cursor position while this buffer
81  /// will have a cursor of zero.
82  pub fn split_off(&mut self, at: usize) -> Self {
83    let at = at + self.cursor;
84    assert!(at <= self.len());
85    let other = match &mut self.inner {
86      BufViewInner::Empty => BufViewInner::Empty,
87      BufViewInner::Bytes(bytes) => BufViewInner::Bytes(bytes.split_off(at)),
88      BufViewInner::JsBuffer(buffer) => {
89        BufViewInner::JsBuffer(buffer.split_off(at))
90      }
91    };
92    Self {
93      inner: other,
94      cursor: 0,
95    }
96  }
97
98  /// Split the underlying buffer. The other piece will have a cursor of zero while this buffer
99  /// will maintain the current cursor position.
100  pub fn split_to(&mut self, at: usize) -> Self {
101    assert!(at <= self.len());
102    let at = at + self.cursor;
103    let other = match &mut self.inner {
104      BufViewInner::Empty => BufViewInner::Empty,
105      BufViewInner::Bytes(bytes) => BufViewInner::Bytes(bytes.split_to(at)),
106      BufViewInner::JsBuffer(buffer) => {
107        BufViewInner::JsBuffer(buffer.split_to(at))
108      }
109    };
110    let cursor = std::mem::take(&mut self.cursor);
111    Self {
112      inner: other,
113      cursor,
114    }
115  }
116}
117
118impl Buf for BufView {
119  fn remaining(&self) -> usize {
120    self.len()
121  }
122
123  fn chunk(&self) -> &[u8] {
124    self.deref()
125  }
126
127  fn advance(&mut self, cnt: usize) {
128    self.advance_cursor(cnt)
129  }
130}
131
132impl Deref for BufView {
133  type Target = [u8];
134
135  fn deref(&self) -> &[u8] {
136    let buf = match &self.inner {
137      BufViewInner::Empty => &[],
138      BufViewInner::Bytes(bytes) => bytes.deref(),
139      BufViewInner::JsBuffer(js_buf) => js_buf.deref(),
140    };
141    &buf[self.cursor..]
142  }
143}
144
145impl AsRef<[u8]> for BufView {
146  fn as_ref(&self) -> &[u8] {
147    self.deref()
148  }
149}
150
151impl From<JsBuffer> for BufView {
152  fn from(buf: JsBuffer) -> Self {
153    Self::from_inner(BufViewInner::JsBuffer(buf.into_parts()))
154  }
155}
156
157impl From<Vec<u8>> for BufView {
158  fn from(vec: Vec<u8>) -> Self {
159    Self::from_inner(BufViewInner::Bytes(vec.into()))
160  }
161}
162
163impl From<Box<[u8]>> for BufView {
164  fn from(data: Box<[u8]>) -> Self {
165    Self::from_inner(BufViewInner::Bytes(data.into()))
166  }
167}
168
169impl From<bytes::Bytes> for BufView {
170  fn from(buf: bytes::Bytes) -> Self {
171    Self::from_inner(BufViewInner::Bytes(buf))
172  }
173}
174
175/// BufMutView is a wrapper around an underlying contiguous chunk of writable
176/// bytes. It can be created from a `JsBuffer` or a `Vec<u8>` and implements
177/// `DerefMut<[u8]>` and `AsMut<[u8]>`.
178///
179/// The wrapper has the ability to constrain the exposed view to a sub-region of
180/// the underlying buffer. This is useful for write operations, because they may
181/// have to be called multiple times, with different views onto the buffer to be
182/// able to write it entirely.
183///
184/// A `BufMutView` can be turned into a `BufView` by calling `BufMutView::into_view`.
185#[derive(Debug)]
186pub struct BufMutView {
187  inner: BufMutViewInner,
188  cursor: usize,
189}
190
191#[derive(Debug)]
192enum BufMutViewInner {
193  JsBuffer(V8Slice<u8>),
194  Bytes(BytesMut),
195}
196
197impl Default for BufMutView {
198  fn default() -> Self {
199    BufMutView {
200      inner: BufMutViewInner::Bytes(BytesMut::default()),
201      cursor: 0,
202    }
203  }
204}
205
206impl BufMutView {
207  fn from_inner(inner: BufMutViewInner) -> Self {
208    Self { inner, cursor: 0 }
209  }
210
211  pub fn new(len: usize) -> Self {
212    let bytes = BytesMut::zeroed(len);
213    Self::from_inner(BufMutViewInner::Bytes(bytes))
214  }
215
216  /// Get the length of the buffer view. This is the length of the underlying
217  /// buffer minus the cursor position.
218  pub fn len(&self) -> usize {
219    match &self.inner {
220      BufMutViewInner::JsBuffer(js_buf) => js_buf.len() - self.cursor,
221      BufMutViewInner::Bytes(bytes) => bytes.len() - self.cursor,
222    }
223  }
224
225  /// Is the buffer view empty?
226  pub fn is_empty(&self) -> bool {
227    self.len() == 0
228  }
229
230  /// Advance the internal cursor of the buffer view by `n` bytes.
231  pub fn advance_cursor(&mut self, n: usize) {
232    assert!(self.len() >= n);
233    self.cursor += n;
234  }
235
236  /// Reset the internal cursor of the buffer view to the beginning of the
237  /// buffer. Returns the old cursor position.
238  pub fn reset_cursor(&mut self) -> usize {
239    let old = self.cursor;
240    self.cursor = 0;
241    old
242  }
243
244  /// Turn this `BufMutView` into a `BufView`.
245  pub fn into_view(self) -> BufView {
246    let inner = match self.inner {
247      BufMutViewInner::JsBuffer(js_buf) => BufViewInner::JsBuffer(js_buf),
248      BufMutViewInner::Bytes(bytes) => BufViewInner::Bytes(bytes.into()),
249    };
250    BufView {
251      inner,
252      cursor: self.cursor,
253    }
254  }
255
256  /// Attempts to unwrap the underlying buffer into a [`BytesMut`], consuming the `BufMutView`. If
257  /// this buffer does not have a [`BytesMut`], returns `Self`.
258  pub fn maybe_unwrap_bytes(self) -> Result<BytesMut, Self> {
259    match self.inner {
260      BufMutViewInner::JsBuffer(_) => Err(self),
261      BufMutViewInner::Bytes(bytes) => Ok(bytes),
262    }
263  }
264
265  /// This attempts to grow the `BufMutView` to a target size, by a maximum increment. This method
266  /// will be replaced by a better API in the future and should not be used at this time.
267  #[must_use = "The result of this method should be tested"]
268  #[deprecated = "API will be replaced in the future"]
269  #[doc(hidden)]
270  pub fn maybe_resize(
271    &mut self,
272    target_size: usize,
273    maximum_increment: usize,
274  ) -> Option<usize> {
275    match &mut self.inner {
276      BufMutViewInner::Bytes(bytes) => {
277        use std::cmp::Ordering::*;
278        let len = bytes.len();
279        let target_size = target_size + self.cursor;
280        match target_size.cmp(&len) {
281          Greater => {
282            bytes
283              .resize(std::cmp::min(target_size, len + maximum_increment), 0);
284          }
285          Less => {
286            bytes.truncate(target_size);
287          }
288          Equal => {}
289        }
290        Some(bytes.len())
291      }
292      _ => None,
293    }
294  }
295
296  /// This attempts to grow the `BufMutView` to a target size, by a maximum increment. This method
297  /// will be replaced by a better API in the future and should not be used at this time.
298  #[must_use = "The result of this method should be tested"]
299  #[deprecated = "API will be replaced in the future"]
300  #[doc(hidden)]
301  pub fn maybe_grow(&mut self, target_size: usize) -> Option<usize> {
302    match &mut self.inner {
303      BufMutViewInner::Bytes(bytes) => {
304        let len = bytes.len();
305        let target_size = target_size + self.cursor;
306        if target_size > len {
307          bytes.resize(target_size, 0);
308        }
309        Some(bytes.len())
310      }
311      _ => None,
312    }
313  }
314
315  /// Adjust the length of the remaining buffer and ensure that the cursor continues to
316  /// stay in-bounds.
317  pub fn truncate(&mut self, size: usize) {
318    match &mut self.inner {
319      BufMutViewInner::Bytes(bytes) => bytes.truncate(size + self.cursor),
320      BufMutViewInner::JsBuffer(buffer) => buffer.truncate(size + self.cursor),
321    }
322    self.cursor = std::cmp::min(self.cursor, self.len());
323  }
324
325  /// Split the underlying buffer. The other piece will maintain the current cursor position while this buffer
326  /// will have a cursor of zero.
327  pub fn split_off(&mut self, at: usize) -> Self {
328    let at = at + self.cursor;
329    assert!(at <= self.len());
330    let other = match &mut self.inner {
331      BufMutViewInner::Bytes(bytes) => {
332        BufMutViewInner::Bytes(bytes.split_off(at))
333      }
334      BufMutViewInner::JsBuffer(buffer) => {
335        BufMutViewInner::JsBuffer(buffer.split_off(at))
336      }
337    };
338    Self {
339      inner: other,
340      cursor: 0,
341    }
342  }
343
344  /// Split the underlying buffer. The other piece will have a cursor of zero while this buffer
345  /// will maintain the current cursor position.
346  pub fn split_to(&mut self, at: usize) -> Self {
347    assert!(at <= self.len());
348    let at = at + self.cursor;
349    let other = match &mut self.inner {
350      BufMutViewInner::Bytes(bytes) => {
351        BufMutViewInner::Bytes(bytes.split_to(at))
352      }
353      BufMutViewInner::JsBuffer(buffer) => {
354        BufMutViewInner::JsBuffer(buffer.split_to(at))
355      }
356    };
357    let cursor = std::mem::take(&mut self.cursor);
358    Self {
359      inner: other,
360      cursor,
361    }
362  }
363}
364
365impl Buf for BufMutView {
366  fn remaining(&self) -> usize {
367    self.len()
368  }
369
370  fn chunk(&self) -> &[u8] {
371    self.deref()
372  }
373
374  fn advance(&mut self, cnt: usize) {
375    self.advance_cursor(cnt)
376  }
377}
378
379impl Deref for BufMutView {
380  type Target = [u8];
381
382  fn deref(&self) -> &[u8] {
383    let buf = match &self.inner {
384      BufMutViewInner::JsBuffer(js_buf) => js_buf.deref(),
385      BufMutViewInner::Bytes(vec) => vec.deref(),
386    };
387    &buf[self.cursor..]
388  }
389}
390
391impl DerefMut for BufMutView {
392  fn deref_mut(&mut self) -> &mut [u8] {
393    let buf = match &mut self.inner {
394      BufMutViewInner::JsBuffer(js_buf) => js_buf.deref_mut(),
395      BufMutViewInner::Bytes(vec) => vec.deref_mut(),
396    };
397    &mut buf[self.cursor..]
398  }
399}
400
401impl AsRef<[u8]> for BufMutView {
402  fn as_ref(&self) -> &[u8] {
403    self.deref()
404  }
405}
406
407impl AsMut<[u8]> for BufMutView {
408  fn as_mut(&mut self) -> &mut [u8] {
409    self.deref_mut()
410  }
411}
412
413impl From<JsBuffer> for BufMutView {
414  fn from(buf: JsBuffer) -> Self {
415    Self::from_inner(BufMutViewInner::JsBuffer(buf.into_parts()))
416  }
417}
418
419impl From<BytesMut> for BufMutView {
420  fn from(buf: BytesMut) -> Self {
421    Self::from_inner(BufMutViewInner::Bytes(buf))
422  }
423}
424
425#[cfg(test)]
426mod tests {
427  use super::*;
428
429  #[test]
430  pub fn bufview_read_and_truncate() {
431    let mut buf = BufView::from(vec![1, 2, 3, 4]);
432    assert_eq!(4, buf.len());
433    assert_eq!(0, buf.cursor);
434    assert_eq!(1, buf.get_u8());
435    assert_eq!(3, buf.len());
436    // The cursor is at position 1, so this truncates the underlying buffer to 2+1
437    buf.truncate(2);
438    assert_eq!(2, buf.len());
439    assert_eq!(2, buf.get_u8());
440    assert_eq!(1, buf.len());
441
442    buf.reset_cursor();
443    assert_eq!(3, buf.len());
444  }
445
446  #[test]
447  pub fn bufview_split() {
448    let mut buf = BufView::from(Vec::from_iter(0..100));
449    assert_eq!(100, buf.len());
450    buf.advance_cursor(25);
451    assert_eq!(75, buf.len());
452    let mut other = buf.split_off(10);
453    assert_eq!(25, buf.cursor);
454    assert_eq!(10, buf.len());
455    assert_eq!(65, other.len());
456
457    let other2 = other.split_to(20);
458    assert_eq!(20, other2.len());
459    assert_eq!(45, other.len());
460
461    assert_eq!(100, buf.cursor + buf.len() + other.len() + other2.len());
462    buf.reset_cursor();
463    assert_eq!(100, buf.cursor + buf.len() + other.len() + other2.len());
464  }
465
466  #[test]
467  pub fn bufmutview_read_and_truncate() {
468    let mut buf = BufMutView::from(BytesMut::from([1, 2, 3, 4].as_slice()));
469    assert_eq!(4, buf.len());
470    assert_eq!(0, buf.cursor);
471    assert_eq!(1, buf.get_u8());
472    assert_eq!(3, buf.len());
473    // The cursor is at position 1, so this truncates the underlying buffer to 2+1
474    buf.truncate(2);
475    assert_eq!(2, buf.len());
476    assert_eq!(2, buf.get_u8());
477    assert_eq!(1, buf.len());
478
479    buf.reset_cursor();
480    assert_eq!(3, buf.len());
481  }
482
483  #[test]
484  pub fn bufmutview_split() {
485    let mut buf =
486      BufMutView::from(BytesMut::from(Vec::from_iter(0..100).as_slice()));
487    assert_eq!(100, buf.len());
488    buf.advance_cursor(25);
489    assert_eq!(75, buf.len());
490    let mut other = buf.split_off(10);
491    assert_eq!(25, buf.cursor);
492    assert_eq!(10, buf.len());
493    assert_eq!(65, other.len());
494
495    let other2 = other.split_to(20);
496    assert_eq!(20, other2.len());
497    assert_eq!(45, other.len());
498
499    assert_eq!(100, buf.cursor + buf.len() + other.len() + other2.len());
500    buf.reset_cursor();
501    assert_eq!(100, buf.cursor + buf.len() + other.len() + other2.len());
502  }
503
504  #[test]
505  #[allow(deprecated, reason = "test code")]
506  fn bufmutview_resize() {
507    let new =
508      || BufMutView::from(BytesMut::from(Vec::from_iter(0..100).as_slice()));
509    let mut buf = new();
510    assert_eq!(100, buf.len());
511    buf.maybe_resize(200, 10).unwrap();
512    assert_eq!(110, buf.len());
513
514    let mut buf = new();
515    assert_eq!(100, buf.len());
516    buf.maybe_resize(200, 100).unwrap();
517    assert_eq!(200, buf.len());
518
519    let mut buf = new();
520    assert_eq!(100, buf.len());
521    buf.maybe_resize(200, 1000).unwrap();
522    assert_eq!(200, buf.len());
523
524    let mut buf = new();
525    buf.advance_cursor(50);
526    assert_eq!(50, buf.len());
527    buf.maybe_resize(100, 100).unwrap();
528    assert_eq!(100, buf.len());
529    buf.reset_cursor();
530    assert_eq!(150, buf.len());
531  }
532
533  #[test]
534  #[allow(deprecated, reason = "test code")]
535  fn bufmutview_grow() {
536    let new =
537      || BufMutView::from(BytesMut::from(Vec::from_iter(0..100).as_slice()));
538    let mut buf = new();
539    assert_eq!(100, buf.len());
540    buf.maybe_grow(200).unwrap();
541    assert_eq!(200, buf.len());
542
543    let mut buf = new();
544    buf.advance_cursor(50);
545    assert_eq!(50, buf.len());
546    buf.maybe_grow(100).unwrap();
547    assert_eq!(100, buf.len());
548    buf.reset_cursor();
549    assert_eq!(150, buf.len());
550  }
551}