Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

io: make port BufReader.readByte() return number | EOF #472

Merged
merged 1 commit into from
May 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions io/bufio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,10 @@ export class BufReader implements Reader {
return p;
}

/** Returns the next byte [0, 255] or -1 if EOF. */
async readByte(): Promise<number> {
/** Returns the next byte [0, 255] or `EOF`. */
async readByte(): Promise<number | EOF> {
while (this.r === this.w) {
if (this.eof) return -1;
if (this.eof) return EOF;
await this._fill(); // buffer is empty.
}
const c = this.buf[this.r];
Expand Down
2 changes: 1 addition & 1 deletion io/bufio_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ async function readBytes(buf: BufReader): Promise<string> {
let nb = 0;
while (true) {
let c = await buf.readByte();
if (c < 0) {
if (c === EOF) {
break; // EOF
}
b[nb] = c;
Expand Down
47 changes: 29 additions & 18 deletions io/ioutil.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { BufReader } from "./bufio.ts";
import { BufReader, EOF, UnexpectedEOFError } from "./bufio.ts";
type Reader = Deno.Reader;
type Writer = Deno.Writer;
import { assert } from "../testing/asserts.ts";
Expand Down Expand Up @@ -30,36 +30,47 @@ export async function copyN(
}

/** Read big endian 16bit short from BufReader */
export async function readShort(buf: BufReader): Promise<number> {
const [high, low] = [await buf.readByte(), await buf.readByte()];
export async function readShort(buf: BufReader): Promise<number | EOF> {
const high = await buf.readByte();
if (high === EOF) return EOF;
const low = await buf.readByte();
if (low === EOF) throw new UnexpectedEOFError();
return (high << 8) | low;
}

/** Read big endian 32bit integer from BufReader */
export async function readInt(buf: BufReader): Promise<number> {
const [high, low] = [await readShort(buf), await readShort(buf)];
export async function readInt(buf: BufReader): Promise<number | EOF> {
const high = await readShort(buf);
if (high === EOF) return EOF;
const low = await readShort(buf);
if (low === EOF) throw new UnexpectedEOFError();
return (high << 16) | low;
}

const BIT32 = 0xffffffff;
const MAX_SAFE_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);

/** Read big endian 64bit long from BufReader */
export async function readLong(buf: BufReader): Promise<number> {
const [high, low] = [await readInt(buf), await readInt(buf)];
// ECMAScript doesn't support 64bit bit ops.
return high ? high * (BIT32 + 1) + low : low;
export async function readLong(buf: BufReader): Promise<number | EOF> {
const high = await readInt(buf);
if (high === EOF) return EOF;
const low = await readInt(buf);
if (low === EOF) throw new UnexpectedEOFError();
const big = (BigInt(high) << 32n) | BigInt(low);
// We probably should provide a similar API that returns BigInt values.
if (big > MAX_SAFE_INTEGER) {
throw new RangeError(
"Long value too big to be represented as a Javascript number."
);
}
return Number(big);
}

/** Slice number into 64bit big endian byte array */
export function sliceLongToBytes(d: number, dest = new Array(8)): number[] {
let mask = 0xff;
let low = (d << 32) >>> 32;
let high = (d - low) / (BIT32 + 1);
let shift = 24;
for (let i = 0; i < 4; i++) {
dest[i] = (high >>> shift) & mask;
dest[i + 4] = (low >>> shift) & mask;
shift -= 8;
let big = BigInt(d);
for (let i = 0; i < 8; i++) {
dest[7 - i] = Number(big & 0xffn);
big >>= 8n;
}
return dest;
}
4 changes: 2 additions & 2 deletions io/ioutil_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ test(async function testReadInt(): Promise<void> {

test(async function testReadLong(): Promise<void> {
const r = new BinaryReader(
new Uint8Array([0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78])
new Uint8Array([0x00, 0x00, 0x00, 0x78, 0x12, 0x34, 0x56, 0x78])
);
const long = await readLong(new BufReader(r));
assertEquals(long, 0x1234567812345678);
assertEquals(long, 0x7812345678);
});

test(async function testReadLong2(): Promise<void> {
Expand Down
10 changes: 8 additions & 2 deletions ws/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ export async function writeFrame(
/** Read websocket frame from given BufReader */
export async function readFrame(buf: BufReader): Promise<WebSocketFrame> {
let b = await buf.readByte();
if (b === EOF) throw new UnexpectedEOFError();
let isLastFrame = false;
switch (b >>> 4) {
case 0b1000:
Expand All @@ -156,12 +157,17 @@ export async function readFrame(buf: BufReader): Promise<WebSocketFrame> {
const opcode = b & 0x0f;
// has_mask & payload
b = await buf.readByte();
if (b === EOF) throw new UnexpectedEOFError();
const hasMask = b >>> 7;
let payloadLength = b & 0b01111111;
if (payloadLength === 126) {
payloadLength = await readShort(buf);
const l = await readShort(buf);
if (l === EOF) throw new UnexpectedEOFError();
payloadLength = l;
} else if (payloadLength === 127) {
payloadLength = await readLong(buf);
const l = await readLong(buf);
if (l === EOF) throw new UnexpectedEOFError();
payloadLength = Number(l);
}
// mask
let mask;
Expand Down