mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-04 18:29:23 +02:00
feat(adb): start converting to Uint8Array
This commit is contained in:
parent
a7e259c5ad
commit
014145f775
32 changed files with 268 additions and 332 deletions
|
@ -6,7 +6,7 @@ import { AdbFrameBuffer, AdbPower, AdbReverseCommand, AdbSubprocess, AdbSync, Ad
|
||||||
import { AdbFeatures } from './features';
|
import { AdbFeatures } from './features';
|
||||||
import { AdbCommand } from './packet';
|
import { AdbCommand } from './packet';
|
||||||
import { AdbLogger, AdbPacketDispatcher, AdbSocket } from './socket';
|
import { AdbLogger, AdbPacketDispatcher, AdbSocket } from './socket';
|
||||||
import { ReadableStream, WritableStream } from "./stream";
|
import { DecodeUtf8Stream, GatherStringStream, ReadableStream, WritableStream } from "./stream";
|
||||||
import { decodeUtf8 } from "./utils";
|
import { decodeUtf8 } from "./utils";
|
||||||
|
|
||||||
export enum AdbPropKey {
|
export enum AdbPropKey {
|
||||||
|
@ -52,8 +52,8 @@ export class Adb {
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
backend: AdbBackend,
|
backend: AdbBackend,
|
||||||
readable: ReadableStream<ArrayBuffer>,
|
readable: ReadableStream<Uint8Array>,
|
||||||
writable: WritableStream<ArrayBuffer>,
|
writable: WritableStream<Uint8Array>,
|
||||||
logger?: AdbLogger
|
logger?: AdbLogger
|
||||||
) {
|
) {
|
||||||
this._backend = backend;
|
this._backend = backend;
|
||||||
|
@ -225,11 +225,11 @@ export class Adb {
|
||||||
|
|
||||||
public async createSocketAndWait(service: string): Promise<string> {
|
public async createSocketAndWait(service: string): Promise<string> {
|
||||||
const socket = await this.createSocket(service);
|
const socket = await this.createSocket(service);
|
||||||
let result = '';
|
const gatherStream = new GatherStringStream();
|
||||||
for await (const chunk of socket.readable) {
|
await socket.readable
|
||||||
result += decodeUtf8(chunk);
|
.pipeThrough(new DecodeUtf8Stream())
|
||||||
}
|
.pipeTo(gatherStream.writable);
|
||||||
return result;
|
return gatherStream.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async dispose(): Promise<void> {
|
public async dispose(): Promise<void> {
|
||||||
|
|
|
@ -5,15 +5,15 @@ import { calculatePublicKey, calculatePublicKeyLength, sign } from './crypto';
|
||||||
import { AdbCommand, AdbPacket, AdbPacketInit } from './packet';
|
import { AdbCommand, AdbPacket, AdbPacketInit } from './packet';
|
||||||
import { calculateBase64EncodedLength, encodeBase64 } from './utils';
|
import { calculateBase64EncodedLength, encodeBase64 } from './utils';
|
||||||
|
|
||||||
export type AdbKeyIterable = Iterable<ArrayBuffer> | AsyncIterable<ArrayBuffer>;
|
export type AdbKeyIterable = Iterable<Uint8Array> | AsyncIterable<Uint8Array>;
|
||||||
|
|
||||||
export interface AdbCredentialStore {
|
export interface AdbCredentialStore {
|
||||||
/**
|
/**
|
||||||
* Generate and store a RSA private key with modulus length `2048` and public exponent `65537`.
|
* Generate and store a RSA private key with modulus length `2048` and public exponent `65537`.
|
||||||
*
|
*
|
||||||
* The returned `ArrayBuffer` is the private key in PKCS #8 format.
|
* The returned `Uint8Array` is the private key in PKCS #8 format.
|
||||||
*/
|
*/
|
||||||
generateKey(): ValueOrPromise<ArrayBuffer>;
|
generateKey(): ValueOrPromise<Uint8Array>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Synchronously or asynchronously iterate through all stored RSA private keys.
|
* Synchronously or asynchronously iterate through all stored RSA private keys.
|
||||||
|
@ -63,7 +63,7 @@ export const AdbSignatureAuthenticator: AdbAuthenticator = async function* (
|
||||||
command: AdbCommand.Auth,
|
command: AdbCommand.Auth,
|
||||||
arg0: AdbAuthType.Signature,
|
arg0: AdbAuthType.Signature,
|
||||||
arg1: 0,
|
arg1: 0,
|
||||||
payload: signature,
|
payload: new Uint8Array(signature),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -78,12 +78,13 @@ export const AdbPublicKeyAuthenticator: AdbAuthenticator = async function* (
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let privateKey: ArrayBuffer | undefined;
|
let privateKey: Uint8Array | undefined;
|
||||||
for await (const key of credentialStore.iterateKeys()) {
|
for await (const key of credentialStore.iterateKeys()) {
|
||||||
privateKey = key;
|
privateKey = key;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!privateKey) {
|
if (!privateKey) {
|
||||||
privateKey = await credentialStore.generateKey();
|
privateKey = await credentialStore.generateKey();
|
||||||
}
|
}
|
||||||
|
@ -93,7 +94,7 @@ export const AdbPublicKeyAuthenticator: AdbAuthenticator = async function* (
|
||||||
|
|
||||||
// The public key is null terminated,
|
// The public key is null terminated,
|
||||||
// So we allocate the buffer with one extra byte.
|
// So we allocate the buffer with one extra byte.
|
||||||
const publicKeyBuffer = new ArrayBuffer(publicKeyBase64Length + 1);
|
const publicKeyBuffer = new Uint8Array(publicKeyBase64Length + 1);
|
||||||
|
|
||||||
calculatePublicKey(privateKey, publicKeyBuffer);
|
calculatePublicKey(privateKey, publicKeyBuffer);
|
||||||
encodeBase64(publicKeyBuffer, 0, publicKeyLength, publicKeyBuffer);
|
encodeBase64(publicKeyBuffer, 0, publicKeyLength, publicKeyBuffer);
|
||||||
|
|
|
@ -6,5 +6,5 @@ export interface AdbBackend {
|
||||||
|
|
||||||
readonly name: string | undefined;
|
readonly name: string | undefined;
|
||||||
|
|
||||||
connect(): ValueOrPromise<ReadableWritablePair<ArrayBuffer, ArrayBuffer>>;
|
connect(): ValueOrPromise<ReadableWritablePair<Uint8Array, Uint8Array>>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ export const AdbFrameBufferV1 =
|
||||||
.uint32('green_length')
|
.uint32('green_length')
|
||||||
.uint32('alpha_offset')
|
.uint32('alpha_offset')
|
||||||
.uint32('alpha_length')
|
.uint32('alpha_length')
|
||||||
.uint8ClampedArray('data', { lengthField: 'size' });
|
.uint8Array('data', { lengthField: 'size' });
|
||||||
|
|
||||||
export type AdbFrameBufferV1 = typeof AdbFrameBufferV1['TDeserializeResult'];
|
export type AdbFrameBufferV1 = typeof AdbFrameBufferV1['TDeserializeResult'];
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ export const AdbFrameBufferV2 =
|
||||||
.uint32('green_length')
|
.uint32('green_length')
|
||||||
.uint32('alpha_offset')
|
.uint32('alpha_offset')
|
||||||
.uint32('alpha_length')
|
.uint32('alpha_length')
|
||||||
.uint8ClampedArray('data', { lengthField: 'size' });
|
.uint8Array('data', { lengthField: 'size' });
|
||||||
|
|
||||||
export type AdbFrameBufferV2 = typeof AdbFrameBufferV2['TDeserializeResult'];
|
export type AdbFrameBufferV2 = typeof AdbFrameBufferV2['TDeserializeResult'];
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,10 @@ import { AdbSync } from "./sync";
|
||||||
|
|
||||||
export function install(
|
export function install(
|
||||||
adb: Adb,
|
adb: Adb,
|
||||||
): WritableStream<ArrayBuffer> {
|
): WritableStream<Uint8Array> {
|
||||||
const filename = `/data/local/tmp/${Math.random().toString().substring(2)}.apk`;
|
const filename = `/data/local/tmp/${Math.random().toString().substring(2)}.apk`;
|
||||||
|
|
||||||
return new WrapWritableStream<ArrayBuffer, WritableStream<ArrayBuffer>, AdbSync>({
|
return new WrapWritableStream<Uint8Array, WritableStream<Uint8Array>, AdbSync>({
|
||||||
async start() {
|
async start() {
|
||||||
// Upload apk file to tmp folder
|
// Upload apk file to tmp folder
|
||||||
const sync = await adb.sync();
|
const sync = await adb.sync();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { PromiseResolver } from "@yume-chan/async";
|
import { PromiseResolver } from "@yume-chan/async";
|
||||||
import type { Adb } from "../../adb";
|
import type { Adb } from "../../adb";
|
||||||
import type { AdbSocket } from "../../socket";
|
import type { AdbSocket } from "../../socket";
|
||||||
import { ReadableStream, TransformStream } from "../../stream";
|
import { ReadableStream, ReadableStreamDefaultController, WrapReadableStream } from "../../stream";
|
||||||
import type { AdbSubprocessProtocol } from "./types";
|
import type { AdbSubprocessProtocol } from "./types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,26 +24,39 @@ export class AdbNoneSubprocessProtocol implements AdbSubprocessProtocol {
|
||||||
// Legacy shell forwards all data to stdin.
|
// Legacy shell forwards all data to stdin.
|
||||||
public get stdin() { return this.socket.writable; }
|
public get stdin() { return this.socket.writable; }
|
||||||
|
|
||||||
private _stdout: ReadableStream<ArrayBuffer>;
|
private _stdout: ReadableStream<Uint8Array>;
|
||||||
// Legacy shell doesn't support splitting output streams.
|
// Legacy shell doesn't support splitting output streams.
|
||||||
public get stdout() { return this._stdout; }
|
public get stdout() { return this._stdout; }
|
||||||
|
|
||||||
// `stderr` of Legacy shell is always empty.
|
// `stderr` of Legacy shell is always empty.
|
||||||
private _stderr = new TransformStream<ArrayBuffer, ArrayBuffer>();
|
private _stderr: ReadableStream<Uint8Array>;
|
||||||
public get stderr() { return this._stderr.readable; }
|
public get stderr() { return this._stderr; }
|
||||||
|
|
||||||
private _exit = new PromiseResolver<number>();
|
private _exit = new PromiseResolver<number>();
|
||||||
public get exit() { return this._exit.promise; }
|
public get exit() { return this._exit.promise; }
|
||||||
|
|
||||||
public constructor(socket: AdbSocket) {
|
public constructor(socket: AdbSocket) {
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
this._stdout = this.socket.readable
|
|
||||||
.pipeThrough(new TransformStream({
|
let stderrController!: ReadableStreamDefaultController<Uint8Array>;
|
||||||
flush: () => {
|
this._stderr = new ReadableStream<Uint8Array>({
|
||||||
this._stderr.writable.close();
|
start(controller) {
|
||||||
this._exit.resolve(0);
|
stderrController = controller;
|
||||||
},
|
},
|
||||||
}));
|
});
|
||||||
|
|
||||||
|
this._stdout = new WrapReadableStream<Uint8Array, ReadableStream<Uint8Array>, undefined>({
|
||||||
|
async start() {
|
||||||
|
return {
|
||||||
|
readable: socket.readable,
|
||||||
|
state: undefined,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
async close() {
|
||||||
|
// Close `stderr` on exit.
|
||||||
|
stderrController.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public resize() {
|
public resize() {
|
||||||
|
|
|
@ -20,13 +20,13 @@ export enum AdbShellProtocolId {
|
||||||
const AdbShellProtocolPacket = new Struct({ littleEndian: true })
|
const AdbShellProtocolPacket = new Struct({ littleEndian: true })
|
||||||
.uint8('id', placeholder<AdbShellProtocolId>())
|
.uint8('id', placeholder<AdbShellProtocolId>())
|
||||||
.uint32('length')
|
.uint32('length')
|
||||||
.arrayBuffer('data', { lengthField: 'length' });
|
.uint8Array('data', { lengthField: 'length' });
|
||||||
|
|
||||||
type AdbShellProtocolPacketInit = typeof AdbShellProtocolPacket['TInit'];
|
type AdbShellProtocolPacketInit = typeof AdbShellProtocolPacket['TInit'];
|
||||||
|
|
||||||
type AdbShellProtocolPacket = StructValueType<typeof AdbShellProtocolPacket>;
|
type AdbShellProtocolPacket = StructValueType<typeof AdbShellProtocolPacket>;
|
||||||
|
|
||||||
class StdinSerializeStream extends TransformStream<ArrayBuffer, AdbShellProtocolPacketInit>{
|
class StdinSerializeStream extends TransformStream<Uint8Array, AdbShellProtocolPacketInit>{
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
transform(chunk, controller) {
|
transform(chunk, controller) {
|
||||||
|
@ -42,7 +42,7 @@ class StdinSerializeStream extends TransformStream<ArrayBuffer, AdbShellProtocol
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StdoutDeserializeStream extends TransformStream<AdbShellProtocolPacket, ArrayBuffer>{
|
class StdoutDeserializeStream extends TransformStream<AdbShellProtocolPacket, Uint8Array>{
|
||||||
constructor(type: AdbShellProtocolId.Stdout | AdbShellProtocolId.Stderr) {
|
constructor(type: AdbShellProtocolId.Stdout | AdbShellProtocolId.Stderr) {
|
||||||
super({
|
super({
|
||||||
transform(chunk, controller) {
|
transform(chunk, controller) {
|
||||||
|
@ -106,13 +106,13 @@ export class AdbShellSubprocessProtocol implements AdbSubprocessProtocol {
|
||||||
private readonly _socket: AdbSocket;
|
private readonly _socket: AdbSocket;
|
||||||
private _socketWriter: WritableStreamDefaultWriter<AdbShellProtocolPacketInit>;
|
private _socketWriter: WritableStreamDefaultWriter<AdbShellProtocolPacketInit>;
|
||||||
|
|
||||||
private _stdin = new TransformStream<ArrayBuffer, ArrayBuffer>();
|
private _stdin: WritableStream<Uint8Array>;
|
||||||
public get stdin() { return this._stdin.writable; }
|
public get stdin() { return this._stdin; }
|
||||||
|
|
||||||
private _stdout: ReadableStream<ArrayBuffer>;
|
private _stdout: ReadableStream<Uint8Array>;
|
||||||
public get stdout() { return this._stdout; }
|
public get stdout() { return this._stdout; }
|
||||||
|
|
||||||
private _stderr: ReadableStream<ArrayBuffer>;
|
private _stderr: ReadableStream<Uint8Array>;
|
||||||
public get stderr() { return this._stderr; }
|
public get stderr() { return this._stderr; }
|
||||||
|
|
||||||
private readonly _exit = new PromiseResolver<number>();
|
private readonly _exit = new PromiseResolver<number>();
|
||||||
|
@ -125,9 +125,11 @@ export class AdbShellSubprocessProtocol implements AdbSubprocessProtocol {
|
||||||
// cspell: disable-next-line
|
// cspell: disable-next-line
|
||||||
// https://www.plantuml.com/plantuml/png/bL91QiCm4Bpx5SAdv90lb1JISmiw5XzaQKf5PIkiLZIqzEyLSg8ks13gYtOykpFhiOw93N6UGjVDqK7rZsxKqNw0U_NTgVAy4empOy2mm4_olC0VEVEE47GUpnGjKdgXoD76q4GIEpyFhOwP_m28hW0NNzxNUig1_JdW0bA7muFIJDco1daJ_1SAX9bgvoPJPyIkSekhNYctvIGXrCH6tIsPL5fs-s6J5yc9BpWXhKtNdF2LgVYPGM_6GlMwfhWUsIt4lbScANrwlgVVUifPSVi__t44qStnwPvZwobdSmHHlL57p2vFuHS0
|
// https://www.plantuml.com/plantuml/png/bL91QiCm4Bpx5SAdv90lb1JISmiw5XzaQKf5PIkiLZIqzEyLSg8ks13gYtOykpFhiOw93N6UGjVDqK7rZsxKqNw0U_NTgVAy4empOy2mm4_olC0VEVEE47GUpnGjKdgXoD76q4GIEpyFhOwP_m28hW0NNzxNUig1_JdW0bA7muFIJDco1daJ_1SAX9bgvoPJPyIkSekhNYctvIGXrCH6tIsPL5fs-s6J5yc9BpWXhKtNdF2LgVYPGM_6GlMwfhWUsIt4lbScANrwlgVVUifPSVi__t44qStnwPvZwobdSmHHlL57p2vFuHS0
|
||||||
|
|
||||||
|
// TODO: AdbShellSubprocessProtocol: Optimize stream graph
|
||||||
|
|
||||||
const [stdout, stderr] = socket.readable
|
const [stdout, stderr] = socket.readable
|
||||||
.pipeThrough(new StructDeserializeStream(AdbShellProtocolPacket))
|
.pipeThrough(new StructDeserializeStream(AdbShellProtocolPacket))
|
||||||
.pipeThrough(new TransformStream({
|
.pipeThrough(new TransformStream<AdbShellProtocolPacket, AdbShellProtocolPacket>({
|
||||||
transform: (chunk, controller) => {
|
transform: (chunk, controller) => {
|
||||||
if (chunk.id === AdbShellProtocolId.Exit) {
|
if (chunk.id === AdbShellProtocolId.Exit) {
|
||||||
this._exit.resolve(new Uint8Array(chunk.data)[0]!);
|
this._exit.resolve(new Uint8Array(chunk.data)[0]!);
|
||||||
|
@ -151,9 +153,9 @@ export class AdbShellSubprocessProtocol implements AdbSubprocessProtocol {
|
||||||
.pipeThrough(new StructSerializeStream(AdbShellProtocolPacket))
|
.pipeThrough(new StructSerializeStream(AdbShellProtocolPacket))
|
||||||
.pipeTo(socket.writable);
|
.pipeTo(socket.writable);
|
||||||
|
|
||||||
this._stdin.readable
|
const { readable, writable } = new StdinSerializeStream();
|
||||||
.pipeThrough(new StdinSerializeStream())
|
this._stdin = writable;
|
||||||
.pipeTo(multiplexer.createWriteable());
|
readable.pipeTo(multiplexer.createWriteable());
|
||||||
|
|
||||||
this._socketWriter = multiplexer.createWriteable().getWriter();
|
this._socketWriter = multiplexer.createWriteable().getWriter();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,12 @@ export interface AdbSubprocessProtocol {
|
||||||
/**
|
/**
|
||||||
* A WritableStream that writes to the `stdin` pipe.
|
* A WritableStream that writes to the `stdin` pipe.
|
||||||
*/
|
*/
|
||||||
readonly stdin: WritableStream<ArrayBuffer>;
|
readonly stdin: WritableStream<Uint8Array>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `stdout` pipe of the process.
|
* The `stdout` pipe of the process.
|
||||||
*/
|
*/
|
||||||
readonly stdout: ReadableStream<ArrayBuffer>;
|
readonly stdout: ReadableStream<Uint8Array>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `stderr` pipe of the process.
|
* The `stderr` pipe of the process.
|
||||||
|
@ -20,7 +20,7 @@ export interface AdbSubprocessProtocol {
|
||||||
* Note: Some `AdbShell` doesn't separate `stdout` and `stderr`,
|
* Note: Some `AdbShell` doesn't separate `stdout` and `stderr`,
|
||||||
* All output will be sent to `stdout`.
|
* All output will be sent to `stdout`.
|
||||||
*/
|
*/
|
||||||
readonly stderr: ReadableStream<ArrayBuffer>;
|
readonly stderr: ReadableStream<Uint8Array>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A `Promise` that resolves to the exit code of the process.
|
* A `Promise` that resolves to the exit code of the process.
|
||||||
|
|
|
@ -20,7 +20,7 @@ const ResponseTypes = {
|
||||||
|
|
||||||
export async function* adbSyncOpenDir(
|
export async function* adbSyncOpenDir(
|
||||||
stream: AdbBufferedStream,
|
stream: AdbBufferedStream,
|
||||||
writer: WritableStreamDefaultWriter<ArrayBuffer>,
|
writer: WritableStreamDefaultWriter<Uint8Array>,
|
||||||
path: string,
|
path: string,
|
||||||
): AsyncGenerator<AdbSyncEntryResponse, void, void> {
|
): AsyncGenerator<AdbSyncEntryResponse, void, void> {
|
||||||
await adbSyncWriteRequest(writer, AdbSyncRequestId.List, path);
|
await adbSyncWriteRequest(writer, AdbSyncRequestId.List, path);
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { AdbSyncDoneResponse, adbSyncReadResponse, AdbSyncResponseId } from './r
|
||||||
export const AdbSyncDataResponse =
|
export const AdbSyncDataResponse =
|
||||||
new Struct({ littleEndian: true })
|
new Struct({ littleEndian: true })
|
||||||
.uint32('dataLength')
|
.uint32('dataLength')
|
||||||
.arrayBuffer('data', { lengthField: 'dataLength' })
|
.uint8Array('data', { lengthField: 'dataLength' })
|
||||||
.extra({ id: AdbSyncResponseId.Data as const });
|
.extra({ id: AdbSyncResponseId.Data as const });
|
||||||
|
|
||||||
const ResponseTypes = {
|
const ResponseTypes = {
|
||||||
|
@ -16,9 +16,9 @@ const ResponseTypes = {
|
||||||
|
|
||||||
export function adbSyncPull(
|
export function adbSyncPull(
|
||||||
stream: AdbBufferedStream,
|
stream: AdbBufferedStream,
|
||||||
writer: WritableStreamDefaultWriter<ArrayBuffer>,
|
writer: WritableStreamDefaultWriter<Uint8Array>,
|
||||||
path: string,
|
path: string,
|
||||||
): ReadableStream<ArrayBuffer> {
|
): ReadableStream<Uint8Array> {
|
||||||
return new ReadableStream({
|
return new ReadableStream({
|
||||||
async start() {
|
async start() {
|
||||||
await adbSyncWriteRequest(writer, AdbSyncRequestId.Receive, path);
|
await adbSyncWriteRequest(writer, AdbSyncRequestId.Receive, path);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import Struct from '@yume-chan/struct';
|
import Struct from '@yume-chan/struct';
|
||||||
import { AdbBufferedStream, WritableStream, WritableStreamDefaultWriter } from '../../stream';
|
import { AdbBufferedStream, ChunkStream, WritableStream, WritableStreamDefaultWriter } from '../../stream';
|
||||||
import { chunkArrayLike } from '../../utils';
|
|
||||||
import { AdbSyncRequestId, adbSyncWriteRequest } from './request';
|
import { AdbSyncRequestId, adbSyncWriteRequest } from './request';
|
||||||
import { adbSyncReadResponse, AdbSyncResponseId } from './response';
|
import { adbSyncReadResponse, AdbSyncResponseId } from './response';
|
||||||
import { LinuxFileType } from './stat';
|
import { LinuxFileType } from './stat';
|
||||||
|
@ -17,21 +16,20 @@ export const ADB_SYNC_MAX_PACKET_SIZE = 64 * 1024;
|
||||||
|
|
||||||
export function adbSyncPush(
|
export function adbSyncPush(
|
||||||
stream: AdbBufferedStream,
|
stream: AdbBufferedStream,
|
||||||
writer: WritableStreamDefaultWriter<ArrayBuffer>,
|
writer: WritableStreamDefaultWriter<Uint8Array>,
|
||||||
filename: string,
|
filename: string,
|
||||||
mode: number = (LinuxFileType.File << 12) | 0o666,
|
mode: number = (LinuxFileType.File << 12) | 0o666,
|
||||||
mtime: number = (Date.now() / 1000) | 0,
|
mtime: number = (Date.now() / 1000) | 0,
|
||||||
packetSize: number = ADB_SYNC_MAX_PACKET_SIZE,
|
packetSize: number = ADB_SYNC_MAX_PACKET_SIZE,
|
||||||
): WritableStream<ArrayBuffer> {
|
): WritableStream<Uint8Array> {
|
||||||
return new WritableStream({
|
const { readable, writable } = new ChunkStream(packetSize);
|
||||||
|
readable.pipeTo(new WritableStream({
|
||||||
async start() {
|
async start() {
|
||||||
const pathAndMode = `${filename},${mode.toString()}`;
|
const pathAndMode = `${filename},${mode.toString()}`;
|
||||||
await adbSyncWriteRequest(writer, AdbSyncRequestId.Send, pathAndMode);
|
await adbSyncWriteRequest(writer, AdbSyncRequestId.Send, pathAndMode);
|
||||||
},
|
},
|
||||||
async write(chunk) {
|
async write(chunk) {
|
||||||
for (const buffer of chunkArrayLike(chunk, packetSize)) {
|
await adbSyncWriteRequest(writer, AdbSyncRequestId.Data, chunk);
|
||||||
await adbSyncWriteRequest(writer, AdbSyncRequestId.Data, buffer);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
async close() {
|
async close() {
|
||||||
await adbSyncWriteRequest(writer, AdbSyncRequestId.Done, mtime);
|
await adbSyncWriteRequest(writer, AdbSyncRequestId.Done, mtime);
|
||||||
|
@ -40,5 +38,6 @@ export function adbSyncPush(
|
||||||
}, {
|
}, {
|
||||||
highWaterMark: 16 * 1024,
|
highWaterMark: 16 * 1024,
|
||||||
size(chunk) { return chunk.byteLength; }
|
size(chunk) { return chunk.byteLength; }
|
||||||
});
|
}));
|
||||||
|
return writable;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,29 +21,29 @@ export const AdbSyncNumberRequest =
|
||||||
export const AdbSyncDataRequest =
|
export const AdbSyncDataRequest =
|
||||||
new Struct({ littleEndian: true })
|
new Struct({ littleEndian: true })
|
||||||
.fields(AdbSyncNumberRequest)
|
.fields(AdbSyncNumberRequest)
|
||||||
.arrayBuffer('data', { lengthField: 'arg' });
|
.uint8Array('data', { lengthField: 'arg' });
|
||||||
|
|
||||||
export async function adbSyncWriteRequest(
|
export async function adbSyncWriteRequest(
|
||||||
writer: WritableStreamDefaultWriter<ArrayBuffer>,
|
writer: WritableStreamDefaultWriter<Uint8Array>,
|
||||||
id: AdbSyncRequestId | string,
|
id: AdbSyncRequestId | string,
|
||||||
value: number | string | ArrayBuffer
|
value: number | string | Uint8Array
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
let buffer: ArrayBuffer;
|
let buffer: Uint8Array;
|
||||||
if (typeof value === 'number') {
|
if (typeof value === 'number') {
|
||||||
buffer = AdbSyncNumberRequest.serialize({
|
buffer = new Uint8Array(AdbSyncNumberRequest.serialize({
|
||||||
id,
|
id,
|
||||||
arg: value,
|
arg: value,
|
||||||
});
|
}));
|
||||||
} else if (typeof value === 'string') {
|
} else if (typeof value === 'string') {
|
||||||
buffer = AdbSyncDataRequest.serialize({
|
buffer = new Uint8Array(AdbSyncDataRequest.serialize({
|
||||||
id,
|
id,
|
||||||
data: encodeUtf8(value),
|
data: encodeUtf8(value),
|
||||||
});
|
}));
|
||||||
} else {
|
} else {
|
||||||
buffer = AdbSyncDataRequest.serialize({
|
buffer = new Uint8Array(AdbSyncDataRequest.serialize({
|
||||||
id,
|
id,
|
||||||
data: value,
|
data: value,
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
await writer.write(buffer);
|
await writer.write(buffer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,7 +94,7 @@ const Lstat2ResponseType = {
|
||||||
|
|
||||||
export async function adbSyncLstat(
|
export async function adbSyncLstat(
|
||||||
stream: AdbBufferedStream,
|
stream: AdbBufferedStream,
|
||||||
writer: WritableStreamDefaultWriter<ArrayBuffer>,
|
writer: WritableStreamDefaultWriter<Uint8Array>,
|
||||||
path: string,
|
path: string,
|
||||||
v2: boolean,
|
v2: boolean,
|
||||||
): Promise<AdbSyncLstatResponse | AdbSyncStatResponse> {
|
): Promise<AdbSyncLstatResponse | AdbSyncStatResponse> {
|
||||||
|
@ -115,7 +115,7 @@ export async function adbSyncLstat(
|
||||||
|
|
||||||
export async function adbSyncStat(
|
export async function adbSyncStat(
|
||||||
stream: AdbBufferedStream,
|
stream: AdbBufferedStream,
|
||||||
writer: WritableStreamDefaultWriter<ArrayBuffer>,
|
writer: WritableStreamDefaultWriter<Uint8Array>,
|
||||||
path: string,
|
path: string,
|
||||||
): Promise<AdbSyncStatResponse> {
|
): Promise<AdbSyncStatResponse> {
|
||||||
await adbSyncWriteRequest(writer, AdbSyncRequestId.Stat, path);
|
await adbSyncWriteRequest(writer, AdbSyncRequestId.Stat, path);
|
||||||
|
|
|
@ -14,7 +14,7 @@ export class AdbSync extends AutoDisposable {
|
||||||
|
|
||||||
protected stream: AdbBufferedStream;
|
protected stream: AdbBufferedStream;
|
||||||
|
|
||||||
protected writer: WritableStreamDefaultWriter<ArrayBuffer>;
|
protected writer: WritableStreamDefaultWriter<Uint8Array>;
|
||||||
|
|
||||||
protected sendLock = this.addDisposable(new AutoResetEvent());
|
protected sendLock = this.addDisposable(new AutoResetEvent());
|
||||||
|
|
||||||
|
@ -89,8 +89,8 @@ export class AdbSync extends AutoDisposable {
|
||||||
* @param filename The full path of the file on device to read.
|
* @param filename The full path of the file on device to read.
|
||||||
* @returns A `ReadableStream` that reads from the file.
|
* @returns A `ReadableStream` that reads from the file.
|
||||||
*/
|
*/
|
||||||
public read(filename: string): ReadableStream<ArrayBuffer> {
|
public read(filename: string): ReadableStream<Uint8Array> {
|
||||||
return new WrapReadableStream<ArrayBuffer, ReadableStream<ArrayBuffer>, undefined>({
|
return new WrapReadableStream<Uint8Array, ReadableStream<Uint8Array>, undefined>({
|
||||||
start: async () => {
|
start: async () => {
|
||||||
await this.sendLock.wait();
|
await this.sendLock.wait();
|
||||||
return {
|
return {
|
||||||
|
@ -116,7 +116,7 @@ export class AdbSync extends AutoDisposable {
|
||||||
filename: string,
|
filename: string,
|
||||||
mode?: number,
|
mode?: number,
|
||||||
mtime?: number,
|
mtime?: number,
|
||||||
): WritableStream<ArrayBuffer> {
|
): WritableStream<Uint8Array> {
|
||||||
return new WrapWritableStream({
|
return new WrapWritableStream({
|
||||||
start: async () => {
|
start: async () => {
|
||||||
await this.sendLock.wait();
|
await this.sendLock.wait();
|
||||||
|
@ -128,7 +128,7 @@ export class AdbSync extends AutoDisposable {
|
||||||
mode,
|
mode,
|
||||||
mtime,
|
mtime,
|
||||||
),
|
),
|
||||||
state: {},
|
state: undefined,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
close: async () => {
|
close: async () => {
|
||||||
|
|
|
@ -6,15 +6,15 @@ const BigInt2 = BigInt(2);
|
||||||
const BigInt64 = BigInt(64);
|
const BigInt64 = BigInt(64);
|
||||||
|
|
||||||
export function getBig(
|
export function getBig(
|
||||||
buffer: ArrayBuffer,
|
array: Uint8Array,
|
||||||
offset = 0,
|
offset = 0,
|
||||||
length = buffer.byteLength - offset
|
length = array.byteLength - offset
|
||||||
): bigint {
|
): bigint {
|
||||||
const view = new DataView(buffer);
|
const view = new DataView(array.buffer, array.byteOffset, array.byteLength);
|
||||||
|
|
||||||
let result = BigInt0;
|
let result = BigInt0;
|
||||||
|
|
||||||
// Now `length` must be a multiplication of 8
|
// Currently `length` must be a multiplication of 8
|
||||||
// Support for arbitrary length can be easily added
|
// Support for arbitrary length can be easily added
|
||||||
|
|
||||||
for (let i = offset; i < offset + length; i += 8) {
|
for (let i = offset; i < offset + length; i += 8) {
|
||||||
|
@ -40,8 +40,8 @@ export function setBig(buffer: ArrayBuffer, value: bigint, offset: number = 0) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setBigLE(buffer: ArrayBuffer, value: bigint, offset = 0) {
|
export function setBigLE(array: Uint8Array, value: bigint, offset = 0) {
|
||||||
const view = new DataView(buffer);
|
const view = new DataView(array.buffer, array.byteOffset, array.byteLength);
|
||||||
while (value > BigInt0) {
|
while (value > BigInt0) {
|
||||||
setBigUint64(view, offset, value, true);
|
setBigUint64(view, offset, value, true);
|
||||||
offset += 8;
|
offset += 8;
|
||||||
|
@ -75,7 +75,7 @@ const RsaPrivateKeyNLength = 2048 / 8;
|
||||||
const RsaPrivateKeyDOffset = 303;
|
const RsaPrivateKeyDOffset = 303;
|
||||||
const RsaPrivateKeyDLength = 2048 / 8;
|
const RsaPrivateKeyDLength = 2048 / 8;
|
||||||
|
|
||||||
export function parsePrivateKey(key: ArrayBuffer): [n: bigint, d: bigint] {
|
export function parsePrivateKey(key: Uint8Array): [n: bigint, d: bigint] {
|
||||||
let n = getBig(key, RsaPrivateKeyNOffset, RsaPrivateKeyNLength);
|
let n = getBig(key, RsaPrivateKeyNOffset, RsaPrivateKeyNLength);
|
||||||
let d = getBig(key, RsaPrivateKeyDOffset, RsaPrivateKeyDLength);
|
let d = getBig(key, RsaPrivateKeyDOffset, RsaPrivateKeyDLength);
|
||||||
|
|
||||||
|
@ -114,18 +114,18 @@ export function calculatePublicKeyLength() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function calculatePublicKey(
|
export function calculatePublicKey(
|
||||||
privateKey: ArrayBuffer
|
privateKey: Uint8Array
|
||||||
): ArrayBuffer;
|
): Uint8Array;
|
||||||
export function calculatePublicKey(
|
export function calculatePublicKey(
|
||||||
privateKey: ArrayBuffer,
|
privateKey: Uint8Array,
|
||||||
output: ArrayBuffer,
|
output: Uint8Array,
|
||||||
outputOffset?: number
|
outputOffset?: number
|
||||||
): number;
|
): number;
|
||||||
export function calculatePublicKey(
|
export function calculatePublicKey(
|
||||||
privateKey: ArrayBuffer,
|
privateKey: Uint8Array,
|
||||||
output?: ArrayBuffer,
|
output?: Uint8Array,
|
||||||
outputOffset: number = 0
|
outputOffset: number = 0
|
||||||
): ArrayBuffer | number {
|
): Uint8Array | number {
|
||||||
// Android has its own public key generation algorithm
|
// Android has its own public key generation algorithm
|
||||||
// See https://android.googlesource.com/platform/system/core.git/+/91784040db2b9273687f88d8b95f729d4a61ecc2/libcrypto_utils/android_pubkey.cpp#111
|
// See https://android.googlesource.com/platform/system/core.git/+/91784040db2b9273687f88d8b95f729d4a61ecc2/libcrypto_utils/android_pubkey.cpp#111
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ export function calculatePublicKey(
|
||||||
let outputType: 'ArrayBuffer' | 'number';
|
let outputType: 'ArrayBuffer' | 'number';
|
||||||
const outputLength = calculatePublicKeyLength();
|
const outputLength = calculatePublicKeyLength();
|
||||||
if (!output) {
|
if (!output) {
|
||||||
output = new ArrayBuffer(outputLength);
|
output = new Uint8Array(outputLength);
|
||||||
outputType = 'ArrayBuffer';
|
outputType = 'ArrayBuffer';
|
||||||
} else {
|
} else {
|
||||||
if (output.byteLength - outputOffset < outputLength) {
|
if (output.byteLength - outputOffset < outputLength) {
|
||||||
|
@ -159,7 +159,7 @@ export function calculatePublicKey(
|
||||||
outputType = 'number';
|
outputType = 'number';
|
||||||
}
|
}
|
||||||
|
|
||||||
const outputView = new DataView(output);
|
const outputView = new DataView(output.buffer, output.byteOffset, output.byteLength);
|
||||||
|
|
||||||
// modulusLengthInWords
|
// modulusLengthInWords
|
||||||
outputView.setUint32(outputOffset, 2048 / 8 / 4, true);
|
outputView.setUint32(outputOffset, 2048 / 8 / 4, true);
|
||||||
|
@ -227,14 +227,14 @@ export const Asn1Null = 0x05;
|
||||||
export const Asn1Oid = 0x06;
|
export const Asn1Oid = 0x06;
|
||||||
|
|
||||||
// PKCS#1 SHA-1 hash digest info
|
// PKCS#1 SHA-1 hash digest info
|
||||||
export const Sha1DigestInfo = [
|
export const Sha1DigestInfo = new Uint8Array([
|
||||||
Asn1Sequence, 0x0d + Sha1DigestLength,
|
Asn1Sequence, 0x0d + Sha1DigestLength,
|
||||||
Asn1Sequence, 0x09,
|
Asn1Sequence, 0x09,
|
||||||
// SHA-1 (1 3 14 3 2 26)
|
// SHA-1 (1 3 14 3 2 26)
|
||||||
Asn1Oid, 0x05, 1 * 40 + 3, 14, 3, 2, 26,
|
Asn1Oid, 0x05, 1 * 40 + 3, 14, 3, 2, 26,
|
||||||
Asn1Null, 0x00,
|
Asn1Null, 0x00,
|
||||||
Asn1OctetString, Sha1DigestLength
|
Asn1OctetString, Sha1DigestLength
|
||||||
];
|
]);
|
||||||
|
|
||||||
// SubtleCrypto.sign() will hash the given data and sign the hash
|
// SubtleCrypto.sign() will hash the given data and sign the hash
|
||||||
// But we don't need the hashing step
|
// But we don't need the hashing step
|
||||||
|
@ -242,7 +242,7 @@ export const Sha1DigestInfo = [
|
||||||
// encrypt the given data with its private key)
|
// encrypt the given data with its private key)
|
||||||
// However SubtileCrypto.encrypt() doesn't accept 'RSASSA-PKCS1-v1_5' algorithm
|
// However SubtileCrypto.encrypt() doesn't accept 'RSASSA-PKCS1-v1_5' algorithm
|
||||||
// So we need to implement the encryption by ourself
|
// So we need to implement the encryption by ourself
|
||||||
export function sign(privateKey: ArrayBuffer, data: ArrayBuffer): ArrayBuffer {
|
export function sign(privateKey: Uint8Array, data: Uint8Array): ArrayBuffer {
|
||||||
const [n, d] = parsePrivateKey(privateKey);
|
const [n, d] = parsePrivateKey(privateKey);
|
||||||
|
|
||||||
// PKCS#1 padding
|
// PKCS#1 padding
|
||||||
|
@ -255,7 +255,7 @@ export function sign(privateKey: ArrayBuffer, data: ArrayBuffer): ArrayBuffer {
|
||||||
padded[index] = 1;
|
padded[index] = 1;
|
||||||
index += 1;
|
index += 1;
|
||||||
|
|
||||||
const fillLength = padded.length - Sha1DigestInfo.length - data.byteLength - 1;
|
const fillLength = padded.length - Sha1DigestInfo.length - data.length - 1;
|
||||||
while (index < fillLength) {
|
while (index < fillLength) {
|
||||||
padded[index] = 0xff;
|
padded[index] = 0xff;
|
||||||
index += 1;
|
index += 1;
|
||||||
|
@ -264,14 +264,14 @@ export function sign(privateKey: ArrayBuffer, data: ArrayBuffer): ArrayBuffer {
|
||||||
padded[index] = 0;
|
padded[index] = 0;
|
||||||
index += 1;
|
index += 1;
|
||||||
|
|
||||||
padded.set(new Uint8Array(Sha1DigestInfo), index);
|
padded.set(Sha1DigestInfo, index);
|
||||||
index += Sha1DigestInfo.length;
|
index += Sha1DigestInfo.length;
|
||||||
|
|
||||||
padded.set(new Uint8Array(data), index);
|
padded.set(data, index);
|
||||||
|
|
||||||
// Encryption
|
// Encryption
|
||||||
// signature = padded ** d % n
|
// signature = padded ** d % n
|
||||||
let signature = powMod(getBig(padded.buffer), d, n);
|
let signature = powMod(getBig(padded), d, n);
|
||||||
|
|
||||||
// Put into an ArrayBuffer
|
// Put into an ArrayBuffer
|
||||||
const result = new ArrayBuffer(256);
|
const result = new ArrayBuffer(256);
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
declare global {
|
||||||
|
interface ArrayBuffer {
|
||||||
|
// Disallow assigning `Arraybuffer` to `Uint8Array`
|
||||||
|
__brand: never;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export * from './adb';
|
export * from './adb';
|
||||||
export * from './auth';
|
export * from './auth';
|
||||||
export * from './backend';
|
export * from './backend';
|
||||||
|
|
|
@ -22,13 +22,13 @@ const AdbPacketHeader =
|
||||||
export const AdbPacket =
|
export const AdbPacket =
|
||||||
new Struct({ littleEndian: true })
|
new Struct({ littleEndian: true })
|
||||||
.fields(AdbPacketHeader)
|
.fields(AdbPacketHeader)
|
||||||
.arrayBuffer('payload', { lengthField: 'payloadLength' });
|
.uint8Array('payload', { lengthField: 'payloadLength' });
|
||||||
|
|
||||||
export type AdbPacket = typeof AdbPacket['TDeserializeResult'];
|
export type AdbPacket = typeof AdbPacket['TDeserializeResult'];
|
||||||
|
|
||||||
export type AdbPacketInit = Omit<typeof AdbPacket['TInit'], 'checksum' | 'magic'>;
|
export type AdbPacketInit = Omit<typeof AdbPacket['TInit'], 'checksum' | 'magic'>;
|
||||||
|
|
||||||
export class AdbPacketSerializeStream extends TransformStream<AdbPacketInit, ArrayBuffer>{
|
export class AdbPacketSerializeStream extends TransformStream<AdbPacketInit, Uint8Array>{
|
||||||
public calculateChecksum = true;
|
public calculateChecksum = true;
|
||||||
|
|
||||||
public constructor() {
|
public constructor() {
|
||||||
|
@ -49,7 +49,7 @@ export class AdbPacketSerializeStream extends TransformStream<AdbPacketInit, Arr
|
||||||
payloadLength: init.payload.byteLength,
|
payloadLength: init.payload.byteLength,
|
||||||
};
|
};
|
||||||
|
|
||||||
controller.enqueue(AdbPacketHeader.serialize(packet));
|
controller.enqueue(new Uint8Array(AdbPacketHeader.serialize(packet)));
|
||||||
|
|
||||||
if (packet.payloadLength) {
|
if (packet.payloadLength) {
|
||||||
controller.enqueue(packet.payload);
|
controller.enqueue(packet.payload);
|
||||||
|
|
|
@ -34,12 +34,12 @@ export class AdbSocketController implements AdbSocketInfo {
|
||||||
public readonly localCreated!: boolean;
|
public readonly localCreated!: boolean;
|
||||||
public readonly serviceString!: string;
|
public readonly serviceString!: string;
|
||||||
|
|
||||||
private readonly _readablePassthrough: TransformStream<ArrayBuffer, ArrayBuffer>;
|
private readonly _readablePassthrough: TransformStream<Uint8Array, Uint8Array>;
|
||||||
private readonly _readablePassthroughWriter: WritableStreamDefaultWriter<ArrayBuffer>;
|
private readonly _readablePassthroughWriter: WritableStreamDefaultWriter<Uint8Array>;
|
||||||
public get readable() { return this._readablePassthrough.readable; }
|
public get readable() { return this._readablePassthrough.readable; }
|
||||||
|
|
||||||
private _writePromise: PromiseResolver<void> | undefined;
|
private _writePromise: PromiseResolver<void> | undefined;
|
||||||
public readonly writable: WritableStream<ArrayBuffer>;
|
public readonly writable: WritableStream<Uint8Array>;
|
||||||
|
|
||||||
private _writableClosed = false;
|
private _writableClosed = false;
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ export class AdbSocketController implements AdbSocketInfo {
|
||||||
|
|
||||||
const { readable, writable } = new ChunkStream(this.dispatcher.maxPayloadSize);
|
const { readable, writable } = new ChunkStream(this.dispatcher.maxPayloadSize);
|
||||||
this.writable = writable;
|
this.writable = writable;
|
||||||
readable.pipeTo(new WritableStream<ArrayBuffer>({
|
readable.pipeTo(new WritableStream<Uint8Array>({
|
||||||
write: async (chunk) => {
|
write: async (chunk) => {
|
||||||
if (this._writableClosed) {
|
if (this._writableClosed) {
|
||||||
throw new Error('Socket closed');
|
throw new Error('Socket closed');
|
||||||
|
@ -87,7 +87,7 @@ export class AdbSocketController implements AdbSocketInfo {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
public enqueue(packet: ArrayBuffer) {
|
public enqueue(packet: Uint8Array) {
|
||||||
return this._readablePassthroughWriter.write(packet);
|
return this._readablePassthroughWriter.write(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ export interface AdbIncomingSocketEventArgs {
|
||||||
socket: AdbSocket;
|
socket: AdbSocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EmptyArrayBuffer = new ArrayBuffer(0);
|
const EmptyUint8Array = new Uint8Array(0);
|
||||||
|
|
||||||
export class AdbPacketDispatcher extends AutoDisposable {
|
export class AdbPacketDispatcher extends AutoDisposable {
|
||||||
// ADB socket id starts from 1
|
// ADB socket id starts from 1
|
||||||
|
@ -51,7 +51,7 @@ export class AdbPacketDispatcher extends AutoDisposable {
|
||||||
|
|
||||||
private _abortController = new AbortController();
|
private _abortController = new AbortController();
|
||||||
|
|
||||||
public constructor(readable: ReadableStream<ArrayBuffer>, writable: WritableStream<ArrayBuffer>, logger?: AdbLogger) {
|
public constructor(readable: ReadableStream<Uint8Array>, writable: WritableStream<Uint8Array>, logger?: AdbLogger) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
|
@ -221,23 +221,27 @@ export class AdbPacketDispatcher extends AutoDisposable {
|
||||||
command: AdbCommand,
|
command: AdbCommand,
|
||||||
arg0: number,
|
arg0: number,
|
||||||
arg1: number,
|
arg1: number,
|
||||||
payload?: string | ArrayBuffer
|
payload?: string | Uint8Array
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
public async sendPacket(
|
public async sendPacket(
|
||||||
packetOrCommand: AdbPacketInit | AdbCommand,
|
packetOrCommand: AdbPacketInit | AdbCommand,
|
||||||
arg0?: number,
|
arg0?: number,
|
||||||
arg1?: number,
|
arg1?: number,
|
||||||
payload: string | ArrayBuffer = EmptyArrayBuffer,
|
payload: string | Uint8Array = EmptyUint8Array,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
let init: AdbPacketInit;
|
let init: AdbPacketInit;
|
||||||
if (arg0 === undefined) {
|
if (arg0 === undefined) {
|
||||||
init = packetOrCommand as AdbPacketInit;
|
init = packetOrCommand as AdbPacketInit;
|
||||||
} else {
|
} else {
|
||||||
|
if (typeof payload === 'string') {
|
||||||
|
payload = encodeUtf8(payload);
|
||||||
|
}
|
||||||
|
|
||||||
init = {
|
init = {
|
||||||
command: packetOrCommand as AdbCommand,
|
command: packetOrCommand as AdbCommand,
|
||||||
arg0: arg0 as number,
|
arg0: arg0 as number,
|
||||||
arg1: arg1 as number,
|
arg1: arg1 as number,
|
||||||
payload: typeof payload === 'string' ? encodeUtf8(payload) : payload,
|
payload,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@ export class AdbSocket implements AdbSocketInfo {
|
||||||
public get localCreated() { return this.controller.localCreated; }
|
public get localCreated() { return this.controller.localCreated; }
|
||||||
public get serviceString() { return this.controller.serviceString; }
|
public get serviceString() { return this.controller.serviceString; }
|
||||||
|
|
||||||
public get readable(): ReadableStream<ArrayBuffer> { return this.controller.readable; }
|
public get readable(): ReadableStream<Uint8Array> { return this.controller.readable; }
|
||||||
public get writable(): WritableStream<ArrayBuffer> { return this.controller.writable; }
|
public get writable(): WritableStream<Uint8Array> { return this.controller.writable; }
|
||||||
|
|
||||||
public constructor(controller: AdbSocketController) {
|
public constructor(controller: AdbSocketController) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
|
|
|
@ -14,11 +14,11 @@ export class BufferedStreamEndedError extends Error {
|
||||||
export class BufferedStream {
|
export class BufferedStream {
|
||||||
private buffer: Uint8Array | undefined;
|
private buffer: Uint8Array | undefined;
|
||||||
|
|
||||||
protected readonly stream: ReadableStream<ArrayBuffer>;
|
protected readonly stream: ReadableStream<Uint8Array>;
|
||||||
|
|
||||||
protected readonly reader: ReadableStreamDefaultReader<ArrayBuffer>;
|
protected readonly reader: ReadableStreamDefaultReader<Uint8Array>;
|
||||||
|
|
||||||
public constructor(stream: ReadableStream<ArrayBuffer>) {
|
public constructor(stream: ReadableStream<Uint8Array>) {
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
this.reader = stream.getReader();
|
this.reader = stream.getReader();
|
||||||
}
|
}
|
||||||
|
@ -29,14 +29,14 @@ export class BufferedStream {
|
||||||
* @param readToEnd When `true`, allow less data to be returned if the stream has reached its end.
|
* @param readToEnd When `true`, allow less data to be returned if the stream has reached its end.
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
public async read(length: number, readToEnd: boolean = false): Promise<ArrayBuffer> {
|
public async read(length: number, readToEnd: boolean = false): Promise<Uint8Array> {
|
||||||
let array: Uint8Array;
|
let array: Uint8Array;
|
||||||
let index: number;
|
let index: number;
|
||||||
if (this.buffer) {
|
if (this.buffer) {
|
||||||
const buffer = this.buffer;
|
const buffer = this.buffer;
|
||||||
if (buffer.byteLength > length) {
|
if (buffer.byteLength > length) {
|
||||||
this.buffer = buffer.subarray(length);
|
this.buffer = buffer.subarray(length);
|
||||||
return buffer.slice(0, length).buffer;
|
return buffer.slice(0, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
array = new Uint8Array(length);
|
array = new Uint8Array(length);
|
||||||
|
@ -47,7 +47,7 @@ export class BufferedStream {
|
||||||
const result = await this.reader.read();
|
const result = await this.reader.read();
|
||||||
if (result.done) {
|
if (result.done) {
|
||||||
if (readToEnd) {
|
if (readToEnd) {
|
||||||
return new ArrayBuffer(0);
|
return new Uint8Array(0);
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Unexpected end of stream');
|
throw new Error('Unexpected end of stream');
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ export class BufferedStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.byteLength > length) {
|
if (value.byteLength > length) {
|
||||||
this.buffer = new Uint8Array(value, length);
|
this.buffer = value.subarray(length);
|
||||||
return value.slice(0, length);
|
return value.slice(0, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ export class BufferedStream {
|
||||||
const result = await this.reader.read();
|
const result = await this.reader.read();
|
||||||
if (result.done) {
|
if (result.done) {
|
||||||
if (readToEnd) {
|
if (readToEnd) {
|
||||||
return new ArrayBuffer(0);
|
return new Uint8Array(0);
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Unexpected end of stream');
|
throw new Error('Unexpected end of stream');
|
||||||
}
|
}
|
||||||
|
@ -82,16 +82,16 @@ export class BufferedStream {
|
||||||
|
|
||||||
const { value } = result;
|
const { value } = result;
|
||||||
if (value.byteLength > left) {
|
if (value.byteLength > left) {
|
||||||
array.set(new Uint8Array(value, 0, left), index);
|
array.set(value.subarray(0, left), index);
|
||||||
this.buffer = new Uint8Array(value, left);
|
this.buffer = value.subarray(left);
|
||||||
return array.buffer;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
array.set(new Uint8Array(value), index);
|
array.set(value, index);
|
||||||
index += value.byteLength;
|
index += value.byteLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
return array.buffer;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
public close() {
|
public close() {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import Struct, { decodeUtf8, StructLike, StructValueType } from "@yume-chan/struct";
|
import Struct, { StructLike, StructValueType } from "@yume-chan/struct";
|
||||||
import { chunkArrayLike } from "../utils/chunk";
|
import { decodeUtf8 } from "../utils";
|
||||||
import { BufferedStream, BufferedStreamEndedError } from "./buffered";
|
import { BufferedStream, BufferedStreamEndedError } from "./buffered";
|
||||||
import { TransformStream, WritableStream, WritableStreamDefaultWriter, ReadableStream, ReadableStreamDefaultReader } from "./detect";
|
import { ReadableStream, ReadableStreamDefaultReader, TransformStream, WritableStream, WritableStreamDefaultWriter } from "./detect";
|
||||||
|
|
||||||
export class DecodeUtf8Stream extends TransformStream<ArrayBuffer, string>{
|
export class DecodeUtf8Stream extends TransformStream<Uint8Array, string>{
|
||||||
public constructor() {
|
public constructor() {
|
||||||
super({
|
super({
|
||||||
transform(chunk, controller) {
|
transform(chunk, controller) {
|
||||||
|
@ -29,10 +29,10 @@ export class GatherStringStream extends TransformStream<string, string>{
|
||||||
|
|
||||||
// TODO: Find other ways to implement `StructTransformStream`
|
// TODO: Find other ways to implement `StructTransformStream`
|
||||||
export class StructDeserializeStream<T extends StructLike<any>>
|
export class StructDeserializeStream<T extends StructLike<any>>
|
||||||
extends TransformStream<ArrayBuffer, StructValueType<T>>{
|
extends TransformStream<Uint8Array, StructValueType<T>>{
|
||||||
public constructor(struct: T) {
|
public constructor(struct: T) {
|
||||||
// Convert incoming chunk to a `ReadableStream`
|
// Convert incoming chunk to a `ReadableStream`
|
||||||
const passthrough = new TransformStream<ArrayBuffer, ArrayBuffer>();
|
const passthrough = new TransformStream<Uint8Array, Uint8Array>();
|
||||||
const passthroughWriter = passthrough.writable.getWriter();
|
const passthroughWriter = passthrough.writable.getWriter();
|
||||||
// Convert the `ReadableSteam` to a `BufferedStream`
|
// Convert the `ReadableSteam` to a `BufferedStream`
|
||||||
const bufferedStream = new BufferedStream(passthrough.readable);
|
const bufferedStream = new BufferedStream(passthrough.readable);
|
||||||
|
@ -66,11 +66,11 @@ export class StructDeserializeStream<T extends StructLike<any>>
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StructSerializeStream<T extends Struct<any, any, any, any>>
|
export class StructSerializeStream<T extends Struct<any, any, any, any>>
|
||||||
extends TransformStream<T['TInit'], ArrayBuffer>{
|
extends TransformStream<T['TInit'], Uint8Array>{
|
||||||
constructor(struct: T) {
|
constructor(struct: T) {
|
||||||
super({
|
super({
|
||||||
transform(chunk, controller) {
|
transform(chunk, controller) {
|
||||||
controller.enqueue(struct.serialize(chunk));
|
controller.enqueue(new Uint8Array(struct.serialize(chunk)));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -149,12 +149,14 @@ export class WrapReadableStream<T, R extends ReadableStream<T>, S> extends Reada
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ChunkStream extends TransformStream<ArrayBuffer, ArrayBuffer>{
|
export class ChunkStream extends TransformStream<Uint8Array, Uint8Array>{
|
||||||
public constructor(size: number) {
|
public constructor(size: number) {
|
||||||
super({
|
super({
|
||||||
transform(chunk, controller) {
|
transform(chunk, controller) {
|
||||||
for (const piece of chunkArrayLike(chunk, size)) {
|
for (let start = 0; start < chunk.length; start += size) {
|
||||||
controller.enqueue(piece);
|
const end = start + size;
|
||||||
|
controller.enqueue(chunk.slice(start, end));
|
||||||
|
start = end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -33,43 +33,41 @@ describe('base64', () => {
|
||||||
|
|
||||||
describe('decodeBase64', () => {
|
describe('decodeBase64', () => {
|
||||||
it("input length 0", () => {
|
it("input length 0", () => {
|
||||||
expect(new Uint8Array(decodeBase64(''))).toEqual(new Uint8Array());
|
expect(decodeBase64('')).toEqual(new Uint8Array());
|
||||||
});
|
});
|
||||||
|
|
||||||
it("input length 1", () => {
|
it("input length 1", () => {
|
||||||
expect(new Uint8Array(decodeBase64('AA=='))).toEqual(new Uint8Array([0]));
|
expect(decodeBase64('AA==')).toEqual(new Uint8Array([0]));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("input length 2", () => {
|
it("input length 2", () => {
|
||||||
expect(new Uint8Array(decodeBase64('AAE='))).toEqual(new Uint8Array([0, 1]));
|
expect(decodeBase64('AAE=')).toEqual(new Uint8Array([0, 1]));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("input length 3", () => {
|
it("input length 3", () => {
|
||||||
/* cspell: disable-next-line */
|
/* cspell: disable-next-line */
|
||||||
expect(new Uint8Array(decodeBase64('AAEC'))).toEqual(new Uint8Array([0, 1, 2]));
|
expect(decodeBase64('AAEC')).toEqual(new Uint8Array([0, 1, 2]));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("input length 4", () => {
|
it("input length 4", () => {
|
||||||
/* cspell: disable-next-line */
|
/* cspell: disable-next-line */
|
||||||
expect(new Uint8Array(decodeBase64('AAECAw=='))).toEqual(new Uint8Array([0, 1, 2, 3]));
|
expect(decodeBase64('AAECAw==')).toEqual(new Uint8Array([0, 1, 2, 3]));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("input length 5", () => {
|
it("input length 5", () => {
|
||||||
/* cspell: disable-next-line */
|
/* cspell: disable-next-line */
|
||||||
expect(new Uint8Array(decodeBase64('AAECAwQ='))).toEqual(new Uint8Array([0, 1, 2, 3, 4]));
|
expect(decodeBase64('AAECAwQ=')).toEqual(new Uint8Array([0, 1, 2, 3, 4]));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("input length 6", () => {
|
it("input length 6", () => {
|
||||||
/* cspell: disable-next-line */
|
/* cspell: disable-next-line */
|
||||||
expect(new Uint8Array(decodeBase64('AAECAwQF'))).toEqual(new Uint8Array([0, 1, 2, 3, 4, 5]));
|
expect(decodeBase64('AAECAwQF')).toEqual(new Uint8Array([0, 1, 2, 3, 4, 5]));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("all byte values", () => {
|
it("all byte values", () => {
|
||||||
expect(
|
expect(
|
||||||
new Uint8Array(
|
decodeBase64(
|
||||||
decodeBase64(
|
'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w=='
|
||||||
'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w=='
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
).toEqual(new Uint8Array(Array.from({ length: 256 }, (_, index) => index)));
|
).toEqual(new Uint8Array(Array.from({ length: 256 }, (_, index) => index)));
|
||||||
});
|
});
|
||||||
|
@ -78,21 +76,17 @@ describe('base64', () => {
|
||||||
describe('encodeBase64', () => {
|
describe('encodeBase64', () => {
|
||||||
it('input length 0', () => {
|
it('input length 0', () => {
|
||||||
expect(
|
expect(
|
||||||
new Uint8Array(
|
encodeBase64(
|
||||||
encodeBase64(
|
new Uint8Array()
|
||||||
new Uint8Array()
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
).toEqual(new Uint8Array());
|
).toEqual(new Uint8Array());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('input length 1', () => {
|
it('input length 1', () => {
|
||||||
expect(
|
expect(
|
||||||
new Uint8Array(
|
encodeBase64(
|
||||||
encodeBase64(
|
new Uint8Array(
|
||||||
new Uint8Array(
|
[0]
|
||||||
[0]
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).toEqual(new Uint8Array([65, 65, 61, 61])); // AA==
|
).toEqual(new Uint8Array([65, 65, 61, 61])); // AA==
|
||||||
|
@ -100,11 +94,9 @@ describe('base64', () => {
|
||||||
|
|
||||||
it('input length 2', () => {
|
it('input length 2', () => {
|
||||||
expect(
|
expect(
|
||||||
new Uint8Array(
|
encodeBase64(
|
||||||
encodeBase64(
|
new Uint8Array(
|
||||||
new Uint8Array(
|
[0, 1]
|
||||||
[0, 1]
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).toEqual(new Uint8Array([65, 65, 69, 61])); // AAE=
|
).toEqual(new Uint8Array([65, 65, 69, 61])); // AAE=
|
||||||
|
@ -112,11 +104,9 @@ describe('base64', () => {
|
||||||
|
|
||||||
it('input length 3', () => {
|
it('input length 3', () => {
|
||||||
expect(
|
expect(
|
||||||
new Uint8Array(
|
encodeBase64(
|
||||||
encodeBase64(
|
new Uint8Array(
|
||||||
new Uint8Array(
|
[0, 1, 2]
|
||||||
[0, 1, 2]
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
/* cspell: disable-next-line */
|
/* cspell: disable-next-line */
|
||||||
|
@ -125,11 +115,9 @@ describe('base64', () => {
|
||||||
|
|
||||||
it('input length 4', () => {
|
it('input length 4', () => {
|
||||||
expect(
|
expect(
|
||||||
new Uint8Array(
|
encodeBase64(
|
||||||
encodeBase64(
|
new Uint8Array(
|
||||||
new Uint8Array(
|
[0, 1, 2, 3]
|
||||||
[0, 1, 2, 3]
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
/* cspell: disable-next-line */
|
/* cspell: disable-next-line */
|
||||||
|
@ -138,11 +126,9 @@ describe('base64', () => {
|
||||||
|
|
||||||
it('input length 5', () => {
|
it('input length 5', () => {
|
||||||
expect(
|
expect(
|
||||||
new Uint8Array(
|
encodeBase64(
|
||||||
encodeBase64(
|
new Uint8Array(
|
||||||
new Uint8Array(
|
[0, 1, 2, 3, 4]
|
||||||
[0, 1, 2, 3, 4]
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
/* cspell: disable-next-line */
|
/* cspell: disable-next-line */
|
||||||
|
@ -151,11 +137,9 @@ describe('base64', () => {
|
||||||
|
|
||||||
it('input length 6', () => {
|
it('input length 6', () => {
|
||||||
expect(
|
expect(
|
||||||
new Uint8Array(
|
encodeBase64(
|
||||||
encodeBase64(
|
new Uint8Array(
|
||||||
new Uint8Array(
|
[0, 1, 2, 3, 4, 5]
|
||||||
[0, 1, 2, 3, 4, 5]
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
/* cspell: disable-next-line */
|
/* cspell: disable-next-line */
|
||||||
|
@ -164,11 +148,9 @@ describe('base64', () => {
|
||||||
|
|
||||||
it("all byte values", () => {
|
it("all byte values", () => {
|
||||||
expect(
|
expect(
|
||||||
new Uint8Array(
|
encodeBase64(
|
||||||
encodeBase64(
|
new Uint8Array(
|
||||||
new Uint8Array(
|
Array.from({ length: 256 }, (_, index) => index)
|
||||||
Array.from({ length: 256 }, (_, index) => index)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).toEqual(new Uint8Array([65, 65, 69, 67, 65, 119, 81, 70, 66, 103, 99, 73, 67, 81, 111, 76, 68, 65, 48, 79, 68, 120, 65, 82, 69, 104, 77, 85, 70, 82, 89, 88, 71, 66, 107, 97, 71, 120, 119, 100, 72, 104, 56, 103, 73, 83, 73, 106, 74, 67, 85, 109, 74, 121, 103, 112, 75, 105, 115, 115, 76, 83, 52, 118, 77, 68, 69, 121, 77, 122, 81, 49, 78, 106, 99, 52, 79, 84, 111, 55, 80, 68, 48, 43, 80, 48, 66, 66, 81, 107, 78, 69, 82, 85, 90, 72, 83, 69, 108, 75, 83, 48, 120, 78, 84, 107, 57, 81, 85, 86, 74, 84, 86, 70, 86, 87, 86, 49, 104, 90, 87, 108, 116, 99, 88, 86, 53, 102, 89, 71, 70, 105, 89, 50, 82, 108, 90, 109, 100, 111, 97, 87, 112, 114, 98, 71, 49, 117, 98, 51, 66, 120, 99, 110, 78, 48, 100, 88, 90, 51, 101, 72, 108, 54, 101, 51, 120, 57, 102, 110, 43, 65, 103, 89, 75, 68, 104, 73, 87, 71, 104, 52, 105, 74, 105, 111, 117, 77, 106, 89, 54, 80, 107, 74, 71, 83, 107, 53, 83, 86, 108, 112, 101, 89, 109, 90, 113, 98, 110, 74, 50, 101, 110, 54, 67, 104, 111, 113, 79, 107, 112, 97, 97, 110, 113, 75, 109, 113, 113, 54, 121, 116, 114, 113, 43, 119, 115, 98, 75, 122, 116, 76, 87, 50, 116, 55, 105, 53, 117, 114, 117, 56, 118, 98, 54, 47, 119, 77, 72, 67, 119, 56, 84, 70, 120, 115, 102, 73, 121, 99, 114, 76, 122, 77, 51, 79, 122, 57, 68, 82, 48, 116, 80, 85, 49, 100, 98, 88, 50, 78, 110, 97, 50, 57, 122, 100, 51, 116, 47, 103, 52, 101, 76, 106, 53, 79, 88, 109, 53, 43, 106, 112, 54, 117, 118, 115, 55, 101, 55, 118, 56, 80, 72, 121, 56, 47, 84, 49, 57, 118, 102, 52, 43, 102, 114, 55, 47, 80, 51, 43, 47, 119, 61, 61]));
|
).toEqual(new Uint8Array([65, 65, 69, 67, 65, 119, 81, 70, 66, 103, 99, 73, 67, 81, 111, 76, 68, 65, 48, 79, 68, 120, 65, 82, 69, 104, 77, 85, 70, 82, 89, 88, 71, 66, 107, 97, 71, 120, 119, 100, 72, 104, 56, 103, 73, 83, 73, 106, 74, 67, 85, 109, 74, 121, 103, 112, 75, 105, 115, 115, 76, 83, 52, 118, 77, 68, 69, 121, 77, 122, 81, 49, 78, 106, 99, 52, 79, 84, 111, 55, 80, 68, 48, 43, 80, 48, 66, 66, 81, 107, 78, 69, 82, 85, 90, 72, 83, 69, 108, 75, 83, 48, 120, 78, 84, 107, 57, 81, 85, 86, 74, 84, 86, 70, 86, 87, 86, 49, 104, 90, 87, 108, 116, 99, 88, 86, 53, 102, 89, 71, 70, 105, 89, 50, 82, 108, 90, 109, 100, 111, 97, 87, 112, 114, 98, 71, 49, 117, 98, 51, 66, 120, 99, 110, 78, 48, 100, 88, 90, 51, 101, 72, 108, 54, 101, 51, 120, 57, 102, 110, 43, 65, 103, 89, 75, 68, 104, 73, 87, 71, 104, 52, 105, 74, 105, 111, 117, 77, 106, 89, 54, 80, 107, 74, 71, 83, 107, 53, 83, 86, 108, 112, 101, 89, 109, 90, 113, 98, 110, 74, 50, 101, 110, 54, 67, 104, 111, 113, 79, 107, 112, 97, 97, 110, 113, 75, 109, 113, 113, 54, 121, 116, 114, 113, 43, 119, 115, 98, 75, 122, 116, 76, 87, 50, 116, 55, 105, 53, 117, 114, 117, 56, 118, 98, 54, 47, 119, 77, 72, 67, 119, 56, 84, 70, 120, 115, 102, 73, 121, 99, 114, 76, 122, 77, 51, 79, 122, 57, 68, 82, 48, 116, 80, 85, 49, 100, 98, 88, 50, 78, 110, 97, 50, 57, 122, 100, 51, 116, 47, 103, 52, 101, 76, 106, 53, 79, 88, 109, 53, 43, 106, 112, 54, 117, 118, 115, 55, 101, 55, 118, 56, 80, 72, 121, 56, 47, 84, 49, 57, 118, 102, 52, 43, 102, 114, 55, 47, 80, 51, 43, 47, 119, 61, 61]));
|
||||||
|
|
|
@ -26,46 +26,35 @@ export function calculateBase64EncodedLength(inputLength: number): [outputLength
|
||||||
}
|
}
|
||||||
|
|
||||||
export function encodeBase64(
|
export function encodeBase64(
|
||||||
input: ArrayBuffer | Uint8Array,
|
input: Uint8Array,
|
||||||
inputOffset?: number,
|
inputOffset?: number,
|
||||||
inputLength?: number,
|
inputLength?: number,
|
||||||
): ArrayBuffer; // overload 1
|
): Uint8Array; // overload 1
|
||||||
export function encodeBase64(
|
export function encodeBase64(
|
||||||
input: ArrayBuffer | Uint8Array,
|
input: Uint8Array,
|
||||||
output: ArrayBuffer | Uint8Array,
|
output: Uint8Array,
|
||||||
outputOffset?: number
|
outputOffset?: number
|
||||||
): number; // overload 2
|
): number; // overload 2
|
||||||
export function encodeBase64(
|
export function encodeBase64(
|
||||||
input: ArrayBuffer | Uint8Array,
|
input: Uint8Array,
|
||||||
inputOffset: number,
|
inputOffset: number,
|
||||||
output: ArrayBuffer | Uint8Array,
|
output: Uint8Array,
|
||||||
outputOffset?: number
|
outputOffset?: number
|
||||||
): number; // overload 3
|
): number; // overload 3
|
||||||
export function encodeBase64(
|
export function encodeBase64(
|
||||||
input: ArrayBuffer | Uint8Array,
|
input: Uint8Array,
|
||||||
inputOffset: number,
|
inputOffset: number,
|
||||||
inputLength: number,
|
inputLength: number,
|
||||||
output: ArrayBuffer | Uint8Array,
|
output: Uint8Array,
|
||||||
outputOffset?: number
|
outputOffset?: number
|
||||||
): number; // overload 4
|
): number; // overload 4
|
||||||
export function encodeBase64(
|
export function encodeBase64(
|
||||||
input: ArrayBuffer | Uint8Array,
|
input: Uint8Array,
|
||||||
arg1?: number | ArrayBuffer | Uint8Array,
|
arg1?: number | Uint8Array,
|
||||||
arg2?: number | ArrayBuffer | Uint8Array,
|
arg2?: number | Uint8Array,
|
||||||
_arg3?: number | ArrayBuffer | Uint8Array,
|
_arg3?: number | Uint8Array,
|
||||||
_arg4?: number,
|
_arg4?: number,
|
||||||
): ArrayBuffer | number {
|
): Uint8Array | number {
|
||||||
if (input instanceof ArrayBuffer) {
|
|
||||||
input = new Uint8Array(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Because `Uint8Array` is type compatible with `ArrayBuffer`,
|
|
||||||
// TypeScript doesn't correctly narrow `input` to `Uint8Array` when assigning.
|
|
||||||
// Manually eliminate `ArrayBuffer` from `input` with a type guard.
|
|
||||||
if (input instanceof ArrayBuffer) {
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
let inputOffset: number;
|
let inputOffset: number;
|
||||||
let inputLength: number;
|
let inputLength: number;
|
||||||
let output: Uint8Array;
|
let output: Uint8Array;
|
||||||
|
@ -82,46 +71,33 @@ export function encodeBase64(
|
||||||
outputArgumentIndex = 3;
|
outputArgumentIndex = 3;
|
||||||
} else {
|
} else {
|
||||||
// overload 3
|
// overload 3
|
||||||
inputLength = input.byteLength - inputOffset;
|
inputLength = input.length - inputOffset;
|
||||||
outputArgumentIndex = 2;
|
outputArgumentIndex = 2;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// overload 2
|
// overload 2
|
||||||
inputOffset = 0;
|
inputOffset = 0;
|
||||||
inputLength = input.byteLength;
|
inputLength = input.length;
|
||||||
outputArgumentIndex = 1;
|
outputArgumentIndex = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [outputLength, paddingLength] = calculateBase64EncodedLength(inputLength);
|
const [outputLength, paddingLength] = calculateBase64EncodedLength(inputLength);
|
||||||
|
|
||||||
let maybeOutput: ArrayBuffer | Uint8Array | undefined = arguments[outputArgumentIndex];
|
let maybeOutput: Uint8Array | undefined = arguments[outputArgumentIndex];
|
||||||
let outputType: 'ArrayBuffer' | 'number';
|
let outputType: 'Uint8Array' | 'number';
|
||||||
if (maybeOutput) {
|
if (maybeOutput) {
|
||||||
outputOffset = arguments[outputArgumentIndex + 1] ?? 0;
|
outputOffset = arguments[outputArgumentIndex + 1] ?? 0;
|
||||||
|
|
||||||
if (maybeOutput.byteLength - outputOffset < outputLength) {
|
if (maybeOutput.length - outputOffset < outputLength) {
|
||||||
throw new Error('output buffer is too small');
|
throw new Error('output buffer is too small');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maybeOutput instanceof ArrayBuffer) {
|
output = maybeOutput;
|
||||||
output = new Uint8Array(maybeOutput);
|
|
||||||
} else {
|
|
||||||
output = maybeOutput;
|
|
||||||
}
|
|
||||||
|
|
||||||
outputType = 'number';
|
outputType = 'number';
|
||||||
} else {
|
} else {
|
||||||
const buffer = new ArrayBuffer(outputLength);
|
output = new Uint8Array(outputLength);
|
||||||
output = new Uint8Array(buffer);
|
|
||||||
outputOffset = 0;
|
outputOffset = 0;
|
||||||
outputType = 'ArrayBuffer';
|
outputType = 'Uint8Array';
|
||||||
}
|
|
||||||
|
|
||||||
// Because `Uint8Array` is type compatible with `ArrayBuffer`,
|
|
||||||
// TypeScript doesn't correctly narrow `output` to `Uint8Array` when assigning.
|
|
||||||
// Manually eliminate `ArrayBuffer` from `output` with a type guard.
|
|
||||||
if (output instanceof ArrayBuffer) {
|
|
||||||
return output;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.buffer === output.buffer) {
|
if (input.buffer === output.buffer) {
|
||||||
|
@ -209,14 +185,14 @@ export function encodeBase64(
|
||||||
outputIndex -= 1;
|
outputIndex -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outputType === 'ArrayBuffer') {
|
if (outputType === 'Uint8Array') {
|
||||||
return output.buffer;
|
return output;
|
||||||
} else {
|
} else {
|
||||||
return outputLength;
|
return outputLength;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function decodeBase64(input: string): ArrayBuffer {
|
export function decodeBase64(input: string): Uint8Array {
|
||||||
let padding: number;
|
let padding: number;
|
||||||
if (input[input.length - 2] === '=') {
|
if (input[input.length - 2] === '=') {
|
||||||
padding = 2;
|
padding = 2;
|
||||||
|
@ -275,5 +251,5 @@ export function decodeBase64(input: string): ArrayBuffer {
|
||||||
result[dIndex] = (a << 2) | ((b & 0b11_0000) >> 4);
|
result[dIndex] = (a << 2) | ((b & 0b11_0000) >> 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.buffer;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
export function* chunkArrayLike(
|
|
||||||
value: ArrayLike<number> | ArrayBufferLike,
|
|
||||||
size: number
|
|
||||||
): Generator<ArrayBuffer, void, void> {
|
|
||||||
if ('length' in value) {
|
|
||||||
value = new Uint8Array(value).buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.byteLength <= size) {
|
|
||||||
return yield value;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < value.byteLength; i += size) {
|
|
||||||
yield value.slice(i, i + size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function* chunkAsyncIterable(
|
|
||||||
value: AsyncIterable<ArrayBuffer>,
|
|
||||||
size: number
|
|
||||||
): AsyncGenerator<ArrayBuffer, void, void> {
|
|
||||||
let result = new Uint8Array(size);
|
|
||||||
let index = 0;
|
|
||||||
for await (let buffer of value) {
|
|
||||||
// `result` has some data, `result + buffer` is enough
|
|
||||||
if (index !== 0 && index + buffer.byteLength >= size) {
|
|
||||||
const remainder = size - index;
|
|
||||||
result.set(new Uint8Array(buffer, 0, remainder), index);
|
|
||||||
yield result.buffer;
|
|
||||||
|
|
||||||
result = new Uint8Array(size);
|
|
||||||
index = 0;
|
|
||||||
|
|
||||||
if (buffer.byteLength > remainder) {
|
|
||||||
// `buffer` still has some data
|
|
||||||
buffer = buffer.slice(remainder);
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// `result` is empty, `buffer` alone is enough
|
|
||||||
if (buffer.byteLength >= size) {
|
|
||||||
let remainder = false;
|
|
||||||
for (const chunk of chunkArrayLike(buffer, size)) {
|
|
||||||
if (chunk.byteLength === size) {
|
|
||||||
yield chunk;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// `buffer` still has some data
|
|
||||||
remainder = true;
|
|
||||||
buffer = chunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!remainder) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// `result` has some data but `result + buffer` is still not enough
|
|
||||||
// or after previous steps `buffer` still has some data
|
|
||||||
result.set(new Uint8Array(buffer), index);
|
|
||||||
index += buffer.byteLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index !== 0) {
|
|
||||||
yield result.buffer.slice(0, index);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,10 +4,10 @@ const Utf8Encoder = new TextEncoder();
|
||||||
// @ts-expect-error @types/node missing `TextDecoder`
|
// @ts-expect-error @types/node missing `TextDecoder`
|
||||||
const Utf8Decoder = new TextDecoder();
|
const Utf8Decoder = new TextDecoder();
|
||||||
|
|
||||||
export function encodeUtf8(input: string): ArrayBuffer {
|
export function encodeUtf8(input: string): Uint8Array {
|
||||||
return Utf8Encoder.encode(input).buffer;
|
return Utf8Encoder.encode(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function decodeUtf8(buffer: ArrayBuffer): string {
|
export function decodeUtf8(buffer: Uint8Array): string {
|
||||||
return Utf8Decoder.decode(buffer);
|
return Utf8Decoder.decode(buffer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
export * from './auto-reset-event';
|
export * from './auto-reset-event';
|
||||||
export * from './base64';
|
export * from './base64';
|
||||||
export * from './chunk';
|
|
||||||
export * from './encoding';
|
export * from './encoding';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { StructAsyncDeserializeStream, StructDefaultOptions, StructDeserializeStream, StructFieldDefinition, StructFieldValue, StructOptions, StructValue } from './basic';
|
import { StructAsyncDeserializeStream, StructDefaultOptions, StructDeserializeStream, StructFieldDefinition, StructFieldValue, StructOptions, StructValue } from './basic';
|
||||||
import { Struct } from './struct';
|
import { Struct } from './struct';
|
||||||
import { ArrayBufferFieldType, BigIntFieldDefinition, BigIntFieldType, FixedLengthArrayBufferLikeFieldDefinition, NumberFieldDefinition, NumberFieldType, StringFieldType, Uint8ClampedArrayFieldType, VariableLengthArrayBufferLikeFieldDefinition } from './types';
|
import { ArrayBufferFieldType, BigIntFieldDefinition, BigIntFieldType, FixedLengthArrayBufferLikeFieldDefinition, NumberFieldDefinition, NumberFieldType, StringFieldType, ArrayBufferViewFieldType, VariableLengthArrayBufferLikeFieldDefinition } from './types';
|
||||||
import { ValueOrPromise } from './utils';
|
import { ValueOrPromise } from './utils';
|
||||||
|
|
||||||
class MockDeserializationStream implements StructDeserializeStream {
|
class MockDeserializationStream implements StructDeserializeStream {
|
||||||
|
@ -181,7 +181,7 @@ describe('Struct', () => {
|
||||||
|
|
||||||
const definition = struct['_fields'][0]![1] as FixedLengthArrayBufferLikeFieldDefinition;
|
const definition = struct['_fields'][0]![1] as FixedLengthArrayBufferLikeFieldDefinition;
|
||||||
expect(definition).toBeInstanceOf(FixedLengthArrayBufferLikeFieldDefinition);
|
expect(definition).toBeInstanceOf(FixedLengthArrayBufferLikeFieldDefinition);
|
||||||
expect(definition.type).toBeInstanceOf(Uint8ClampedArrayFieldType);
|
expect(definition.type).toBeInstanceOf(ArrayBufferViewFieldType);
|
||||||
expect(definition.options.length).toBe(10);
|
expect(definition.options.length).toBe(10);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ describe('Struct', () => {
|
||||||
|
|
||||||
const definition = struct['_fields'][1]![1] as VariableLengthArrayBufferLikeFieldDefinition;
|
const definition = struct['_fields'][1]![1] as VariableLengthArrayBufferLikeFieldDefinition;
|
||||||
expect(definition).toBeInstanceOf(VariableLengthArrayBufferLikeFieldDefinition);
|
expect(definition).toBeInstanceOf(VariableLengthArrayBufferLikeFieldDefinition);
|
||||||
expect(definition.type).toBeInstanceOf(Uint8ClampedArrayFieldType);
|
expect(definition.type).toBeInstanceOf(ArrayBufferViewFieldType);
|
||||||
expect(definition.options.lengthField).toBe('barLength');
|
expect(definition.options.lengthField).toBe('barLength');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import type { StructAsyncDeserializeStream, StructDeserializeStream, StructFieldDefinition, StructFieldValue, StructOptions } from './basic';
|
import type { StructAsyncDeserializeStream, StructDeserializeStream, StructFieldDefinition, StructFieldValue, StructOptions } from './basic';
|
||||||
import { StructDefaultOptions, StructValue } from './basic';
|
import { StructDefaultOptions, StructValue } from './basic';
|
||||||
import { Syncbird } from "./syncbird";
|
import { Syncbird } from "./syncbird";
|
||||||
import { ArrayBufferFieldType, ArrayBufferLikeFieldType, BigIntFieldDefinition, BigIntFieldType, FixedLengthArrayBufferLikeFieldDefinition, FixedLengthArrayBufferLikeFieldOptions, LengthField, NumberFieldDefinition, NumberFieldType, StringFieldType, Uint8ClampedArrayFieldType, VariableLengthArrayBufferLikeFieldDefinition, VariableLengthArrayBufferLikeFieldOptions } from './types';
|
import { ArrayBufferFieldType, ArrayBufferLikeFieldType, BigIntFieldDefinition, BigIntFieldType, FixedLengthArrayBufferLikeFieldDefinition, FixedLengthArrayBufferLikeFieldOptions, LengthField, NumberFieldDefinition, NumberFieldType, StringFieldType, ArrayBufferViewFieldType, VariableLengthArrayBufferLikeFieldDefinition, VariableLengthArrayBufferLikeFieldOptions } from './types';
|
||||||
import { Evaluate, Identity, Overwrite, ValueOrPromise } from "./utils";
|
import { Evaluate, Identity, Overwrite, ValueOrPromise } from "./utils";
|
||||||
|
|
||||||
export interface StructLike<TValue> {
|
export interface StructLike<TValue> {
|
||||||
|
@ -450,17 +450,30 @@ export class Struct<
|
||||||
return this.arrayBufferLike(name, ArrayBufferFieldType.instance, options);
|
return this.arrayBufferLike(name, ArrayBufferFieldType.instance, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public uint8Array: BoundArrayBufferLikeFieldDefinitionCreator<
|
||||||
|
TFields,
|
||||||
|
TOmitInitKey,
|
||||||
|
TExtra,
|
||||||
|
TPostDeserialized,
|
||||||
|
ArrayBufferViewFieldType<Uint8Array>
|
||||||
|
> = (
|
||||||
|
name: PropertyKey,
|
||||||
|
options: any
|
||||||
|
): any => {
|
||||||
|
return this.arrayBufferLike(name, ArrayBufferViewFieldType.uint8Array, options);
|
||||||
|
};
|
||||||
|
|
||||||
public uint8ClampedArray: BoundArrayBufferLikeFieldDefinitionCreator<
|
public uint8ClampedArray: BoundArrayBufferLikeFieldDefinitionCreator<
|
||||||
TFields,
|
TFields,
|
||||||
TOmitInitKey,
|
TOmitInitKey,
|
||||||
TExtra,
|
TExtra,
|
||||||
TPostDeserialized,
|
TPostDeserialized,
|
||||||
Uint8ClampedArrayFieldType
|
ArrayBufferViewFieldType<Uint8ClampedArray>
|
||||||
> = (
|
> = (
|
||||||
name: PropertyKey,
|
name: PropertyKey,
|
||||||
options: any
|
options: any
|
||||||
): any => {
|
): any => {
|
||||||
return this.arrayBufferLike(name, Uint8ClampedArrayFieldType.instance, options);
|
return this.arrayBufferLike(name, ArrayBufferViewFieldType.uint8ClampedArray, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
public string: BoundArrayBufferLikeFieldDefinitionCreator<
|
public string: BoundArrayBufferLikeFieldDefinitionCreator<
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { StructDefaultOptions, StructDeserializeStream, StructValue } from '../basic';
|
import { StructDefaultOptions, StructDeserializeStream, StructValue } from '../basic';
|
||||||
import { ArrayBufferFieldType, ArrayBufferLikeFieldDefinition, ArrayBufferLikeFieldType, StringFieldType, Uint8ClampedArrayFieldType } from './array-buffer';
|
import { ArrayBufferFieldType, ArrayBufferLikeFieldDefinition, ArrayBufferLikeFieldType, StringFieldType, ArrayBufferViewFieldType } from './array-buffer';
|
||||||
|
|
||||||
class MockDeserializationStream implements StructDeserializeStream {
|
class MockDeserializationStream implements StructDeserializeStream {
|
||||||
public buffer = new ArrayBuffer(0);
|
public buffer = new ArrayBuffer(0);
|
||||||
|
@ -32,18 +32,18 @@ describe('Types', () => {
|
||||||
|
|
||||||
describe('Uint8ClampedArrayFieldType', () => {
|
describe('Uint8ClampedArrayFieldType', () => {
|
||||||
it('should have a static instance', () => {
|
it('should have a static instance', () => {
|
||||||
expect(Uint8ClampedArrayFieldType.instance).toBeInstanceOf(Uint8ClampedArrayFieldType);
|
expect(ArrayBufferViewFieldType.instance).toBeInstanceOf(ArrayBufferViewFieldType);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('`#toArrayBuffer` should return its `buffer`', () => {
|
it('`#toArrayBuffer` should return its `buffer`', () => {
|
||||||
const array = new Uint8ClampedArray(10);
|
const array = new Uint8ClampedArray(10);
|
||||||
const buffer = array.buffer;
|
const buffer = array.buffer;
|
||||||
expect(Uint8ClampedArrayFieldType.instance.toArrayBuffer(array)).toBe(buffer);
|
expect(ArrayBufferViewFieldType.instance.toArrayBuffer(array)).toBe(buffer);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('`#fromArrayBuffer` should return a view of the `ArrayBuffer`', () => {
|
it('`#fromArrayBuffer` should return a view of the `ArrayBuffer`', () => {
|
||||||
const arrayBuffer = new ArrayBuffer(10);
|
const arrayBuffer = new ArrayBuffer(10);
|
||||||
const array = Uint8ClampedArrayFieldType.instance.fromArrayBuffer(arrayBuffer);
|
const array = ArrayBufferViewFieldType.instance.fromArrayBuffer(arrayBuffer);
|
||||||
expect(array).toHaveProperty('buffer', arrayBuffer);
|
expect(array).toHaveProperty('buffer', arrayBuffer);
|
||||||
expect(array).toHaveProperty('byteOffset', 0);
|
expect(array).toHaveProperty('byteOffset', 0);
|
||||||
expect(array).toHaveProperty('byteLength', 10);
|
expect(array).toHaveProperty('byteLength', 10);
|
||||||
|
@ -51,7 +51,7 @@ describe('Types', () => {
|
||||||
|
|
||||||
it('`#getSize` should return the `byteLength` of the `Uint8ClampedArray`', () => {
|
it('`#getSize` should return the `byteLength` of the `Uint8ClampedArray`', () => {
|
||||||
const array = new Uint8ClampedArray(10);
|
const array = new Uint8ClampedArray(10);
|
||||||
expect(Uint8ClampedArrayFieldType.instance.getSize(array)).toBe(10);
|
expect(ArrayBufferViewFieldType.instance.getSize(array)).toBe(10);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ describe('Types', () => {
|
||||||
|
|
||||||
it('should work with `Uint8ClampedArrayFieldType`', async () => {
|
it('should work with `Uint8ClampedArrayFieldType`', async () => {
|
||||||
const size = 10;
|
const size = 10;
|
||||||
const definition = new MockArrayBufferFieldDefinition(Uint8ClampedArrayFieldType.instance, size);
|
const definition = new MockArrayBufferFieldDefinition(ArrayBufferViewFieldType.instance, size);
|
||||||
|
|
||||||
const context = new MockDeserializationStream();
|
const context = new MockDeserializationStream();
|
||||||
const buffer = new ArrayBuffer(size);
|
const buffer = new ArrayBuffer(size);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// cspell: ignore syncbird
|
||||||
|
|
||||||
import { StructAsyncDeserializeStream, StructDeserializeStream, StructFieldDefinition, StructFieldValue, StructOptions, StructValue } from '../basic';
|
import { StructAsyncDeserializeStream, StructDeserializeStream, StructFieldDefinition, StructFieldValue, StructOptions, StructValue } from '../basic';
|
||||||
import { Syncbird } from "../syncbird";
|
import { Syncbird } from "../syncbird";
|
||||||
import { decodeUtf8, encodeUtf8, ValueOrPromise } from "../utils";
|
import { decodeUtf8, encodeUtf8, ValueOrPromise } from "../utils";
|
||||||
|
@ -56,24 +58,30 @@ export class ArrayBufferFieldType extends ArrayBufferLikeFieldType<ArrayBuffer>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Am ArrayBufferLike type that converts between `ArrayBuffer` and `Uint8ClampedArray` */
|
export type ArrayBufferViewConstructor<T> = new (array: ArrayLike<number> | ArrayBufferLike) => T;
|
||||||
export class Uint8ClampedArrayFieldType
|
|
||||||
extends ArrayBufferLikeFieldType<Uint8ClampedArray, Uint8ClampedArray> {
|
|
||||||
public static readonly instance = new Uint8ClampedArrayFieldType();
|
|
||||||
|
|
||||||
protected constructor() {
|
/** Am ArrayBufferLike type that converts between `ArrayBuffer` and `Uint8ClampedArray` */
|
||||||
|
export class ArrayBufferViewFieldType<T extends ArrayBufferView>
|
||||||
|
extends ArrayBufferLikeFieldType<T, T> {
|
||||||
|
public static readonly uint8Array = new ArrayBufferViewFieldType(Uint8Array);
|
||||||
|
public static readonly uint8ClampedArray = new ArrayBufferViewFieldType(Uint8ClampedArray);
|
||||||
|
|
||||||
|
protected type: ArrayBufferViewConstructor<T>;
|
||||||
|
|
||||||
|
public constructor(type: ArrayBufferViewConstructor<T>) {
|
||||||
super();
|
super();
|
||||||
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public toArrayBuffer(value: Uint8ClampedArray): ArrayBuffer {
|
public toArrayBuffer(value: T): ArrayBuffer {
|
||||||
return value.buffer;
|
return value.buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public fromArrayBuffer(arrayBuffer: ArrayBuffer): Uint8ClampedArray {
|
public fromArrayBuffer(arrayBuffer: ArrayBuffer): T {
|
||||||
return new Uint8ClampedArray(arrayBuffer);
|
return new this.type(arrayBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSize(value: Uint8ClampedArray): number {
|
public getSize(value: T): number {
|
||||||
return value.byteLength;
|
return value.byteLength;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,10 +53,10 @@ const Utf8Encoder = new TextEncoder();
|
||||||
// @ts-expect-error @types/node missing `TextDecoder`
|
// @ts-expect-error @types/node missing `TextDecoder`
|
||||||
const Utf8Decoder = new TextDecoder();
|
const Utf8Decoder = new TextDecoder();
|
||||||
|
|
||||||
export function encodeUtf8(input: string): ArrayBuffer {
|
export function encodeUtf8(input: string): Uint8Array {
|
||||||
return Utf8Encoder.encode(input).buffer;
|
return Utf8Encoder.encode(input).buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function decodeUtf8(buffer: ArrayBuffer): string {
|
export function decodeUtf8(buffer: Uint8Array): string {
|
||||||
return Utf8Decoder.decode(buffer);
|
return Utf8Decoder.decode(buffer);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue