1
0
Fork 0
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:
Aaron O'Mullan 2022-04-27 07:03:44 -07:00 committed by GitHub
parent 9853c96cc4
commit 8b8b21b553
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 129 additions and 64 deletions

View file

@ -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);
},
);

View file

@ -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 = {

View file

@ -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?
}