1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-24 15:19:26 -05:00

Rewrite readFile and writeFile (#2000)

Using open/read/write
This commit is contained in:
Bartek Iwańczuk 2019-03-28 04:29:36 +01:00 committed by Ryan Dahl
parent d0b6152f11
commit 597ee38ef2
10 changed files with 337 additions and 190 deletions

View file

@ -39,8 +39,6 @@ union Any {
Read,
ReadDir,
ReadDirRes,
ReadFile,
ReadFileRes,
ReadRes,
Readlink,
ReadlinkRes,
@ -69,7 +67,6 @@ union Any {
WorkerGetMessageRes,
WorkerPostMessage,
Write,
WriteFile,
WriteRes,
}
@ -296,14 +293,6 @@ table Remove {
recursive: bool;
}
table ReadFile {
filename: string;
}
table ReadFileRes {
data: [ubyte];
}
table ReadDir {
path: string;
}
@ -312,16 +301,6 @@ table ReadDirRes {
entries: [StatRes];
}
table WriteFile {
filename: string;
data: [ubyte];
update_perm: bool;
perm: uint;
// perm specified by https://godoc.org/os#FileMode
is_create: bool;
is_append: bool;
}
table CopyFile {
from: string;
to: string;

View file

@ -174,7 +174,6 @@ pub fn op_selector_std(inner_type: msg::Any) -> Option<OpCreator> {
msg::Any::Permissions => Some(op_permissions),
msg::Any::Read => Some(op_read),
msg::Any::ReadDir => Some(op_read_dir),
msg::Any::ReadFile => Some(op_read_file),
msg::Any::Readlink => Some(op_read_link),
msg::Any::Remove => Some(op_remove),
msg::Any::Rename => Some(op_rename),
@ -193,7 +192,6 @@ pub fn op_selector_std(inner_type: msg::Any) -> Option<OpCreator> {
msg::Any::WorkerGetMessage => Some(op_worker_get_message),
msg::Any::WorkerPostMessage => Some(op_worker_post_message),
msg::Any::Write => Some(op_write),
msg::Any::WriteFile => Some(op_write_file),
_ => None,
}
}
@ -1026,45 +1024,6 @@ fn op_remove(
})
}
// Prototype https://github.com/denoland/deno/blob/golang/os.go#L171-L184
fn op_read_file(
sc: &IsolateStateContainer,
base: &msg::Base<'_>,
data: deno_buf,
) -> Box<OpWithError> {
assert_eq!(data.len(), 0);
let inner = base.inner_as_read_file().unwrap();
let cmd_id = base.cmd_id();
let filename_ = inner.filename().unwrap();
let filename = PathBuf::from(filename_);
debug!("op_read_file {}", filename.display());
if let Err(e) = sc.state().check_read(&filename_) {
return odd_future(e);
}
blocking(base.sync(), move || {
let vec = fs::read(&filename)?;
// Build the response message. memcpy data into inner.
// TODO(ry) zero-copy.
let builder = &mut FlatBufferBuilder::new();
let data_off = builder.create_vector(vec.as_slice());
let inner = msg::ReadFileRes::create(
builder,
&msg::ReadFileResArgs {
data: Some(data_off),
},
);
Ok(serialize_response(
cmd_id,
builder,
msg::BaseArgs {
inner: Some(inner.as_union_value()),
inner_type: msg::Any::ReadFileRes,
..Default::default()
},
))
})
}
fn op_copy_file(
sc: &IsolateStateContainer,
base: &msg::Base<'_>,
@ -1260,36 +1219,6 @@ fn op_read_dir(
})
}
fn op_write_file(
sc: &IsolateStateContainer,
base: &msg::Base<'_>,
data: deno_buf,
) -> Box<OpWithError> {
let inner = base.inner_as_write_file().unwrap();
let filename = String::from(inner.filename().unwrap());
let update_perm = inner.update_perm();
let perm = inner.perm();
let is_create = inner.is_create();
let is_append = inner.is_append();
if let Err(e) = sc.state().check_write(&filename) {
return odd_future(e);
}
blocking(base.sync(), move || -> OpResult {
debug!("op_write_file {} {}", filename, data.len());
deno_fs::write_file_2(
Path::new(&filename),
data,
update_perm,
perm,
is_create,
is_append,
)?;
Ok(empty_buf())
})
}
fn op_rename(
sc: &IsolateStateContainer,
base: &msg::Base<'_>,

View file

@ -5,7 +5,7 @@
// https://github.com/golang/go/blob/master/LICENSE
//import * as io from "./io";
import { Reader, Writer, ReadResult } from "./io";
import { Reader, Writer, ReadResult, SyncReader, SyncWriter } from "./io";
import { assert } from "./util";
import { TextDecoder } from "./text_encoding";
import { DenoError, ErrorKind } from "./errors";
@ -32,7 +32,7 @@ function copyBytes(dst: Uint8Array, src: Uint8Array, off = 0): number {
/** A Buffer is a variable-sized buffer of bytes with read() and write()
* methods. Based on https://golang.org/pkg/bytes/#Buffer
*/
export class Buffer implements Reader, Writer {
export class Buffer implements Reader, SyncReader, Writer, SyncWriter {
private buf: Uint8Array; // contents are the bytes buf[off : len(buf)]
private off = 0; // read at buf[off], write at buf[buf.byteLength]
@ -126,11 +126,11 @@ export class Buffer implements Reader, Writer {
this.buf = new Uint8Array(this.buf.buffer, 0, len);
}
/** read() reads the next len(p) bytes from the buffer or until the buffer
/** readSync() reads the next len(p) bytes from the buffer or until the buffer
* is drained. The return value n is the number of bytes read. If the
* buffer has no data to return, eof in the response will be true.
*/
async read(p: Uint8Array): Promise<ReadResult> {
readSync(p: Uint8Array): ReadResult {
if (this.empty()) {
// Buffer is empty, reset to recover space.
this.reset();
@ -145,11 +145,21 @@ export class Buffer implements Reader, Writer {
return { nread, eof: false };
}
async write(p: Uint8Array): Promise<number> {
async read(p: Uint8Array): Promise<ReadResult> {
const rr = this.readSync(p);
return Promise.resolve(rr);
}
writeSync(p: Uint8Array): number {
const m = this._grow(p.byteLength);
return copyBytes(this.buf, p, m);
}
async write(p: Uint8Array): Promise<number> {
const n = this.writeSync(p);
return Promise.resolve(n);
}
/** _grow() grows the buffer to guarantee space for n more bytes.
* It returns the index where bytes should be written.
* If the buffer can't grow it will throw with ErrTooLarge.
@ -226,6 +236,27 @@ export class Buffer implements Reader, Writer {
}
}
}
/** Sync version of `readFrom`
*/
readFromSync(r: SyncReader): number {
let n = 0;
while (true) {
try {
const i = this._grow(MIN_READ);
this._reslice(i);
const fub = new Uint8Array(this.buf.buffer, i);
const { nread, eof } = r.readSync(fub);
this._reslice(i + nread);
n += nread;
if (eof) {
return n;
}
} catch (e) {
return n;
}
}
}
}
/** Read `r` until EOF and return the content as `Uint8Array`.
@ -235,3 +266,11 @@ export async function readAll(r: Reader): Promise<Uint8Array> {
await buf.readFrom(r);
return buf.bytes();
}
/** Read synchronously `r` until EOF and return the content as `Uint8Array`.
*/
export function readAllSync(r: SyncReader): Uint8Array {
const buf = new Buffer();
buf.readFromSync(r);
return buf.bytes();
}

View file

@ -5,7 +5,7 @@
// https://github.com/golang/go/blob/master/LICENSE
import { assertEquals, test } from "./test_util.ts";
const { Buffer, readAll } = Deno;
const { Buffer, readAll, readAllSync } = Deno;
type Buffer = Deno.Buffer;
// N controls how many iterations of certain checks are performed.
@ -193,6 +193,23 @@ test(async function bufferReadFrom() {
}
});
test(async function bufferReadFromSync() {
init();
const buf = new Buffer();
for (let i = 3; i < 30; i += 3) {
const s = await fillBytes(
buf,
"",
5,
testBytes.subarray(0, Math.floor(testBytes.byteLength / i))
);
const b = new Buffer();
b.readFromSync(buf);
const fub = new Uint8Array(testString.length);
await empty(b, s, fub);
}
});
test(async function bufferTestGrow() {
const tmp = new Uint8Array(72);
for (let startLen of [0, 100, 1000, 10000, 100000]) {
@ -226,3 +243,13 @@ test(async function testReadAll() {
assertEquals(testBytes[i], actualBytes[i]);
}
});
test(function testReadAllSync() {
init();
const reader = new Buffer(testBytes.buffer as ArrayBuffer);
const actualBytes = readAllSync(reader);
assertEquals(testBytes.byteLength, actualBytes.byteLength);
for (let i = 0; i < testBytes.length; ++i) {
assertEquals(testBytes[i], actualBytes[i]);
}
});

View file

@ -6,12 +6,16 @@ export { chdir, cwd } from "./dir";
export {
File,
open,
openSync,
stdin,
stdout,
stderr,
read,
readSync,
write,
writeSync,
seek,
seekSync,
close,
OpenMode
} from "./files";
@ -21,9 +25,12 @@ export {
ReadResult,
SeekMode,
Reader,
SyncReader,
Writer,
SyncWriter,
Closer,
Seeker,
SyncSeeker,
ReadCloser,
WriteCloser,
ReadSeeker,
@ -31,7 +38,7 @@ export {
ReadWriteCloser,
ReadWriteSeeker
} from "./io";
export { Buffer, readAll } from "./buffer";
export { Buffer, readAll, readAllSync } from "./buffer";
export { mkdirSync, mkdir } from "./mkdir";
export {
makeTempDirSync,

View file

@ -1,10 +1,53 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { Reader, Writer, Seeker, Closer, ReadResult, SeekMode } from "./io";
import {
Reader,
Writer,
Seeker,
Closer,
ReadResult,
SeekMode,
SyncReader,
SyncWriter,
SyncSeeker
} from "./io";
import * as dispatch from "./dispatch";
import * as msg from "gen/msg_generated";
import { assert } from "./util";
import * as flatbuffers from "./flatbuffers";
function reqOpen(
filename: string,
mode: OpenMode
): [flatbuffers.Builder, msg.Any, flatbuffers.Offset] {
const builder = flatbuffers.createBuilder();
const filename_ = builder.createString(filename);
const mode_ = builder.createString(mode);
msg.Open.startOpen(builder);
msg.Open.addFilename(builder, filename_);
msg.Open.addMode(builder, mode_);
const inner = msg.Open.endOpen(builder);
return [builder, msg.Any.Open, inner];
}
function resOpen(baseRes: null | msg.Base): File {
assert(baseRes != null);
assert(msg.Any.OpenRes === baseRes!.innerType());
const res = new msg.OpenRes();
assert(baseRes!.inner(res) != null);
const rid = res.rid();
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return new File(rid);
}
/** Open a file and return an instance of the `File` object
* synchronously.
*
* const file = Deno.openSync("/foo/bar.txt");
*/
export function openSync(filename: string, mode: OpenMode = "r"): File {
return resOpen(dispatch.sendSync(...reqOpen(filename, mode)));
}
/** Open a file and return an instance of the `File` object.
*
* (async () => {
@ -15,33 +58,21 @@ export async function open(
filename: string,
mode: OpenMode = "r"
): Promise<File> {
const builder = flatbuffers.createBuilder();
const filename_ = builder.createString(filename);
const mode_ = builder.createString(mode);
msg.Open.startOpen(builder);
msg.Open.addFilename(builder, filename_);
msg.Open.addMode(builder, mode_);
const inner = msg.Open.endOpen(builder);
const baseRes = await dispatch.sendAsync(builder, msg.Any.Open, inner);
assert(baseRes != null);
assert(msg.Any.OpenRes === baseRes!.innerType());
const res = new msg.OpenRes();
assert(baseRes!.inner(res) != null);
const rid = res.rid();
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return new File(rid);
return resOpen(await dispatch.sendAsync(...reqOpen(filename, mode)));
}
/** Read from a file ID into an array buffer.
*
* Resolves with the `ReadResult` for the operation.
*/
export async function read(rid: number, p: Uint8Array): Promise<ReadResult> {
function reqRead(
rid: number,
p: Uint8Array
): [flatbuffers.Builder, msg.Any, flatbuffers.Offset, Uint8Array] {
const builder = flatbuffers.createBuilder();
msg.Read.startRead(builder);
msg.Read.addRid(builder, rid);
const inner = msg.Read.endRead(builder);
const baseRes = await dispatch.sendAsync(builder, msg.Any.Read, inner, p);
return [builder, msg.Any.Read, inner, p];
}
function resRead(baseRes: null | msg.Base): ReadResult {
assert(baseRes != null);
assert(msg.Any.ReadRes === baseRes!.innerType());
const res = new msg.ReadRes();
@ -49,16 +80,47 @@ export async function read(rid: number, p: Uint8Array): Promise<ReadResult> {
return { nread: res.nread(), eof: res.eof() };
}
/** Write to the file ID the contents of the array buffer.
/** Read synchronously from a file ID into an array buffer.
*
* Return `ReadResult` for the operation.
*
* const file = Deno.openSync("/foo/bar.txt");
* const buf = new Uint8Array(100);
* const { nread, eof } = Deno.readSync(file.rid, buf);
* const text = new TextDecoder.decode(buf);
*
* Resolves with the number of bytes written.
*/
export async function write(rid: number, p: Uint8Array): Promise<number> {
export function readSync(rid: number, p: Uint8Array): ReadResult {
return resRead(dispatch.sendSync(...reqRead(rid, p)));
}
/** Read from a file ID into an array buffer.
*
* Resolves with the `ReadResult` for the operation.
*
* (async () => {
* const file = await Deno.open("/foo/bar.txt");
* const buf = new Uint8Array(100);
* const { nread, eof } = await Deno.read(file.rid, buf);
* const text = new TextDecoder.decode(buf);
* })();
*/
export async function read(rid: number, p: Uint8Array): Promise<ReadResult> {
return resRead(await dispatch.sendAsync(...reqRead(rid, p)));
}
function reqWrite(
rid: number,
p: Uint8Array
): [flatbuffers.Builder, msg.Any, flatbuffers.Offset, Uint8Array] {
const builder = flatbuffers.createBuilder();
msg.Write.startWrite(builder);
msg.Write.addRid(builder, rid);
const inner = msg.Write.endWrite(builder);
const baseRes = await dispatch.sendAsync(builder, msg.Any.Write, inner, p);
return [builder, msg.Any.Write, inner, p];
}
function resWrite(baseRes: null | msg.Base): number {
assert(baseRes != null);
assert(msg.Any.WriteRes === baseRes!.innerType());
const res = new msg.WriteRes();
@ -66,21 +128,71 @@ export async function write(rid: number, p: Uint8Array): Promise<number> {
return res.nbyte();
}
/** Seek a file ID to the given offset under mode given by `whence`.
/** Write synchronously to the file ID the contents of the array buffer.
*
* Resolves with the number of bytes written.
*
* const encoder = new TextEncoder();
* const data = encoder.encode("Hello world\n");
* const file = Deno.openSync("/foo/bar.txt");
* Deno.writeSync(file.rid, data);
*/
export function writeSync(rid: number, p: Uint8Array): number {
return resWrite(dispatch.sendSync(...reqWrite(rid, p)));
}
/** Write to the file ID the contents of the array buffer.
*
* Resolves with the number of bytes written.
*
* (async () => {
* const encoder = new TextEncoder();
* const data = encoder.encode("Hello world\n");
* const file = await Deno.open("/foo/bar.txt");
* await Deno.write(file.rid, data);
* })();
*
*/
export async function seek(
export async function write(rid: number, p: Uint8Array): Promise<number> {
return resWrite(await dispatch.sendAsync(...reqWrite(rid, p)));
}
function reqSeek(
rid: number,
offset: number,
whence: SeekMode
): Promise<void> {
): [flatbuffers.Builder, msg.Any, flatbuffers.Offset] {
const builder = flatbuffers.createBuilder();
msg.Seek.startSeek(builder);
msg.Seek.addRid(builder, rid);
msg.Seek.addOffset(builder, offset);
msg.Seek.addWhence(builder, whence);
const inner = msg.Seek.endSeek(builder);
await dispatch.sendAsync(builder, msg.Any.Seek, inner);
return [builder, msg.Any.Seek, inner];
}
/** Seek a file ID synchronously to the given offset under mode given by `whence`.
*
* const file = Deno.openSync("/foo/bar.txt");
* Deno.seekSync(file.rid, 0, 0);
*/
export function seekSync(rid: number, offset: number, whence: SeekMode): void {
dispatch.sendSync(...reqSeek(rid, offset, whence));
}
/** Seek a file ID to the given offset under mode given by `whence`.
*
* (async () => {
* const file = await Deno.open("/foo/bar.txt");
* await Deno.seek(file.rid, 0, 0);
* })();
*/
export async function seek(
rid: number,
offset: number,
whence: SeekMode
): Promise<void> {
await dispatch.sendAsync(...reqSeek(rid, offset, whence));
}
/** Close the file ID. */
@ -93,21 +205,41 @@ export function close(rid: number): void {
}
/** The Deno abstraction for reading and writing files. */
export class File implements Reader, Writer, Seeker, Closer {
export class File
implements
Reader,
SyncReader,
Writer,
SyncWriter,
Seeker,
SyncSeeker,
Closer {
constructor(readonly rid: number) {}
write(p: Uint8Array): Promise<number> {
return write(this.rid, p);
}
writeSync(p: Uint8Array): number {
return writeSync(this.rid, p);
}
read(p: Uint8Array): Promise<ReadResult> {
return read(this.rid, p);
}
readSync(p: Uint8Array): ReadResult {
return readSync(this.rid, p);
}
seek(offset: number, whence: SeekMode): Promise<void> {
return seek(this.rid, offset, whence);
}
seekSync(offset: number, whence: SeekMode): void {
return seekSync(this.rid, offset, whence);
}
close(): void {
close(this.rid);
}

View file

@ -163,6 +163,19 @@ testPerm({ read: true }, async function seekStart() {
assertEquals(decoded, "world!");
});
testPerm({ read: true }, function seekSyncStart() {
const filename = "tests/hello.txt";
const file = Deno.openSync(filename);
// Deliberately move 1 step forward
file.readSync(new Uint8Array(1)); // "H"
// Skipping "Hello "
file.seekSync(6, Deno.SeekMode.SEEK_START);
const buf = new Uint8Array(6);
file.readSync(buf);
const decoded = new TextDecoder().decode(buf);
assertEquals(decoded, "world!");
});
testPerm({ read: true }, async function seekCurrent() {
const filename = "tests/hello.txt";
const file = await Deno.open(filename);
@ -176,6 +189,19 @@ testPerm({ read: true }, async function seekCurrent() {
assertEquals(decoded, "world!");
});
testPerm({ read: true }, function seekSyncCurrent() {
const filename = "tests/hello.txt";
const file = Deno.openSync(filename);
// Deliberately move 1 step forward
file.readSync(new Uint8Array(1)); // "H"
// Skipping "ello "
file.seekSync(5, Deno.SeekMode.SEEK_CURRENT);
const buf = new Uint8Array(6);
file.readSync(buf);
const decoded = new TextDecoder().decode(buf);
assertEquals(decoded, "world!");
});
testPerm({ read: true }, async function seekEnd() {
const filename = "tests/hello.txt";
const file = await Deno.open(filename);
@ -186,6 +212,16 @@ testPerm({ read: true }, async function seekEnd() {
assertEquals(decoded, "world!");
});
testPerm({ read: true }, function seekSyncEnd() {
const filename = "tests/hello.txt";
const file = Deno.openSync(filename);
file.seekSync(-6, Deno.SeekMode.SEEK_END);
const buf = new Uint8Array(6);
file.readSync(buf);
const decoded = new TextDecoder().decode(buf);
assertEquals(decoded, "world!");
});
testPerm({ read: true }, async function seekMode() {
const filename = "tests/hello.txt";
const file = await Deno.open(filename);

View file

@ -49,6 +49,10 @@ export interface Reader {
read(p: Uint8Array): Promise<ReadResult>;
}
export interface SyncReader {
readSync(p: Uint8Array): ReadResult;
}
// Writer is the interface that wraps the basic write() method.
// https://golang.org/pkg/io/#Writer
export interface Writer {
@ -63,6 +67,9 @@ export interface Writer {
write(p: Uint8Array): Promise<number>;
}
export interface SyncWriter {
writeSync(p: Uint8Array): number;
}
// https://golang.org/pkg/io/#Closer
export interface Closer {
// The behavior of Close after the first call is undefined. Specific
@ -85,6 +92,10 @@ export interface Seeker {
seek(offset: number, whence: SeekMode): Promise<void>;
}
export interface SyncSeeker {
seekSync(offset: number, whence: SeekMode): void;
}
// https://golang.org/pkg/io/#ReadCloser
export interface ReadCloser extends Reader, Closer {}

View file

@ -1,29 +1,6 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import * as msg from "gen/msg_generated";
import * as flatbuffers from "./flatbuffers";
import { assert } from "./util";
import * as dispatch from "./dispatch";
function req(
filename: string
): [flatbuffers.Builder, msg.Any, flatbuffers.Offset] {
const builder = flatbuffers.createBuilder();
const filename_ = builder.createString(filename);
msg.ReadFile.startReadFile(builder);
msg.ReadFile.addFilename(builder, filename_);
const inner = msg.ReadFile.endReadFile(builder);
return [builder, msg.Any.ReadFile, inner];
}
function res(baseRes: null | msg.Base): Uint8Array {
assert(baseRes != null);
assert(msg.Any.ReadFileRes === baseRes!.innerType());
const inner = new msg.ReadFileRes();
assert(baseRes!.inner(inner) != null);
const dataArray = inner.dataArray();
assert(dataArray != null);
return new Uint8Array(dataArray!);
}
import { open, openSync } from "./files";
import { readAll, readAllSync } from "./buffer";
/** Read the entire contents of a file synchronously.
*
@ -32,7 +9,10 @@ function res(baseRes: null | msg.Base): Uint8Array {
* console.log(decoder.decode(data));
*/
export function readFileSync(filename: string): Uint8Array {
return res(dispatch.sendSync(...req(filename)));
const file = openSync(filename);
const contents = readAllSync(file);
file.close();
return contents;
}
/** Read the entire contents of a file.
@ -42,5 +22,8 @@ export function readFileSync(filename: string): Uint8Array {
* console.log(decoder.decode(data));
*/
export async function readFile(filename: string): Promise<Uint8Array> {
return res(await dispatch.sendAsync(...req(filename)));
const file = await open(filename);
const contents = await readAll(file);
file.close();
return contents;
}

View file

@ -1,35 +1,7 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import * as msg from "gen/msg_generated";
import * as flatbuffers from "./flatbuffers";
import * as dispatch from "./dispatch";
function req(
filename: string,
data: Uint8Array,
options: WriteFileOptions
): [flatbuffers.Builder, msg.Any, flatbuffers.Offset, Uint8Array] {
const builder = flatbuffers.createBuilder();
const filename_ = builder.createString(filename);
msg.WriteFile.startWriteFile(builder);
msg.WriteFile.addFilename(builder, filename_);
// Perm is not updated by default
if (options.perm !== undefined && options.perm !== null) {
msg.WriteFile.addUpdatePerm(builder, true);
msg.WriteFile.addPerm(builder, options.perm!);
} else {
msg.WriteFile.addUpdatePerm(builder, false);
msg.WriteFile.addPerm(builder, 0o666);
}
// Create is turned on by default
if (options.create !== undefined) {
msg.WriteFile.addIsCreate(builder, !!options.create);
} else {
msg.WriteFile.addIsCreate(builder, true);
}
msg.WriteFile.addIsAppend(builder, !!options.append);
const inner = msg.WriteFile.endWriteFile(builder);
return [builder, msg.Any.WriteFile, inner, data];
}
import { stat, statSync } from "./stat";
import { open, openSync } from "./files";
import { chmod, chmodSync } from "./chmod";
/** Options for writing to a file.
* `perm` would change the file's permission if set.
@ -53,7 +25,23 @@ export function writeFileSync(
data: Uint8Array,
options: WriteFileOptions = {}
): void {
dispatch.sendSync(...req(filename, data, options));
if (options.create !== undefined) {
const create = !!options.create;
if (!create) {
// verify that file exists
statSync(filename);
}
}
const openMode = !!options.append ? "a" : "w";
const file = openSync(filename, openMode);
if (options.perm !== undefined && options.perm !== null) {
chmodSync(filename, options.perm);
}
file.writeSync(data);
file.close();
}
/** Write a new file, with given filename and data.
@ -67,5 +55,21 @@ export async function writeFile(
data: Uint8Array,
options: WriteFileOptions = {}
): Promise<void> {
await dispatch.sendAsync(...req(filename, data, options));
if (options.create !== undefined) {
const create = !!options.create;
if (!create) {
// verify that file exists
await stat(filename);
}
}
const openMode = !!options.append ? "a" : "w";
const file = await open(filename, openMode);
if (options.perm !== undefined && options.perm !== null) {
await chmod(filename, options.perm);
}
await file.write(data);
file.close();
}