diff --git a/apps/demo/components/log-view.tsx b/apps/demo/components/log-view.tsx index 3a79d3a1..01746008 100644 --- a/apps/demo/components/log-view.tsx +++ b/apps/demo/components/log-view.tsx @@ -24,7 +24,7 @@ const classNames = mergeStyleSets({ }); function serializePacket(packet: AdbPacketInit) { - const command = decodeUtf8(new Uint32Array([packet.command]).buffer); + const command = decodeUtf8(new Uint32Array([packet.command])); const parts = [ command, @@ -35,7 +35,7 @@ function serializePacket(packet: AdbPacketInit) { if (packet.payload) { parts.push( Array.from( - new Uint8Array(packet.payload), + packet.payload, byte => byte.toString(16).padStart(2, '0') ).join(' ') ); diff --git a/apps/demo/components/terminal.tsx b/apps/demo/components/terminal.tsx index cbdeb13b..3c4b8413 100644 --- a/apps/demo/components/terminal.tsx +++ b/apps/demo/components/terminal.tsx @@ -36,16 +36,16 @@ export class AdbTerminal extends AutoDisposable { this._socketAbortController = new AbortController(); - value.stdout.pipeTo(new WritableStream({ + value.stdout.pipeTo(new WritableStream({ write: (chunk) => { - this.terminal.write(new Uint8Array(chunk)); + this.terminal.write(chunk); }, }), { signal: this._socketAbortController.signal, }); - value.stderr.pipeTo(new WritableStream({ + value.stderr.pipeTo(new WritableStream({ write: (chunk) => { - this.terminal.write(new Uint8Array(chunk)); + this.terminal.write(chunk); }, }), { signal: this._socketAbortController.signal, diff --git a/apps/demo/pages/file-manager.tsx b/apps/demo/pages/file-manager.tsx index 68e0cf9e..ad49e2a9 100644 --- a/apps/demo/pages/file-manager.tsx +++ b/apps/demo/pages/file-manager.tsx @@ -4,7 +4,6 @@ import { getFileTypeIconNameFromExtensionOrType } from '@fluentui/react-file-typ import { DEFAULT_BASE_URL as FILE_TYPE_ICONS_BASE_URL } from '@fluentui/react-file-type-icons/lib-commonjs/initializeFileTypeIcons'; import { useConst } from '@fluentui/react-hooks'; import { AdbSyncEntryResponse, ADB_SYNC_MAX_PACKET_SIZE, ChunkStream, LinuxFileType, ReadableStream, TransformStream } from '@yume-chan/adb'; -import { ExtractViewBufferStream } from "@yume-chan/adb-backend-direct-sockets"; import { action, autorun, makeAutoObservable, observable, runInAction } from "mobx"; import { observer } from "mobx-react-lite"; import { NextPage } from "next"; @@ -21,7 +20,7 @@ import { asyncEffect, formatSize, formatSpeed, Icons, pickFile, RouteStackProps * Because of internal buffer of upstream/downstream streams, * the progress value won't be 100% accurate. But it's usually good enough. */ -export class ProgressStream extends TransformStream { +export class ProgressStream extends TransformStream { public constructor(onProgress: (value: number) => void) { let progress = 0; super({ @@ -171,13 +170,7 @@ class FileManagerState { item.name, { size: item.size } ); - await readable - .pipeThrough(new TransformStream({ - transform(chunk, controller) { - controller.enqueue(new Uint8Array(chunk)); - }, - })) - .pipeTo(writeable); + await readable.pipeTo(writeable); } catch (e) { globalState.showErrorDialog(e instanceof Error ? e.message : `${e}`); } finally { @@ -477,7 +470,6 @@ class FileManagerState { try { await (file.stream() as unknown as ReadableStream) - .pipeThrough(new ExtractViewBufferStream()) .pipeThrough(new ChunkStream(ADB_SYNC_MAX_PACKET_SIZE)) .pipeThrough(new ProgressStream(action((uploaded) => { this.uploadedSize = uploaded; @@ -558,12 +550,7 @@ const FileManager: NextPage = (): JSX.Element | null => { const previewImage = useCallback(async (path: string) => { const sync = await globalState.device!.sync(); try { - const readable = (await sync.read(path)) - .pipeThrough(new TransformStream({ - transform(chunk, controller) { - controller.enqueue(new Uint8Array(chunk)); - }, - })); + const readable = sync.read(path); const response = new Response(readable); const blob = await response.blob(); const url = window.URL.createObjectURL(blob); diff --git a/apps/demo/pages/framebuffer.tsx b/apps/demo/pages/framebuffer.tsx index 05a5186e..3eb825a5 100644 --- a/apps/demo/pages/framebuffer.tsx +++ b/apps/demo/pages/framebuffer.tsx @@ -24,7 +24,7 @@ class FrameBufferState { setImage(image: AdbFrameBuffer) { this.width = image.width; this.height = image.height; - this.imageData = new ImageData(image.data, image.width, image.height); + this.imageData = new ImageData(new Uint8ClampedArray(image.data), image.width, image.height); } toggleDemoModeVisible() { diff --git a/apps/demo/pages/install.tsx b/apps/demo/pages/install.tsx index 6ecbc5fa..e02d08b6 100644 --- a/apps/demo/pages/install.tsx +++ b/apps/demo/pages/install.tsx @@ -1,6 +1,5 @@ import { DefaultButton, ProgressIndicator, Stack } from "@fluentui/react"; import { ADB_SYNC_MAX_PACKET_SIZE, ChunkStream, ReadableStream } from "@yume-chan/adb"; -import { ExtractViewBufferStream } from "@yume-chan/adb-backend-direct-sockets"; import { action, makeAutoObservable, observable, runInAction } from "mobx"; import { observer } from "mobx-react-lite"; import { NextPage } from "next"; @@ -59,7 +58,6 @@ class InstallPageState { }); await (file.stream() as unknown as ReadableStream) - .pipeThrough(new ExtractViewBufferStream()) .pipeThrough(new ChunkStream(ADB_SYNC_MAX_PACKET_SIZE)) .pipeThrough(new ProgressStream(action((uploaded) => { if (uploaded !== file.size) { diff --git a/apps/demo/pages/scrcpy.tsx b/apps/demo/pages/scrcpy.tsx index 72a2f0d8..320af92e 100644 --- a/apps/demo/pages/scrcpy.tsx +++ b/apps/demo/pages/scrcpy.tsx @@ -17,7 +17,7 @@ import { ProgressStream } from "./file-manager"; const SERVER_URL = new URL('@yume-chan/scrcpy/bin/scrcpy-server?url', import.meta.url).toString(); class FetchWithProgress { - public readonly promise: Promise; + public readonly promise: Promise; private _downloaded = 0; public get downloaded() { return this._downloaded; } @@ -56,7 +56,7 @@ class FetchWithProgress { result.set(chunk, position); position += chunk.byteLength; } - return result.buffer; + return result; } } @@ -387,7 +387,7 @@ class ScrcpyPageState { this.debouncedServerDownloadedSize = this.serverDownloadedSize; }), 1000); - let serverBuffer: ArrayBuffer; + let serverBuffer: Uint8Array; try { serverBuffer = await fetchServer(action(([downloaded, total]) => { @@ -408,7 +408,7 @@ class ScrcpyPageState { }), 1000); try { - await new ReadableStream({ + await new ReadableStream({ start(controller) { controller.enqueue(serverBuffer); controller.close(); @@ -450,7 +450,7 @@ class ScrcpyPageState { // Run scrcpy once will delete the server file // Re-push it - await new ReadableStream({ + await new ReadableStream({ start(controller) { controller.enqueue(serverBuffer); controller.close(); @@ -492,7 +492,7 @@ class ScrcpyPageState { options ); - client.stdout.pipeTo(new WritableStream({ + client.stdout.pipeTo(new WritableStream({ write: action((line) => { this.log.push(line); }), diff --git a/libraries/adb-backend-direct-sockets/src/index.ts b/libraries/adb-backend-direct-sockets/src/index.ts index d103a5c1..758a7497 100644 --- a/libraries/adb-backend-direct-sockets/src/index.ts +++ b/libraries/adb-backend-direct-sockets/src/index.ts @@ -1,19 +1,4 @@ -import { AdbBackend, ReadableStream, ReadableWritablePair, TransformStream, WritableStream } from '@yume-chan/adb'; - -/** - * Transform an `ArrayBufferView` stream to an `ArrayBuffer` stream. - * - * The view must wrap the whole buffer (`byteOffset === 0` && `byteLength === buffer.byteLength`). - */ -export class ExtractViewBufferStream extends TransformStream{ - constructor() { - super({ - transform(chunk, controller) { - controller.enqueue(chunk.buffer); - } - }); - } -} +import { AdbBackend, ReadableStream, WritableStream } from '@yume-chan/adb'; declare global { interface TCPSocket { @@ -44,28 +29,6 @@ declare global { } } -export class AdbDirectSocketsBackendStreams implements ReadableWritablePair{ - private socket: TCPSocket; - - private _readableTransformStream: ExtractViewBufferStream; - public get readable(): ReadableStream { - return this._readableTransformStream.readable; - } - - public get writable(): WritableStream { - return this.socket.writable; - } - - constructor(socket: TCPSocket) { - this.socket = socket; - - // Although Direct Sockets spec didn't say, - // WebTransport spec and File spec all have the `Uint8Array` wraps the whole `ArrayBuffer`. - this._readableTransformStream = new ExtractViewBufferStream(); - this.socket.readable.pipeTo(this._readableTransformStream.writable); - } -} - export default class AdbDirectSocketsBackend implements AdbBackend { public static isSupported(): boolean { return typeof window !== 'undefined' && !!window.navigator?.openTCPSocket; @@ -87,12 +50,10 @@ export default class AdbDirectSocketsBackend implements AdbBackend { } public async connect() { - const socket = await navigator.openTCPSocket({ + return await navigator.openTCPSocket({ remoteAddress: this.host, remotePort: this.port, noDelay: true, }); - - return new AdbDirectSocketsBackendStreams(socket); } } diff --git a/libraries/adb-backend-webusb/src/backend.ts b/libraries/adb-backend-webusb/src/backend.ts index ef972313..9cc4c9ec 100644 --- a/libraries/adb-backend-webusb/src/backend.ts +++ b/libraries/adb-backend-webusb/src/backend.ts @@ -6,15 +6,15 @@ export const WebUsbDeviceFilter: USBDeviceFilter = { protocolCode: 1, }; -export class AdbWebUsbBackendStream implements ReadableWritablePair{ - private _readable: ReadableStream; +export class AdbWebUsbBackendStream implements ReadableWritablePair{ + private _readable: ReadableStream; public get readable() { return this._readable; } - private _writable: WritableStream; + private _writable: WritableStream; public get writable() { return this._writable; } public constructor(device: USBDevice, inEndpoint: USBEndpoint, outEndpoint: USBEndpoint) { - this._readable = new ReadableStream({ + this._readable = new ReadableStream({ pull: async (controller) => { let result = await device.transferIn(inEndpoint.endpointNumber, inEndpoint.packetSize); @@ -24,8 +24,8 @@ export class AdbWebUsbBackendStream implements ReadableWritablePair { await device.close(); @@ -35,7 +35,7 @@ export class AdbWebUsbBackendStream implements ReadableWritablePair({ write: async (chunk) => { await device.transferOut(outEndpoint.endpointNumber, chunk); }, diff --git a/libraries/adb-backend-ws/src/index.ts b/libraries/adb-backend-ws/src/index.ts index 8a935f0f..f817296f 100644 --- a/libraries/adb-backend-ws/src/index.ts +++ b/libraries/adb-backend-ws/src/index.ts @@ -21,10 +21,10 @@ export default class AdbWsBackend implements AdbBackend { }; }); - const readable = new ReadableStream({ + const readable = new ReadableStream({ start: (controller) => { socket.onmessage = ({ data }: { data: ArrayBuffer; }) => { - controller.enqueue(data); + controller.enqueue(new Uint8Array(data)); }; socket.onclose = () => { controller.close(); @@ -35,7 +35,7 @@ export default class AdbWsBackend implements AdbBackend { size(chunk) { return chunk.byteLength; }, }); - const writable = new WritableStream({ + const writable = new WritableStream({ write: (chunk) => { socket.send(chunk); }, diff --git a/libraries/adb-credential-web/src/index.ts b/libraries/adb-credential-web/src/index.ts index e8f970da..93ef402f 100644 --- a/libraries/adb-credential-web/src/index.ts +++ b/libraries/adb-credential-web/src/index.ts @@ -1,16 +1,16 @@ // cspell: ignore RSASSA -import { type AdbCredentialStore, calculateBase64EncodedLength, calculatePublicKey, calculatePublicKeyLength, decodeBase64, encodeBase64 } from "@yume-chan/adb"; +import { calculateBase64EncodedLength, calculatePublicKey, calculatePublicKeyLength, decodeBase64, encodeBase64, type AdbCredentialStore } from "@yume-chan/adb"; const Utf8Encoder = new TextEncoder(); const Utf8Decoder = new TextDecoder(); -export function encodeUtf8(input: string): ArrayBuffer { - return Utf8Encoder.encode(input).buffer; +export function encodeUtf8(input: string): Uint8Array { + return Utf8Encoder.encode(input); } -export function decodeUtf8(buffer: ArrayBuffer): string { - return Utf8Decoder.decode(buffer); +export function decodeUtf8(array: Uint8Array): string { + return Utf8Decoder.decode(array); } export default class AdbWebCredentialStore implements AdbCredentialStore { @@ -20,14 +20,14 @@ export default class AdbWebCredentialStore implements AdbCredentialStore { this.localStorageKey = localStorageKey; } - public *iterateKeys(): Generator { + public *iterateKeys(): Generator { const privateKey = window.localStorage.getItem(this.localStorageKey); if (privateKey) { yield decodeBase64(privateKey); } } - public async generateKey(): Promise { + public async generateKey(): Promise { const { privateKey: cryptoKey } = await crypto.subtle.generateKey( { name: 'RSASSA-PKCS1-v1_5', @@ -40,7 +40,7 @@ export default class AdbWebCredentialStore implements AdbCredentialStore { ['sign', 'verify'] ); - const privateKey = await crypto.subtle.exportKey('pkcs8', cryptoKey!); + const privateKey = new Uint8Array(await crypto.subtle.exportKey('pkcs8', cryptoKey!)); window.localStorage.setItem(this.localStorageKey, decodeUtf8(encodeBase64(privateKey))); // The authentication module in core doesn't need public keys. @@ -49,7 +49,7 @@ export default class AdbWebCredentialStore implements AdbCredentialStore { // so also save the public key for their convenience. const publicKeyLength = calculatePublicKeyLength(); const [publicKeyBase64Length] = calculateBase64EncodedLength(publicKeyLength); - const publicKeyBuffer = new ArrayBuffer(publicKeyBase64Length); + const publicKeyBuffer = new Uint8Array(publicKeyBase64Length); calculatePublicKey(privateKey, publicKeyBuffer); encodeBase64(publicKeyBuffer, 0, publicKeyLength, publicKeyBuffer); window.localStorage.setItem(this.localStorageKey + '.pub', decodeUtf8(publicKeyBuffer)); diff --git a/libraries/adb/src/commands/subprocess/legacy.ts b/libraries/adb/src/commands/subprocess/legacy.ts index 073a8a4c..f7b80537 100644 --- a/libraries/adb/src/commands/subprocess/legacy.ts +++ b/libraries/adb/src/commands/subprocess/legacy.ts @@ -52,9 +52,11 @@ export class AdbNoneSubprocessProtocol implements AdbSubprocessProtocol { state: undefined, }; }, - async close() { + close: async () => { // Close `stderr` on exit. stderrController.close(); + + this._exit.resolve(0); } }); } diff --git a/libraries/adb/src/commands/sync/pull.ts b/libraries/adb/src/commands/sync/pull.ts index 8e74ff31..8d3f5c3d 100644 --- a/libraries/adb/src/commands/sync/pull.ts +++ b/libraries/adb/src/commands/sync/pull.ts @@ -19,7 +19,7 @@ export function adbSyncPull( writer: WritableStreamDefaultWriter, path: string, ): ReadableStream { - return new ReadableStream({ + return new ReadableStream({ async start() { await adbSyncWriteRequest(writer, AdbSyncRequestId.Receive, path); }, diff --git a/libraries/adb/src/commands/sync/push.ts b/libraries/adb/src/commands/sync/push.ts index 977bdf53..bf75de9a 100644 --- a/libraries/adb/src/commands/sync/push.ts +++ b/libraries/adb/src/commands/sync/push.ts @@ -23,7 +23,7 @@ export function adbSyncPush( packetSize: number = ADB_SYNC_MAX_PACKET_SIZE, ): WritableStream { const { readable, writable } = new ChunkStream(packetSize); - readable.pipeTo(new WritableStream({ + readable.pipeTo(new WritableStream({ async start() { const pathAndMode = `${filename},${mode.toString()}`; await adbSyncWriteRequest(writer, AdbSyncRequestId.Send, pathAndMode); diff --git a/libraries/adb/src/commands/sync/request.ts b/libraries/adb/src/commands/sync/request.ts index 3d80748e..53f27aa5 100644 --- a/libraries/adb/src/commands/sync/request.ts +++ b/libraries/adb/src/commands/sync/request.ts @@ -30,20 +30,20 @@ export async function adbSyncWriteRequest( ): Promise { let buffer: Uint8Array; if (typeof value === 'number') { - buffer = new Uint8Array(AdbSyncNumberRequest.serialize({ + buffer = AdbSyncNumberRequest.serialize({ id, arg: value, - })); + }); } else if (typeof value === 'string') { - buffer = new Uint8Array(AdbSyncDataRequest.serialize({ + buffer = AdbSyncDataRequest.serialize({ id, data: encodeUtf8(value), - })); + }); } else { - buffer = new Uint8Array(AdbSyncDataRequest.serialize({ + buffer = AdbSyncDataRequest.serialize({ id, data: value, - })); + }); } await writer.write(buffer); } diff --git a/libraries/adb/src/packet.ts b/libraries/adb/src/packet.ts index 658c9840..7a0175cf 100644 --- a/libraries/adb/src/packet.ts +++ b/libraries/adb/src/packet.ts @@ -36,7 +36,7 @@ export class AdbPacketSerializeStream extends TransformStream { let checksum: number; if (this.calculateChecksum && init.payload) { - const array = new Uint8Array(init.payload); + const array = init.payload; checksum = array.reduce((result, item) => result + item, 0); } else { checksum = 0; @@ -49,7 +49,7 @@ export class AdbPacketSerializeStream extends TransformStream({ write: async (packet) => { try { this.logger?.onIncomingPacket?.(packet); diff --git a/libraries/adb/src/stream/buffered.ts b/libraries/adb/src/stream/buffered.ts index 3aa9e490..3844952d 100644 --- a/libraries/adb/src/stream/buffered.ts +++ b/libraries/adb/src/stream/buffered.ts @@ -36,7 +36,7 @@ export class BufferedStream { const buffer = this.buffer; if (buffer.byteLength > length) { this.buffer = buffer.subarray(length); - return buffer.slice(0, length); + return buffer.subarray(0, length); } array = new Uint8Array(length); @@ -60,11 +60,11 @@ export class BufferedStream { if (value.byteLength > length) { this.buffer = value.subarray(length); - return value.slice(0, length); + return value.subarray(0, length); } array = new Uint8Array(length); - array.set(new Uint8Array(value), 0); + array.set(value, 0); index = value.byteLength; } diff --git a/libraries/adb/src/stream/transform.ts b/libraries/adb/src/stream/transform.ts index e756a683..7290c1fd 100644 --- a/libraries/adb/src/stream/transform.ts +++ b/libraries/adb/src/stream/transform.ts @@ -70,7 +70,7 @@ export class StructSerializeStream> constructor(struct: T) { super({ transform(chunk, controller) { - controller.enqueue(new Uint8Array(struct.serialize(chunk))); + controller.enqueue(struct.serialize(chunk)); }, }); } @@ -140,6 +140,7 @@ export class WrapReadableStream, S> extends Reada pull: async (controller) => { const result = await this.reader.read(); if (result.done) { + wrapper.close?.(this.state); controller.close(); } else { controller.enqueue(result.value); @@ -153,9 +154,9 @@ export class ChunkStream extends TransformStream{ public constructor(size: number) { super({ transform(chunk, controller) { - for (let start = 0; start < chunk.length; start += size) { + for (let start = 0; start < chunk.byteLength;) { const end = start + size; - controller.enqueue(chunk.slice(start, end)); + controller.enqueue(chunk.subarray(start, end)); start = end; } } diff --git a/libraries/adb/src/utils/encoding.ts b/libraries/adb/src/utils/encoding.ts index 3d8e25af..29093ffc 100644 --- a/libraries/adb/src/utils/encoding.ts +++ b/libraries/adb/src/utils/encoding.ts @@ -8,6 +8,6 @@ export function encodeUtf8(input: string): Uint8Array { return Utf8Encoder.encode(input); } -export function decodeUtf8(buffer: Uint8Array): string { +export function decodeUtf8(buffer: ArrayBufferView | ArrayBuffer): string { return Utf8Decoder.decode(buffer); } diff --git a/libraries/android-bin/src/bug-report.ts b/libraries/android-bin/src/bug-report.ts index de24d943..73c67273 100644 --- a/libraries/android-bin/src/bug-report.ts +++ b/libraries/android-bin/src/bug-report.ts @@ -50,8 +50,8 @@ export class BugReportZ extends AdbCommandBase { return version.major > 1 || version.minor >= 2; } - public stream(): ReadableStream { - return new WrapReadableStream, undefined>({ + public stream(): ReadableStream { + return new WrapReadableStream, undefined>({ start: async () => { const process = await this.adb.subprocess.spawn(['bugreportz', '-s']); return { @@ -86,7 +86,7 @@ export class BugReportZ extends AdbCommandBase { await process.stdout .pipeThrough(new DecodeUtf8Stream()) .pipeThrough(new SplitLineStream()) - .pipeTo(new WritableStream({ + .pipeTo(new WritableStream({ write(line) { // (Not 100% sure) `BEGIN:` and `PROGRESS:` only appear when `-p` is specified. let match = line.match(BugReportZ.PROGRESS_REGEX); diff --git a/libraries/scrcpy/src/client.ts b/libraries/scrcpy/src/client.ts index 8f66c774..b6fa26ee 100644 --- a/libraries/scrcpy/src/client.ts +++ b/libraries/scrcpy/src/client.ts @@ -124,7 +124,7 @@ export class ScrcpyClient { private _videoStream: TransformStream; public get videoStream() { return this._videoStream.readable; } - private _controlStreamWriter: WritableStreamDefaultWriter | undefined; + private _controlStreamWriter: WritableStreamDefaultWriter | undefined; private readonly clipboardChangeEvent = new EventEmitter(); public get onClipboardChange() { return this.clipboardChangeEvent.event; } @@ -179,7 +179,7 @@ export class ScrcpyClient { try { while (true) { const type = await buffered.read(1); - switch (new Uint8Array(type)[0]) { + switch (type[0]) { case 0: const { content } = await ClipboardMessage.deserialize(buffered); this.clipboardChangeEvent.fire(content!); diff --git a/libraries/scrcpy/src/decoder/tinyh264/index.ts b/libraries/scrcpy/src/decoder/tinyh264/index.ts index d10223ea..3c9b51ce 100644 --- a/libraries/scrcpy/src/decoder/tinyh264/index.ts +++ b/libraries/scrcpy/src/decoder/tinyh264/index.ts @@ -37,7 +37,7 @@ export class TinyH264Decoder implements H264Decoder { this._renderer = document.createElement('canvas'); - this._writable = new WritableStream({ + this._writable = new WritableStream({ write: async (packet) => { switch (packet.type) { case 'configuration': @@ -49,7 +49,7 @@ export class TinyH264Decoder implements H264Decoder { } const wrapper = await this._initializer.promise; - wrapper.feed(packet.data); + wrapper.feed(packet.data.slice().buffer); break; } } diff --git a/libraries/scrcpy/src/decoder/web-codecs/index.ts b/libraries/scrcpy/src/decoder/web-codecs/index.ts index 30a42200..e6d1c1d8 100644 --- a/libraries/scrcpy/src/decoder/web-codecs/index.ts +++ b/libraries/scrcpy/src/decoder/web-codecs/index.ts @@ -32,7 +32,7 @@ export class WebCodecsDecoder implements H264Decoder { error() { }, }); - this._writable = new WritableStream({ + this._writable = new WritableStream({ write: async (packet) => { switch (packet.type) { case 'configuration': diff --git a/libraries/scrcpy/src/options/1_16/index.ts b/libraries/scrcpy/src/options/1_16/index.ts index f2ec0166..cd461cb8 100644 --- a/libraries/scrcpy/src/options/1_16/index.ts +++ b/libraries/scrcpy/src/options/1_16/index.ts @@ -5,7 +5,7 @@ import { ScrcpyClientConnection, ScrcpyClientConnectionOptions, ScrcpyClientForw import { AndroidKeyEventAction, ScrcpyControlMessageType } from "../../message"; import type { ScrcpyBackOrScreenOnEvent1_18 } from "../1_18"; import type { ScrcpyInjectScrollControlMessage1_22 } from "../1_22"; -import { toScrcpyOptionValue, type VideoStreamPacket, type ScrcpyOptions, type ScrcpyOptionValue } from "../common"; +import { toScrcpyOptionValue, type ScrcpyOptions, type ScrcpyOptionValue, type VideoStreamPacket } from "../common"; import { parse_sequence_parameter_set } from "./sps"; export enum ScrcpyLogLevel { @@ -114,7 +114,7 @@ export const VideoPacket = new Struct() .int64('pts') .uint32('size') - .arrayBuffer('data', { lengthField: 'size' }); + .uint8Array('data', { lengthField: 'size' }); export const NoPts = BigInt(-1); @@ -135,7 +135,7 @@ export const ScrcpyInjectScrollControlMessage1_16 = export class ScrcpyOptions1_16 implements ScrcpyOptions { public value: Partial; - private _streamHeader: ArrayBuffer | undefined; + private _streamHeader: Uint8Array | undefined; public constructor(value: Partial) { if (new.target === ScrcpyOptions1_16 && @@ -223,7 +223,7 @@ export class ScrcpyOptions1_16 { serializeBackOrScreenOnControlMessage( message: ScrcpyBackOrScreenOnEvent1_18, - ): ArrayBuffer | undefined; + ): Uint8Array | undefined; serializeInjectScrollControlMessage( message: ScrcpyInjectScrollControlMessage1_22, - ): ArrayBuffer; + ): Uint8Array; } diff --git a/libraries/scrcpy/src/push-server.ts b/libraries/scrcpy/src/push-server.ts index e72bb916..7adc1143 100644 --- a/libraries/scrcpy/src/push-server.ts +++ b/libraries/scrcpy/src/push-server.ts @@ -11,7 +11,7 @@ export function pushServer( ) { const { path = DEFAULT_SERVER_PATH } = options; - return new WrapWritableStream, AdbSync>({ + return new WrapWritableStream, AdbSync>({ async start() { const sync = await device.sync(); return { diff --git a/libraries/struct/src/struct.ts b/libraries/struct/src/struct.ts index 3cf33a16..7677dab3 100644 --- a/libraries/struct/src/struct.ts +++ b/libraries/struct/src/struct.ts @@ -3,7 +3,7 @@ import type { StructAsyncDeserializeStream, StructDeserializeStream, StructFieldDefinition, StructFieldValue, StructOptions } from './basic'; import { StructDefaultOptions, StructValue } from './basic'; import { Syncbird } from "./syncbird"; -import { ArrayBufferFieldType, ArrayBufferLikeFieldType, BigIntFieldDefinition, BigIntFieldType, FixedLengthArrayBufferLikeFieldDefinition, FixedLengthArrayBufferLikeFieldOptions, LengthField, NumberFieldDefinition, NumberFieldType, StringFieldType, ArrayBufferViewFieldType, VariableLengthArrayBufferLikeFieldDefinition, VariableLengthArrayBufferLikeFieldOptions } from './types'; +import { BigIntFieldDefinition, BigIntFieldType, BufferFieldSubType, FixedLengthBufferLikeFieldDefinition, FixedLengthBufferLikeFieldOptions, LengthField, NumberFieldDefinition, NumberFieldType, StringBufferFieldSubType, Uint8ArrayBufferFieldSubType, VariableLengthBufferLikeFieldDefinition, VariableLengthBufferLikeFieldOptions } from './types'; import { Evaluate, Identity, Overwrite, ValueOrPromise } from "./utils"; export interface StructLike { @@ -57,12 +57,12 @@ interface ArrayBufferLikeFieldCreator< */ < TName extends PropertyKey, - TType extends ArrayBufferLikeFieldType, + TType extends BufferFieldSubType, TTypeScriptType = TType['TTypeScriptType'], >( name: TName, type: TType, - options: FixedLengthArrayBufferLikeFieldOptions, + options: FixedLengthBufferLikeFieldOptions, typescriptType?: TTypeScriptType, ): AddFieldDescriptor< TFields, @@ -70,9 +70,9 @@ interface ArrayBufferLikeFieldCreator< TExtra, TPostDeserialized, TName, - FixedLengthArrayBufferLikeFieldDefinition< + FixedLengthBufferLikeFieldDefinition< TType, - FixedLengthArrayBufferLikeFieldOptions + FixedLengthBufferLikeFieldOptions > >; @@ -81,8 +81,8 @@ interface ArrayBufferLikeFieldCreator< */ < TName extends PropertyKey, - TType extends ArrayBufferLikeFieldType, - TOptions extends VariableLengthArrayBufferLikeFieldOptions, + TType extends BufferFieldSubType, + TOptions extends VariableLengthBufferLikeFieldOptions, TTypeScriptType = TType['TTypeScriptType'], >( name: TName, @@ -95,7 +95,7 @@ interface ArrayBufferLikeFieldCreator< TExtra, TPostDeserialized, TName, - VariableLengthArrayBufferLikeFieldDefinition< + VariableLengthBufferLikeFieldDefinition< TType, TOptions > @@ -110,14 +110,14 @@ interface BoundArrayBufferLikeFieldDefinitionCreator< TOmitInitKey extends PropertyKey, TExtra extends object, TPostDeserialized, - TType extends ArrayBufferLikeFieldType + TType extends BufferFieldSubType > { < TName extends PropertyKey, TTypeScriptType = TType['TTypeScriptType'], >( name: TName, - options: FixedLengthArrayBufferLikeFieldOptions, + options: FixedLengthBufferLikeFieldOptions, typescriptType?: TTypeScriptType, ): AddFieldDescriptor< TFields, @@ -125,16 +125,16 @@ interface BoundArrayBufferLikeFieldDefinitionCreator< TExtra, TPostDeserialized, TName, - FixedLengthArrayBufferLikeFieldDefinition< + FixedLengthBufferLikeFieldDefinition< TType, - FixedLengthArrayBufferLikeFieldOptions + FixedLengthBufferLikeFieldOptions > >; < TName extends PropertyKey, TLengthField extends LengthField, - TOptions extends VariableLengthArrayBufferLikeFieldOptions, + TOptions extends VariableLengthBufferLikeFieldOptions, TTypeScriptType = TType['TTypeScriptType'], >( name: TName, @@ -146,7 +146,7 @@ interface BoundArrayBufferLikeFieldDefinitionCreator< TExtra, TPostDeserialized, TName, - VariableLengthArrayBufferLikeFieldDefinition< + VariableLengthBufferLikeFieldDefinition< TType, TOptions > @@ -421,59 +421,33 @@ export class Struct< TPostDeserialized > = ( name: PropertyKey, - type: ArrayBufferLikeFieldType, - options: FixedLengthArrayBufferLikeFieldOptions | VariableLengthArrayBufferLikeFieldOptions + type: BufferFieldSubType, + options: FixedLengthBufferLikeFieldOptions | VariableLengthBufferLikeFieldOptions ): any => { if ('length' in options) { return this.field( name, - new FixedLengthArrayBufferLikeFieldDefinition(type, options), + new FixedLengthBufferLikeFieldDefinition(type, options), ); } else { return this.field( name, - new VariableLengthArrayBufferLikeFieldDefinition(type, options), + new VariableLengthBufferLikeFieldDefinition(type, options), ); } }; - public arrayBuffer: BoundArrayBufferLikeFieldDefinitionCreator< - TFields, - TOmitInitKey, - TExtra, - TPostDeserialized, - ArrayBufferFieldType - > = ( - name: PropertyKey, - options: any - ): any => { - return this.arrayBufferLike(name, ArrayBufferFieldType.instance, options); - }; - public uint8Array: BoundArrayBufferLikeFieldDefinitionCreator< TFields, TOmitInitKey, TExtra, TPostDeserialized, - ArrayBufferViewFieldType + Uint8ArrayBufferFieldSubType > = ( name: PropertyKey, options: any ): any => { - return this.arrayBufferLike(name, ArrayBufferViewFieldType.uint8Array, options); - }; - - public uint8ClampedArray: BoundArrayBufferLikeFieldDefinitionCreator< - TFields, - TOmitInitKey, - TExtra, - TPostDeserialized, - ArrayBufferViewFieldType - > = ( - name: PropertyKey, - options: any - ): any => { - return this.arrayBufferLike(name, ArrayBufferViewFieldType.uint8ClampedArray, options); + return this.arrayBufferLike(name, Uint8ArrayBufferFieldSubType.Instance, options); }; public string: BoundArrayBufferLikeFieldDefinitionCreator< @@ -481,12 +455,12 @@ export class Struct< TOmitInitKey, TExtra, TPostDeserialized, - StringFieldType + StringBufferFieldSubType > = ( name: PropertyKey, options: any ): any => { - return this.arrayBufferLike(name, StringFieldType.instance, options); + return this.arrayBufferLike(name, StringBufferFieldSubType.Instance, options); }; /** diff --git a/libraries/struct/src/types/array-buffer.spec.ts b/libraries/struct/src/types/array-buffer.spec.ts deleted file mode 100644 index 8c4114e3..00000000 --- a/libraries/struct/src/types/array-buffer.spec.ts +++ /dev/null @@ -1,207 +0,0 @@ -import { StructDefaultOptions, StructDeserializeStream, StructValue } from '../basic'; -import { ArrayBufferFieldType, ArrayBufferLikeFieldDefinition, ArrayBufferLikeFieldType, StringFieldType, ArrayBufferViewFieldType } from './array-buffer'; - -class MockDeserializationStream implements StructDeserializeStream { - public buffer = new ArrayBuffer(0); - - public read = jest.fn((length: number) => this.buffer); -} - -describe('Types', () => { - describe('ArrayBufferLike', () => { - describe('ArrayBufferFieldType', () => { - it('should have a static instance', () => { - expect(ArrayBufferFieldType.instance).toBeInstanceOf(ArrayBufferFieldType); - }); - - it('`#toArrayBuffer` should return the same `ArrayBuffer`', () => { - const arrayBuffer = new ArrayBuffer(10); - expect(ArrayBufferFieldType.instance.toArrayBuffer(arrayBuffer)).toBe(arrayBuffer); - }); - - it('`#fromArrayBuffer` should return the same `ArrayBuffer`', () => { - const arrayBuffer = new ArrayBuffer(10); - expect(ArrayBufferFieldType.instance.fromArrayBuffer(arrayBuffer)).toBe(arrayBuffer); - }); - - it('`#getSize` should return the `byteLength` of the `ArrayBuffer`', () => { - const arrayBuffer = new ArrayBuffer(10); - expect(ArrayBufferFieldType.instance.getSize(arrayBuffer)).toBe(10); - }); - }); - - describe('Uint8ClampedArrayFieldType', () => { - it('should have a static instance', () => { - expect(ArrayBufferViewFieldType.instance).toBeInstanceOf(ArrayBufferViewFieldType); - }); - - it('`#toArrayBuffer` should return its `buffer`', () => { - const array = new Uint8ClampedArray(10); - const buffer = array.buffer; - expect(ArrayBufferViewFieldType.instance.toArrayBuffer(array)).toBe(buffer); - }); - - it('`#fromArrayBuffer` should return a view of the `ArrayBuffer`', () => { - const arrayBuffer = new ArrayBuffer(10); - const array = ArrayBufferViewFieldType.instance.fromArrayBuffer(arrayBuffer); - expect(array).toHaveProperty('buffer', arrayBuffer); - expect(array).toHaveProperty('byteOffset', 0); - expect(array).toHaveProperty('byteLength', 10); - }); - - it('`#getSize` should return the `byteLength` of the `Uint8ClampedArray`', () => { - const array = new Uint8ClampedArray(10); - expect(ArrayBufferViewFieldType.instance.getSize(array)).toBe(10); - }); - }); - - describe('StringFieldType', () => { - it('should have a static instance', () => { - expect(StringFieldType.instance).toBeInstanceOf(StringFieldType); - }); - - it('`#toArrayBuffer` should return the decoded string', () => { - const text = 'foo'; - const arrayBuffer = Buffer.from(text, 'utf-8'); - expect(StringFieldType.instance.toArrayBuffer(text)).toEqual(arrayBuffer); - }); - - it('`#fromArrayBuffer` should return the encoded ArrayBuffer', () => { - const text = 'foo'; - const arrayBuffer = Buffer.from(text, 'utf-8'); - expect(StringFieldType.instance.fromArrayBuffer(arrayBuffer)).toBe(text); - }); - - it('`#getSize` should return -1', () => { - expect(StringFieldType.instance.getSize()).toBe(-1); - }); - }); - - class MockArrayBufferFieldDefinition - extends ArrayBufferLikeFieldDefinition { - public getSize(): number { - return this.options; - } - } - - describe('ArrayBufferLikeFieldDefinition', () => { - it('should work with `ArrayBufferFieldType`', async () => { - const size = 10; - const definition = new MockArrayBufferFieldDefinition(ArrayBufferFieldType.instance, size); - - const context = new MockDeserializationStream(); - const buffer = new ArrayBuffer(size); - context.buffer = buffer; - const struct = new StructValue(); - - const fieldValue = await definition.deserialize(StructDefaultOptions, context, struct); - expect(context.read).toBeCalledTimes(1); - expect(context.read).toBeCalledWith(size); - expect(fieldValue).toHaveProperty('arrayBuffer', buffer); - - expect(fieldValue.get()).toBe(buffer); - }); - - it('should work with `Uint8ClampedArrayFieldType`', async () => { - const size = 10; - const definition = new MockArrayBufferFieldDefinition(ArrayBufferViewFieldType.instance, size); - - const context = new MockDeserializationStream(); - const buffer = new ArrayBuffer(size); - context.buffer = buffer; - const struct = new StructValue(); - - const fieldValue = await definition.deserialize(StructDefaultOptions, context, struct); - expect(context.read).toBeCalledTimes(1); - expect(context.read).toBeCalledWith(size); - expect(fieldValue).toHaveProperty('arrayBuffer', buffer); - - const value = fieldValue.get(); - expect(value).toBeInstanceOf(Uint8ClampedArray); - expect(value).toHaveProperty('buffer', buffer); - }); - - it('should work when `#getSize` returns `0`', async () => { - const size = 0; - const definition = new MockArrayBufferFieldDefinition(ArrayBufferFieldType.instance, size); - - const context = new MockDeserializationStream(); - const buffer = new ArrayBuffer(size); - context.buffer = buffer; - const struct = new StructValue(); - - const fieldValue = await definition.deserialize(StructDefaultOptions, context, struct); - expect(context.read).toBeCalledTimes(0); - expect(fieldValue['arrayBuffer']).toBeInstanceOf(ArrayBuffer); - expect(fieldValue['arrayBuffer']).toHaveProperty('byteLength', 0); - - const value = fieldValue.get(); - expect(value).toBeInstanceOf(ArrayBuffer); - expect(value).toHaveProperty('byteLength', 0); - }); - }); - - describe('ArrayBufferLikeFieldValue', () => { - describe('#set', () => { - it('should clear `arrayBuffer` field', async () => { - const size = 0; - const definition = new MockArrayBufferFieldDefinition(ArrayBufferFieldType.instance, size); - - const context = new MockDeserializationStream(); - const buffer = new ArrayBuffer(size); - context.buffer = buffer; - const struct = new StructValue(); - - const fieldValue = await definition.deserialize(StructDefaultOptions, context, struct); - - const newValue = new ArrayBuffer(20); - fieldValue.set(newValue); - expect(fieldValue.get()).toBe(newValue); - expect(fieldValue).toHaveProperty('arrayBuffer', undefined); - }); - }); - - describe('#serialize', () => { - it('should be able to serialize with cached `arrayBuffer`', async () => { - const size = 0; - const definition = new MockArrayBufferFieldDefinition(ArrayBufferFieldType.instance, size); - - const context = new MockDeserializationStream(); - const sourceArray = new Uint8Array(Array.from({ length: size }, (_, i) => i)); - const buffer = sourceArray.buffer; - context.buffer = buffer; - const struct = new StructValue(); - - const fieldValue = await definition.deserialize(StructDefaultOptions, context, struct); - - const targetArray = new Uint8Array(size); - const targetView = new DataView(targetArray.buffer); - fieldValue.serialize(targetView, 0); - - expect(targetArray).toEqual(sourceArray); - }); - - it('should be able to serialize a modified value', async () => { - const size = 0; - const definition = new MockArrayBufferFieldDefinition(ArrayBufferFieldType.instance, size); - - const context = new MockDeserializationStream(); - const sourceArray = new Uint8Array(Array.from({ length: size }, (_, i) => i)); - const buffer = sourceArray.buffer; - context.buffer = buffer; - const struct = new StructValue(); - - const fieldValue = await definition.deserialize(StructDefaultOptions, context, struct); - - fieldValue.set(sourceArray.buffer); - - const targetArray = new Uint8Array(size); - const targetView = new DataView(targetArray.buffer); - fieldValue.serialize(targetView, 0); - - expect(targetArray).toEqual(sourceArray); - }); - }); - }); - }); -}); diff --git a/libraries/struct/src/types/array-buffer.ts b/libraries/struct/src/types/array-buffer.ts deleted file mode 100644 index 8a24040b..00000000 --- a/libraries/struct/src/types/array-buffer.ts +++ /dev/null @@ -1,202 +0,0 @@ -// cspell: ignore syncbird - -import { StructAsyncDeserializeStream, StructDeserializeStream, StructFieldDefinition, StructFieldValue, StructOptions, StructValue } from '../basic'; -import { Syncbird } from "../syncbird"; -import { decodeUtf8, encodeUtf8, ValueOrPromise } from "../utils"; - -/** - * Base class for all types that - * can be converted from an ArrayBuffer when deserialized, - * and need to be converted to an ArrayBuffer when serializing - * - * @template TValue The actual TypeScript type of this type - * @template TTypeScriptType Optional another type (should be compatible with `TType`) - * specified by user when creating field definitions. - */ -export abstract class ArrayBufferLikeFieldType { - public readonly TTypeScriptType!: TTypeScriptType; - - /** - * When implemented in derived classes, converts the type-specific `value` to an `ArrayBuffer` - * - * This function should be "pure", i.e., - * same `value` should always be converted to `ArrayBuffer`s that have same content. - */ - public abstract toArrayBuffer(value: TValue): ArrayBuffer; - - /** When implemented in derived classes, converts the `ArrayBuffer` to a type-specific value */ - public abstract fromArrayBuffer(arrayBuffer: ArrayBuffer): TValue; - - /** - * When implemented in derived classes, gets the size in byte of the type-specific `value`. - * - * If the size can't be calculated without first converting the `value` back to an `ArrayBuffer`, - * implementer should returns `-1` so the caller will get its size by first converting it to - * an `ArrayBuffer` (and cache the result). - */ - public abstract getSize(value: TValue): number; -} - -/** An ArrayBufferLike type that's actually an `ArrayBuffer` */ -export class ArrayBufferFieldType extends ArrayBufferLikeFieldType { - public static readonly instance = new ArrayBufferFieldType(); - - protected constructor() { - super(); - } - - public toArrayBuffer(value: ArrayBuffer): ArrayBuffer { - return value; - } - - public fromArrayBuffer(arrayBuffer: ArrayBuffer): ArrayBuffer { - return arrayBuffer; - } - - public getSize(value: ArrayBuffer): number { - return value.byteLength; - } -} - -export type ArrayBufferViewConstructor = new (array: ArrayLike | ArrayBufferLike) => T; - -/** Am ArrayBufferLike type that converts between `ArrayBuffer` and `Uint8ClampedArray` */ -export class ArrayBufferViewFieldType - extends ArrayBufferLikeFieldType { - public static readonly uint8Array = new ArrayBufferViewFieldType(Uint8Array); - public static readonly uint8ClampedArray = new ArrayBufferViewFieldType(Uint8ClampedArray); - - protected type: ArrayBufferViewConstructor; - - public constructor(type: ArrayBufferViewConstructor) { - super(); - this.type = type; - } - - public toArrayBuffer(value: T): ArrayBuffer { - return value.buffer; - } - - public fromArrayBuffer(arrayBuffer: ArrayBuffer): T { - return new this.type(arrayBuffer); - } - - public getSize(value: T): number { - return value.byteLength; - } -} - -/** An ArrayBufferLike type that converts between `ArrayBuffer` and `string` */ -export class StringFieldType - extends ArrayBufferLikeFieldType { - public static readonly instance = new StringFieldType(); - - public toArrayBuffer(value: string): ArrayBuffer { - return encodeUtf8(value); - } - - public fromArrayBuffer(arrayBuffer: ArrayBuffer): string { - return decodeUtf8(arrayBuffer); - } - - public getSize(): number { - // Return `-1`, so `ArrayBufferLikeFieldDefinition` will - // convert this `value` into an `ArrayBuffer` (and cache the result), - // Then get the size from that `ArrayBuffer` - return -1; - } -} - -const EmptyArrayBuffer = new ArrayBuffer(0); - -export abstract class ArrayBufferLikeFieldDefinition< - TType extends ArrayBufferLikeFieldType = ArrayBufferLikeFieldType, - TOptions = void, - TOmitInitKey extends PropertyKey = never, - > extends StructFieldDefinition< - TOptions, - TType['TTypeScriptType'], - TOmitInitKey - >{ - public readonly type: TType; - - public constructor(type: TType, options: TOptions) { - super(options); - this.type = type; - } - - protected getDeserializeSize(struct: StructValue): number { - return this.getSize(); - } - - /** - * When implemented in derived classes, creates a `StructFieldValue` for the current field definition. - */ - public create( - options: Readonly, - struct: StructValue, - value: TType['TTypeScriptType'], - arrayBuffer?: ArrayBuffer, - ): ArrayBufferLikeFieldValue { - return new ArrayBufferLikeFieldValue(this, options, struct, value, arrayBuffer); - } - - public override deserialize( - options: Readonly, - stream: StructDeserializeStream, - struct: StructValue, - ): ArrayBufferLikeFieldValue; - public override deserialize( - options: Readonly, - stream: StructAsyncDeserializeStream, - struct: StructValue, - ): Promise>; - public override deserialize( - options: Readonly, - stream: StructDeserializeStream | StructAsyncDeserializeStream, - struct: StructValue, - ): ValueOrPromise> { - return Syncbird.try(() => { - const size = this.getDeserializeSize(struct); - if (size === 0) { - return EmptyArrayBuffer; - } else { - return stream.read(size); - } - }).then(arrayBuffer => { - const value = this.type.fromArrayBuffer(arrayBuffer); - return this.create(options, struct, value, arrayBuffer); - }).valueOrPromise(); - } -} - -export class ArrayBufferLikeFieldValue< - TDefinition extends ArrayBufferLikeFieldDefinition, any, any>, - > extends StructFieldValue { - protected arrayBuffer: ArrayBuffer | undefined; - - public constructor( - definition: TDefinition, - options: Readonly, - struct: StructValue, - value: TDefinition['TValue'], - arrayBuffer?: ArrayBuffer, - ) { - super(definition, options, struct, value); - this.arrayBuffer = arrayBuffer; - } - - public override set(value: TDefinition['TValue']): void { - super.set(value); - this.arrayBuffer = undefined; - } - - public serialize(dataView: DataView, offset: number): void { - if (!this.arrayBuffer) { - this.arrayBuffer = this.definition.type.toArrayBuffer(this.value); - } - - new Uint8Array(dataView.buffer) - .set(new Uint8Array(this.arrayBuffer), offset); - } -} diff --git a/libraries/struct/src/types/buffer/base.spec.ts b/libraries/struct/src/types/buffer/base.spec.ts new file mode 100644 index 00000000..4539c206 --- /dev/null +++ b/libraries/struct/src/types/buffer/base.spec.ts @@ -0,0 +1,163 @@ +import { StructDefaultOptions, StructDeserializeStream, StructValue } from '../../basic'; +import { BufferFieldSubType, BufferLikeFieldDefinition, StringBufferFieldSubType, Uint8ArrayBufferFieldSubType } from './base'; + +class MockDeserializationStream implements StructDeserializeStream { + public array = new Uint8Array(0); + + public read = jest.fn((length: number) => this.array); +} + +describe('Types', () => { + describe('Buffer', () => { + describe('Uint8ArrayBufferFieldSubType', () => { + it('should have a static instance', () => { + expect(Uint8ArrayBufferFieldSubType.Instance).toBeInstanceOf(Uint8ArrayBufferFieldSubType); + }); + + it('`#toBuffer` should return the same `Uint8Array`', () => { + const array = new Uint8Array(10); + expect(Uint8ArrayBufferFieldSubType.Instance.toBuffer(array)).toBe(array); + }); + + it('`#fromBuffer` should return the same `Uint8Array`', () => { + const buffer = new Uint8Array(10); + expect(Uint8ArrayBufferFieldSubType.Instance.toValue(buffer)).toBe(buffer); + }); + + it('`#getSize` should return the `byteLength` of the `Uint8Array`', () => { + const arrayBuffer = new Uint8Array(10); + expect(Uint8ArrayBufferFieldSubType.Instance.getSize(arrayBuffer)).toBe(10); + }); + }); + + describe('StringBufferFieldSubType', () => { + it('should have a static instance', () => { + expect(StringBufferFieldSubType.Instance).toBeInstanceOf(StringBufferFieldSubType); + }); + + it('`#toBuffer` should return the decoded string', () => { + const text = 'foo'; + const array = new Uint8Array(Buffer.from(text, 'utf-8')); + expect(StringBufferFieldSubType.Instance.toBuffer(text)).toEqual(array); + }); + + it('`#fromBuffer` should return the encoded ArrayBuffer', () => { + const text = 'foo'; + const array = new Uint8Array(Buffer.from(text, 'utf-8')); + expect(StringBufferFieldSubType.Instance.toValue(array)).toBe(text); + }); + + it('`#getSize` should return -1', () => { + expect(StringBufferFieldSubType.Instance.getSize()).toBe(-1); + }); + }); + + class MockArrayBufferFieldDefinition + extends BufferLikeFieldDefinition { + public getSize(): number { + return this.options; + } + } + + describe('BufferLikeFieldDefinition', () => { + it('should work with `Uint8ArrayBufferFieldSubType`', async () => { + const size = 10; + const definition = new MockArrayBufferFieldDefinition(Uint8ArrayBufferFieldSubType.Instance, size); + + const context = new MockDeserializationStream(); + const array = new Uint8Array(size); + context.array = array; + const struct = new StructValue(); + + const fieldValue = await definition.deserialize(StructDefaultOptions, context, struct); + expect(context.read).toBeCalledTimes(1); + expect(context.read).toBeCalledWith(size); + expect(fieldValue).toHaveProperty('arrayBuffer', array); + + expect(fieldValue.get()).toBe(array); + }); + + it('should work when `#getSize` returns `0`', async () => { + const size = 0; + const definition = new MockArrayBufferFieldDefinition(Uint8ArrayBufferFieldSubType.Instance, size); + + const context = new MockDeserializationStream(); + const buffer = new Uint8Array(size); + context.array = buffer; + const struct = new StructValue(); + + const fieldValue = await definition.deserialize(StructDefaultOptions, context, struct); + expect(context.read).toBeCalledTimes(0); + expect(fieldValue['array']).toBeInstanceOf(Uint8Array); + expect(fieldValue['array']).toHaveProperty('byteLength', 0); + + const value = fieldValue.get(); + expect(value).toBeInstanceOf(Uint8Array); + expect(value).toHaveProperty('byteLength', 0); + }); + }); + + describe('ArrayBufferLikeFieldValue', () => { + describe('#set', () => { + it('should clear `arrayBuffer` field', async () => { + const size = 0; + const definition = new MockArrayBufferFieldDefinition(Uint8ArrayBufferFieldSubType.Instance, size); + + const context = new MockDeserializationStream(); + const array = new Uint8Array(size); + context.array = array; + const struct = new StructValue(); + + const fieldValue = await definition.deserialize(StructDefaultOptions, context, struct); + + const newValue = new Uint8Array(20); + fieldValue.set(newValue); + expect(fieldValue.get()).toBe(newValue); + expect(fieldValue).toHaveProperty('arrayBuffer', undefined); + }); + }); + + describe('#serialize', () => { + it('should be able to serialize with cached `arrayBuffer`', async () => { + const size = 0; + const definition = new MockArrayBufferFieldDefinition(Uint8ArrayBufferFieldSubType.Instance, size); + + const context = new MockDeserializationStream(); + const sourceArray = new Uint8Array(Array.from({ length: size }, (_, i) => i)); + const array = sourceArray; + context.array = array; + const struct = new StructValue(); + + const fieldValue = await definition.deserialize(StructDefaultOptions, context, struct); + + const targetArray = new Uint8Array(size); + const targetView = new DataView(targetArray.buffer); + fieldValue.serialize(targetView, 0); + + expect(targetArray).toEqual(sourceArray); + }); + + it('should be able to serialize a modified value', async () => { + const size = 0; + const definition = new MockArrayBufferFieldDefinition(Uint8ArrayBufferFieldSubType.Instance, size); + + const context = new MockDeserializationStream(); + const sourceArray = new Uint8Array(Array.from({ length: size }, (_, i) => i)); + const array = sourceArray; + context.array = array; + const struct = new StructValue(); + + const fieldValue = await definition.deserialize(StructDefaultOptions, context, struct); + + fieldValue.set(sourceArray); + + const targetArray = new Uint8Array(size); + const targetView = new DataView(targetArray.buffer); + fieldValue.serialize(targetView, 0); + + expect(targetArray).toEqual(sourceArray); + }); + }); + }); + }); +}); diff --git a/libraries/struct/src/types/buffer/base.ts b/libraries/struct/src/types/buffer/base.ts index 80a00fcb..4adce2bb 100644 --- a/libraries/struct/src/types/buffer/base.ts +++ b/libraries/struct/src/types/buffer/base.ts @@ -6,8 +6,8 @@ import { decodeUtf8, encodeUtf8, ValueOrPromise } from "../../utils"; /** * Base class for all types that - * can be converted from an ArrayBuffer when deserialized, - * and need to be converted to an ArrayBuffer when serializing + * can be converted from an `Uint8Array` when deserialized, + * and need to be converted to an `Uint8Array` when serializing * * @template TValue The actual TypeScript type of this type * @template TTypeScriptType Optional another type (should be compatible with `TType`) @@ -17,27 +17,27 @@ export abstract class BufferFieldSubType { public static readonly Instance = new Uint8ArrayBufferFieldSubType(); @@ -49,7 +49,7 @@ export class Uint8ArrayBufferFieldSubType extends BufferFieldSubType return value; } - public fromBuffer(buffer: Uint8Array): Uint8Array { + public toValue(buffer: Uint8Array): Uint8Array { return buffer; } @@ -58,7 +58,7 @@ export class Uint8ArrayBufferFieldSubType extends BufferFieldSubType } } -/** An ArrayBufferLike type that converts between `ArrayBuffer` and `string` */ +/** An `BufferFieldSubType` that converts between `Uint8Array` and `string` */ export class StringBufferFieldSubType extends BufferFieldSubType { public static readonly Instance = new StringBufferFieldSubType(); @@ -67,21 +67,21 @@ export class StringBufferFieldSubType return encodeUtf8(value); } - public fromBuffer(array: Uint8Array): string { + public toValue(array: Uint8Array): string { return decodeUtf8(array); } public getSize(): number { - // Return `-1`, so `ArrayBufferLikeFieldDefinition` will - // convert this `value` into an `ArrayBuffer` (and cache the result), - // Then get the size from that `ArrayBuffer` + // Return `-1`, so `BufferLikeFieldDefinition` will + // convert this `value` into an `Uint8Array` (and cache the result), + // Then get the size from that `Uint8Array` return -1; } } -const EmptyArrayBuffer = new ArrayBuffer(0); +const EmptyBuffer = new Uint8Array(0); -export abstract class ArrayBufferLikeFieldDefinition< +export abstract class BufferLikeFieldDefinition< TType extends BufferFieldSubType = BufferFieldSubType, TOptions = void, TOmitInitKey extends PropertyKey = never, @@ -108,67 +108,67 @@ export abstract class ArrayBufferLikeFieldDefinition< options: Readonly, struct: StructValue, value: TType['TTypeScriptType'], - arrayBuffer?: ArrayBuffer, - ): ArrayBufferLikeFieldValue { - return new ArrayBufferLikeFieldValue(this, options, struct, value, arrayBuffer); + array?: Uint8Array, + ): BufferLikeFieldValue { + return new BufferLikeFieldValue(this, options, struct, value, array); } public override deserialize( options: Readonly, stream: StructDeserializeStream, struct: StructValue, - ): ArrayBufferLikeFieldValue; + ): BufferLikeFieldValue; public override deserialize( options: Readonly, stream: StructAsyncDeserializeStream, struct: StructValue, - ): Promise>; + ): Promise>; public override deserialize( options: Readonly, stream: StructDeserializeStream | StructAsyncDeserializeStream, struct: StructValue, - ): ValueOrPromise> { + ): ValueOrPromise> { return Syncbird.try(() => { const size = this.getDeserializeSize(struct); if (size === 0) { - return EmptyArrayBuffer; + return EmptyBuffer; } else { return stream.read(size); } - }).then(arrayBuffer => { - const value = this.type.fromBuffer(arrayBuffer); - return this.create(options, struct, value, arrayBuffer); + }).then(array => { + const value = this.type.toValue(array); + return this.create(options, struct, value, array); }).valueOrPromise(); } } -export class ArrayBufferLikeFieldValue< - TDefinition extends ArrayBufferLikeFieldDefinition, any, any>, +export class BufferLikeFieldValue< + TDefinition extends BufferLikeFieldDefinition, any, any>, > extends StructFieldValue { - protected arrayBuffer: ArrayBuffer | undefined; + protected array: Uint8Array | undefined; public constructor( definition: TDefinition, options: Readonly, struct: StructValue, value: TDefinition['TValue'], - arrayBuffer?: ArrayBuffer, + array?: Uint8Array, ) { super(definition, options, struct, value); - this.arrayBuffer = arrayBuffer; + this.array = array; } public override set(value: TDefinition['TValue']): void { super.set(value); - this.arrayBuffer = undefined; + this.array = undefined; } public serialize(dataView: DataView, offset: number): void { - if (!this.arrayBuffer) { - this.arrayBuffer = this.definition.type.toBuffer(this.value); + if (!this.array) { + this.array = this.definition.type.toBuffer(this.value); } - new Uint8Array(dataView.buffer) - .set(new Uint8Array(this.arrayBuffer), offset); + new Uint8Array(dataView.buffer, dataView.byteOffset, dataView.byteLength) + .set(this.array, offset); } } diff --git a/libraries/struct/src/types/fixed-length-array-buffer.spec.ts b/libraries/struct/src/types/buffer/fixed-length.spec.ts similarity index 55% rename from libraries/struct/src/types/fixed-length-array-buffer.spec.ts rename to libraries/struct/src/types/buffer/fixed-length.spec.ts index 0b1194e6..c4e9775d 100644 --- a/libraries/struct/src/types/fixed-length-array-buffer.spec.ts +++ b/libraries/struct/src/types/buffer/fixed-length.spec.ts @@ -1,12 +1,12 @@ -import { ArrayBufferFieldType } from './array-buffer'; -import { FixedLengthArrayBufferLikeFieldDefinition } from './fixed-length-array-buffer'; +import { Uint8ArrayBufferFieldSubType } from './base'; +import { FixedLengthBufferLikeFieldDefinition } from './fixed-length'; describe('Types', () => { describe('FixedLengthArrayBufferLikeFieldDefinition', () => { describe('#getSize', () => { it('should return size in its options', () => { - const definition = new FixedLengthArrayBufferLikeFieldDefinition( - ArrayBufferFieldType.instance, + const definition = new FixedLengthBufferLikeFieldDefinition( + Uint8ArrayBufferFieldSubType.Instance, { length: 10 }, ); expect(definition.getSize()).toBe(10); diff --git a/libraries/struct/src/types/buffer/fixed-length.ts b/libraries/struct/src/types/buffer/fixed-length.ts new file mode 100644 index 00000000..87b7a482 --- /dev/null +++ b/libraries/struct/src/types/buffer/fixed-length.ts @@ -0,0 +1,17 @@ +import { BufferFieldSubType, BufferLikeFieldDefinition } from './base'; + +export interface FixedLengthBufferLikeFieldOptions { + length: number; +} + +export class FixedLengthBufferLikeFieldDefinition< + TType extends BufferFieldSubType = BufferFieldSubType, + TOptions extends FixedLengthBufferLikeFieldOptions = FixedLengthBufferLikeFieldOptions, + > extends BufferLikeFieldDefinition< + TType, + TOptions + > { + public getSize(): number { + return this.options.length; + } +}; diff --git a/libraries/struct/src/types/buffer/index.ts b/libraries/struct/src/types/buffer/index.ts new file mode 100644 index 00000000..f0528719 --- /dev/null +++ b/libraries/struct/src/types/buffer/index.ts @@ -0,0 +1,3 @@ +export * from './base'; +export * from './fixed-length'; +export * from './variable-length'; diff --git a/libraries/struct/src/types/variable-length-array-buffer.spec.ts b/libraries/struct/src/types/buffer/variable-length.spec.ts similarity index 84% rename from libraries/struct/src/types/variable-length-array-buffer.spec.ts rename to libraries/struct/src/types/buffer/variable-length.spec.ts index 56db031b..84fae242 100644 --- a/libraries/struct/src/types/variable-length-array-buffer.spec.ts +++ b/libraries/struct/src/types/buffer/variable-length.spec.ts @@ -1,21 +1,21 @@ -import { StructDefaultOptions, StructFieldValue, StructValue } from '../basic'; -import { ArrayBufferFieldType, ArrayBufferLikeFieldType } from './array-buffer'; -import { VariableLengthArrayBufferLikeFieldDefinition, VariableLengthArrayBufferLikeFieldLengthValue, VariableLengthArrayBufferLikeStructFieldValue } from './variable-length-array-buffer'; +import { StructDefaultOptions, StructFieldValue, StructValue } from '../../basic'; +import { BufferFieldSubType, Uint8ArrayBufferFieldSubType } from './base'; +import { VariableLengthBufferLikeFieldDefinition, VariableLengthBufferLikeFieldLengthValue, VariableLengthBufferLikeStructFieldValue } from './variable-length'; class MockOriginalFieldValue extends StructFieldValue { public constructor() { super({} as any, {} as any, {} as any, {}); } - public value: string | number = 0; + public override value: string | number = 0; - public get = jest.fn((): string | number => this.value); + public override get = jest.fn((): string | number => this.value); public size = 0; - public getSize = jest.fn((): number => this.size); + public override getSize = jest.fn((): number => this.size); - public set = jest.fn((value: string | number) => { }); + public override set = jest.fn((value: string | number) => { }); public serialize = jest.fn((dataView: DataView, offset: number): void => { }); } @@ -29,7 +29,7 @@ describe('Types', () => { public size = 0; - public getSize = jest.fn(() => this.size); + public override getSize = jest.fn(() => this.size); public serialize(dataView: DataView, offset: number): void { throw new Error('Method not implemented.'); @@ -40,7 +40,7 @@ describe('Types', () => { it('should return size of its original field value', () => { const mockOriginalFieldValue = new MockOriginalFieldValue(); const mockArrayBufferFieldValue = new MockArrayBufferFieldValue(); - const lengthFieldValue = new VariableLengthArrayBufferLikeFieldLengthValue( + const lengthFieldValue = new VariableLengthBufferLikeFieldLengthValue( mockOriginalFieldValue, mockArrayBufferFieldValue, ); @@ -60,7 +60,7 @@ describe('Types', () => { it('should return size of its `arrayBufferField`', async () => { const mockOriginalFieldValue = new MockOriginalFieldValue(); const mockArrayBufferFieldValue = new MockArrayBufferFieldValue(); - const lengthFieldValue = new VariableLengthArrayBufferLikeFieldLengthValue( + const lengthFieldValue = new VariableLengthBufferLikeFieldLengthValue( mockOriginalFieldValue, mockArrayBufferFieldValue, ); @@ -82,7 +82,7 @@ describe('Types', () => { it('should return size of its `arrayBufferField` as string', async () => { const mockOriginalFieldValue = new MockOriginalFieldValue(); const mockArrayBufferFieldValue = new MockArrayBufferFieldValue(); - const lengthFieldValue = new VariableLengthArrayBufferLikeFieldLengthValue( + const lengthFieldValue = new VariableLengthBufferLikeFieldLengthValue( mockOriginalFieldValue, mockArrayBufferFieldValue, ); @@ -106,7 +106,7 @@ describe('Types', () => { it('should does nothing', async () => { const mockOriginalFieldValue = new MockOriginalFieldValue(); const mockArrayBufferFieldValue = new MockArrayBufferFieldValue(); - const lengthFieldValue = new VariableLengthArrayBufferLikeFieldLengthValue( + const lengthFieldValue = new VariableLengthBufferLikeFieldLengthValue( mockOriginalFieldValue, mockArrayBufferFieldValue, ); @@ -124,7 +124,7 @@ describe('Types', () => { it('should call `serialize` of its `originalField`', async () => { const mockOriginalFieldValue = new MockOriginalFieldValue(); const mockArrayBufferFieldValue = new MockArrayBufferFieldValue(); - const lengthFieldValue = new VariableLengthArrayBufferLikeFieldLengthValue( + const lengthFieldValue = new VariableLengthBufferLikeFieldLengthValue( mockOriginalFieldValue, mockArrayBufferFieldValue, ); @@ -155,7 +155,7 @@ describe('Types', () => { it('should stringify its length if `originalField` is a string', async () => { const mockOriginalFieldValue = new MockOriginalFieldValue(); const mockArrayBufferFieldValue = new MockArrayBufferFieldValue(); - const lengthFieldValue = new VariableLengthArrayBufferLikeFieldLengthValue( + const lengthFieldValue = new VariableLengthBufferLikeFieldLengthValue( mockOriginalFieldValue, mockArrayBufferFieldValue, ); @@ -186,7 +186,7 @@ describe('Types', () => { it('should stringify its length in specified base if `originalField` is a string', async () => { const mockOriginalFieldValue = new MockOriginalFieldValue(); const mockArrayBufferFieldValue = new MockArrayBufferFieldValue(); - const lengthFieldValue = new VariableLengthArrayBufferLikeFieldLengthValue( + const lengthFieldValue = new VariableLengthBufferLikeFieldLengthValue( mockOriginalFieldValue, mockArrayBufferFieldValue, ); @@ -228,14 +228,14 @@ describe('Types', () => { const originalLengthFieldValue = new MockOriginalFieldValue(); struct.set(lengthField, originalLengthFieldValue); - const arrayBufferFieldDefinition = new VariableLengthArrayBufferLikeFieldDefinition( - ArrayBufferFieldType.instance, + const arrayBufferFieldDefinition = new VariableLengthBufferLikeFieldDefinition( + Uint8ArrayBufferFieldSubType.Instance, { lengthField }, ); - const value = new ArrayBuffer(0); + const value = new Uint8Array(0); - const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue( + const arrayBufferFieldValue = new VariableLengthBufferLikeStructFieldValue( arrayBufferFieldDefinition, StructDefaultOptions, struct, @@ -257,14 +257,14 @@ describe('Types', () => { const originalLengthFieldValue = new MockOriginalFieldValue(); struct.set(lengthField, originalLengthFieldValue); - const arrayBufferFieldDefinition = new VariableLengthArrayBufferLikeFieldDefinition( - ArrayBufferFieldType.instance, + const arrayBufferFieldDefinition = new VariableLengthBufferLikeFieldDefinition( + Uint8ArrayBufferFieldSubType.Instance, { lengthField }, ); - const value = new ArrayBuffer(100); + const value = new Uint8Array(100); - const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue( + const arrayBufferFieldValue = new VariableLengthBufferLikeStructFieldValue( arrayBufferFieldDefinition, StructDefaultOptions, struct, @@ -276,7 +276,7 @@ describe('Types', () => { expect(arrayBufferFieldValue).toHaveProperty('options', StructDefaultOptions); expect(arrayBufferFieldValue).toHaveProperty('struct', struct); expect(arrayBufferFieldValue).toHaveProperty('value', value); - expect(arrayBufferFieldValue).toHaveProperty('arrayBuffer', value); + expect(arrayBufferFieldValue).toHaveProperty('array', value); expect(arrayBufferFieldValue).toHaveProperty('length', 100); }); @@ -287,14 +287,14 @@ describe('Types', () => { const originalLengthFieldValue = new MockOriginalFieldValue(); struct.set(lengthField, originalLengthFieldValue); - const arrayBufferFieldDefinition = new VariableLengthArrayBufferLikeFieldDefinition( - ArrayBufferFieldType.instance, + const arrayBufferFieldDefinition = new VariableLengthBufferLikeFieldDefinition( + Uint8ArrayBufferFieldSubType.Instance, { lengthField }, ); - const value = new ArrayBuffer(0); + const value = new Uint8Array(0); - const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue( + const arrayBufferFieldValue = new VariableLengthBufferLikeStructFieldValue( arrayBufferFieldDefinition, StructDefaultOptions, struct, @@ -307,18 +307,18 @@ describe('Types', () => { }); describe('#getSize', () => { - class MockArrayBufferFieldType extends ArrayBufferLikeFieldType { - public toArrayBuffer = jest.fn((value: ArrayBuffer): ArrayBuffer => { + class MockArrayBufferFieldType extends BufferFieldSubType { + public override toBuffer = jest.fn((value: Uint8Array): Uint8Array => { return value; }); - public fromArrayBuffer = jest.fn((arrayBuffer: ArrayBuffer): ArrayBuffer => { + public override toValue = jest.fn((arrayBuffer: Uint8Array): Uint8Array => { return arrayBuffer; }); public size = 0; - public getSize = jest.fn((value: ArrayBuffer): number => { + public override getSize = jest.fn((value: Uint8Array): number => { return this.size; }); } @@ -331,14 +331,14 @@ describe('Types', () => { struct.set(lengthField, originalLengthFieldValue); const arrayBufferFieldType = new MockArrayBufferFieldType(); - const arrayBufferFieldDefinition = new VariableLengthArrayBufferLikeFieldDefinition( + const arrayBufferFieldDefinition = new VariableLengthBufferLikeFieldDefinition( arrayBufferFieldType, { lengthField }, ); - const value = new ArrayBuffer(100); + const value = new Uint8Array(100); - const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue( + const arrayBufferFieldValue = new VariableLengthBufferLikeStructFieldValue( arrayBufferFieldDefinition, StructDefaultOptions, struct, @@ -347,8 +347,8 @@ describe('Types', () => { ); expect(arrayBufferFieldValue.getSize()).toBe(100); - expect(arrayBufferFieldType.fromArrayBuffer).toBeCalledTimes(0); - expect(arrayBufferFieldType.toArrayBuffer).toBeCalledTimes(0); + expect(arrayBufferFieldType.toValue).toBeCalledTimes(0); + expect(arrayBufferFieldType.toBuffer).toBeCalledTimes(0); expect(arrayBufferFieldType.getSize).toBeCalledTimes(0); }); @@ -360,14 +360,14 @@ describe('Types', () => { struct.set(lengthField, originalLengthFieldValue); const arrayBufferFieldType = new MockArrayBufferFieldType(); - const arrayBufferFieldDefinition = new VariableLengthArrayBufferLikeFieldDefinition( + const arrayBufferFieldDefinition = new VariableLengthBufferLikeFieldDefinition( arrayBufferFieldType, { lengthField }, ); - const value = new ArrayBuffer(100); + const value = new Uint8Array(100); - const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue( + const arrayBufferFieldValue = new VariableLengthBufferLikeStructFieldValue( arrayBufferFieldDefinition, StructDefaultOptions, struct, @@ -376,8 +376,8 @@ describe('Types', () => { arrayBufferFieldType.size = 100; expect(arrayBufferFieldValue.getSize()).toBe(100); - expect(arrayBufferFieldType.fromArrayBuffer).toBeCalledTimes(0); - expect(arrayBufferFieldType.toArrayBuffer).toBeCalledTimes(0); + expect(arrayBufferFieldType.toValue).toBeCalledTimes(0); + expect(arrayBufferFieldType.toBuffer).toBeCalledTimes(0); expect(arrayBufferFieldType.getSize).toBeCalledTimes(1); expect(arrayBufferFieldValue).toHaveProperty('arrayBuffer', undefined); expect(arrayBufferFieldValue).toHaveProperty('length', 100); @@ -391,14 +391,14 @@ describe('Types', () => { struct.set(lengthField, originalLengthFieldValue); const arrayBufferFieldType = new MockArrayBufferFieldType(); - const arrayBufferFieldDefinition = new VariableLengthArrayBufferLikeFieldDefinition( + const arrayBufferFieldDefinition = new VariableLengthBufferLikeFieldDefinition( arrayBufferFieldType, { lengthField }, ); - const value = new ArrayBuffer(100); + const value = new Uint8Array(100); - const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue( + const arrayBufferFieldValue = new VariableLengthBufferLikeStructFieldValue( arrayBufferFieldDefinition, StructDefaultOptions, struct, @@ -407,8 +407,8 @@ describe('Types', () => { arrayBufferFieldType.size = -1; expect(arrayBufferFieldValue.getSize()).toBe(100); - expect(arrayBufferFieldType.fromArrayBuffer).toBeCalledTimes(0); - expect(arrayBufferFieldType.toArrayBuffer).toBeCalledTimes(1); + expect(arrayBufferFieldType.toValue).toBeCalledTimes(0); + expect(arrayBufferFieldType.toBuffer).toBeCalledTimes(1); expect(arrayBufferFieldType.getSize).toBeCalledTimes(1); expect(arrayBufferFieldValue).toHaveProperty('arrayBuffer', value); expect(arrayBufferFieldValue).toHaveProperty('length', 100); @@ -423,14 +423,14 @@ describe('Types', () => { const originalLengthFieldValue = new MockOriginalFieldValue(); struct.set(lengthField, originalLengthFieldValue); - const arrayBufferFieldDefinition = new VariableLengthArrayBufferLikeFieldDefinition( - ArrayBufferFieldType.instance, + const arrayBufferFieldDefinition = new VariableLengthBufferLikeFieldDefinition( + Uint8ArrayBufferFieldSubType.Instance, { lengthField }, ); - const value = new ArrayBuffer(100); + const value = new Uint8Array(100); - const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue( + const arrayBufferFieldValue = new VariableLengthBufferLikeStructFieldValue( arrayBufferFieldDefinition, StructDefaultOptions, struct, @@ -451,14 +451,14 @@ describe('Types', () => { const originalLengthFieldValue = new MockOriginalFieldValue(); struct.set(lengthField, originalLengthFieldValue); - const arrayBufferFieldDefinition = new VariableLengthArrayBufferLikeFieldDefinition( - ArrayBufferFieldType.instance, + const arrayBufferFieldDefinition = new VariableLengthBufferLikeFieldDefinition( + Uint8ArrayBufferFieldSubType.Instance, { lengthField }, ); - const value = new ArrayBuffer(100); + const value = new Uint8Array(100); - const arrayBufferFieldValue = new VariableLengthArrayBufferLikeStructFieldValue( + const arrayBufferFieldValue = new VariableLengthBufferLikeStructFieldValue( arrayBufferFieldDefinition, StructDefaultOptions, struct, @@ -476,8 +476,8 @@ describe('Types', () => { describe('VariableLengthArrayBufferLikeFieldDefinition', () => { describe('#getSize', () => { it('should always return `0`', () => { - const definition = new VariableLengthArrayBufferLikeFieldDefinition( - ArrayBufferFieldType.instance, + const definition = new VariableLengthBufferLikeFieldDefinition( + Uint8ArrayBufferFieldSubType.Instance, { lengthField: 'foo' }, ); expect(definition.getSize()).toBe(0); @@ -492,8 +492,8 @@ describe('Types', () => { const originalLengthFieldValue = new MockOriginalFieldValue(); struct.set(lengthField, originalLengthFieldValue); - const definition = new VariableLengthArrayBufferLikeFieldDefinition( - ArrayBufferFieldType.instance, + const definition = new VariableLengthBufferLikeFieldDefinition( + Uint8ArrayBufferFieldSubType.Instance, { lengthField }, ); @@ -514,8 +514,8 @@ describe('Types', () => { const originalLengthFieldValue = new MockOriginalFieldValue(); struct.set(lengthField, originalLengthFieldValue); - const definition = new VariableLengthArrayBufferLikeFieldDefinition( - ArrayBufferFieldType.instance, + const definition = new VariableLengthBufferLikeFieldDefinition( + Uint8ArrayBufferFieldSubType.Instance, { lengthField }, ); @@ -537,8 +537,8 @@ describe('Types', () => { struct.set(lengthField, originalLengthFieldValue); const base = 8; - const definition = new VariableLengthArrayBufferLikeFieldDefinition( - ArrayBufferFieldType.instance, + const definition = new VariableLengthBufferLikeFieldDefinition( + Uint8ArrayBufferFieldSubType.Instance, { lengthField, lengthFieldBase: base }, ); @@ -561,12 +561,12 @@ describe('Types', () => { const originalLengthFieldValue = new MockOriginalFieldValue(); struct.set(lengthField, originalLengthFieldValue); - const definition = new VariableLengthArrayBufferLikeFieldDefinition( - ArrayBufferFieldType.instance, + const definition = new VariableLengthBufferLikeFieldDefinition( + Uint8ArrayBufferFieldSubType.Instance, { lengthField }, ); - const value = new ArrayBuffer(100); + const value = new Uint8Array(100); const arrayBufferFieldValue = definition.create( StructDefaultOptions, struct, @@ -588,12 +588,12 @@ describe('Types', () => { const originalLengthFieldValue = new MockOriginalFieldValue(); struct.set(lengthField, originalLengthFieldValue); - const definition = new VariableLengthArrayBufferLikeFieldDefinition( - ArrayBufferFieldType.instance, + const definition = new VariableLengthBufferLikeFieldDefinition( + Uint8ArrayBufferFieldSubType.Instance, { lengthField }, ); - const value = new ArrayBuffer(100); + const value = new Uint8Array(100); const arrayBufferFieldValue = definition.create( StructDefaultOptions, struct, diff --git a/libraries/struct/src/types/variable-length-array-buffer.ts b/libraries/struct/src/types/buffer/variable-length.ts similarity index 58% rename from libraries/struct/src/types/variable-length-array-buffer.ts rename to libraries/struct/src/types/buffer/variable-length.ts index 690a12e9..f49ecd1e 100644 --- a/libraries/struct/src/types/variable-length-array-buffer.ts +++ b/libraries/struct/src/types/buffer/variable-length.ts @@ -1,10 +1,10 @@ -import { StructFieldDefinition, StructFieldValue, StructOptions, StructValue } from '../basic'; -import type { KeysOfType } from '../utils'; -import { ArrayBufferLikeFieldDefinition, ArrayBufferLikeFieldType, ArrayBufferLikeFieldValue } from './array-buffer'; +import { StructFieldDefinition, StructFieldValue, StructOptions, StructValue } from '../../basic'; +import type { KeysOfType } from '../../utils'; +import { BufferFieldSubType, BufferLikeFieldDefinition, BufferLikeFieldValue } from './base'; export type LengthField = KeysOfType; -export interface VariableLengthArrayBufferLikeFieldOptions< +export interface VariableLengthBufferLikeFieldOptions< TFields = object, TLengthField extends LengthField = any, > { @@ -13,10 +13,10 @@ export interface VariableLengthArrayBufferLikeFieldOptions< lengthFieldBase?: number; } -export class VariableLengthArrayBufferLikeFieldDefinition< - TType extends ArrayBufferLikeFieldType = ArrayBufferLikeFieldType, - TOptions extends VariableLengthArrayBufferLikeFieldOptions = VariableLengthArrayBufferLikeFieldOptions - > extends ArrayBufferLikeFieldDefinition< +export class VariableLengthBufferLikeFieldDefinition< + TType extends BufferFieldSubType = BufferFieldSubType, + TOptions extends VariableLengthBufferLikeFieldOptions = VariableLengthBufferLikeFieldOptions + > extends BufferLikeFieldDefinition< TType, TOptions, TOptions['lengthField'] @@ -37,43 +37,43 @@ export class VariableLengthArrayBufferLikeFieldDefinition< options: Readonly, struct: StructValue, value: TType['TTypeScriptType'], - arrayBuffer?: ArrayBuffer - ): VariableLengthArrayBufferLikeStructFieldValue { - return new VariableLengthArrayBufferLikeStructFieldValue( + array?: Uint8Array + ): VariableLengthBufferLikeStructFieldValue { + return new VariableLengthBufferLikeStructFieldValue( this, options, struct, value, - arrayBuffer, + array, ); } } -export class VariableLengthArrayBufferLikeStructFieldValue< - TDefinition extends VariableLengthArrayBufferLikeFieldDefinition = VariableLengthArrayBufferLikeFieldDefinition, - > extends ArrayBufferLikeFieldValue { +export class VariableLengthBufferLikeStructFieldValue< + TDefinition extends VariableLengthBufferLikeFieldDefinition = VariableLengthBufferLikeFieldDefinition, + > extends BufferLikeFieldValue { protected length: number | undefined; - protected lengthFieldValue: VariableLengthArrayBufferLikeFieldLengthValue; + protected lengthFieldValue: VariableLengthBufferLikeFieldLengthValue; public constructor( definition: TDefinition, options: Readonly, struct: StructValue, value: TDefinition['TValue'], - arrayBuffer?: ArrayBuffer, + array?: Uint8Array, ) { - super(definition, options, struct, value, arrayBuffer); + super(definition, options, struct, value, array); - if (arrayBuffer) { - this.length = arrayBuffer.byteLength; + if (array) { + this.length = array.byteLength; } // Patch the associated length field. const lengthField = this.definition.options.lengthField; const originalValue = struct.get(lengthField); - this.lengthFieldValue = new VariableLengthArrayBufferLikeFieldLengthValue( + this.lengthFieldValue = new VariableLengthBufferLikeFieldLengthValue( originalValue, this, ); @@ -84,8 +84,8 @@ export class VariableLengthArrayBufferLikeStructFieldValue< if (this.length === undefined) { this.length = this.definition.type.getSize(this.value); if (this.length === -1) { - this.arrayBuffer = this.definition.type.toArrayBuffer(this.value); - this.length = this.arrayBuffer.byteLength; + this.array = this.definition.type.toBuffer(this.value); + this.length = this.array.byteLength; } } @@ -94,24 +94,24 @@ export class VariableLengthArrayBufferLikeStructFieldValue< public override set(value: unknown) { super.set(value); - this.arrayBuffer = undefined; + this.array = undefined; this.length = undefined; } } -// Not using `VariableLengthArrayBufferLikeStructFieldValue` directly makes writing tests much easier... -type VariableLengthArrayBufferLikeFieldValueLike = - StructFieldValue>; +// Not using `VariableLengthBufferLikeStructFieldValue` directly makes writing tests much easier... +type VariableLengthBufferLikeFieldValueLike = + StructFieldValue>; -export class VariableLengthArrayBufferLikeFieldLengthValue +export class VariableLengthBufferLikeFieldLengthValue extends StructFieldValue { protected originalField: StructFieldValue; - protected arrayBufferField: VariableLengthArrayBufferLikeFieldValueLike; + protected arrayBufferField: VariableLengthBufferLikeFieldValueLike; public constructor( originalField: StructFieldValue, - arrayBufferField: VariableLengthArrayBufferLikeFieldValueLike, + arrayBufferField: VariableLengthBufferLikeFieldValueLike, ) { super(originalField.definition, originalField.options, originalField.struct, 0); this.originalField = originalField; diff --git a/libraries/struct/src/types/fixed-length-array-buffer.ts b/libraries/struct/src/types/fixed-length-array-buffer.ts deleted file mode 100644 index 488ba4fb..00000000 --- a/libraries/struct/src/types/fixed-length-array-buffer.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { ArrayBufferLikeFieldDefinition, ArrayBufferLikeFieldType } from './array-buffer'; - -export interface FixedLengthArrayBufferLikeFieldOptions { - length: number; -} - -export class FixedLengthArrayBufferLikeFieldDefinition< - TType extends ArrayBufferLikeFieldType = ArrayBufferLikeFieldType, - TOptions extends FixedLengthArrayBufferLikeFieldOptions = FixedLengthArrayBufferLikeFieldOptions, - > extends ArrayBufferLikeFieldDefinition< - TType, - TOptions - > { - public getSize(): number { - return this.options.length; - } -}; diff --git a/libraries/struct/src/types/index.ts b/libraries/struct/src/types/index.ts index af630672..92b40d87 100644 --- a/libraries/struct/src/types/index.ts +++ b/libraries/struct/src/types/index.ts @@ -1,5 +1,3 @@ -export * from './array-buffer'; export * from './bigint'; -export * from './fixed-length-array-buffer'; +export * from './buffer'; export * from './number'; -export * from './variable-length-array-buffer';