From b2786c2232ac75e23eb75f981c65eaecc39f93b4 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 30 May 2019 18:19:28 -0700 Subject: [PATCH] io: make port BufReader.readByte() return `number | EOF` --- io/bufio.ts | 6 +++--- io/bufio_test.ts | 2 +- io/ioutil.ts | 43 +++++++++++++++++++++++++------------------ io/ioutil_test.ts | 4 ++-- ws/mod.ts | 10 ++++++++-- 5 files changed, 39 insertions(+), 26 deletions(-) diff --git a/io/bufio.ts b/io/bufio.ts index 99b5638161f09..dc6d6b3cc46f4 100644 --- a/io/bufio.ts +++ b/io/bufio.ts @@ -192,10 +192,10 @@ export class BufReader implements Reader { return p; } - /** Returns the next byte [0, 255] or -1 if EOF. */ - async readByte(): Promise { + /** Returns the next byte [0, 255] or `EOF`. */ + async readByte(): Promise { 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]; diff --git a/io/bufio_test.ts b/io/bufio_test.ts index 84b6f9142c9ee..6433c7a5a8f0b 100644 --- a/io/bufio_test.ts +++ b/io/bufio_test.ts @@ -35,7 +35,7 @@ async function readBytes(buf: BufReader): Promise { let nb = 0; while (true) { let c = await buf.readByte(); - if (c < 0) { + if (c === EOF) { break; // EOF } b[nb] = c; diff --git a/io/ioutil.ts b/io/ioutil.ts index 484aba2812f6d..cf38f45297bdf 100644 --- a/io/ioutil.ts +++ b/io/ioutil.ts @@ -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"; @@ -30,36 +30,43 @@ export async function copyN( } /** Read big endian 16bit short from BufReader */ -export async function readShort(buf: BufReader): Promise { - const [high, low] = [await buf.readByte(), await buf.readByte()]; +export async function readShort(buf: BufReader): Promise { + 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 { - const [high, low] = [await readShort(buf), await readShort(buf)]; +export async function readInt(buf: BufReader): Promise { + 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 { - 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 { + 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("Value too large."); + 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; } diff --git a/io/ioutil_test.ts b/io/ioutil_test.ts index c1c1ded72af51..4e34f56984eb5 100644 --- a/io/ioutil_test.ts +++ b/io/ioutil_test.ts @@ -40,10 +40,10 @@ test(async function testReadInt(): Promise { test(async function testReadLong(): Promise { 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 { diff --git a/ws/mod.ts b/ws/mod.ts index 86ab2f045dd28..d74e4a92b671b 100644 --- a/ws/mod.ts +++ b/ws/mod.ts @@ -142,6 +142,7 @@ export async function writeFrame( /** Read websocket frame from given BufReader */ export async function readFrame(buf: BufReader): Promise { let b = await buf.readByte(); + if (b === EOF) throw new UnexpectedEOFError(); let isLastFrame = false; switch (b >>> 4) { case 0b1000: @@ -156,12 +157,17 @@ export async function readFrame(buf: BufReader): Promise { 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;