mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-03 01:39:21 +02:00
refactor(adb): simplify sync request/response ID handling
This commit is contained in:
parent
c607d911e1
commit
ba956da6bd
12 changed files with 90 additions and 74 deletions
33
libraries/adb/src/commands/sync/id-common.ts
Normal file
33
libraries/adb/src/commands/sync/id-common.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { getUint32LittleEndian } from "@yume-chan/no-data-view";
|
||||
|
||||
function encodeAsciiUnchecked(value: string): Uint8Array<ArrayBuffer> {
|
||||
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.
|
||||
*
|
||||
* This function skips all checks. The caller must ensure the input is valid.
|
||||
*
|
||||
* @param value A 4 ASCII character string.
|
||||
* @returns A 32-bit integer by encoding the string as little-endian
|
||||
*
|
||||
* #__NO_SIDE_EFFECTS__
|
||||
*/
|
||||
export function adbSyncEncodeId(value: string): number {
|
||||
const buffer = encodeAsciiUnchecked(value);
|
||||
return getUint32LittleEndian(buffer, 0);
|
||||
}
|
||||
|
||||
// https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/adb/file_sync_protocol.h;l=23;drc=888a54dcbf954fdffacc8283a793290abcc589cd
|
||||
|
||||
export const Lstat = adbSyncEncodeId("STAT");
|
||||
export const Stat = adbSyncEncodeId("STA2");
|
||||
export const LstatV2 = adbSyncEncodeId("LST2");
|
||||
|
||||
export const Done = adbSyncEncodeId("DONE");
|
||||
export const Data = adbSyncEncodeId("DATA");
|
12
libraries/adb/src/commands/sync/id-request.ts
Normal file
12
libraries/adb/src/commands/sync/id-request.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { adbSyncEncodeId } from "./id-common.js";
|
||||
|
||||
export { Data, Done, Lstat, LstatV2, Stat } from "./id-common.js";
|
||||
|
||||
// https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/adb/file_sync_protocol.h;l=23;drc=888a54dcbf954fdffacc8283a793290abcc589cd
|
||||
|
||||
export const List = adbSyncEncodeId("LIST");
|
||||
export const ListV2 = adbSyncEncodeId("LIS2");
|
||||
|
||||
export const Send = adbSyncEncodeId("SEND");
|
||||
export const SendV2 = adbSyncEncodeId("SND2");
|
||||
export const Receive = adbSyncEncodeId("RECV");
|
11
libraries/adb/src/commands/sync/id-response.ts
Normal file
11
libraries/adb/src/commands/sync/id-response.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { adbSyncEncodeId } from "./id-common.js";
|
||||
|
||||
export { Data, Done, Lstat, LstatV2, Stat } from "./id-common.js";
|
||||
|
||||
// https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/adb/file_sync_protocol.h;l=23;drc=888a54dcbf954fdffacc8283a793290abcc589cd
|
||||
|
||||
export const Entry = adbSyncEncodeId("DENT");
|
||||
export const EntryV2 = adbSyncEncodeId("DNT2");
|
||||
|
||||
export const Ok = adbSyncEncodeId("OKAY");
|
||||
export const Fail = adbSyncEncodeId("FAIL");
|
12
libraries/adb/src/commands/sync/id.ts
Normal file
12
libraries/adb/src/commands/sync/id.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import * as AdbSyncRequestId from "./id-request.js";
|
||||
import * as AdbSyncResponseId from "./id-response.js";
|
||||
|
||||
// biome-ignore lint/suspicious/noRedeclare: TypeScript declaration merging for enum-like object
|
||||
type AdbSyncRequestId =
|
||||
(typeof AdbSyncRequestId)[keyof typeof AdbSyncRequestId];
|
||||
|
||||
// biome-ignore lint/suspicious/noRedeclare: TypeScript declaration merging for enum-like object
|
||||
type AdbSyncResponseId =
|
||||
(typeof AdbSyncResponseId)[keyof typeof AdbSyncResponseId];
|
||||
|
||||
export { AdbSyncRequestId, AdbSyncResponseId };
|
|
@ -1,3 +1,4 @@
|
|||
export * from "./id.js";
|
||||
export * from "./list.js";
|
||||
export * from "./pull.js";
|
||||
export * from "./push.js";
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import type { StructValue } from "@yume-chan/struct";
|
||||
import { extend, string, u32 } from "@yume-chan/struct";
|
||||
|
||||
import { AdbSyncRequestId, adbSyncWriteRequest } from "./request.js";
|
||||
import { AdbSyncResponseId, adbSyncReadResponses } from "./response.js";
|
||||
import { AdbSyncRequestId, AdbSyncResponseId } from "./id.js";
|
||||
import { adbSyncWriteRequest } from "./request.js";
|
||||
import { adbSyncReadResponses } from "./response.js";
|
||||
import type { AdbSyncSocket } from "./socket.js";
|
||||
import type { AdbSyncStat } from "./stat.js";
|
||||
import {
|
||||
|
@ -36,7 +37,7 @@ export async function* adbSyncOpenDirV2(
|
|||
await adbSyncWriteRequest(locked, AdbSyncRequestId.ListV2, path);
|
||||
for await (const item of adbSyncReadResponses(
|
||||
locked,
|
||||
AdbSyncResponseId.Entry2,
|
||||
AdbSyncResponseId.EntryV2,
|
||||
AdbSyncEntry2Response,
|
||||
)) {
|
||||
// `LST2` can return error codes for failed `lstat` calls.
|
||||
|
|
|
@ -2,8 +2,9 @@ import { ReadableStream } from "@yume-chan/stream-extra";
|
|||
import type { StructValue } from "@yume-chan/struct";
|
||||
import { buffer, struct, u32 } from "@yume-chan/struct";
|
||||
|
||||
import { AdbSyncRequestId, adbSyncWriteRequest } from "./request.js";
|
||||
import { adbSyncReadResponses, AdbSyncResponseId } from "./response.js";
|
||||
import { AdbSyncRequestId, AdbSyncResponseId } from "./id.js";
|
||||
import { adbSyncWriteRequest } from "./request.js";
|
||||
import { adbSyncReadResponses } from "./response.js";
|
||||
import type { AdbSyncSocket } from "./socket.js";
|
||||
|
||||
export const AdbSyncDataResponse = struct(
|
||||
|
|
|
@ -8,8 +8,9 @@ import { struct, u32 } from "@yume-chan/struct";
|
|||
|
||||
import { NOOP } from "../../utils/index.js";
|
||||
|
||||
import { AdbSyncRequestId, adbSyncWriteRequest } from "./request.js";
|
||||
import { AdbSyncResponseId, adbSyncReadResponse } from "./response.js";
|
||||
import { AdbSyncRequestId, AdbSyncResponseId } from "./id.js";
|
||||
import { adbSyncWriteRequest } from "./request.js";
|
||||
import { adbSyncReadResponse } from "./response.js";
|
||||
import type { AdbSyncSocket, AdbSyncSocketLocked } from "./socket.js";
|
||||
import { LinuxFileType } from "./stat.js";
|
||||
|
||||
|
|
|
@ -1,20 +1,5 @@
|
|||
import { encodeUtf8, struct, u32 } from "@yume-chan/struct";
|
||||
|
||||
import { adbSyncEncodeId } from "./response.js";
|
||||
|
||||
export const AdbSyncRequestId = {
|
||||
List: adbSyncEncodeId("LIST"),
|
||||
ListV2: adbSyncEncodeId("LIS2"),
|
||||
Send: adbSyncEncodeId("SEND"),
|
||||
SendV2: adbSyncEncodeId("SND2"),
|
||||
Lstat: adbSyncEncodeId("STAT"),
|
||||
Stat: adbSyncEncodeId("STA2"),
|
||||
LstatV2: adbSyncEncodeId("LST2"),
|
||||
Data: adbSyncEncodeId("DATA"),
|
||||
Done: adbSyncEncodeId("DONE"),
|
||||
Receive: adbSyncEncodeId("RECV"),
|
||||
} as const;
|
||||
|
||||
export const AdbSyncNumberRequest = struct(
|
||||
{ id: u32, arg: u32 },
|
||||
{ littleEndian: true },
|
||||
|
@ -26,13 +11,9 @@ export interface AdbSyncWritable {
|
|||
|
||||
export async function adbSyncWriteRequest(
|
||||
writable: AdbSyncWritable,
|
||||
id: number | string,
|
||||
id: number,
|
||||
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 }),
|
||||
|
|
|
@ -2,39 +2,9 @@ import { getUint32LittleEndian } from "@yume-chan/no-data-view";
|
|||
import type { AsyncExactReadable, StructDeserializer } from "@yume-chan/struct";
|
||||
import { decodeUtf8, string, struct, u32 } from "@yume-chan/struct";
|
||||
|
||||
import { unreachable } from "../../utils/no-op.js";
|
||||
import { unreachable } from "../../utils/index.js";
|
||||
|
||||
function encodeAsciiUnchecked(value: string): Uint8Array<ArrayBuffer> {
|
||||
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
|
||||
*
|
||||
* #__NO_SIDE_EFFECTS__
|
||||
*/
|
||||
export function adbSyncEncodeId(value: string): number {
|
||||
const buffer = encodeAsciiUnchecked(value);
|
||||
return getUint32LittleEndian(buffer, 0);
|
||||
}
|
||||
|
||||
export const AdbSyncResponseId = {
|
||||
Entry: adbSyncEncodeId("DENT"),
|
||||
Entry2: adbSyncEncodeId("DNT2"),
|
||||
Lstat: adbSyncEncodeId("STAT"),
|
||||
Stat: adbSyncEncodeId("STA2"),
|
||||
Lstat2: adbSyncEncodeId("LST2"),
|
||||
Done: adbSyncEncodeId("DONE"),
|
||||
Data: adbSyncEncodeId("DATA"),
|
||||
Ok: adbSyncEncodeId("OKAY"),
|
||||
Fail: adbSyncEncodeId("FAIL"),
|
||||
};
|
||||
import { AdbSyncResponseId } from "./id.js";
|
||||
|
||||
export class AdbSyncError extends Error {}
|
||||
|
||||
|
@ -50,18 +20,14 @@ export const AdbSyncFailResponse = struct(
|
|||
|
||||
export async function adbSyncReadResponse<T>(
|
||||
stream: AsyncExactReadable,
|
||||
id: number | string,
|
||||
id: number,
|
||||
type: StructDeserializer<T>,
|
||||
): Promise<T> {
|
||||
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");
|
||||
unreachable();
|
||||
case id:
|
||||
return await type.deserialize(stream);
|
||||
default:
|
||||
|
@ -73,13 +39,9 @@ export async function adbSyncReadResponse<T>(
|
|||
|
||||
export async function* adbSyncReadResponses<T>(
|
||||
stream: AsyncExactReadable,
|
||||
id: number | string,
|
||||
id: number,
|
||||
type: StructDeserializer<T>,
|
||||
): AsyncGenerator<T, void, void> {
|
||||
if (typeof id === "string") {
|
||||
id = adbSyncEncodeId(id);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const buffer = await stream.readExactly(4);
|
||||
switch (getUint32LittleEndian(buffer, 0)) {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import type { StructValue } from "@yume-chan/struct";
|
||||
import { struct, u32, u64 } from "@yume-chan/struct";
|
||||
|
||||
import { AdbSyncRequestId, adbSyncWriteRequest } from "./request.js";
|
||||
import { AdbSyncResponseId, adbSyncReadResponse } from "./response.js";
|
||||
import { AdbSyncRequestId, AdbSyncResponseId } from "./id.js";
|
||||
import { adbSyncWriteRequest } from "./request.js";
|
||||
import { adbSyncReadResponse } from "./response.js";
|
||||
import type { AdbSyncSocket } from "./socket.js";
|
||||
|
||||
// https://github.com/python/cpython/blob/4e581d64b8aff3e2eda99b12f080c877bb78dfca/Lib/stat.py#L36
|
||||
|
@ -131,7 +132,7 @@ export async function adbSyncLstat(
|
|||
await adbSyncWriteRequest(locked, AdbSyncRequestId.LstatV2, path);
|
||||
return await adbSyncReadResponse(
|
||||
locked,
|
||||
AdbSyncResponseId.Lstat2,
|
||||
AdbSyncResponseId.LstatV2,
|
||||
AdbSyncStatResponse,
|
||||
);
|
||||
} else {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as AdbFeature from "./features-value.js";
|
||||
|
||||
// enum
|
||||
// biome-ignore lint/suspicious/noRedeclare: TypeScript declaration merging for enum-like object
|
||||
type AdbFeature = (typeof AdbFeature)[keyof typeof AdbFeature];
|
||||
|
||||
export { AdbFeature };
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue