diff options
Diffstat (limited to 'core/ops_builtin.rs')
-rw-r--r-- | core/ops_builtin.rs | 74 |
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] |