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

Commit a8b08df6 authored by Simon Chan's avatar Simon Chan
Browse files

feat(adb): avoid small packets in sync push

parent c890cd8c
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -56,7 +56,7 @@ export class Adb implements Closeable {
        // Initially, set to highest-supported version and payload size.
        let version = 0x01000001;
        // Android 4: 4K, Android 7: 256K, Android 9: 1M
        let maxPayloadSize = 0x100000;
        let maxPayloadSize = 1024 * 1024;

        const resolver = new PromiseResolver<string>();
        const authProcessor = new AdbAuthenticationProcessor(
+1 −1
Original line number Diff line number Diff line
@@ -34,7 +34,7 @@ export async function* adbSyncOpenDirV2(
): AsyncGenerator<AdbSyncEntry2Response, void, void> {
    const locked = await socket.lock();
    try {
        await adbSyncWriteRequest(locked, AdbSyncRequestId.List2, path);
        await adbSyncWriteRequest(locked, AdbSyncRequestId.ListV2, path);
        for await (const item of adbSyncReadResponses(
            locked,
            AdbSyncResponseId.Entry2,
+84 −3
Original line number Diff line number Diff line
import type { ReadableStream } from "@yume-chan/stream-extra";
import { ChunkStream, WritableStream } from "@yume-chan/stream-extra";
import Struct from "@yume-chan/struct";
import Struct, { placeholder } from "@yume-chan/struct";

import { AdbSyncRequestId, adbSyncWriteRequest } from "./request.js";
import { AdbSyncResponseId, adbSyncReadResponse } from "./response.js";
@@ -13,7 +13,7 @@ export const AdbSyncOkResponse = new Struct({ littleEndian: true }).uint32(

export const ADB_SYNC_MAX_PACKET_SIZE = 64 * 1024;

export async function adbSyncPush(
export async function adbSyncPushV1(
    socket: AdbSyncSocket,
    filename: string,
    file: ReadableStream<Uint8Array>,
@@ -26,7 +26,7 @@ export async function adbSyncPush(
        const pathAndMode = `${filename},${mode.toString()}`;
        await adbSyncWriteRequest(locked, AdbSyncRequestId.Send, pathAndMode);

        await file.pipeThrough(new ChunkStream(packetSize)).pipeTo(
        await file.pipeThrough(new ChunkStream(packetSize, true)).pipeTo(
            new WritableStream({
                write: async (chunk) => {
                    await adbSyncWriteRequest(
@@ -48,3 +48,84 @@ export async function adbSyncPush(
        locked.release();
    }
}

export enum AdbSyncSendV2Flags {
    None = 0,
    Brotli = 1,
    /**
     * 2
     */
    Lz4 = 1 << 1,
    /**
     * 4
     */
    Zstd = 1 << 2,
    /**
     * 0x80000000
     */
    DryRun = (1 << 31) >>> 0,
}

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

export async function adbSyncPushV2(
    socket: AdbSyncSocket,
    filename: string,
    file: ReadableStream<Uint8Array>,
    mode: number = (LinuxFileType.File << 12) | 0o666,
    mtime: number = (Date.now() / 1000) | 0,
    packetSize: number = ADB_SYNC_MAX_PACKET_SIZE
) {
    const locked = await socket.lock();
    try {
        await adbSyncWriteRequest(locked, AdbSyncRequestId.SendV2, filename);

        await locked.write(
            AdbSyncSendV2Request.serialize({
                id: AdbSyncRequestId.SendV2,
                mode,
                flags: 0,
            })
        );

        await file.pipeThrough(new ChunkStream(packetSize, true)).pipeTo(
            new WritableStream({
                write: async (chunk) => {
                    await adbSyncWriteRequest(
                        locked,
                        AdbSyncRequestId.Data,
                        chunk
                    );
                },
            })
        );

        await adbSyncWriteRequest(locked, AdbSyncRequestId.Done, mtime);
        await adbSyncReadResponse(
            locked,
            AdbSyncResponseId.Ok,
            AdbSyncOkResponse
        );
    } finally {
        locked.release();
    }
}

export function adbSyncPush(
    v2: boolean,
    socket: AdbSyncSocket,
    filename: string,
    file: ReadableStream<Uint8Array>,
    mode: number = (LinuxFileType.File << 12) | 0o666,
    mtime: number = (Date.now() / 1000) | 0,
    packetSize: number = ADB_SYNC_MAX_PACKET_SIZE
) {
    if (v2) {
        return adbSyncPushV2(socket, filename, file, mode, mtime, packetSize);
    } else {
        return adbSyncPushV1(socket, filename, file, mode, mtime, packetSize);
    }
}
+3 −2
Original line number Diff line number Diff line
@@ -4,11 +4,12 @@ import { encodeUtf8 } from "../../utils/index.js";

export enum AdbSyncRequestId {
    List = "LIST",
    List2 = "LIS2",
    ListV2 = "LIS2",
    Send = "SEND",
    SendV2 = "SND2",
    Lstat = "STAT",
    Stat = "STA2",
    Lstat2 = "LST2",
    LstatV2 = "LST2",
    Data = "DATA",
    Done = "DONE",
    Receive = "RECV",
+40 −14
Original line number Diff line number Diff line
@@ -25,11 +25,15 @@ export class AdbSyncSocketLocked implements StructAsyncDeserializeStream {
        this._lock = lock;
    }

    public async flush() {
    public async flush(hard: boolean) {
        if (this._bufferedLength === 0) {
            return;
        }

        if (!hard && this._bufferedLength < this._bufferSize) {
            return;
        }

        if (this._buffered.length === 1) {
            await this._writer.write(this._buffered[0]!);
            this._buffered.length = 0;
@@ -37,6 +41,7 @@ export class AdbSyncSocketLocked implements StructAsyncDeserializeStream {
            return;
        }

        if (hard) {
            const data = new Uint8Array(this._bufferedLength);
            let offset = 0;
            for (const chunk of this._buffered) {
@@ -47,18 +52,39 @@ export class AdbSyncSocketLocked implements StructAsyncDeserializeStream {
            this._bufferedLength = 0;
            // Let AdbSocket chunk the data for us
            await this._writer.write(data);
        } else {
            while (this._bufferedLength >= this._bufferSize) {
                const data = new Uint8Array(this._bufferSize);
                let offset = 0;
                let available = this._bufferSize;
                while (offset < this._bufferSize) {
                    const chunk = this._buffered[0]!;
                    if (chunk.byteLength <= available) {
                        data.set(chunk, offset);
                        offset += chunk.byteLength;
                        available -= chunk.byteLength;
                        this._buffered.shift();
                        this._bufferedLength -= chunk.byteLength;
                    } else {
                        data.set(chunk.subarray(0, available), offset);
                        this._buffered[0] = chunk.subarray(available);
                        this._bufferedLength -= available;
                        break;
                    }
                }
                await this._writer.write(data);
            }
        }
    }

    public async write(data: Uint8Array) {
        this._buffered.push(data);
        this._bufferedLength += data.byteLength;
        if (this._bufferedLength >= this._bufferSize) {
            await this.flush();
        }
        await this.flush(false);
    }

    public async read(length: number) {
        await this.flush();
        await this.flush(true);
        return await this._readable.read(length);
    }

Loading