mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
perf(runtime): read entire files in single ops (#14261)
Co-authored-by: Divy Srivastava <dj.srivastava23@gmail.com>
This commit is contained in:
parent
9853c96cc4
commit
8b8b21b553
3 changed files with 129 additions and 64 deletions
|
@ -1,5 +1,4 @@
|
|||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||
import { writeAllSync } from "../../../test_util/std/io/util.ts";
|
||||
import {
|
||||
assert,
|
||||
assertEquals,
|
||||
|
@ -152,42 +151,3 @@ Deno.test(
|
|||
assert(data.byteLength > 0);
|
||||
},
|
||||
);
|
||||
|
||||
Deno.test(
|
||||
{ permissions: { read: true, write: true } },
|
||||
async function readFileExtendedDuringRead() {
|
||||
// Write 128MB file
|
||||
const filename = Deno.makeTempDirSync() + "/test.txt";
|
||||
const data = new Uint8Array(1024 * 1024 * 128);
|
||||
Deno.writeFileSync(filename, data);
|
||||
const promise = Deno.readFile(filename);
|
||||
queueMicrotask(() => {
|
||||
// Append 128MB to file
|
||||
const f = Deno.openSync(filename, { append: true });
|
||||
writeAllSync(f, data);
|
||||
f.close();
|
||||
});
|
||||
const read = await promise;
|
||||
assertEquals(read.byteLength, data.byteLength * 2);
|
||||
},
|
||||
);
|
||||
|
||||
Deno.test(
|
||||
{ permissions: { read: true, write: true } },
|
||||
async function readFile0LengthExtendedDuringRead() {
|
||||
// Write 0 byte file
|
||||
const filename = Deno.makeTempDirSync() + "/test.txt";
|
||||
const first = new Uint8Array(0);
|
||||
const second = new Uint8Array(1024 * 1024 * 128);
|
||||
Deno.writeFileSync(filename, first);
|
||||
const promise = Deno.readFile(filename);
|
||||
queueMicrotask(() => {
|
||||
// Append 128MB to file
|
||||
const f = Deno.openSync(filename, { append: true });
|
||||
writeAllSync(f, second);
|
||||
f.close();
|
||||
});
|
||||
const read = await promise;
|
||||
assertEquals(read.byteLength, second.byteLength);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -3,44 +3,69 @@
|
|||
|
||||
((window) => {
|
||||
const core = window.Deno.core;
|
||||
const { open, openSync } = window.__bootstrap.files;
|
||||
const { readAllSync, readAll, readAllSyncSized, readAllInnerSized } =
|
||||
window.__bootstrap.io;
|
||||
const { pathFromURL } = window.__bootstrap.util;
|
||||
const { abortSignal } = window.__bootstrap;
|
||||
|
||||
function readFileSync(path) {
|
||||
const file = openSync(path);
|
||||
try {
|
||||
const { size } = file.statSync();
|
||||
if (size === 0) {
|
||||
return readAllSync(file);
|
||||
} else {
|
||||
return readAllSyncSized(file, size);
|
||||
}
|
||||
} finally {
|
||||
file.close();
|
||||
}
|
||||
return core.opSync("op_readfile_sync", pathFromURL(path));
|
||||
}
|
||||
|
||||
async function readFile(path, options) {
|
||||
const file = await open(path);
|
||||
let cancelRid;
|
||||
let abortHandler;
|
||||
if (options?.signal) {
|
||||
options.signal.throwIfAborted();
|
||||
cancelRid = core.opSync("op_cancel_handle");
|
||||
abortHandler = () => core.tryClose(cancelRid);
|
||||
options.signal[abortSignal.add](abortHandler);
|
||||
}
|
||||
|
||||
try {
|
||||
const { size } = await file.stat();
|
||||
if (size === 0) {
|
||||
return await readAll(file);
|
||||
} else {
|
||||
return await readAllInnerSized(file, size, options);
|
||||
}
|
||||
const read = await core.opAsync(
|
||||
"op_readfile_async",
|
||||
pathFromURL(path),
|
||||
cancelRid,
|
||||
);
|
||||
return read;
|
||||
} finally {
|
||||
file.close();
|
||||
if (options?.signal) {
|
||||
options.signal[abortSignal.remove](abortHandler);
|
||||
|
||||
// always throw the abort error when aborted
|
||||
options.signal.throwIfAborted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function readTextFileSync(path) {
|
||||
return core.decode(readFileSync(path));
|
||||
return core.opSync("op_readfile_text_sync", pathFromURL(path));
|
||||
}
|
||||
|
||||
async function readTextFile(path, options) {
|
||||
return core.decode(await readFile(path, options));
|
||||
let cancelRid;
|
||||
let abortHandler;
|
||||
if (options?.signal) {
|
||||
options.signal.throwIfAborted();
|
||||
cancelRid = core.opSync("op_cancel_handle");
|
||||
abortHandler = () => core.tryClose(cancelRid);
|
||||
options.signal[abortSignal.add](abortHandler);
|
||||
}
|
||||
|
||||
try {
|
||||
const read = await core.opAsync(
|
||||
"op_readfile_text_async",
|
||||
pathFromURL(path),
|
||||
cancelRid,
|
||||
);
|
||||
return read;
|
||||
} finally {
|
||||
if (options?.signal) {
|
||||
options.signal[abortSignal.remove](abortHandler);
|
||||
|
||||
// always throw the abort error when aborted
|
||||
options.signal.throwIfAborted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.__bootstrap.readFile = {
|
||||
|
|
|
@ -95,6 +95,10 @@ pub fn init() -> Extension {
|
|||
op_futime_async::decl(),
|
||||
op_utime_sync::decl(),
|
||||
op_utime_async::decl(),
|
||||
op_readfile_sync::decl(),
|
||||
op_readfile_text_sync::decl(),
|
||||
op_readfile_async::decl(),
|
||||
op_readfile_text_async::decl(),
|
||||
])
|
||||
.build()
|
||||
}
|
||||
|
@ -2008,3 +2012,79 @@ fn op_cwd(state: &mut OpState) -> Result<String, AnyError> {
|
|||
let path_str = into_string(path.into_os_string())?;
|
||||
Ok(path_str)
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_readfile_sync(
|
||||
state: &mut OpState,
|
||||
path: String,
|
||||
) -> Result<ZeroCopyBuf, AnyError> {
|
||||
let permissions = state.borrow_mut::<Permissions>();
|
||||
let path = Path::new(&path);
|
||||
permissions.read.check(path)?;
|
||||
Ok(std::fs::read(path)?.into())
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_readfile_text_sync(
|
||||
state: &mut OpState,
|
||||
path: String,
|
||||
) -> Result<String, AnyError> {
|
||||
let permissions = state.borrow_mut::<Permissions>();
|
||||
let path = Path::new(&path);
|
||||
permissions.read.check(path)?;
|
||||
Ok(std::fs::read_to_string(path)?)
|
||||
}
|
||||
|
||||
#[op]
|
||||
async fn op_readfile_async(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
path: String,
|
||||
cancel_rid: Option<ResourceId>,
|
||||
) -> Result<ZeroCopyBuf, AnyError> {
|
||||
{
|
||||
let path = Path::new(&path);
|
||||
let mut state = state.borrow_mut();
|
||||
state.borrow_mut::<Permissions>().read.check(path)?;
|
||||
}
|
||||
let fut = tokio::task::spawn_blocking(move || {
|
||||
let path = Path::new(&path);
|
||||
Ok(std::fs::read(path).map(ZeroCopyBuf::from)?)
|
||||
});
|
||||
if let Some(cancel_rid) = cancel_rid {
|
||||
let cancel_handle = state
|
||||
.borrow_mut()
|
||||
.resource_table
|
||||
.get::<CancelHandle>(cancel_rid);
|
||||
if let Ok(cancel_handle) = cancel_handle {
|
||||
return fut.or_cancel(cancel_handle).await??;
|
||||
}
|
||||
}
|
||||
fut.await?
|
||||
}
|
||||
|
||||
#[op]
|
||||
async fn op_readfile_text_async(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
path: String,
|
||||
cancel_rid: Option<ResourceId>,
|
||||
) -> Result<String, AnyError> {
|
||||
{
|
||||
let path = Path::new(&path);
|
||||
let mut state = state.borrow_mut();
|
||||
state.borrow_mut::<Permissions>().read.check(path)?;
|
||||
}
|
||||
let fut = tokio::task::spawn_blocking(move || {
|
||||
let path = Path::new(&path);
|
||||
Ok(String::from_utf8(std::fs::read(path)?)?)
|
||||
});
|
||||
if let Some(cancel_rid) = cancel_rid {
|
||||
let cancel_handle = state
|
||||
.borrow_mut()
|
||||
.resource_table
|
||||
.get::<CancelHandle>(cancel_rid);
|
||||
if let Ok(cancel_handle) = cancel_handle {
|
||||
return fut.or_cancel(cancel_handle).await??;
|
||||
}
|
||||
}
|
||||
fut.await?
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue