mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
parent
d0b6152f11
commit
597ee38ef2
10 changed files with 337 additions and 190 deletions
21
cli/msg.fbs
21
cli/msg.fbs
|
@ -39,8 +39,6 @@ union Any {
|
||||||
Read,
|
Read,
|
||||||
ReadDir,
|
ReadDir,
|
||||||
ReadDirRes,
|
ReadDirRes,
|
||||||
ReadFile,
|
|
||||||
ReadFileRes,
|
|
||||||
ReadRes,
|
ReadRes,
|
||||||
Readlink,
|
Readlink,
|
||||||
ReadlinkRes,
|
ReadlinkRes,
|
||||||
|
@ -69,7 +67,6 @@ union Any {
|
||||||
WorkerGetMessageRes,
|
WorkerGetMessageRes,
|
||||||
WorkerPostMessage,
|
WorkerPostMessage,
|
||||||
Write,
|
Write,
|
||||||
WriteFile,
|
|
||||||
WriteRes,
|
WriteRes,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,14 +293,6 @@ table Remove {
|
||||||
recursive: bool;
|
recursive: bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
table ReadFile {
|
|
||||||
filename: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
table ReadFileRes {
|
|
||||||
data: [ubyte];
|
|
||||||
}
|
|
||||||
|
|
||||||
table ReadDir {
|
table ReadDir {
|
||||||
path: string;
|
path: string;
|
||||||
}
|
}
|
||||||
|
@ -312,16 +301,6 @@ table ReadDirRes {
|
||||||
entries: [StatRes];
|
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 {
|
table CopyFile {
|
||||||
from: string;
|
from: string;
|
||||||
to: string;
|
to: string;
|
||||||
|
|
71
cli/ops.rs
71
cli/ops.rs
|
@ -174,7 +174,6 @@ pub fn op_selector_std(inner_type: msg::Any) -> Option<OpCreator> {
|
||||||
msg::Any::Permissions => Some(op_permissions),
|
msg::Any::Permissions => Some(op_permissions),
|
||||||
msg::Any::Read => Some(op_read),
|
msg::Any::Read => Some(op_read),
|
||||||
msg::Any::ReadDir => Some(op_read_dir),
|
msg::Any::ReadDir => Some(op_read_dir),
|
||||||
msg::Any::ReadFile => Some(op_read_file),
|
|
||||||
msg::Any::Readlink => Some(op_read_link),
|
msg::Any::Readlink => Some(op_read_link),
|
||||||
msg::Any::Remove => Some(op_remove),
|
msg::Any::Remove => Some(op_remove),
|
||||||
msg::Any::Rename => Some(op_rename),
|
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::WorkerGetMessage => Some(op_worker_get_message),
|
||||||
msg::Any::WorkerPostMessage => Some(op_worker_post_message),
|
msg::Any::WorkerPostMessage => Some(op_worker_post_message),
|
||||||
msg::Any::Write => Some(op_write),
|
msg::Any::Write => Some(op_write),
|
||||||
msg::Any::WriteFile => Some(op_write_file),
|
|
||||||
_ => None,
|
_ => 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(
|
fn op_copy_file(
|
||||||
sc: &IsolateStateContainer,
|
sc: &IsolateStateContainer,
|
||||||
base: &msg::Base<'_>,
|
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(
|
fn op_rename(
|
||||||
sc: &IsolateStateContainer,
|
sc: &IsolateStateContainer,
|
||||||
base: &msg::Base<'_>,
|
base: &msg::Base<'_>,
|
||||||
|
|
49
js/buffer.ts
49
js/buffer.ts
|
@ -5,7 +5,7 @@
|
||||||
// https://github.com/golang/go/blob/master/LICENSE
|
// https://github.com/golang/go/blob/master/LICENSE
|
||||||
|
|
||||||
//import * as io from "./io";
|
//import * as io from "./io";
|
||||||
import { Reader, Writer, ReadResult } from "./io";
|
import { Reader, Writer, ReadResult, SyncReader, SyncWriter } from "./io";
|
||||||
import { assert } from "./util";
|
import { assert } from "./util";
|
||||||
import { TextDecoder } from "./text_encoding";
|
import { TextDecoder } from "./text_encoding";
|
||||||
import { DenoError, ErrorKind } from "./errors";
|
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()
|
/** A Buffer is a variable-sized buffer of bytes with read() and write()
|
||||||
* methods. Based on https://golang.org/pkg/bytes/#Buffer
|
* 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 buf: Uint8Array; // contents are the bytes buf[off : len(buf)]
|
||||||
private off = 0; // read at buf[off], write at buf[buf.byteLength]
|
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);
|
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
|
* 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.
|
* 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()) {
|
if (this.empty()) {
|
||||||
// Buffer is empty, reset to recover space.
|
// Buffer is empty, reset to recover space.
|
||||||
this.reset();
|
this.reset();
|
||||||
|
@ -145,11 +145,21 @@ export class Buffer implements Reader, Writer {
|
||||||
return { nread, eof: false };
|
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);
|
const m = this._grow(p.byteLength);
|
||||||
return copyBytes(this.buf, p, m);
|
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.
|
/** _grow() grows the buffer to guarantee space for n more bytes.
|
||||||
* It returns the index where bytes should be written.
|
* It returns the index where bytes should be written.
|
||||||
* If the buffer can't grow it will throw with ErrTooLarge.
|
* 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`.
|
/** 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);
|
await buf.readFrom(r);
|
||||||
return buf.bytes();
|
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();
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
// https://github.com/golang/go/blob/master/LICENSE
|
// https://github.com/golang/go/blob/master/LICENSE
|
||||||
import { assertEquals, test } from "./test_util.ts";
|
import { assertEquals, test } from "./test_util.ts";
|
||||||
|
|
||||||
const { Buffer, readAll } = Deno;
|
const { Buffer, readAll, readAllSync } = Deno;
|
||||||
type Buffer = Deno.Buffer;
|
type Buffer = Deno.Buffer;
|
||||||
|
|
||||||
// N controls how many iterations of certain checks are performed.
|
// 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() {
|
test(async function bufferTestGrow() {
|
||||||
const tmp = new Uint8Array(72);
|
const tmp = new Uint8Array(72);
|
||||||
for (let startLen of [0, 100, 1000, 10000, 100000]) {
|
for (let startLen of [0, 100, 1000, 10000, 100000]) {
|
||||||
|
@ -226,3 +243,13 @@ test(async function testReadAll() {
|
||||||
assertEquals(testBytes[i], actualBytes[i]);
|
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]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -6,12 +6,16 @@ export { chdir, cwd } from "./dir";
|
||||||
export {
|
export {
|
||||||
File,
|
File,
|
||||||
open,
|
open,
|
||||||
|
openSync,
|
||||||
stdin,
|
stdin,
|
||||||
stdout,
|
stdout,
|
||||||
stderr,
|
stderr,
|
||||||
read,
|
read,
|
||||||
|
readSync,
|
||||||
write,
|
write,
|
||||||
|
writeSync,
|
||||||
seek,
|
seek,
|
||||||
|
seekSync,
|
||||||
close,
|
close,
|
||||||
OpenMode
|
OpenMode
|
||||||
} from "./files";
|
} from "./files";
|
||||||
|
@ -21,9 +25,12 @@ export {
|
||||||
ReadResult,
|
ReadResult,
|
||||||
SeekMode,
|
SeekMode,
|
||||||
Reader,
|
Reader,
|
||||||
|
SyncReader,
|
||||||
Writer,
|
Writer,
|
||||||
|
SyncWriter,
|
||||||
Closer,
|
Closer,
|
||||||
Seeker,
|
Seeker,
|
||||||
|
SyncSeeker,
|
||||||
ReadCloser,
|
ReadCloser,
|
||||||
WriteCloser,
|
WriteCloser,
|
||||||
ReadSeeker,
|
ReadSeeker,
|
||||||
|
@ -31,7 +38,7 @@ export {
|
||||||
ReadWriteCloser,
|
ReadWriteCloser,
|
||||||
ReadWriteSeeker
|
ReadWriteSeeker
|
||||||
} from "./io";
|
} from "./io";
|
||||||
export { Buffer, readAll } from "./buffer";
|
export { Buffer, readAll, readAllSync } from "./buffer";
|
||||||
export { mkdirSync, mkdir } from "./mkdir";
|
export { mkdirSync, mkdir } from "./mkdir";
|
||||||
export {
|
export {
|
||||||
makeTempDirSync,
|
makeTempDirSync,
|
||||||
|
|
194
js/files.ts
194
js/files.ts
|
@ -1,10 +1,53 @@
|
||||||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
// 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 dispatch from "./dispatch";
|
||||||
import * as msg from "gen/msg_generated";
|
import * as msg from "gen/msg_generated";
|
||||||
import { assert } from "./util";
|
import { assert } from "./util";
|
||||||
import * as flatbuffers from "./flatbuffers";
|
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.
|
/** Open a file and return an instance of the `File` object.
|
||||||
*
|
*
|
||||||
* (async () => {
|
* (async () => {
|
||||||
|
@ -15,33 +58,21 @@ export async function open(
|
||||||
filename: string,
|
filename: string,
|
||||||
mode: OpenMode = "r"
|
mode: OpenMode = "r"
|
||||||
): Promise<File> {
|
): Promise<File> {
|
||||||
const builder = flatbuffers.createBuilder();
|
return resOpen(await dispatch.sendAsync(...reqOpen(filename, mode)));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Read from a file ID into an array buffer.
|
function reqRead(
|
||||||
*
|
rid: number,
|
||||||
* Resolves with the `ReadResult` for the operation.
|
p: Uint8Array
|
||||||
*/
|
): [flatbuffers.Builder, msg.Any, flatbuffers.Offset, Uint8Array] {
|
||||||
export async function read(rid: number, p: Uint8Array): Promise<ReadResult> {
|
|
||||||
const builder = flatbuffers.createBuilder();
|
const builder = flatbuffers.createBuilder();
|
||||||
msg.Read.startRead(builder);
|
msg.Read.startRead(builder);
|
||||||
msg.Read.addRid(builder, rid);
|
msg.Read.addRid(builder, rid);
|
||||||
const inner = msg.Read.endRead(builder);
|
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(baseRes != null);
|
||||||
assert(msg.Any.ReadRes === baseRes!.innerType());
|
assert(msg.Any.ReadRes === baseRes!.innerType());
|
||||||
const res = new msg.ReadRes();
|
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() };
|
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();
|
const builder = flatbuffers.createBuilder();
|
||||||
msg.Write.startWrite(builder);
|
msg.Write.startWrite(builder);
|
||||||
msg.Write.addRid(builder, rid);
|
msg.Write.addRid(builder, rid);
|
||||||
const inner = msg.Write.endWrite(builder);
|
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(baseRes != null);
|
||||||
assert(msg.Any.WriteRes === baseRes!.innerType());
|
assert(msg.Any.WriteRes === baseRes!.innerType());
|
||||||
const res = new msg.WriteRes();
|
const res = new msg.WriteRes();
|
||||||
|
@ -66,21 +128,71 @@ export async function write(rid: number, p: Uint8Array): Promise<number> {
|
||||||
return res.nbyte();
|
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,
|
rid: number,
|
||||||
offset: number,
|
offset: number,
|
||||||
whence: SeekMode
|
whence: SeekMode
|
||||||
): Promise<void> {
|
): [flatbuffers.Builder, msg.Any, flatbuffers.Offset] {
|
||||||
const builder = flatbuffers.createBuilder();
|
const builder = flatbuffers.createBuilder();
|
||||||
msg.Seek.startSeek(builder);
|
msg.Seek.startSeek(builder);
|
||||||
msg.Seek.addRid(builder, rid);
|
msg.Seek.addRid(builder, rid);
|
||||||
msg.Seek.addOffset(builder, offset);
|
msg.Seek.addOffset(builder, offset);
|
||||||
msg.Seek.addWhence(builder, whence);
|
msg.Seek.addWhence(builder, whence);
|
||||||
const inner = msg.Seek.endSeek(builder);
|
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. */
|
/** Close the file ID. */
|
||||||
|
@ -93,21 +205,41 @@ export function close(rid: number): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The Deno abstraction for reading and writing files. */
|
/** 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) {}
|
constructor(readonly rid: number) {}
|
||||||
|
|
||||||
write(p: Uint8Array): Promise<number> {
|
write(p: Uint8Array): Promise<number> {
|
||||||
return write(this.rid, p);
|
return write(this.rid, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writeSync(p: Uint8Array): number {
|
||||||
|
return writeSync(this.rid, p);
|
||||||
|
}
|
||||||
|
|
||||||
read(p: Uint8Array): Promise<ReadResult> {
|
read(p: Uint8Array): Promise<ReadResult> {
|
||||||
return read(this.rid, p);
|
return read(this.rid, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readSync(p: Uint8Array): ReadResult {
|
||||||
|
return readSync(this.rid, p);
|
||||||
|
}
|
||||||
|
|
||||||
seek(offset: number, whence: SeekMode): Promise<void> {
|
seek(offset: number, whence: SeekMode): Promise<void> {
|
||||||
return seek(this.rid, offset, whence);
|
return seek(this.rid, offset, whence);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
seekSync(offset: number, whence: SeekMode): void {
|
||||||
|
return seekSync(this.rid, offset, whence);
|
||||||
|
}
|
||||||
|
|
||||||
close(): void {
|
close(): void {
|
||||||
close(this.rid);
|
close(this.rid);
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,6 +163,19 @@ testPerm({ read: true }, async function seekStart() {
|
||||||
assertEquals(decoded, "world!");
|
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() {
|
testPerm({ read: true }, async function seekCurrent() {
|
||||||
const filename = "tests/hello.txt";
|
const filename = "tests/hello.txt";
|
||||||
const file = await Deno.open(filename);
|
const file = await Deno.open(filename);
|
||||||
|
@ -176,6 +189,19 @@ testPerm({ read: true }, async function seekCurrent() {
|
||||||
assertEquals(decoded, "world!");
|
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() {
|
testPerm({ read: true }, async function seekEnd() {
|
||||||
const filename = "tests/hello.txt";
|
const filename = "tests/hello.txt";
|
||||||
const file = await Deno.open(filename);
|
const file = await Deno.open(filename);
|
||||||
|
@ -186,6 +212,16 @@ testPerm({ read: true }, async function seekEnd() {
|
||||||
assertEquals(decoded, "world!");
|
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() {
|
testPerm({ read: true }, async function seekMode() {
|
||||||
const filename = "tests/hello.txt";
|
const filename = "tests/hello.txt";
|
||||||
const file = await Deno.open(filename);
|
const file = await Deno.open(filename);
|
||||||
|
|
11
js/io.ts
11
js/io.ts
|
@ -49,6 +49,10 @@ export interface Reader {
|
||||||
read(p: Uint8Array): Promise<ReadResult>;
|
read(p: Uint8Array): Promise<ReadResult>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SyncReader {
|
||||||
|
readSync(p: Uint8Array): ReadResult;
|
||||||
|
}
|
||||||
|
|
||||||
// Writer is the interface that wraps the basic write() method.
|
// Writer is the interface that wraps the basic write() method.
|
||||||
// https://golang.org/pkg/io/#Writer
|
// https://golang.org/pkg/io/#Writer
|
||||||
export interface Writer {
|
export interface Writer {
|
||||||
|
@ -63,6 +67,9 @@ export interface Writer {
|
||||||
write(p: Uint8Array): Promise<number>;
|
write(p: Uint8Array): Promise<number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SyncWriter {
|
||||||
|
writeSync(p: Uint8Array): number;
|
||||||
|
}
|
||||||
// https://golang.org/pkg/io/#Closer
|
// https://golang.org/pkg/io/#Closer
|
||||||
export interface Closer {
|
export interface Closer {
|
||||||
// The behavior of Close after the first call is undefined. Specific
|
// 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>;
|
seek(offset: number, whence: SeekMode): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SyncSeeker {
|
||||||
|
seekSync(offset: number, whence: SeekMode): void;
|
||||||
|
}
|
||||||
|
|
||||||
// https://golang.org/pkg/io/#ReadCloser
|
// https://golang.org/pkg/io/#ReadCloser
|
||||||
export interface ReadCloser extends Reader, Closer {}
|
export interface ReadCloser extends Reader, Closer {}
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,6 @@
|
||||||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||||
import * as msg from "gen/msg_generated";
|
import { open, openSync } from "./files";
|
||||||
import * as flatbuffers from "./flatbuffers";
|
import { readAll, readAllSync } from "./buffer";
|
||||||
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!);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Read the entire contents of a file synchronously.
|
/** Read the entire contents of a file synchronously.
|
||||||
*
|
*
|
||||||
|
@ -32,7 +9,10 @@ function res(baseRes: null | msg.Base): Uint8Array {
|
||||||
* console.log(decoder.decode(data));
|
* console.log(decoder.decode(data));
|
||||||
*/
|
*/
|
||||||
export function readFileSync(filename: string): Uint8Array {
|
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.
|
/** Read the entire contents of a file.
|
||||||
|
@ -42,5 +22,8 @@ export function readFileSync(filename: string): Uint8Array {
|
||||||
* console.log(decoder.decode(data));
|
* console.log(decoder.decode(data));
|
||||||
*/
|
*/
|
||||||
export async function readFile(filename: string): Promise<Uint8Array> {
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +1,7 @@
|
||||||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||||
import * as msg from "gen/msg_generated";
|
import { stat, statSync } from "./stat";
|
||||||
import * as flatbuffers from "./flatbuffers";
|
import { open, openSync } from "./files";
|
||||||
import * as dispatch from "./dispatch";
|
import { chmod, chmodSync } from "./chmod";
|
||||||
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Options for writing to a file.
|
/** Options for writing to a file.
|
||||||
* `perm` would change the file's permission if set.
|
* `perm` would change the file's permission if set.
|
||||||
|
@ -53,7 +25,23 @@ export function writeFileSync(
|
||||||
data: Uint8Array,
|
data: Uint8Array,
|
||||||
options: WriteFileOptions = {}
|
options: WriteFileOptions = {}
|
||||||
): void {
|
): 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.
|
/** Write a new file, with given filename and data.
|
||||||
|
@ -67,5 +55,21 @@ export async function writeFile(
|
||||||
data: Uint8Array,
|
data: Uint8Array,
|
||||||
options: WriteFileOptions = {}
|
options: WriteFileOptions = {}
|
||||||
): Promise<void> {
|
): 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();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue