mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-03 09:49:24 +02:00
refactor: code cleanup
This commit is contained in:
parent
c440e83828
commit
721b6c0da6
68 changed files with 1161 additions and 1036 deletions
|
@ -40,13 +40,13 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@jest/globals": "^30.0.0-alpha.4",
|
||||
"@types/node": "^20.12.12",
|
||||
"@types/node": "^20.13.0",
|
||||
"@yume-chan/eslint-config": "workspace:^1.0.0",
|
||||
"@yume-chan/tsconfig": "workspace:^1.0.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"jest": "^30.0.0-alpha.4",
|
||||
"prettier": "^3.2.5",
|
||||
"ts-jest": "^29.1.2",
|
||||
"prettier": "^3.3.0",
|
||||
"ts-jest": "^29.1.4",
|
||||
"typescript": "^5.4.5"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import { BufferedReadableStream } from "@yume-chan/stream-extra";
|
|||
import Struct, { ExactReadableEndedError, encodeUtf8 } from "@yume-chan/struct";
|
||||
|
||||
import type { Adb, AdbIncomingSocketHandler } from "../adb.js";
|
||||
import { hexToNumber } from "../utils/index.js";
|
||||
import { hexToNumber, sequenceEqual } from "../utils/index.js";
|
||||
|
||||
export interface AdbForwardListener {
|
||||
deviceSerial: string;
|
||||
|
@ -82,10 +82,8 @@ export class AdbReverseCommand extends AutoDisposable {
|
|||
const stream = await this.createBufferedStream(service);
|
||||
|
||||
const response = await stream.readExactly(4);
|
||||
for (let i = 0; i < 4; i += 1) {
|
||||
if (response[i] !== OKAY[i]) {
|
||||
await AdbReverseErrorResponse.deserialize(stream);
|
||||
}
|
||||
if (!sequenceEqual(response, OKAY)) {
|
||||
await AdbReverseErrorResponse.deserialize(stream);
|
||||
}
|
||||
|
||||
return stream;
|
||||
|
|
|
@ -48,7 +48,7 @@ export async function adbSyncWriteRequest(
|
|||
// `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 }),
|
||||
AdbSyncNumberRequest.serialize({ id, arg: value.length }),
|
||||
);
|
||||
await writable.write(value);
|
||||
}
|
||||
|
|
|
@ -4,9 +4,7 @@ import type {
|
|||
StructLike,
|
||||
StructValueType,
|
||||
} from "@yume-chan/struct";
|
||||
import Struct from "@yume-chan/struct";
|
||||
|
||||
import { decodeUtf8 } from "../../utils/index.js";
|
||||
import Struct, { decodeUtf8 } from "@yume-chan/struct";
|
||||
|
||||
function encodeAsciiUnchecked(value: string): Uint8Array {
|
||||
const result = new Uint8Array(value.length);
|
||||
|
|
46
libraries/adb/src/commands/sync/socket.spec.ts
Normal file
46
libraries/adb/src/commands/sync/socket.spec.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
import { describe, expect, it } from "@jest/globals";
|
||||
import { ReadableStream, WritableStream } from "@yume-chan/stream-extra";
|
||||
|
||||
import { AdbSyncSocket } from "./socket.js";
|
||||
|
||||
describe("AdbSyncSocket", () => {
|
||||
describe("lock", () => {
|
||||
it("should wait for the previous lock to be released", async () => {
|
||||
const result: number[] = [];
|
||||
|
||||
const socket = new AdbSyncSocket(
|
||||
{
|
||||
service: "",
|
||||
close() {},
|
||||
closed: Promise.resolve(),
|
||||
readable: new ReadableStream(),
|
||||
writable: new WritableStream(),
|
||||
},
|
||||
1024,
|
||||
);
|
||||
|
||||
const locked = await socket.lock();
|
||||
result.push(1);
|
||||
|
||||
void socket.lock().then((locked) => {
|
||||
result.push(3);
|
||||
locked.release();
|
||||
});
|
||||
|
||||
// Queue some microtasks to allow the above `then` callback run (although it shouldn't)
|
||||
for (let i = 0; i < 10; i += 1) {
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
locked.release();
|
||||
result.push(2);
|
||||
|
||||
// Queue some microtasks to allow the above `then` callback run
|
||||
for (let i = 0; i < 10; i += 1) {
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
expect(result).toEqual([1, 2, 3]);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -47,7 +47,7 @@ export class AdbTcpIpCommand extends AdbCommandBase {
|
|||
|
||||
async setPort(port: number): Promise<string> {
|
||||
if (port <= 0) {
|
||||
throw new Error(`Invalid port ${port}`);
|
||||
throw new TypeError(`Invalid port ${port}`);
|
||||
}
|
||||
|
||||
const output = await this.adb.createSocketAndWait(`tcpip:${port}`);
|
||||
|
|
|
@ -175,8 +175,8 @@ export function adbGeneratePublicKey(
|
|||
output = new Uint8Array(outputLength);
|
||||
outputType = "Uint8Array";
|
||||
} else {
|
||||
if (output.byteLength < outputLength) {
|
||||
throw new Error("output buffer is too small");
|
||||
if (output.length < outputLength) {
|
||||
throw new TypeError("output buffer is too small");
|
||||
}
|
||||
|
||||
outputType = "number";
|
||||
|
@ -185,7 +185,7 @@ export function adbGeneratePublicKey(
|
|||
const outputView = new DataView(
|
||||
output.buffer,
|
||||
output.byteOffset,
|
||||
output.byteLength,
|
||||
output.length,
|
||||
);
|
||||
let outputOffset = 0;
|
||||
|
||||
|
|
|
@ -16,10 +16,9 @@ import {
|
|||
Consumable,
|
||||
WritableStream,
|
||||
} from "@yume-chan/stream-extra";
|
||||
import { EMPTY_UINT8_ARRAY } from "@yume-chan/struct";
|
||||
import { EMPTY_UINT8_ARRAY, decodeUtf8, encodeUtf8 } from "@yume-chan/struct";
|
||||
|
||||
import type { AdbIncomingSocketHandler, AdbSocket, Closeable } from "../adb.js";
|
||||
import { decodeUtf8, encodeUtf8 } from "../utils/index.js";
|
||||
|
||||
import type { AdbPacketData, AdbPacketInit } from "./packet.js";
|
||||
import { AdbCommand, calculateChecksum } from "./packet.js";
|
||||
|
@ -219,14 +218,14 @@ export class AdbPacketDispatcher implements Closeable {
|
|||
#handleOkay(packet: AdbPacketData) {
|
||||
let ackBytes: number;
|
||||
if (this.options.initialDelayedAckBytes !== 0) {
|
||||
if (packet.payload.byteLength !== 4) {
|
||||
if (packet.payload.length !== 4) {
|
||||
throw new Error(
|
||||
"Invalid OKAY packet. Payload size should be 4",
|
||||
);
|
||||
}
|
||||
ackBytes = getUint32LittleEndian(packet.payload, 0);
|
||||
} else {
|
||||
if (packet.payload.byteLength !== 0) {
|
||||
if (packet.payload.length !== 0) {
|
||||
throw new Error(
|
||||
"Invalid OKAY packet. Payload size should be 0",
|
||||
);
|
||||
|
@ -429,8 +428,8 @@ export class AdbPacketDispatcher implements Closeable {
|
|||
payload = encodeUtf8(payload);
|
||||
}
|
||||
|
||||
if (payload.byteLength > this.options.maxPayloadSize) {
|
||||
throw new Error("payload too large");
|
||||
if (payload.length > this.options.maxPayloadSize) {
|
||||
throw new TypeError("payload too large");
|
||||
}
|
||||
|
||||
await Consumable.WritableStream.write(this.#writer, {
|
||||
|
|
|
@ -59,14 +59,14 @@ export class AdbPacketSerializeStream extends TransformStream<
|
|||
transform: async (chunk, controller) => {
|
||||
await chunk.tryConsume(async (chunk) => {
|
||||
const init = chunk as AdbPacketInit & AdbPacketHeaderInit;
|
||||
init.payloadLength = init.payload.byteLength;
|
||||
init.payloadLength = init.payload.length;
|
||||
|
||||
await Consumable.ReadableStream.enqueue(
|
||||
controller,
|
||||
AdbPacketHeader.serialize(init, headerBuffer),
|
||||
);
|
||||
|
||||
if (init.payload.byteLength) {
|
||||
if (init.payloadLength) {
|
||||
// USB protocol preserves packet boundaries,
|
||||
// so we must write payload separately as native ADB does,
|
||||
// otherwise the read operation on device will fail.
|
||||
|
|
|
@ -110,7 +110,7 @@ export class AdbDaemonSocketController
|
|||
}
|
||||
|
||||
async #writeChunk(data: Uint8Array, signal: AbortSignal) {
|
||||
const length = data.byteLength;
|
||||
const length = data.length;
|
||||
while (this.#availableWriteBytes < length) {
|
||||
// Only one lock is required because Web Streams API guarantees
|
||||
// that `write` is not reentrant.
|
||||
|
|
|
@ -215,11 +215,10 @@ export class AdbDaemonTransport implements AdbTransport {
|
|||
)
|
||||
.then(
|
||||
() => {
|
||||
if (resolver.state === "running") {
|
||||
resolver.reject(
|
||||
new Error("Connection closed unexpectedly"),
|
||||
);
|
||||
}
|
||||
// If `resolver` is already settled, call `reject` won't do anything.
|
||||
resolver.reject(
|
||||
new Error("Connection closed unexpectedly"),
|
||||
);
|
||||
},
|
||||
(e) => {
|
||||
resolver.reject(e);
|
||||
|
@ -333,7 +332,7 @@ export class AdbDaemonTransport implements AdbTransport {
|
|||
|
||||
if (features.includes(AdbFeature.DelayedAck)) {
|
||||
if (initialDelayedAckBytes <= 0) {
|
||||
throw new Error(
|
||||
throw new TypeError(
|
||||
"`initialDelayedAckBytes` must be greater than 0 when DelayedAck feature is enabled.",
|
||||
);
|
||||
}
|
||||
|
|
|
@ -23,24 +23,15 @@ import {
|
|||
import type { AdbIncomingSocketHandler, AdbSocket, Closeable } from "../adb.js";
|
||||
import { AdbBanner } from "../banner.js";
|
||||
import type { AdbFeature } from "../features.js";
|
||||
import { NOOP, hexToNumber, write4HexDigits } from "../utils/index.js";
|
||||
import {
|
||||
NOOP,
|
||||
hexToNumber,
|
||||
sequenceEqual,
|
||||
write4HexDigits,
|
||||
} from "../utils/index.js";
|
||||
|
||||
import { AdbServerTransport } from "./transport.js";
|
||||
|
||||
function sequenceEqual(a: Uint8Array, b: Uint8Array): boolean {
|
||||
if (a.length !== b.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < a.length; i += 1) {
|
||||
if (a[i] !== b[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const OKAY = encodeUtf8("OKAY");
|
||||
const FAIL = encodeUtf8("FAIL");
|
||||
|
||||
|
@ -108,8 +99,8 @@ class AdbServerStream {
|
|||
const response = await this.readExactly(4);
|
||||
if (sequenceEqual(response, OKAY)) {
|
||||
// `OKAY` is followed by data length and data
|
||||
// But different services want to read the data differently
|
||||
// So we don't read the data here
|
||||
// But different services want to parse the data differently
|
||||
// So don't read the data here
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -421,7 +412,7 @@ export class AdbServerClient {
|
|||
if ("tcp" in device) {
|
||||
return `host-local:${command}`;
|
||||
}
|
||||
throw new Error("Invalid device selector");
|
||||
throw new TypeError("Invalid device selector");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -507,7 +498,7 @@ export class AdbServerClient {
|
|||
} else if ("tcp" in device) {
|
||||
switchService = `host:tport:local`;
|
||||
} else {
|
||||
throw new Error("Invalid device selector");
|
||||
throw new TypeError("Invalid device selector");
|
||||
}
|
||||
|
||||
const connection = await this.createConnection(switchService);
|
||||
|
@ -576,7 +567,7 @@ export class AdbServerClient {
|
|||
} else if ("tcp" in device) {
|
||||
type = "local";
|
||||
} else {
|
||||
throw new Error("Invalid device selector");
|
||||
throw new TypeError("Invalid device selector");
|
||||
}
|
||||
|
||||
// `waitFor` can't use `connectDevice`, because the device
|
||||
|
|
|
@ -66,7 +66,7 @@ export function encodeBase64(
|
|||
return output;
|
||||
} else {
|
||||
if (output.length < outputLength) {
|
||||
throw new Error("output buffer is too small");
|
||||
throw new TypeError("output buffer is too small");
|
||||
}
|
||||
|
||||
output = output.subarray(0, outputLength);
|
||||
|
@ -124,7 +124,7 @@ export function encodeBase64(
|
|||
// Input is in the middle of output,
|
||||
// It's not possible to read either the first or the last three bytes
|
||||
// before they are overwritten by the output.
|
||||
throw new Error("input and output cannot overlap");
|
||||
throw new TypeError("input and output cannot overlap");
|
||||
}
|
||||
|
||||
return outputLength;
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
import { PromiseResolver } from "@yume-chan/async";
|
||||
import type { Disposable } from "@yume-chan/event";
|
||||
|
||||
interface WaitEntry {
|
||||
condition: () => boolean;
|
||||
resolver: PromiseResolver<void>;
|
||||
}
|
||||
|
||||
export class ConditionalVariable implements Disposable {
|
||||
#locked = false;
|
||||
readonly #queue: WaitEntry[] = [];
|
||||
|
||||
wait(condition: () => boolean): Promise<void> {
|
||||
if (!this.#locked) {
|
||||
this.#locked = true;
|
||||
if (this.#queue.length === 0 && condition()) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
const resolver = new PromiseResolver<void>();
|
||||
this.#queue.push({ condition, resolver });
|
||||
return resolver.promise;
|
||||
}
|
||||
|
||||
notifyOne() {
|
||||
const entry = this.#queue.shift();
|
||||
if (entry) {
|
||||
if (entry.condition()) {
|
||||
entry.resolver.resolve();
|
||||
}
|
||||
} else {
|
||||
this.#locked = false;
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
for (const item of this.#queue) {
|
||||
item.resolver.reject(
|
||||
new Error("The ConditionalVariable has been disposed"),
|
||||
);
|
||||
}
|
||||
this.#queue.length = 0;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
function hexCharToNumber(char: number) {
|
||||
if (char < 48) {
|
||||
throw new Error(`Invalid hex char ${char}`);
|
||||
throw new TypeError(`Invalid hex char ${char}`);
|
||||
}
|
||||
if (char < 58) {
|
||||
// 0-9
|
||||
|
@ -8,7 +8,7 @@ function hexCharToNumber(char: number) {
|
|||
}
|
||||
|
||||
if (char < 65) {
|
||||
throw new Error(`Invalid hex char ${char}`);
|
||||
throw new TypeError(`Invalid hex char ${char}`);
|
||||
}
|
||||
if (char < 71) {
|
||||
// A-F
|
||||
|
@ -16,14 +16,14 @@ function hexCharToNumber(char: number) {
|
|||
}
|
||||
|
||||
if (char < 97) {
|
||||
throw new Error(`Invalid hex char ${char}`);
|
||||
throw new TypeError(`Invalid hex char ${char}`);
|
||||
}
|
||||
if (char < 103) {
|
||||
// a-f
|
||||
return char - 87;
|
||||
}
|
||||
|
||||
throw new Error(`Invalid hex char ${char}`);
|
||||
throw new TypeError(`Invalid hex char ${char}`);
|
||||
}
|
||||
|
||||
// It's 22x faster than converting `data` to string then `Number.parseInt`
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export { decodeUtf8, encodeUtf8 } from "@yume-chan/struct";
|
||||
export * from "./auto-reset-event.js";
|
||||
export * from "./base64.js";
|
||||
export * from "./conditional-variable.js";
|
||||
export * from "./hex.js";
|
||||
export * from "./no-op.js";
|
||||
export * from "./sequence-equal.js";
|
||||
|
|
13
libraries/adb/src/utils/sequence-equal.ts
Normal file
13
libraries/adb/src/utils/sequence-equal.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
export function sequenceEqual(a: Uint8Array, b: Uint8Array): boolean {
|
||||
if (a.length !== b.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < a.length; i += 1) {
|
||||
if (a[i] !== b[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue