improved viewers
This commit is contained in:
+32
@@ -0,0 +1,32 @@
|
||||
// Integer Utility
|
||||
|
||||
export const UINT32_MAX = 0xffff_ffff;
|
||||
|
||||
// DataView extension to handle int64 / uint64,
|
||||
// where the actual range is 53-bits integer (a.k.a. safe integer)
|
||||
|
||||
export function setUint64(view: DataView, offset: number, value: number): void {
|
||||
const high = value / 0x1_0000_0000;
|
||||
const low = value; // high bits are truncated by DataView
|
||||
view.setUint32(offset, high);
|
||||
view.setUint32(offset + 4, low);
|
||||
}
|
||||
|
||||
export function setInt64(view: DataView, offset: number, value: number): void {
|
||||
const high = Math.floor(value / 0x1_0000_0000);
|
||||
const low = value; // high bits are truncated by DataView
|
||||
view.setUint32(offset, high);
|
||||
view.setUint32(offset + 4, low);
|
||||
}
|
||||
|
||||
export function getInt64(view: DataView, offset: number): number {
|
||||
const high = view.getInt32(offset);
|
||||
const low = view.getUint32(offset + 4);
|
||||
return high * 0x1_0000_0000 + low;
|
||||
}
|
||||
|
||||
export function getUint64(view: DataView, offset: number): number {
|
||||
const high = view.getUint32(offset);
|
||||
const low = view.getUint32(offset + 4);
|
||||
return high * 0x1_0000_0000 + low;
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export function prettyByte(byte: number): string {
|
||||
return `${byte < 0 ? "-" : ""}0x${Math.abs(byte).toString(16).padStart(2, "0")}`;
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
// utility for whatwg streams
|
||||
|
||||
// The living standard of whatwg streams says
|
||||
// ReadableStream is also AsyncIterable, but
|
||||
// as of June 2019, no browser implements it.
|
||||
// See https://streams.spec.whatwg.org/ for details
|
||||
export type ReadableStreamLike<T> = AsyncIterable<T> | ReadableStream<T>;
|
||||
|
||||
export function isAsyncIterable<T>(object: ReadableStreamLike<T>): object is AsyncIterable<T> {
|
||||
return (object as any)[Symbol.asyncIterator] != null;
|
||||
}
|
||||
|
||||
function assertNonNull<T>(value: T | null | undefined): asserts value is T {
|
||||
if (value == null) {
|
||||
throw new Error("Assertion Failure: value must not be null nor undefined");
|
||||
}
|
||||
}
|
||||
|
||||
export async function* asyncIterableFromStream<T>(stream: ReadableStream<T>): AsyncIterable<T> {
|
||||
const reader = stream.getReader();
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
return;
|
||||
}
|
||||
assertNonNull(value);
|
||||
yield value;
|
||||
}
|
||||
} finally {
|
||||
reader.releaseLock();
|
||||
}
|
||||
}
|
||||
|
||||
export function ensureAsyncIterable<T>(streamLike: ReadableStreamLike<T>): AsyncIterable<T> {
|
||||
if (isAsyncIterable(streamLike)) {
|
||||
return streamLike;
|
||||
} else {
|
||||
return asyncIterableFromStream(streamLike);
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
export function ensureUint8Array(buffer: ArrayLike<number> | Uint8Array | ArrayBufferView | ArrayBuffer): Uint8Array {
|
||||
if (buffer instanceof Uint8Array) {
|
||||
return buffer;
|
||||
} else if (ArrayBuffer.isView(buffer)) {
|
||||
return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
||||
} else if (buffer instanceof ArrayBuffer) {
|
||||
return new Uint8Array(buffer);
|
||||
} else {
|
||||
// ArrayLike<number>
|
||||
return Uint8Array.from(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
export function createDataView(buffer: ArrayLike<number> | ArrayBufferView | ArrayBuffer): DataView {
|
||||
if (buffer instanceof ArrayBuffer) {
|
||||
return new DataView(buffer);
|
||||
}
|
||||
|
||||
const bufferView = ensureUint8Array(buffer);
|
||||
return new DataView(bufferView.buffer, bufferView.byteOffset, bufferView.byteLength);
|
||||
}
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
||||
import { UINT32_MAX } from "./int";
|
||||
|
||||
const TEXT_ENCODING_AVAILABLE =
|
||||
(typeof process === "undefined" || process?.env?.["TEXT_ENCODING"] !== "never") &&
|
||||
typeof TextEncoder !== "undefined" &&
|
||||
typeof TextDecoder !== "undefined";
|
||||
|
||||
export function utf8Count(str: string): number {
|
||||
const strLength = str.length;
|
||||
|
||||
let byteLength = 0;
|
||||
let pos = 0;
|
||||
while (pos < strLength) {
|
||||
let value = str.charCodeAt(pos++);
|
||||
|
||||
if ((value & 0xffffff80) === 0) {
|
||||
// 1-byte
|
||||
byteLength++;
|
||||
continue;
|
||||
} else if ((value & 0xfffff800) === 0) {
|
||||
// 2-bytes
|
||||
byteLength += 2;
|
||||
} else {
|
||||
// handle surrogate pair
|
||||
if (value >= 0xd800 && value <= 0xdbff) {
|
||||
// high surrogate
|
||||
if (pos < strLength) {
|
||||
const extra = str.charCodeAt(pos);
|
||||
if ((extra & 0xfc00) === 0xdc00) {
|
||||
++pos;
|
||||
value = ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((value & 0xffff0000) === 0) {
|
||||
// 3-byte
|
||||
byteLength += 3;
|
||||
} else {
|
||||
// 4-byte
|
||||
byteLength += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
return byteLength;
|
||||
}
|
||||
|
||||
export function utf8EncodeJs(str: string, output: Uint8Array, outputOffset: number): void {
|
||||
const strLength = str.length;
|
||||
let offset = outputOffset;
|
||||
let pos = 0;
|
||||
while (pos < strLength) {
|
||||
let value = str.charCodeAt(pos++);
|
||||
|
||||
if ((value & 0xffffff80) === 0) {
|
||||
// 1-byte
|
||||
output[offset++] = value;
|
||||
continue;
|
||||
} else if ((value & 0xfffff800) === 0) {
|
||||
// 2-bytes
|
||||
output[offset++] = ((value >> 6) & 0x1f) | 0xc0;
|
||||
} else {
|
||||
// handle surrogate pair
|
||||
if (value >= 0xd800 && value <= 0xdbff) {
|
||||
// high surrogate
|
||||
if (pos < strLength) {
|
||||
const extra = str.charCodeAt(pos);
|
||||
if ((extra & 0xfc00) === 0xdc00) {
|
||||
++pos;
|
||||
value = ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((value & 0xffff0000) === 0) {
|
||||
// 3-byte
|
||||
output[offset++] = ((value >> 12) & 0x0f) | 0xe0;
|
||||
output[offset++] = ((value >> 6) & 0x3f) | 0x80;
|
||||
} else {
|
||||
// 4-byte
|
||||
output[offset++] = ((value >> 18) & 0x07) | 0xf0;
|
||||
output[offset++] = ((value >> 12) & 0x3f) | 0x80;
|
||||
output[offset++] = ((value >> 6) & 0x3f) | 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
output[offset++] = (value & 0x3f) | 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
const sharedTextEncoder = TEXT_ENCODING_AVAILABLE ? new TextEncoder() : undefined;
|
||||
export const TEXT_ENCODER_THRESHOLD = !TEXT_ENCODING_AVAILABLE
|
||||
? UINT32_MAX
|
||||
: typeof process !== "undefined" && process?.env?.["TEXT_ENCODING"] !== "force"
|
||||
? 200
|
||||
: 0;
|
||||
|
||||
function utf8EncodeTEencode(str: string, output: Uint8Array, outputOffset: number): void {
|
||||
output.set(sharedTextEncoder!.encode(str), outputOffset);
|
||||
}
|
||||
|
||||
function utf8EncodeTEencodeInto(str: string, output: Uint8Array, outputOffset: number): void {
|
||||
sharedTextEncoder!.encodeInto(str, output.subarray(outputOffset));
|
||||
}
|
||||
|
||||
export const utf8EncodeTE = sharedTextEncoder?.encodeInto ? utf8EncodeTEencodeInto : utf8EncodeTEencode;
|
||||
|
||||
const CHUNK_SIZE = 0x1_000;
|
||||
|
||||
export function utf8DecodeJs(bytes: Uint8Array, inputOffset: number, byteLength: number): string {
|
||||
let offset = inputOffset;
|
||||
const end = offset + byteLength;
|
||||
|
||||
const units: Array<number> = [];
|
||||
let result = "";
|
||||
while (offset < end) {
|
||||
const byte1 = bytes[offset++]!;
|
||||
if ((byte1 & 0x80) === 0) {
|
||||
// 1 byte
|
||||
units.push(byte1);
|
||||
} else if ((byte1 & 0xe0) === 0xc0) {
|
||||
// 2 bytes
|
||||
const byte2 = bytes[offset++]! & 0x3f;
|
||||
units.push(((byte1 & 0x1f) << 6) | byte2);
|
||||
} else if ((byte1 & 0xf0) === 0xe0) {
|
||||
// 3 bytes
|
||||
const byte2 = bytes[offset++]! & 0x3f;
|
||||
const byte3 = bytes[offset++]! & 0x3f;
|
||||
units.push(((byte1 & 0x1f) << 12) | (byte2 << 6) | byte3);
|
||||
} else if ((byte1 & 0xf8) === 0xf0) {
|
||||
// 4 bytes
|
||||
const byte2 = bytes[offset++]! & 0x3f;
|
||||
const byte3 = bytes[offset++]! & 0x3f;
|
||||
const byte4 = bytes[offset++]! & 0x3f;
|
||||
let unit = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0c) | (byte3 << 0x06) | byte4;
|
||||
if (unit > 0xffff) {
|
||||
unit -= 0x10000;
|
||||
units.push(((unit >>> 10) & 0x3ff) | 0xd800);
|
||||
unit = 0xdc00 | (unit & 0x3ff);
|
||||
}
|
||||
units.push(unit);
|
||||
} else {
|
||||
units.push(byte1);
|
||||
}
|
||||
|
||||
if (units.length >= CHUNK_SIZE) {
|
||||
result += String.fromCharCode(...units);
|
||||
units.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (units.length > 0) {
|
||||
result += String.fromCharCode(...units);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const sharedTextDecoder = TEXT_ENCODING_AVAILABLE ? new TextDecoder() : null;
|
||||
export const TEXT_DECODER_THRESHOLD = !TEXT_ENCODING_AVAILABLE
|
||||
? UINT32_MAX
|
||||
: typeof process !== "undefined" && process?.env?.["TEXT_DECODER"] !== "force"
|
||||
? 200
|
||||
: 0;
|
||||
|
||||
export function utf8DecodeTD(bytes: Uint8Array, inputOffset: number, byteLength: number): string {
|
||||
const stringBytes = bytes.subarray(inputOffset, inputOffset + byteLength);
|
||||
return sharedTextDecoder!.decode(stringBytes);
|
||||
}
|
||||
Reference in New Issue
Block a user