Skip to content

Commit

Permalink
Rewrite readFile and writeFile (denoland#2000)
Browse files Browse the repository at this point in the history
Using open/read/write
  • Loading branch information
bartlomieju authored and ry committed Mar 28, 2019
1 parent d0b6152 commit 597ee38
Show file tree
Hide file tree
Showing 10 changed files with 330 additions and 183 deletions.
21 changes: 0 additions & 21 deletions cli/msg.fbs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ union Any {
Read,
ReadDir,
ReadDirRes,
ReadFile,
ReadFileRes,
ReadRes,
Readlink,
ReadlinkRes,
Expand Down Expand Up @@ -69,7 +67,6 @@ union Any {
WorkerGetMessageRes,
WorkerPostMessage,
Write,
WriteFile,
WriteRes,
}

Expand Down Expand Up @@ -296,14 +293,6 @@ table Remove {
recursive: bool;
}

table ReadFile {
filename: string;
}

table ReadFileRes {
data: [ubyte];
}

table ReadDir {
path: string;
}
Expand All @@ -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;
Expand Down
71 changes: 0 additions & 71 deletions cli/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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,
}
}
Expand Down Expand Up @@ -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<'_>,
Expand Down Expand Up @@ -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<'_>,
Expand Down
49 changes: 44 additions & 5 deletions js/buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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]

Expand Down Expand Up @@ -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();
Expand All @@ -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.
Expand Down Expand Up @@ -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`.
Expand All @@ -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();
}
29 changes: 28 additions & 1 deletion js/buffer_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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]) {
Expand Down Expand Up @@ -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]);
}
});
9 changes: 8 additions & 1 deletion js/deno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -21,17 +25,20 @@ export {
ReadResult,
SeekMode,
Reader,
SyncReader,
Writer,
SyncWriter,
Closer,
Seeker,
SyncSeeker,
ReadCloser,
WriteCloser,
ReadSeeker,
WriteSeeker,
ReadWriteCloser,
ReadWriteSeeker
} from "./io";
export { Buffer, readAll } from "./buffer";
export { Buffer, readAll, readAllSync } from "./buffer";
export { mkdirSync, mkdir } from "./mkdir";
export {
makeTempDirSync,
Expand Down

0 comments on commit 597ee38

Please sign in to comment.