Loading libraries/adb-backend-webusb/src/backend.ts +36 −24 Original line number Diff line number Diff line import { AdbPacket, AdbPacketSerializeStream, DuplexStreamFactory, pipeFrom, ReadableStream, type AdbBackend, type AdbPacketCore, type AdbPacketInit, type ReadableWritablePair, type WritableStream } from '@yume-chan/adb'; import type { StructAsyncDeserializeStream } from "@yume-chan/struct"; import { AdbPacketHeader, AdbPacketSerializeStream, DuplexStreamFactory, pipeFrom, ReadableStream, type AdbBackend, type AdbPacketCore, type AdbPacketInit, type ReadableWritablePair, type WritableStream } from '@yume-chan/adb'; import type { StructDeserializeStream } from "@yume-chan/struct"; export const ADB_DEVICE_FILTER: USBDeviceFilter = { classCode: 0xFF, Loading @@ -7,6 +7,23 @@ export const ADB_DEVICE_FILTER: USBDeviceFilter = { protocolCode: 1, }; class Uint8ArrayStructDeserializeStream implements StructDeserializeStream { private buffer: Uint8Array; private offset: number; public constructor(buffer: Uint8Array) { this.buffer = buffer; this.offset = 0; } public read(length: number): Uint8Array { const result = this.buffer.subarray(this.offset, this.offset + length); this.offset += length; return result; } } export class AdbWebUsbBackendStream implements ReadableWritablePair<AdbPacketCore, AdbPacketInit>{ private _readable: ReadableStream<AdbPacketCore>; public get readable() { return this._readable; } Loading Loading @@ -34,30 +51,25 @@ export class AdbWebUsbBackendStream implements ReadableWritablePair<AdbPacketCor navigator.usb.addEventListener('disconnect', handleUsbDisconnect); const incomingStream: StructAsyncDeserializeStream = { async read(length) { // `ReadableStream<Uin8Array>` don't know how many bytes the consumer need in each `pull`, // But `transferIn(endpointNumber, packetSize)` is much slower than `transferIn(endpointNumber, length)` // So `AdbBackend` is refactored to use `ReadableStream<AdbPacketCore>` directly, // (let each backend deserialize the packets in their own way) const result = await device.transferIn(inEndpoint.endpointNumber, length); // TODO: The WebUSB spec requires `transferIn` to return `status: babble` when // the `length` argument is smaller than what the device actually returns. // But Chrome's implementation on Windows never returns `babble` because WinUSB // allows partial reads by default. // When `Struct.deserialize` calls this `read`, the `length` is each fields' length, // instead of packet length. So this only works on Windows. // From spec, the `result.data` always covers the whole `buffer`. return new Uint8Array(result.data!.buffer); } }; this._readable = factory.createWrapReadable(new ReadableStream<AdbPacketCore>({ async pull(controller) { const value = await AdbPacket.deserialize(incomingStream); controller.enqueue(value); // The `length` argument in `transferIn` must not be smaller than what the device sent, // otherwise it will return `babble` status without any data. // But using `inEndpoint.packetSize` as `length` (ensures it can read packets in any size) // leads to poor performance due to unnecessarily large allocations and corresponding GCs. // So we read exactly 24 bytes (packet header) followed by exactly `payloadLength`. const result = await device.transferIn(inEndpoint.endpointNumber, 24); // TODO: webusb-backend: handle `babble` by discarding the data and receive again // From spec, the `result.data` always covers the whole `buffer`. const buffer = new Uint8Array(result.data!.buffer); const stream = new Uint8ArrayStructDeserializeStream(buffer); const packet = AdbPacketHeader.deserialize(stream); if (packet.payloadLength !== 0) { const payload = await device.transferIn(inEndpoint.endpointNumber, packet.payloadLength!); // Use the cast to avoid allocate another object. (packet as unknown as AdbPacketCore).payload = new Uint8Array(payload.data!.buffer); } controller.enqueue(packet as unknown as AdbPacketCore); }, })); Loading libraries/struct/src/syncbird.ts +4 −2 Original line number Diff line number Diff line Loading @@ -96,7 +96,7 @@ Syncbird.config({ const _then = (Syncbird.prototype as any)._then; Syncbird.prototype._then = function <T, TResult1 = T, TResult2 = never>( this: Syncbird<T>, onfulfilled: ((value: T) => unknown) | undefined | null, onfulfilled: ((value: T, internalData?: unknown) => unknown) | undefined | null, onrejected: ((reason: any) => unknown) | undefined | null, _: never, receiver: unknown, Loading @@ -110,7 +110,9 @@ Syncbird.prototype._then = function <T, TResult1 = T, TResult2 = never>( return Syncbird.resolve( onfulfilled.call( receiver, this.value() this.value(), // Some Bluebird internal methods (for example `reduce`) need this `internalData` internalData ) ); } Loading Loading
libraries/adb-backend-webusb/src/backend.ts +36 −24 Original line number Diff line number Diff line import { AdbPacket, AdbPacketSerializeStream, DuplexStreamFactory, pipeFrom, ReadableStream, type AdbBackend, type AdbPacketCore, type AdbPacketInit, type ReadableWritablePair, type WritableStream } from '@yume-chan/adb'; import type { StructAsyncDeserializeStream } from "@yume-chan/struct"; import { AdbPacketHeader, AdbPacketSerializeStream, DuplexStreamFactory, pipeFrom, ReadableStream, type AdbBackend, type AdbPacketCore, type AdbPacketInit, type ReadableWritablePair, type WritableStream } from '@yume-chan/adb'; import type { StructDeserializeStream } from "@yume-chan/struct"; export const ADB_DEVICE_FILTER: USBDeviceFilter = { classCode: 0xFF, Loading @@ -7,6 +7,23 @@ export const ADB_DEVICE_FILTER: USBDeviceFilter = { protocolCode: 1, }; class Uint8ArrayStructDeserializeStream implements StructDeserializeStream { private buffer: Uint8Array; private offset: number; public constructor(buffer: Uint8Array) { this.buffer = buffer; this.offset = 0; } public read(length: number): Uint8Array { const result = this.buffer.subarray(this.offset, this.offset + length); this.offset += length; return result; } } export class AdbWebUsbBackendStream implements ReadableWritablePair<AdbPacketCore, AdbPacketInit>{ private _readable: ReadableStream<AdbPacketCore>; public get readable() { return this._readable; } Loading Loading @@ -34,30 +51,25 @@ export class AdbWebUsbBackendStream implements ReadableWritablePair<AdbPacketCor navigator.usb.addEventListener('disconnect', handleUsbDisconnect); const incomingStream: StructAsyncDeserializeStream = { async read(length) { // `ReadableStream<Uin8Array>` don't know how many bytes the consumer need in each `pull`, // But `transferIn(endpointNumber, packetSize)` is much slower than `transferIn(endpointNumber, length)` // So `AdbBackend` is refactored to use `ReadableStream<AdbPacketCore>` directly, // (let each backend deserialize the packets in their own way) const result = await device.transferIn(inEndpoint.endpointNumber, length); // TODO: The WebUSB spec requires `transferIn` to return `status: babble` when // the `length` argument is smaller than what the device actually returns. // But Chrome's implementation on Windows never returns `babble` because WinUSB // allows partial reads by default. // When `Struct.deserialize` calls this `read`, the `length` is each fields' length, // instead of packet length. So this only works on Windows. // From spec, the `result.data` always covers the whole `buffer`. return new Uint8Array(result.data!.buffer); } }; this._readable = factory.createWrapReadable(new ReadableStream<AdbPacketCore>({ async pull(controller) { const value = await AdbPacket.deserialize(incomingStream); controller.enqueue(value); // The `length` argument in `transferIn` must not be smaller than what the device sent, // otherwise it will return `babble` status without any data. // But using `inEndpoint.packetSize` as `length` (ensures it can read packets in any size) // leads to poor performance due to unnecessarily large allocations and corresponding GCs. // So we read exactly 24 bytes (packet header) followed by exactly `payloadLength`. const result = await device.transferIn(inEndpoint.endpointNumber, 24); // TODO: webusb-backend: handle `babble` by discarding the data and receive again // From spec, the `result.data` always covers the whole `buffer`. const buffer = new Uint8Array(result.data!.buffer); const stream = new Uint8ArrayStructDeserializeStream(buffer); const packet = AdbPacketHeader.deserialize(stream); if (packet.payloadLength !== 0) { const payload = await device.transferIn(inEndpoint.endpointNumber, packet.payloadLength!); // Use the cast to avoid allocate another object. (packet as unknown as AdbPacketCore).payload = new Uint8Array(payload.data!.buffer); } controller.enqueue(packet as unknown as AdbPacketCore); }, })); Loading
libraries/struct/src/syncbird.ts +4 −2 Original line number Diff line number Diff line Loading @@ -96,7 +96,7 @@ Syncbird.config({ const _then = (Syncbird.prototype as any)._then; Syncbird.prototype._then = function <T, TResult1 = T, TResult2 = never>( this: Syncbird<T>, onfulfilled: ((value: T) => unknown) | undefined | null, onfulfilled: ((value: T, internalData?: unknown) => unknown) | undefined | null, onrejected: ((reason: any) => unknown) | undefined | null, _: never, receiver: unknown, Loading @@ -110,7 +110,9 @@ Syncbird.prototype._then = function <T, TResult1 = T, TResult2 = never>( return Syncbird.resolve( onfulfilled.call( receiver, this.value() this.value(), // Some Bluebird internal methods (for example `reduce`) need this `internalData` internalData ) ); } Loading