From c1fbdee3f81d0e5bf5cc0ec65b298001420dfb87 Mon Sep 17 00:00:00 2001 From: axetroy Date: Thu, 23 May 2019 01:54:53 +0800 Subject: [PATCH 1/6] add encoding/hex module --- encoding/hex.ts | 134 +++++++++++++++++++++++++++++++++++++++++++ encoding/hex_test.ts | 121 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 255 insertions(+) create mode 100644 encoding/hex.ts create mode 100644 encoding/hex_test.ts diff --git a/encoding/hex.ts b/encoding/hex.ts new file mode 100644 index 000000000000..b4c29666f63a --- /dev/null +++ b/encoding/hex.ts @@ -0,0 +1,134 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. + +const hextable = new TextEncoder().encode("0123456789abcdef"); +const bufferSize = 1024; + +function invalidByteError(byte: number): Error { + return new Error( + "encoding/hex: invalid byte: " + + new TextDecoder().decode(new Uint8Array([byte])) + ); +} + +// fromHexChar converts a hex character into its value and a success flag. +function fromHexChar(byte: number): [number, boolean] { + switch (true) { + case 48 <= byte && byte <= 57: // '0' <= byte && byte <= '9' + return [byte - 48, true]; + case 97 <= byte && byte <= 102: // 'a' <= byte && byte <= 'f' + return [byte - 97 + 10, true]; + case 65 <= byte && byte <= 70: // 'A' <= byte && byte <= 'F' + return [byte - 65 + 10, true]; + } + return [0, false]; +} + +export function encode(dest: Uint8Array, src: Uint8Array): number { + if (dest.length !== encodedLen(src.length)) { + // throw new Error("Out of index."); + } + for (let i = 0; i < src.length; i++) { + const v = src[i]; + dest[i * 2] = hextable[v >> 4]; + dest[i * 2 + 1] = hextable[v & 0x0f]; + } + return encodedLen(src.length); +} + +export function encodedLen(n: number): number { + return n * 2; +} + +export function encodeToString(buf: Uint8Array): string { + return Array.from(buf) + .map(b => b.toString(16).padStart(2, "0")) + .join(""); +} + +// Decode decodes src into DecodedLen(len(src)) bytes, +// returning the actual number of bytes written to dst. +// +// Decode expects that src contains only hexadecimal +// characters and that src has even length. +// If the input is malformed, Decode returns the number +// of bytes decoded before the error. +export function decode(dest: Uint8Array, src: Uint8Array): [number, Error] { + var i = 0; + for (; i < src.length / 2; i++) { + const [a, aOK] = fromHexChar(src[i * 2]); + if (!aOK) { + return [i, invalidByteError(src[i * 2])]; + } + + const [b, bOK] = fromHexChar(src[i * 2 + 1]); + if (!bOK) { + return [i, invalidByteError(src[i * 2 + 1])]; + } + + dest[i] = (a << 4) | b; + } + + if (src.length % 2 == 1) { + // Check for invalid char before reporting bad length, + // since the invalid char (if present) is an earlier problem. + const [, ok] = fromHexChar(src[i * 2]); + if (!ok) { + return [i, invalidByteError(src[i * 2])]; + } + } + + return [i, undefined]; +} + +// DecodedLen returns the length of a decoding of x source bytes. +// Specifically, it returns x / 2. +export function decodedLen(x: number): number { + return x / 2; +} + +// DecodeString returns the bytes represented by the hexadecimal string s. +// +// DecodeString expects that src contains only hexadecimal +// characters and that src has even length. +// If the input is malformed, DecodeString returns +// the bytes decoded before the error. +export function decodeString(s: string): Uint8Array { + const src = new TextEncoder().encode(s); + // We can use the source slice itself as the destination + // because the decode loop increments by one and then the 'seen' byte is not used anymore. + const [n, err] = decode(src, src); + + if (err) { + throw err; + } + + return src.slice(0, n); +} + +// TODO: encoder +export class Encoder implements Deno.Writer { + private err: Error; + private out = new Uint8Array(bufferSize); + constructor() {} + async write(p: Uint8Array): Promise { + let n = 0; + + for (; p.length > 0 && !this.err; ) { + let chunkSize = bufferSize / 2; + if (p.length < chunkSize) { + chunkSize = p.length; + } + + const encoded = encode(this.out.slice(), p.slice(chunkSize)); + const written = this.out.slice(0, encoded).length; + + n += written / 2; + p = p.slice(chunkSize); + } + + return n; + } + toString(): string { + return ""; + } +} diff --git a/encoding/hex_test.ts b/encoding/hex_test.ts new file mode 100644 index 000000000000..33a8991bc2d4 --- /dev/null +++ b/encoding/hex_test.ts @@ -0,0 +1,121 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { test, runIfMain } from "../testing/mod.ts"; +import { assertEquals, assertThrows } from "../testing/asserts.ts"; + +import { + encodedLen, + encode, + encodeToString, + decodedLen, + decode, + decodeString +} from "./hex.ts"; + +const testCases = [ + // encoded(hex) / decoded(Uint8Array) + ["", []], + ["0001020304050607", [0, 1, 2, 3, 4, 5, 6, 7]], + ["08090a0b0c0d0e0f", [8, 9, 10, 11, 12, 13, 14, 15]], + ["f0f1f2f3f4f5f6f7", [0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7]], + ["f8f9fafbfcfdfeff", [0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff]], + ["67", Array.from(new TextEncoder().encode("g"))], + ["e3a1", [0xe3, 0xa1]] +]; + +test({ + name: "[encoding.hex] encodedLen", + fn(): void { + assertEquals(encodedLen(0), 0); + assertEquals(encodedLen(1), 2); + assertEquals(encodedLen(2), 4); + assertEquals(encodedLen(3), 6); + assertEquals(encodedLen(4), 8); + } +}); + +test({ + name: "[encoding.hex] encode", + fn(): void { + { + const srcStr = "abc"; + const src = new TextEncoder().encode(srcStr); + const dest = new Uint8Array(encodedLen(src.length)); + const int = encode(dest, src); + assertEquals(src, new Uint8Array([97, 98, 99])); + assertEquals(int, 6); + } + + { + const srcStr = "abc"; + const src = new TextEncoder().encode(srcStr); + const dest = new Uint8Array(2); // out of index + assertThrows( + () => { + encode(dest, src); + }, + Error, + "Out of index." + ); + } + + for (const [enc, dec] of testCases) { + const dest = new Uint8Array(encodedLen(dec.length)); + const src = new Uint8Array(dec as number[]); + const n = encode(dest, src); + assertEquals(dest.length, n); + assertEquals(new TextDecoder().decode(dest), enc); + } + } +}); + +test({ + name: "[encoding.hex] encodeToString", + fn(): void { + for (const [enc, dec] of testCases) { + assertEquals(encodeToString(new Uint8Array(dec as number[])), enc); + } + } +}); + +test({ + name: "[encoding.hex] decodedLen", + fn(): void { + assertEquals(decodedLen(0), 0); + assertEquals(decodedLen(2), 1); + assertEquals(decodedLen(4), 2); + assertEquals(decodedLen(6), 3); + assertEquals(decodedLen(8), 4); + } +}); + +test({ + name: "[encoding.hex] decode", + fn(): void { + const extraTestcase = [ + ["F8F9FAFBFCFDFEFF", [0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff]] + ]; + + const cases = testCases.concat(extraTestcase); + + for (const [enc, dec] of cases) { + const dest = new Uint8Array(decodedLen(enc.length)); + const src = new TextEncoder().encode(enc as string); + const [, err] = decode(dest, src); + assertEquals(err, undefined); + assertEquals(Array.from(dest), Array.from(dec as number[])); + } + } +}); + +test({ + name: "[encoding.hex] decodeString", + fn(): void { + for (const [enc, dec] of testCases) { + const dst = decodeString(enc as string); + + assertEquals(dec, Array.from(dst)); + } + } +}); + +runIfMain(import.meta); From 3ffa096a7c736fad8a8d08d4ece7afdc3332f0db Mon Sep 17 00:00:00 2001 From: axetroy Date: Thu, 23 May 2019 14:05:29 +0800 Subject: [PATCH 2/6] update --- encoding/hex.ts | 103 ++++++++++++++++++++++++++++++++++--------- encoding/hex_test.ts | 60 ++++++++++++++++++++++++- encoding/test.ts | 2 + test.ts | 1 + 4 files changed, 142 insertions(+), 24 deletions(-) create mode 100644 encoding/test.ts diff --git a/encoding/hex.ts b/encoding/hex.ts index b4c29666f63a..1cfe3781d528 100644 --- a/encoding/hex.ts +++ b/encoding/hex.ts @@ -1,15 +1,20 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { copyBytes } from "../io/util.ts"; const hextable = new TextEncoder().encode("0123456789abcdef"); const bufferSize = 1024; -function invalidByteError(byte: number): Error { +export function errInvalidByte(byte: number): Error { return new Error( "encoding/hex: invalid byte: " + new TextDecoder().decode(new Uint8Array([byte])) ); } +export function errLength(): Error { + return new Error("encoding/hex: odd length hex string"); +} + // fromHexChar converts a hex character into its value and a success flag. function fromHexChar(byte: number): [number, boolean] { switch (true) { @@ -23,9 +28,13 @@ function fromHexChar(byte: number): [number, boolean] { return [0, false]; } +export function encodedLen(n: number): number { + return n * 2; +} + export function encode(dest: Uint8Array, src: Uint8Array): number { if (dest.length !== encodedLen(src.length)) { - // throw new Error("Out of index."); + throw new Error("Out of index."); } for (let i = 0; i < src.length; i++) { const v = src[i]; @@ -35,14 +44,10 @@ export function encode(dest: Uint8Array, src: Uint8Array): number { return encodedLen(src.length); } -export function encodedLen(n: number): number { - return n * 2; -} - -export function encodeToString(buf: Uint8Array): string { - return Array.from(buf) - .map(b => b.toString(16).padStart(2, "0")) - .join(""); +export function encodeToString(src: Uint8Array): string { + const dest = new Uint8Array(encodedLen(src.length)); + encode(dest, src); + return new TextDecoder().decode(dest); } // Decode decodes src into DecodedLen(len(src)) bytes, @@ -54,15 +59,14 @@ export function encodeToString(buf: Uint8Array): string { // of bytes decoded before the error. export function decode(dest: Uint8Array, src: Uint8Array): [number, Error] { var i = 0; - for (; i < src.length / 2; i++) { + for (; i < Math.floor(src.length / 2); i++) { const [a, aOK] = fromHexChar(src[i * 2]); if (!aOK) { - return [i, invalidByteError(src[i * 2])]; + return [i, errInvalidByte(src[i * 2])]; } - const [b, bOK] = fromHexChar(src[i * 2 + 1]); if (!bOK) { - return [i, invalidByteError(src[i * 2 + 1])]; + return [i, errInvalidByte(src[i * 2 + 1])]; } dest[i] = (a << 4) | b; @@ -73,8 +77,9 @@ export function decode(dest: Uint8Array, src: Uint8Array): [number, Error] { // since the invalid char (if present) is an earlier problem. const [, ok] = fromHexChar(src[i * 2]); if (!ok) { - return [i, invalidByteError(src[i * 2])]; + return [i, errInvalidByte(src[i * 2])]; } + return [i, errLength()]; } return [i, undefined]; @@ -105,22 +110,20 @@ export function decodeString(s: string): Uint8Array { return src.slice(0, n); } -// TODO: encoder export class Encoder implements Deno.Writer { - private err: Error; private out = new Uint8Array(bufferSize); - constructor() {} + constructor(private w: Deno.Writer) {} async write(p: Uint8Array): Promise { let n = 0; - for (; p.length > 0 && !this.err; ) { + for (; p.length > 0; ) { let chunkSize = bufferSize / 2; if (p.length < chunkSize) { chunkSize = p.length; } const encoded = encode(this.out.slice(), p.slice(chunkSize)); - const written = this.out.slice(0, encoded).length; + const written = await this.w.write(this.out.slice(0, encoded)); n += written / 2; p = p.slice(chunkSize); @@ -128,7 +131,63 @@ export class Encoder implements Deno.Writer { return n; } - toString(): string { - return ""; +} + +export class Decoder implements Deno.Reader { + private in = new Uint8Array(); // input buffer (encoded form) + private arr = new Uint8Array(bufferSize); // backing array for in + private err: Error; + constructor(private r: Deno.Reader) {} + async read(p: Uint8Array): Promise { + // Fill internal buffer with sufficient bytes to decode + if (this.in.length < 2 && !this.err) { + let numCopy = 0; + let numRead = 0; + + numCopy = copyBytes(this.arr.slice(), this.in); + const { nread, eof } = await this.r.read(this.arr.slice(numCopy)); + numRead = nread; + this.in = this.arr.slice(0, numCopy + numRead); + + if (eof && this.in.length % 2 != 0) { + const [, ok] = fromHexChar(this.in[this.in.length - 1]); + if (!ok) { + this.err = errInvalidByte(this.in[this.in.length - 1]); + } else { + this.err = new Error("unexpected EOF"); + } + } + } + + // Decode internal buffer into output buffer + const numAvail = this.in.length / 2; + if (numAvail && p.length > numAvail) { + p = p.slice(0, numAvail); + } + + const [numDec, err] = decode(p, this.in.slice(0, p.length * 2)); + + this.in = this.in.slice(2 * numDec); + + if (err) { + // Decode error; discard input remainder + throw err; + } + + if (this.in.length < 2) { + // Only throw errors when buffer fully consumed + if (this.err) { + throw err; + } + return { + nread: numDec, + eof: true + }; + } + + return { + nread: numDec, + eof: false + }; } } diff --git a/encoding/hex_test.ts b/encoding/hex_test.ts index 33a8991bc2d4..546ecd239cfc 100644 --- a/encoding/hex_test.ts +++ b/encoding/hex_test.ts @@ -8,9 +8,15 @@ import { encodeToString, decodedLen, decode, - decodeString + decodeString, + errLength, + errInvalidByte } from "./hex.ts"; +function toByte(s: string): number { + return new TextEncoder().encode(s)[0]; +} + const testCases = [ // encoded(hex) / decoded(Uint8Array) ["", []], @@ -22,6 +28,19 @@ const testCases = [ ["e3a1", [0xe3, 0xa1]] ]; +const errCases = [ + // encoded(hex) / error + ["", "", undefined], + ["0", "", errLength()], + ["zd4aa", "", errInvalidByte(toByte("z"))], + ["d4aaz", "\xd4\xaa", errInvalidByte(toByte("z"))], + ["30313", "01", errLength()], + ["0g", "", errInvalidByte(new TextEncoder().encode("g")[0])], + ["00gg", "\x00", errInvalidByte(new TextEncoder().encode("g")[0])], + ["0\x01", "", errInvalidByte(new TextEncoder().encode("\x01")[0])], + ["ffeed", "\xff\xee", errLength()] +]; + test({ name: "[encoding.hex] encodedLen", fn(): void { @@ -50,7 +69,7 @@ test({ const src = new TextEncoder().encode(srcStr); const dest = new Uint8Array(2); // out of index assertThrows( - () => { + (): void => { encode(dest, src); }, Error, @@ -91,6 +110,8 @@ test({ test({ name: "[encoding.hex] decode", fn(): void { + // Case for decoding uppercase hex characters, since + // Encode always uses lowercase. const extraTestcase = [ ["F8F9FAFBFCFDFEFF", [0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff]] ]; @@ -118,4 +139,39 @@ test({ } }); +test({ + name: "[encoding.hex] decode error", + fn(): void { + for (const [input, output, expectedErr] of errCases) { + const out = new Uint8Array((input as string).length + 10); + const [n, err] = decode(out, new TextEncoder().encode(input as string)); + assertEquals( + new TextDecoder("ascii").decode(out.slice(0, n)), + output as string + ); + assertEquals(err, expectedErr); + } + } +}); + +test({ + name: "[encoding.hex] decodeString error", + fn(): void { + for (const [input, output, expectedErr] of errCases) { + if (expectedErr) { + assertThrows( + (): void => { + decodeString(input as string); + }, + Error, + (expectedErr as Error).message + ); + } else { + const out = decodeString(input as string); + assertEquals(new TextDecoder("ascii").decode(out), output as string); + } + } + } +}); + runIfMain(import.meta); diff --git a/encoding/test.ts b/encoding/test.ts new file mode 100644 index 000000000000..be40cbff2fa1 --- /dev/null +++ b/encoding/test.ts @@ -0,0 +1,2 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import "./hex_test.ts"; diff --git a/test.ts b/test.ts index 221bbc9858b8..938cf2c199e9 100755 --- a/test.ts +++ b/test.ts @@ -18,5 +18,6 @@ import "./textproto/test.ts"; import "./toml/test.ts"; import "./util/test.ts"; import "./ws/test.ts"; +import "./encoding/test.ts"; import "./testing/main.ts"; From 218b0d025f6bdbb63eab36a18d2aa99775bc08bb Mon Sep 17 00:00:00 2001 From: axetroy Date: Thu, 23 May 2019 21:00:21 +0800 Subject: [PATCH 3/6] update comment --- encoding/hex.ts | 73 +++++++++++++++++++++++++++++++------------- encoding/hex_test.ts | 5 +++ 2 files changed, 56 insertions(+), 22 deletions(-) diff --git a/encoding/hex.ts b/encoding/hex.ts index 1cfe3781d528..8aa68d8d41c8 100644 --- a/encoding/hex.ts +++ b/encoding/hex.ts @@ -1,3 +1,8 @@ +// Ported from Go +// https://github.com/golang/go/blob/go1.12.5/src/encoding/hex/hex.go +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. import { copyBytes } from "../io/util.ts"; @@ -28,36 +33,51 @@ function fromHexChar(byte: number): [number, boolean] { return [0, false]; } +/** + * EncodedLen returns the length of an encoding of n source bytes. Specifically, it returns n * 2. + * @param n + */ export function encodedLen(n: number): number { return n * 2; } -export function encode(dest: Uint8Array, src: Uint8Array): number { - if (dest.length !== encodedLen(src.length)) { +/** + * Encode encodes `src` into `encodedLen(src.length)` bytes of `dst`. + * As a convenience, it returns the number of bytes written to `dst`, but this value is always `encodedLen(src.length)`. + * Encode implements hexadecimal encoding. + * @param dst + * @param src + */ +export function encode(dst: Uint8Array, src: Uint8Array): number { + if (dst.length !== encodedLen(src.length)) { throw new Error("Out of index."); } for (let i = 0; i < src.length; i++) { const v = src[i]; - dest[i * 2] = hextable[v >> 4]; - dest[i * 2 + 1] = hextable[v & 0x0f]; + dst[i * 2] = hextable[v >> 4]; + dst[i * 2 + 1] = hextable[v & 0x0f]; } return encodedLen(src.length); } +/** + * EncodeToString returns the hexadecimal encoding of `src`. + * @param src + */ export function encodeToString(src: Uint8Array): string { const dest = new Uint8Array(encodedLen(src.length)); encode(dest, src); return new TextDecoder().decode(dest); } -// Decode decodes src into DecodedLen(len(src)) bytes, -// returning the actual number of bytes written to dst. -// -// Decode expects that src contains only hexadecimal -// characters and that src has even length. -// If the input is malformed, Decode returns the number -// of bytes decoded before the error. -export function decode(dest: Uint8Array, src: Uint8Array): [number, Error] { +/** + * Decode decodes `src` into `decodedLen(src.length)` bytes, returning the actual number of bytes written to `dst`. + * Decode expects that `src` contains only hexadecimal characters and that `src` has even length. + * If the input is malformed, Decode returns the number of bytes decoded before the error. + * @param dst + * @param src + */ +export function decode(dst: Uint8Array, src: Uint8Array): [number, Error] { var i = 0; for (; i < Math.floor(src.length / 2); i++) { const [a, aOK] = fromHexChar(src[i * 2]); @@ -69,7 +89,7 @@ export function decode(dest: Uint8Array, src: Uint8Array): [number, Error] { return [i, errInvalidByte(src[i * 2 + 1])]; } - dest[i] = (a << 4) | b; + dst[i] = (a << 4) | b; } if (src.length % 2 == 1) { @@ -85,18 +105,20 @@ export function decode(dest: Uint8Array, src: Uint8Array): [number, Error] { return [i, undefined]; } -// DecodedLen returns the length of a decoding of x source bytes. -// Specifically, it returns x / 2. +/** + * DecodedLen returns the length of a decoding of `x` source bytes. Specifically, it returns `x / 2`. + * @param x + */ export function decodedLen(x: number): number { - return x / 2; + return Math.floor(x / 2); } -// DecodeString returns the bytes represented by the hexadecimal string s. -// -// DecodeString expects that src contains only hexadecimal -// characters and that src has even length. -// If the input is malformed, DecodeString returns -// the bytes decoded before the error. +/** + * DecodeString returns the bytes represented by the hexadecimal string `s`. + * DecodeString expects that src contains only hexadecimal characters and that src has even length. + * If the input is malformed, DecodeString will throws an error. + * @param s + */ export function decodeString(s: string): Uint8Array { const src = new TextEncoder().encode(s); // We can use the source slice itself as the destination @@ -110,6 +132,9 @@ export function decodeString(s: string): Uint8Array { return src.slice(0, n); } +/** + * A `deno.Writer` implements that writes lowercase hexadecimal characters to `w`. + */ export class Encoder implements Deno.Writer { private out = new Uint8Array(bufferSize); constructor(private w: Deno.Writer) {} @@ -133,6 +158,10 @@ export class Encoder implements Deno.Writer { } } +/** + * A `deno.Reader` implements that decodes hexadecimal characters from `r`. + * Decoder expects that `r` contain only an even number of hexadecimal characters. + */ export class Decoder implements Deno.Reader { private in = new Uint8Array(); // input buffer (encoded form) private arr = new Uint8Array(bufferSize); // backing array for in diff --git a/encoding/hex_test.ts b/encoding/hex_test.ts index 546ecd239cfc..5ea81ebdbcd6 100644 --- a/encoding/hex_test.ts +++ b/encoding/hex_test.ts @@ -1,3 +1,8 @@ +// Ported from Go +// https://github.com/golang/go/blob/go1.12.5/src/encoding/hex/hex.go +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. import { test, runIfMain } from "../testing/mod.ts"; import { assertEquals, assertThrows } from "../testing/asserts.ts"; From 5dda88764746af2d1095d7b4ecad56772ebe733a Mon Sep 17 00:00:00 2001 From: axetroy Date: Thu, 6 Jun 2019 11:00:27 +0800 Subject: [PATCH 4/6] remove Encoder/Decoder. it's no ready yet. --- encoding/hex.ts | 183 ++++++++++++++++++++++++------------------------ 1 file changed, 92 insertions(+), 91 deletions(-) diff --git a/encoding/hex.ts b/encoding/hex.ts index 8aa68d8d41c8..2ee1b92bdca8 100644 --- a/encoding/hex.ts +++ b/encoding/hex.ts @@ -4,10 +4,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { copyBytes } from "../io/util.ts"; +// import { copyBytes } from "../io/util.ts"; const hextable = new TextEncoder().encode("0123456789abcdef"); -const bufferSize = 1024; +// const bufferSize = 1024; export function errInvalidByte(byte: number): Error { return new Error( @@ -117,7 +117,7 @@ export function decodedLen(x: number): number { * DecodeString returns the bytes represented by the hexadecimal string `s`. * DecodeString expects that src contains only hexadecimal characters and that src has even length. * If the input is malformed, DecodeString will throws an error. - * @param s + * @param s the `string` need to decode to `Uint8Array` */ export function decodeString(s: string): Uint8Array { const src = new TextEncoder().encode(s); @@ -132,91 +132,92 @@ export function decodeString(s: string): Uint8Array { return src.slice(0, n); } -/** - * A `deno.Writer` implements that writes lowercase hexadecimal characters to `w`. - */ -export class Encoder implements Deno.Writer { - private out = new Uint8Array(bufferSize); - constructor(private w: Deno.Writer) {} - async write(p: Uint8Array): Promise { - let n = 0; - - for (; p.length > 0; ) { - let chunkSize = bufferSize / 2; - if (p.length < chunkSize) { - chunkSize = p.length; - } - - const encoded = encode(this.out.slice(), p.slice(chunkSize)); - const written = await this.w.write(this.out.slice(0, encoded)); - - n += written / 2; - p = p.slice(chunkSize); - } - - return n; - } -} - -/** - * A `deno.Reader` implements that decodes hexadecimal characters from `r`. - * Decoder expects that `r` contain only an even number of hexadecimal characters. - */ -export class Decoder implements Deno.Reader { - private in = new Uint8Array(); // input buffer (encoded form) - private arr = new Uint8Array(bufferSize); // backing array for in - private err: Error; - constructor(private r: Deno.Reader) {} - async read(p: Uint8Array): Promise { - // Fill internal buffer with sufficient bytes to decode - if (this.in.length < 2 && !this.err) { - let numCopy = 0; - let numRead = 0; - - numCopy = copyBytes(this.arr.slice(), this.in); - const { nread, eof } = await this.r.read(this.arr.slice(numCopy)); - numRead = nread; - this.in = this.arr.slice(0, numCopy + numRead); - - if (eof && this.in.length % 2 != 0) { - const [, ok] = fromHexChar(this.in[this.in.length - 1]); - if (!ok) { - this.err = errInvalidByte(this.in[this.in.length - 1]); - } else { - this.err = new Error("unexpected EOF"); - } - } - } - - // Decode internal buffer into output buffer - const numAvail = this.in.length / 2; - if (numAvail && p.length > numAvail) { - p = p.slice(0, numAvail); - } - - const [numDec, err] = decode(p, this.in.slice(0, p.length * 2)); - - this.in = this.in.slice(2 * numDec); - - if (err) { - // Decode error; discard input remainder - throw err; - } - - if (this.in.length < 2) { - // Only throw errors when buffer fully consumed - if (this.err) { - throw err; - } - return { - nread: numDec, - eof: true - }; - } - - return { - nread: numDec, - eof: false - }; - } -} +// TODO: add Encoder/Decoder in the future +// /** +// * A `deno.Writer` implements that writes lowercase hexadecimal characters to `w`. +// */ +// export class Encoder implements Deno.Writer { +// private out = new Uint8Array(bufferSize); +// constructor(private w: Deno.Writer) {} +// async write(p: Uint8Array): Promise { +// let n = 0; + +// for (; p.length > 0; ) { +// let chunkSize = bufferSize / 2; +// if (p.length < chunkSize) { +// chunkSize = p.length; +// } + +// const encoded = encode(this.out.slice(), p.slice(chunkSize)); +// const written = await this.w.write(this.out.slice(0, encoded)); + +// n += written / 2; +// p = p.slice(chunkSize); +// } + +// return n; +// } +// } + +// /** +// * A `deno.Reader` implements that decodes hexadecimal characters from `r`. +// * Decoder expects that `r` contain only an even number of hexadecimal characters. +// */ +// export class Decoder implements Deno.Reader { +// private in = new Uint8Array(); // input buffer (encoded form) +// private arr = new Uint8Array(bufferSize); // backing array for in +// private err: Error; +// constructor(private r: Deno.Reader) {} +// async read(p: Uint8Array): Promise { +// // Fill internal buffer with sufficient bytes to decode +// if (this.in.length < 2 && !this.err) { +// let numCopy = 0; +// let numRead = 0; + +// numCopy = copyBytes(this.arr.slice(), this.in); +// const { nread, eof } = await this.r.read(this.arr.slice(numCopy)); +// numRead = nread; +// this.in = this.arr.slice(0, numCopy + numRead); + +// if (eof && this.in.length % 2 != 0) { +// const [, ok] = fromHexChar(this.in[this.in.length - 1]); +// if (!ok) { +// this.err = errInvalidByte(this.in[this.in.length - 1]); +// } else { +// this.err = new Error("unexpected EOF"); +// } +// } +// } + +// // Decode internal buffer into output buffer +// const numAvail = this.in.length / 2; +// if (numAvail && p.length > numAvail) { +// p = p.slice(0, numAvail); +// } + +// const [numDec, err] = decode(p, this.in.slice(0, p.length * 2)); + +// this.in = this.in.slice(2 * numDec); + +// if (err) { +// // Decode error; discard input remainder +// throw err; +// } + +// if (this.in.length < 2) { +// // Only throw errors when buffer fully consumed +// if (this.err) { +// throw err; +// } +// return { +// nread: numDec, +// eof: true +// }; +// } + +// return { +// nread: numDec, +// eof: false +// }; +// } +// } From 3c8e56466006f8d978c2f61678672dd4dae6a5ee Mon Sep 17 00:00:00 2001 From: axetroy Date: Thu, 6 Jun 2019 11:17:09 +0800 Subject: [PATCH 5/6] fix eslint --- encoding/hex.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/encoding/hex.ts b/encoding/hex.ts index 2ee1b92bdca8..819db0899a83 100644 --- a/encoding/hex.ts +++ b/encoding/hex.ts @@ -77,7 +77,10 @@ export function encodeToString(src: Uint8Array): string { * @param dst * @param src */ -export function decode(dst: Uint8Array, src: Uint8Array): [number, Error] { +export function decode( + dst: Uint8Array, + src: Uint8Array +): [number, Error | void] { var i = 0; for (; i < Math.floor(src.length / 2); i++) { const [a, aOK] = fromHexChar(src[i * 2]); From 2d653dcfd60343e74b3599387bc631d84983e32c Mon Sep 17 00:00:00 2001 From: axetroy Date: Sun, 16 Jun 2019 21:39:39 +0800 Subject: [PATCH 6/6] remove dead code --- encoding/hex.ts | 103 ++++-------------------------------------------- 1 file changed, 7 insertions(+), 96 deletions(-) diff --git a/encoding/hex.ts b/encoding/hex.ts index 819db0899a83..83c88ac7836b 100644 --- a/encoding/hex.ts +++ b/encoding/hex.ts @@ -4,10 +4,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -// import { copyBytes } from "../io/util.ts"; const hextable = new TextEncoder().encode("0123456789abcdef"); -// const bufferSize = 1024; export function errInvalidByte(byte: number): Error { return new Error( @@ -43,13 +41,15 @@ export function encodedLen(n: number): number { /** * Encode encodes `src` into `encodedLen(src.length)` bytes of `dst`. - * As a convenience, it returns the number of bytes written to `dst`, but this value is always `encodedLen(src.length)`. + * As a convenience, it returns the number of bytes written to `dst` + * but this value is always `encodedLen(src.length)`. * Encode implements hexadecimal encoding. * @param dst * @param src */ export function encode(dst: Uint8Array, src: Uint8Array): number { - if (dst.length !== encodedLen(src.length)) { + const srcLength = encodedLen(src.length); + if (dst.length !== srcLength) { throw new Error("Out of index."); } for (let i = 0; i < src.length; i++) { @@ -57,7 +57,7 @@ export function encode(dst: Uint8Array, src: Uint8Array): number { dst[i * 2] = hextable[v >> 4]; dst[i * 2 + 1] = hextable[v & 0x0f]; } - return encodedLen(src.length); + return srcLength; } /** @@ -71,7 +71,8 @@ export function encodeToString(src: Uint8Array): string { } /** - * Decode decodes `src` into `decodedLen(src.length)` bytes, returning the actual number of bytes written to `dst`. + * Decode decodes `src` into `decodedLen(src.length)` bytes + * returning the actual number of bytes written to `dst`. * Decode expects that `src` contains only hexadecimal characters and that `src` has even length. * If the input is malformed, Decode returns the number of bytes decoded before the error. * @param dst @@ -134,93 +135,3 @@ export function decodeString(s: string): Uint8Array { return src.slice(0, n); } - -// TODO: add Encoder/Decoder in the future -// /** -// * A `deno.Writer` implements that writes lowercase hexadecimal characters to `w`. -// */ -// export class Encoder implements Deno.Writer { -// private out = new Uint8Array(bufferSize); -// constructor(private w: Deno.Writer) {} -// async write(p: Uint8Array): Promise { -// let n = 0; - -// for (; p.length > 0; ) { -// let chunkSize = bufferSize / 2; -// if (p.length < chunkSize) { -// chunkSize = p.length; -// } - -// const encoded = encode(this.out.slice(), p.slice(chunkSize)); -// const written = await this.w.write(this.out.slice(0, encoded)); - -// n += written / 2; -// p = p.slice(chunkSize); -// } - -// return n; -// } -// } - -// /** -// * A `deno.Reader` implements that decodes hexadecimal characters from `r`. -// * Decoder expects that `r` contain only an even number of hexadecimal characters. -// */ -// export class Decoder implements Deno.Reader { -// private in = new Uint8Array(); // input buffer (encoded form) -// private arr = new Uint8Array(bufferSize); // backing array for in -// private err: Error; -// constructor(private r: Deno.Reader) {} -// async read(p: Uint8Array): Promise { -// // Fill internal buffer with sufficient bytes to decode -// if (this.in.length < 2 && !this.err) { -// let numCopy = 0; -// let numRead = 0; - -// numCopy = copyBytes(this.arr.slice(), this.in); -// const { nread, eof } = await this.r.read(this.arr.slice(numCopy)); -// numRead = nread; -// this.in = this.arr.slice(0, numCopy + numRead); - -// if (eof && this.in.length % 2 != 0) { -// const [, ok] = fromHexChar(this.in[this.in.length - 1]); -// if (!ok) { -// this.err = errInvalidByte(this.in[this.in.length - 1]); -// } else { -// this.err = new Error("unexpected EOF"); -// } -// } -// } - -// // Decode internal buffer into output buffer -// const numAvail = this.in.length / 2; -// if (numAvail && p.length > numAvail) { -// p = p.slice(0, numAvail); -// } - -// const [numDec, err] = decode(p, this.in.slice(0, p.length * 2)); - -// this.in = this.in.slice(2 * numDec); - -// if (err) { -// // Decode error; discard input remainder -// throw err; -// } - -// if (this.in.length < 2) { -// // Only throw errors when buffer fully consumed -// if (this.err) { -// throw err; -// } -// return { -// nread: numDec, -// eof: true -// }; -// } - -// return { -// nread: numDec, -// eof: false -// }; -// } -// }