summaryrefslogtreecommitdiff
path: root/core/ops_builtin.rs
diff options
context:
space:
mode:
Diffstat (limited to 'core/ops_builtin.rs')
-rw-r--r--core/ops_builtin.rs74
1 files changed, 64 insertions, 10 deletions
diff --git a/core/ops_builtin.rs b/core/ops_builtin.rs
index 7393d4b69..41741bf28 100644
--- a/core/ops_builtin.rs
+++ b/core/ops_builtin.rs
@@ -2,6 +2,8 @@
use crate::error::format_file_name;
use crate::error::type_error;
use crate::include_js_files;
+use crate::io::BufMutView;
+use crate::io::BufView;
use crate::ops_metrics::OpMetrics;
use crate::resources::ResourceId;
use crate::Extension;
@@ -166,7 +168,8 @@ async fn op_read(
buf: ZeroCopyBuf,
) -> Result<u32, Error> {
let resource = state.borrow().resource_table.get_any(rid)?;
- resource.read_return(buf).await.map(|(n, _)| n as u32)
+ let view = BufMutView::from(buf);
+ resource.read_byob(view).await.map(|(n, _)| n as u32)
}
#[op]
@@ -175,18 +178,67 @@ async fn op_read_all(
rid: ResourceId,
) -> Result<ZeroCopyBuf, Error> {
let resource = state.borrow().resource_table.get_any(rid)?;
- let (min, maximum) = resource.size_hint();
- let size = maximum.unwrap_or(min) as usize;
- let mut buffer = Vec::with_capacity(size);
+ // The number of bytes we attempt to grow the buffer by each time it fills
+ // up and we have more data to read. We start at 64 KB. The grow_len is
+ // doubled if the nread returned from a single read is equal or greater than
+ // the grow_len. This allows us to reduce allocations for resources that can
+ // read large chunks of data at a time.
+ let mut grow_len: usize = 64 * 1024;
+
+ let (min, maybe_max) = resource.size_hint();
+ // Try to determine an optimial starting buffer size for this resource based
+ // on the size hint.
+ let initial_size = match (min, maybe_max) {
+ (min, Some(max)) if min == max => min as usize,
+ (_min, Some(max)) if (max as usize) < grow_len => max as usize,
+ (min, _) if (min as usize) < grow_len => grow_len,
+ (min, _) => min as usize,
+ };
+
+ let mut buf = BufMutView::new(initial_size);
loop {
- let tmp = ZeroCopyBuf::new_temp(vec![0u8; 64 * 1024]);
- let (nread, tmp) = resource.clone().read_return(tmp).await?;
- if nread == 0 {
- return Ok(buffer.into());
+ // if the buffer does not have much remaining space, we may have to grow it.
+ if buf.len() < grow_len {
+ let vec = buf.get_mut_vec();
+ match maybe_max {
+ Some(max) if vec.len() >= max as usize => {
+ // no need to resize the vec, because the vec is already large enough
+ // to accomodate the maximum size of the read data.
+ }
+ Some(max) if (max as usize) < vec.len() + grow_len => {
+ // grow the vec to the maximum size of the read data
+ vec.resize(max as usize, 0);
+ }
+ _ => {
+ // grow the vec by grow_len
+ vec.resize(vec.len() + grow_len, 0);
+ }
+ }
+ }
+ let (n, new_buf) = resource.clone().read_byob(buf).await?;
+ buf = new_buf;
+ buf.advance_cursor(n);
+ if n == 0 {
+ break;
+ }
+ if n >= grow_len {
+ // we managed to read more or equal data than fits in a single grow_len in
+ // a single go, so let's attempt to read even more next time. this reduces
+ // allocations for resources that can read large chunks of data at a time.
+ grow_len *= 2;
}
- buffer.extend_from_slice(&tmp[..nread]);
}
+
+ let nread = buf.reset_cursor();
+ let mut vec = buf.unwrap_vec();
+ // If the buffer is larger than the amount of data read, shrink it to the
+ // amount of data read.
+ if nread < vec.len() {
+ vec.truncate(nread);
+ }
+
+ Ok(ZeroCopyBuf::from(vec))
}
#[op]
@@ -196,7 +248,9 @@ async fn op_write(
buf: ZeroCopyBuf,
) -> Result<u32, Error> {
let resource = state.borrow().resource_table.get_any(rid)?;
- resource.write(buf).await.map(|n| n as u32)
+ let view = BufView::from(buf);
+ let resp = resource.write(view).await?;
+ Ok(resp.nwritten() as u32)
}
#[op]