feat(encoding/binary): add varnumBytes(), varbigBytes() (denoland/deno#5173)

This commit is contained in:
Oliver Lenehan 2020-06-09 08:58:55 +10:00 committed by denobot
parent dce2ff677a
commit 431c07de25
2 changed files with 79 additions and 27 deletions

View File

@ -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<DataType, number> = {
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;
}

View File

@ -12,6 +12,8 @@ import {
varnum,
writeVarbig,
writeVarnum,
varbigBytes,
varnumBytes,
} from "./binary.ts";
Deno.test("testGetNBytes", async function (): Promise<void> {
@ -160,3 +162,29 @@ Deno.test("testWriteVarnumLittleEndian", async function (): Promise<void> {
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]));
});