Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 2db3e8f8 authored by Simon Chan's avatar Simon Chan
Browse files

refactor(adb): pre-encode packet ID as number in sync command

parent 25efbe64
Loading
Loading
Loading
Loading
+2 −4
Original line number Diff line number Diff line
@@ -17,8 +17,7 @@ export interface AdbSyncEntry extends AdbSyncStat {
export const AdbSyncEntryResponse = new Struct({ littleEndian: true })
    .concat(AdbSyncLstatResponse)
    .uint32("nameLength")
    .string("name", { lengthField: "nameLength" })
    .extra({ id: AdbSyncResponseId.Entry as const });
    .string("name", { lengthField: "nameLength" });

export type AdbSyncEntryResponse =
    (typeof AdbSyncEntryResponse)["TDeserializeResult"];
@@ -26,8 +25,7 @@ export type AdbSyncEntryResponse =
export const AdbSyncEntry2Response = new Struct({ littleEndian: true })
    .concat(AdbSyncStatResponse)
    .uint32("nameLength")
    .string("name", { lengthField: "nameLength" })
    .extra({ id: AdbSyncResponseId.Entry2 as const });
    .string("name", { lengthField: "nameLength" });

export type AdbSyncEntry2Response =
    (typeof AdbSyncEntry2Response)["TDeserializeResult"];
+2 −2
Original line number Diff line number Diff line
@@ -8,8 +8,7 @@ import type { AdbSyncSocket } from "./socket.js";

export const AdbSyncDataResponse = new Struct({ littleEndian: true })
    .uint32("dataLength")
    .uint8Array("data", { lengthField: "dataLength" })
    .extra({ id: AdbSyncResponseId.Data as const });
    .uint8Array("data", { lengthField: "dataLength" });

export type AdbSyncDataResponse =
    (typeof AdbSyncDataResponse)["TDeserializeResult"];
@@ -52,6 +51,7 @@ export function adbSyncPull(
    socket: AdbSyncSocket,
    path: string,
): ReadableStream<Uint8Array> {
    // TODO: use `ReadableStream.from` when it's supported
    return new PushReadableStream(async (controller) => {
        for await (const data of adbSyncPullGenerator(socket, path)) {
            await controller.enqueue(data);
+1 −1
Original line number Diff line number Diff line
@@ -111,7 +111,7 @@ export interface AdbSyncPushV2Options extends AdbSyncPushV1Options {
}

export const AdbSyncSendV2Request = new Struct({ littleEndian: true })
    .uint32("id", placeholder<AdbSyncRequestId>())
    .uint32("id")
    .uint32("mode")
    .uint32("flags", placeholder<AdbSyncSendV2Flags>());

+21 −16
Original line number Diff line number Diff line
@@ -2,21 +2,23 @@ import Struct from "@yume-chan/struct";

import { encodeUtf8 } from "../../utils/index.js";

export enum AdbSyncRequestId {
    List = "LIST",
    ListV2 = "LIS2",
    Send = "SEND",
    SendV2 = "SND2",
    Lstat = "STAT",
    Stat = "STA2",
    LstatV2 = "LST2",
    Data = "DATA",
    Done = "DONE",
    Receive = "RECV",
import { adbSyncEncodeId } from "./response.js";

export namespace AdbSyncRequestId {
    export const List = adbSyncEncodeId("LIST");
    export const ListV2 = adbSyncEncodeId("LIS2");
    export const Send = adbSyncEncodeId("SEND");
    export const SendV2 = adbSyncEncodeId("SND2");
    export const Lstat = adbSyncEncodeId("STAT");
    export const Stat = adbSyncEncodeId("STA2");
    export const LstatV2 = adbSyncEncodeId("LST2");
    export const Data = adbSyncEncodeId("DATA");
    export const Done = adbSyncEncodeId("DONE");
    export const Receive = adbSyncEncodeId("RECV");
}

export const AdbSyncNumberRequest = new Struct({ littleEndian: true })
    .string("id", { length: 4 })
    .uint32("id")
    .uint32("arg");

export interface AdbSyncWritable {
@@ -25,9 +27,13 @@ export interface AdbSyncWritable {

export async function adbSyncWriteRequest(
    writable: AdbSyncWritable,
    id: AdbSyncRequestId | string,
    id: number | string,
    value: number | string | Uint8Array,
): Promise<void> {
    if (typeof id === "string") {
        id = adbSyncEncodeId(id);
    }

    if (typeof value === "number") {
        await writable.write(
            AdbSyncNumberRequest.serialize({ id, arg: value }),
@@ -39,9 +45,8 @@ export async function adbSyncWriteRequest(
        value = encodeUtf8(value);
    }

    // `writable` will copy inputs to an internal buffer,
    // so we write header and `buffer` separately,
    // to avoid an extra copy.
    // `writable` is buffered, it copies inputs to an internal buffer,
    // so don't concatenate headers and data here, that will be an unnecessary copy.
    await writable.write(
        AdbSyncNumberRequest.serialize({ id, arg: value.byteLength }),
    );
+47 −18
Original line number Diff line number Diff line
import { getUint32LittleEndian } from "@yume-chan/no-data-view";
import type {
    AsyncExactReadable,
    StructLike,
@@ -7,16 +8,34 @@ import Struct from "@yume-chan/struct";

import { decodeUtf8 } from "../../utils/index.js";

export enum AdbSyncResponseId {
    Entry = "DENT",
    Entry2 = "DNT2",
    Lstat = "STAT",
    Stat = "STA2",
    Lstat2 = "LST2",
    Done = "DONE",
    Data = "DATA",
    Ok = "OKAY",
    Fail = "FAIL",
function encodeAsciiUnchecked(value: string): Uint8Array {
    const result = new Uint8Array(value.length);
    for (let i = 0; i < value.length; i += 1) {
        result[i] = value.charCodeAt(i);
    }
    return result;
}

/**
 * Encode ID to numbers for faster comparison
 * @param value A 4-character string
 * @returns A 32-bit integer by encoding the string as little-endian
 */
export function adbSyncEncodeId(value: string): number {
    const buffer = encodeAsciiUnchecked(value);
    return getUint32LittleEndian(buffer, 0);
}

export namespace AdbSyncResponseId {
    export const Entry = adbSyncEncodeId("DENT");
    export const Entry2 = adbSyncEncodeId("DNT2");
    export const Lstat = adbSyncEncodeId("STAT");
    export const Stat = adbSyncEncodeId("STA2");
    export const Lstat2 = adbSyncEncodeId("LST2");
    export const Done = adbSyncEncodeId("DONE");
    export const Data = adbSyncEncodeId("DATA");
    export const Ok = adbSyncEncodeId("OKAY");
    export const Fail = adbSyncEncodeId("FAIL");
}

export class AdbSyncError extends Error {}
@@ -30,18 +49,24 @@ export const AdbSyncFailResponse = new Struct({ littleEndian: true })

export async function adbSyncReadResponse<T>(
    stream: AsyncExactReadable,
    id: AdbSyncResponseId,
    id: number | string,
    type: StructLike<T>,
): Promise<T> {
    const actualId = decodeUtf8(await stream.readExactly(4));
    switch (actualId) {
    if (typeof id === "string") {
        id = adbSyncEncodeId(id);
    }

    const buffer = await stream.readExactly(4);
    switch (getUint32LittleEndian(buffer, 0)) {
        case AdbSyncResponseId.Fail:
            await AdbSyncFailResponse.deserialize(stream);
            throw new Error("Unreachable");
        case id:
            return await type.deserialize(stream);
        default:
            throw new Error(`Expected '${id}', but got '${actualId}'`);
            throw new Error(
                `Expected '${id}', but got '${decodeUtf8(buffer)}'`,
            );
    }
}

@@ -49,12 +74,16 @@ export async function* adbSyncReadResponses<
    T extends Struct<object, PropertyKey, object, unknown>,
>(
    stream: AsyncExactReadable,
    id: AdbSyncResponseId,
    id: number | string,
    type: T,
): AsyncGenerator<StructValueType<T>, void, void> {
    if (typeof id === "string") {
        id = adbSyncEncodeId(id);
    }

    while (true) {
        const actualId = decodeUtf8(await stream.readExactly(4));
        switch (actualId) {
        const buffer = await stream.readExactly(4);
        switch (getUint32LittleEndian(buffer, 0)) {
            case AdbSyncResponseId.Fail:
                await AdbSyncFailResponse.deserialize(stream);
                throw new Error("Unreachable");
@@ -70,7 +99,7 @@ export async function* adbSyncReadResponses<
                break;
            default:
                throw new Error(
                    `Expected '${id}' or '${AdbSyncResponseId.Done}', but got '${actualId}'`,
                    `Expected '${id}' or '${AdbSyncResponseId.Done}', but got '${decodeUtf8(buffer)}'`,
                );
        }
    }
Loading