From 431c07de251c10e9e1c5b6a499755ad63924b24a Mon Sep 17 00:00:00 2001 From: Oliver Lenehan Date: Tue, 9 Jun 2020 08:58:55 +1000 Subject: [PATCH] feat(encoding/binary): add varnumBytes(), varbigBytes() (denoland/deno#5173) --- encoding/binary.ts | 78 +++++++++++++++++++++++++++-------------- encoding/binary_test.ts | 28 +++++++++++++++ 2 files changed, 79 insertions(+), 27 deletions(-) diff --git a/encoding/binary.ts b/encoding/binary.ts index dd9c26243..aed9343b9 100644 --- a/encoding/binary.ts +++ b/encoding/binary.ts @@ -1,9 +1,9 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -type RawBaseTypes = "int8" | "int16" | "int32" | "uint8" | "uint16" | "uint32"; -type RawNumberTypes = RawBaseTypes | "float32" | "float64"; -type RawBigTypes = RawBaseTypes | "int64" | "uint64"; -type RawTypes = RawNumberTypes | RawBigTypes; +type RawBaseType = "int8" | "int16" | "int32" | "uint8" | "uint16" | "uint32"; +type RawNumberType = RawBaseType | "float32" | "float64"; +type RawBigType = RawBaseType | "int64" | "uint64"; +export type DataType = RawNumberType | RawBigType; /** How encoded binary data is ordered. */ export type Endianness = "little" | "big"; @@ -11,7 +11,7 @@ export type Endianness = "little" | "big"; /** Options for working with the `number` type. */ export interface VarnumOptions { /** The binary format used. */ - dataType?: RawNumberTypes; + dataType?: RawNumberType; /** The binary encoding order used. */ endian?: Endianness; } @@ -19,12 +19,12 @@ export interface VarnumOptions { /** Options for working with the `bigint` type. */ export interface VarbigOptions { /** The binary format used. */ - dataType?: RawBigTypes; + dataType?: RawBigType; /** The binary encoding order used. */ endian?: Endianness; } -const rawTypeSizes = { +const rawTypeSizes: Record = { int8: 1, uint8: 1, int16: 2, @@ -35,16 +35,16 @@ const rawTypeSizes = { uint64: 8, float32: 4, float64: 8, -}; +} as const; -/** Returns the number of bytes required to store the given data-type. */ -export function sizeof(dataType: RawTypes): number { +/** Number of bytes required to store `dataType`. */ +export function sizeof(dataType: DataType): number { return rawTypeSizes[dataType]; } /** Reads `n` bytes from `r`. * - * Returns it in a `Uint8Array`, or throws `Deno.errors.UnexpectedEof` if `n` bytes cannot be read. */ + * Resolves it in a `Uint8Array`, or throws `Deno.errors.UnexpectedEof` if `n` bytes cannot be read. */ export async function getNBytes( r: Deno.Reader, n: number @@ -55,8 +55,9 @@ export async function getNBytes( return scratch; } -/** Decode a number from `b`, and return it as a `number`. Data-type defaults to `int32`. - * Returns `null` if `b` is too short for the data-type given in `o`. */ +/** Decodes a number from `b`. If `o.bytes` is shorter than `sizeof(o.dataType)`, returns `null`. + * + * `o.dataType` defaults to `"int32"`. */ export function varnum(b: Uint8Array, o: VarnumOptions = {}): number | null { o.dataType = o.dataType ?? "int32"; const littleEndian = (o.endian ?? "big") === "little" ? true : false; @@ -82,8 +83,9 @@ export function varnum(b: Uint8Array, o: VarnumOptions = {}): number | null { } } -/** Decode an integer from `b`, and return it as a `bigint`. Data-type defaults to `int64`. - * Returns `null` if `b` is too short for the data-type given in `o`. */ +/** Decodes a bigint from `b`. If `o.bytes` is shorter than `sizeof(o.dataType)`, returns `null`. + * + * `o.dataType` defaults to `"int64"`. */ export function varbig(b: Uint8Array, o: VarbigOptions = {}): bigint | null { o.dataType = o.dataType ?? "int64"; const littleEndian = (o.endian ?? "big") === "little" ? true : false; @@ -109,8 +111,9 @@ export function varbig(b: Uint8Array, o: VarbigOptions = {}): bigint | null { } } -/** Encode a number `x` into `b`, and return the number of bytes used. Data-type defaults to `int32`. - * Returns 0 if `b` is too short for the data-type given in `o`. */ +/** Encodes number `x` into `b`. Returns the number of bytes used, or `0` if `b` is shorter than `sizeof(o.dataType)`. + * + * `o.dataType` defaults to `"int32"`. */ export function putVarnum( b: Uint8Array, x: number, @@ -149,8 +152,9 @@ export function putVarnum( return sizeof(o.dataType); } -/** Encode an integer `x` into `b`, and return the number of bytes used. Data-type defaults to `int64`. - * Returns 0 if `b` is too short for the data-type given in `o`. */ +/** Encodes bigint `x` into `b`. Returns the number of bytes used, or `0` if `b` is shorter than `sizeof(o.dataType)`. + * + * `o.dataType` defaults to `"int64"`. */ export function putVarbig( b: Uint8Array, x: bigint, @@ -189,9 +193,9 @@ export function putVarbig( return sizeof(o.dataType); } -/** Reads a number from `r`, comsuming `sizeof(o.dataType)` bytes. Data-type defaults to `int32`. +/** Decodes a number from `r`, comsuming `sizeof(o.dataType)` bytes. If less than `sizeof(o.dataType)` bytes were read, throws `Deno.errors.unexpectedEof`. * - * Returns it as `number`, or throws `Deno.errors.UnexpectedEof` if not enough bytes can be read. */ + * `o.dataType` defaults to `"int32"`. */ export async function readVarnum( r: Deno.Reader, o: VarnumOptions = {} @@ -201,9 +205,9 @@ export async function readVarnum( return varnum(scratch, o) as number; } -/** Reads an integer from `r`, comsuming `sizeof(o.dataType)` bytes. Data-type defaults to `int64`. +/** Decodes a bigint from `r`, comsuming `sizeof(o.dataType)` bytes. If less than `sizeof(o.dataType)` bytes were read, throws `Deno.errors.unexpectedEof`. * - * Returns it as `bigint`, or throws `Deno.errors.UnexpectedEof` if not enough bytes can be read. */ + * `o.dataType` defaults to `"int64"`. */ export async function readVarbig( r: Deno.Reader, o: VarbigOptions = {} @@ -213,9 +217,9 @@ export async function readVarbig( return varbig(scratch, o) as bigint; } -/** Writes a number `x` to `w`. Data-type defaults to `int32`. +/** Encodes and writes `x` to `w`. Resolves to the number of bytes written. * - * Returns the number of bytes written. */ + * `o.dataType` defaults to `"int32"`. */ export function writeVarnum( w: Deno.Writer, x: number, @@ -227,9 +231,9 @@ export function writeVarnum( return w.write(scratch); } -/** Writes an integer `x` to `w`. Data-type defaults to `int64`. +/** Encodes and writes `x` to `w`. Resolves to the number of bytes written. * - * Returns the number of bytes written. */ + * `o.dataType` defaults to `"int64"`. */ export function writeVarbig( w: Deno.Writer, x: bigint, @@ -240,3 +244,23 @@ export function writeVarbig( putVarbig(scratch, x, o); return w.write(scratch); } + +/** Encodes `x` into a new `Uint8Array`. + * + * `o.dataType` defaults to `"int32"` */ +export function varnumBytes(x: number, o: VarnumOptions = {}): Uint8Array { + o.dataType = o.dataType ?? "int32"; + const b = new Uint8Array(sizeof(o.dataType)); + putVarnum(b, x, o); + return b; +} + +/** Encodes `x` into a new `Uint8Array`. + * + * `o.dataType` defaults to `"int64"` */ +export function varbigBytes(x: bigint, o: VarbigOptions = {}): Uint8Array { + o.dataType = o.dataType ?? "int64"; + const b = new Uint8Array(sizeof(o.dataType)); + putVarbig(b, x, o); + return b; +} diff --git a/encoding/binary_test.ts b/encoding/binary_test.ts index ebd0b0b41..6213bc2e3 100644 --- a/encoding/binary_test.ts +++ b/encoding/binary_test.ts @@ -12,6 +12,8 @@ import { varnum, writeVarbig, writeVarnum, + varbigBytes, + varnumBytes, } from "./binary.ts"; Deno.test("testGetNBytes", async function (): Promise { @@ -160,3 +162,29 @@ Deno.test("testWriteVarnumLittleEndian", async function (): Promise { await buff.read(data); assertEquals(data, new Uint8Array([0x01, 0x02, 0x03, 0x04])); }); + +Deno.test("testVarbigBytes", function (): void { + const rslt = varbigBytes(0x0102030405060708n); + assertEquals( + rslt, + new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]) + ); +}); + +Deno.test("testVarbigBytesLittleEndian", function (): void { + const rslt = varbigBytes(0x0807060504030201n, { endian: "little" }); + assertEquals( + rslt, + new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]) + ); +}); + +Deno.test("testVarnumBytes", function (): void { + const rslt = varnumBytes(0x01020304); + assertEquals(rslt, new Uint8Array([0x01, 0x02, 0x03, 0x04])); +}); + +Deno.test("testVarnumBytesLittleEndian", function (): void { + const rslt = varnumBytes(0x04030201, { endian: "little" }); + assertEquals(rslt, new Uint8Array([0x01, 0x02, 0x03, 0x04])); +});