refactor(struct): improve tree-shaking

This commit is contained in:
Simon Chan 2024-11-01 22:06:18 +08:00
parent e8d59b232e
commit c91baa9116
No known key found for this signature in database
GPG key ID: A8B69F750B9BCEDD
32 changed files with 266 additions and 266 deletions

View file

@ -1,12 +1,12 @@
import { BufferedReadableStream } from "@yume-chan/stream-extra"; import { BufferedReadableStream } from "@yume-chan/stream-extra";
import type { StructValue } from "@yume-chan/struct"; import type { StructValue } from "@yume-chan/struct";
import { buffer, Struct, StructEmptyError, u32 } from "@yume-chan/struct"; import { buffer, struct, StructEmptyError, u32 } from "@yume-chan/struct";
import type { Adb } from "../adb.js"; import type { Adb } from "../adb.js";
const Version = new Struct({ version: u32 }, { littleEndian: true }); const Version = struct({ version: u32 }, { littleEndian: true });
export const AdbFrameBufferV1 = new Struct( export const AdbFrameBufferV1 = struct(
{ {
bpp: u32, bpp: u32,
size: u32, size: u32,
@ -27,7 +27,7 @@ export const AdbFrameBufferV1 = new Struct(
export type AdbFrameBufferV1 = StructValue<typeof AdbFrameBufferV1>; export type AdbFrameBufferV1 = StructValue<typeof AdbFrameBufferV1>;
export const AdbFrameBufferV2 = new Struct( export const AdbFrameBufferV2 = struct(
{ {
bpp: u32, bpp: u32,
colorSpace: u32, colorSpace: u32,

View file

@ -2,10 +2,10 @@
import { BufferedReadableStream } from "@yume-chan/stream-extra"; import { BufferedReadableStream } from "@yume-chan/stream-extra";
import { import {
ExactReadableEndedError,
Struct,
encodeUtf8, encodeUtf8,
ExactReadableEndedError,
string, string,
struct,
} from "@yume-chan/struct"; } from "@yume-chan/struct";
import type { Adb, AdbIncomingSocketHandler } from "../adb.js"; import type { Adb, AdbIncomingSocketHandler } from "../adb.js";
@ -19,7 +19,7 @@ export interface AdbForwardListener {
remoteName: string; remoteName: string;
} }
const AdbReverseStringResponse = new Struct( const AdbReverseStringResponse = struct(
{ {
length: string(4), length: string(4),
content: string({ content: string({
@ -38,7 +38,6 @@ const AdbReverseStringResponse = new Struct(
export class AdbReverseError extends Error { export class AdbReverseError extends Error {
constructor(message: string) { constructor(message: string) {
super(message); super(message);
Object.setPrototypeOf(this, new.target.prototype);
} }
} }
@ -50,7 +49,9 @@ export class AdbReverseNotSupportedError extends AdbReverseError {
} }
} }
const AdbReverseErrorResponse = new Struct(AdbReverseStringResponse.fields, { const AdbReverseErrorResponse = struct(
/* #__PURE__ */ (() => AdbReverseStringResponse.fields)(),
{
littleEndian: true, littleEndian: true,
postDeserialize: (value) => { postDeserialize: (value) => {
// https://issuetracker.google.com/issues/37066218 // https://issuetracker.google.com/issues/37066218
@ -62,7 +63,8 @@ const AdbReverseErrorResponse = new Struct(AdbReverseStringResponse.fields, {
throw new AdbReverseError(value.content); throw new AdbReverseError(value.content);
} }
}, },
}); },
);
// Like `hexToNumber`, it's much faster than first converting `buffer` to a string // Like `hexToNumber`, it's much faster than first converting `buffer` to a string
function decimalToNumber(buffer: Uint8Array) { function decimalToNumber(buffer: Uint8Array) {

View file

@ -11,7 +11,7 @@ import {
WritableStream, WritableStream,
} from "@yume-chan/stream-extra"; } from "@yume-chan/stream-extra";
import type { StructValue } from "@yume-chan/struct"; import type { StructValue } from "@yume-chan/struct";
import { Struct, buffer, u32, u8 } from "@yume-chan/struct"; import { buffer, struct, u32, u8 } from "@yume-chan/struct";
import type { Adb, AdbSocket } from "../../../adb.js"; import type { Adb, AdbSocket } from "../../../adb.js";
import { AdbFeature } from "../../../features.js"; import { AdbFeature } from "../../../features.js";
@ -32,7 +32,7 @@ export type AdbShellProtocolId =
(typeof AdbShellProtocolId)[keyof typeof AdbShellProtocolId]; (typeof AdbShellProtocolId)[keyof typeof AdbShellProtocolId];
// This packet format is used in both directions. // This packet format is used in both directions.
export const AdbShellProtocolPacket = new Struct( export const AdbShellProtocolPacket = struct(
{ {
id: u8.as<AdbShellProtocolId>(), id: u8.as<AdbShellProtocolId>(),
data: buffer(u32), data: buffer(u32),

View file

@ -1,5 +1,5 @@
import type { StructValue } from "@yume-chan/struct"; import type { StructValue } from "@yume-chan/struct";
import { Struct, string, u32 } from "@yume-chan/struct"; import { string, struct, u32 } from "@yume-chan/struct";
import { AdbSyncRequestId, adbSyncWriteRequest } from "./request.js"; import { AdbSyncRequestId, adbSyncWriteRequest } from "./request.js";
import { AdbSyncResponseId, adbSyncReadResponses } from "./response.js"; import { AdbSyncResponseId, adbSyncReadResponses } from "./response.js";
@ -15,21 +15,21 @@ export interface AdbSyncEntry extends AdbSyncStat {
name: string; name: string;
} }
export const AdbSyncEntryResponse = new Struct( export const AdbSyncEntryResponse = struct(
{ /* #__PURE__ */ (() => ({
...AdbSyncLstatResponse.fields, ...AdbSyncLstatResponse.fields,
name: string(u32), name: string(u32),
}, }))(),
{ littleEndian: true, extra: AdbSyncLstatResponse.extra }, { littleEndian: true, extra: AdbSyncLstatResponse.extra },
); );
export type AdbSyncEntryResponse = StructValue<typeof AdbSyncEntryResponse>; export type AdbSyncEntryResponse = StructValue<typeof AdbSyncEntryResponse>;
export const AdbSyncEntry2Response = new Struct( export const AdbSyncEntry2Response = struct(
{ /* #__PURE__ */ (() => ({
...AdbSyncStatResponse.fields, ...AdbSyncStatResponse.fields,
name: string(u32), name: string(u32),
}, }))(),
{ littleEndian: true, extra: AdbSyncStatResponse.extra }, { littleEndian: true, extra: AdbSyncStatResponse.extra },
); );

View file

@ -1,13 +1,13 @@
import type { ReadableStream } from "@yume-chan/stream-extra"; import type { ReadableStream } from "@yume-chan/stream-extra";
import { PushReadableStream } from "@yume-chan/stream-extra"; import { PushReadableStream } from "@yume-chan/stream-extra";
import type { StructValue } from "@yume-chan/struct"; import type { StructValue } from "@yume-chan/struct";
import { buffer, Struct, u32 } from "@yume-chan/struct"; import { buffer, struct, u32 } from "@yume-chan/struct";
import { AdbSyncRequestId, adbSyncWriteRequest } from "./request.js"; import { AdbSyncRequestId, adbSyncWriteRequest } from "./request.js";
import { adbSyncReadResponses, AdbSyncResponseId } from "./response.js"; import { adbSyncReadResponses, AdbSyncResponseId } from "./response.js";
import type { AdbSyncSocket } from "./socket.js"; import type { AdbSyncSocket } from "./socket.js";
export const AdbSyncDataResponse = new Struct( export const AdbSyncDataResponse = struct(
{ data: buffer(u32) }, { data: buffer(u32) },
{ littleEndian: true }, { littleEndian: true },
); );

View file

@ -4,7 +4,7 @@ import {
DistributionStream, DistributionStream,
MaybeConsumable, MaybeConsumable,
} from "@yume-chan/stream-extra"; } from "@yume-chan/stream-extra";
import { Struct, u32 } from "@yume-chan/struct"; import { struct, u32 } from "@yume-chan/struct";
import { NOOP } from "../../utils/index.js"; import { NOOP } from "../../utils/index.js";
@ -25,7 +25,7 @@ export interface AdbSyncPushV1Options {
packetSize?: number; packetSize?: number;
} }
export const AdbSyncOkResponse = new Struct( export const AdbSyncOkResponse = struct(
{ unused: u32 }, { unused: u32 },
{ littleEndian: true }, { littleEndian: true },
); );
@ -114,7 +114,7 @@ export interface AdbSyncPushV2Options extends AdbSyncPushV1Options {
dryRun?: boolean; dryRun?: boolean;
} }
export const AdbSyncSendV2Request = new Struct( export const AdbSyncSendV2Request = struct(
{ id: u32, mode: u32, flags: u32.as<AdbSyncSendV2Flags>() }, { id: u32, mode: u32, flags: u32.as<AdbSyncSendV2Flags>() },
{ littleEndian: true }, { littleEndian: true },
); );

View file

@ -1,4 +1,4 @@
import { encodeUtf8, Struct, u32 } from "@yume-chan/struct"; import { encodeUtf8, struct, u32 } from "@yume-chan/struct";
import { adbSyncEncodeId } from "./response.js"; import { adbSyncEncodeId } from "./response.js";
@ -15,7 +15,7 @@ export const AdbSyncRequestId = {
Receive: adbSyncEncodeId("RECV"), Receive: adbSyncEncodeId("RECV"),
} as const; } as const;
export const AdbSyncNumberRequest = new Struct( export const AdbSyncNumberRequest = struct(
{ id: u32, arg: u32 }, { id: u32, arg: u32 },
{ littleEndian: true }, { littleEndian: true },
); );

View file

@ -1,6 +1,6 @@
import { getUint32LittleEndian } from "@yume-chan/no-data-view"; import { getUint32LittleEndian } from "@yume-chan/no-data-view";
import type { AsyncExactReadable, StructLike } from "@yume-chan/struct"; import type { AsyncExactReadable, StructLike } from "@yume-chan/struct";
import { Struct, decodeUtf8, string, u32 } from "@yume-chan/struct"; import { decodeUtf8, string, struct, u32 } from "@yume-chan/struct";
function encodeAsciiUnchecked(value: string): Uint8Array { function encodeAsciiUnchecked(value: string): Uint8Array {
const result = new Uint8Array(value.length); const result = new Uint8Array(value.length);
@ -36,7 +36,7 @@ export const AdbSyncResponseId = {
export class AdbSyncError extends Error {} export class AdbSyncError extends Error {}
export const AdbSyncFailResponse = new Struct( export const AdbSyncFailResponse = struct(
{ message: string(u32) }, { message: string(u32) },
{ {
littleEndian: true, littleEndian: true,

View file

@ -1,5 +1,5 @@
import type { StructValue } from "@yume-chan/struct"; import type { StructValue } from "@yume-chan/struct";
import { Struct, u32, u64 } from "@yume-chan/struct"; import { struct, u32, u64 } from "@yume-chan/struct";
import { AdbSyncRequestId, adbSyncWriteRequest } from "./request.js"; import { AdbSyncRequestId, adbSyncWriteRequest } from "./request.js";
import { AdbSyncResponseId, adbSyncReadResponse } from "./response.js"; import { AdbSyncResponseId, adbSyncReadResponse } from "./response.js";
@ -27,7 +27,7 @@ export interface AdbSyncStat {
ctime?: bigint; ctime?: bigint;
} }
export const AdbSyncLstatResponse = new Struct( export const AdbSyncLstatResponse = struct(
{ mode: u32, size: u32, mtime: u32 }, { mode: u32, size: u32, mtime: u32 },
{ {
littleEndian: true, littleEndian: true,
@ -86,7 +86,7 @@ const AdbSyncStatErrorName =
]), ]),
); );
export const AdbSyncStatResponse = new Struct( export const AdbSyncStatResponse = struct(
{ {
error: u32.as<AdbSyncStatErrorCode>(), error: u32.as<AdbSyncStatErrorCode>(),
dev: u64, dev: u64,

View file

@ -1,6 +1,6 @@
import { Consumable, TransformStream } from "@yume-chan/stream-extra"; import { Consumable, TransformStream } from "@yume-chan/stream-extra";
import type { StructInit, StructValue } from "@yume-chan/struct"; import type { StructInit, StructValue } from "@yume-chan/struct";
import { buffer, s32, Struct, u32 } from "@yume-chan/struct"; import { buffer, s32, struct, u32 } from "@yume-chan/struct";
export const AdbCommand = { export const AdbCommand = {
Auth: 0x48545541, // 'AUTH' Auth: 0x48545541, // 'AUTH'
@ -13,7 +13,7 @@ export const AdbCommand = {
export type AdbCommand = (typeof AdbCommand)[keyof typeof AdbCommand]; export type AdbCommand = (typeof AdbCommand)[keyof typeof AdbCommand];
export const AdbPacketHeader = new Struct( export const AdbPacketHeader = struct(
{ {
command: u32, command: u32,
arg0: u32, arg0: u32,
@ -29,8 +29,11 @@ export type AdbPacketHeader = StructValue<typeof AdbPacketHeader>;
type AdbPacketHeaderInit = StructInit<typeof AdbPacketHeader>; type AdbPacketHeaderInit = StructInit<typeof AdbPacketHeader>;
export const AdbPacket = new Struct( export const AdbPacket = struct(
{ ...AdbPacketHeader.fields, payload: buffer("payloadLength") }, /* #__PURE__ */ (() => ({
...AdbPacketHeader.fields,
payload: buffer("payloadLength"),
}))(),
{ littleEndian: true }, { littleEndian: true },
); );

View file

@ -11,7 +11,7 @@ import {
WritableStream, WritableStream,
} from "@yume-chan/stream-extra"; } from "@yume-chan/stream-extra";
import type { AsyncExactReadable, StructValue } from "@yume-chan/struct"; import type { AsyncExactReadable, StructValue } from "@yume-chan/struct";
import { Struct, decodeUtf8, u16, u32 } from "@yume-chan/struct"; import { decodeUtf8, struct, u16, u32 } from "@yume-chan/struct";
// `adb logcat` is an alias to `adb shell logcat` // `adb logcat` is an alias to `adb shell logcat`
// so instead of adding to core library, it's implemented here // so instead of adding to core library, it's implemented here
@ -99,7 +99,7 @@ export interface LogcatOptions {
const NANOSECONDS_PER_SECOND = /* #__PURE__ */ BigInt(1e9); const NANOSECONDS_PER_SECOND = /* #__PURE__ */ BigInt(1e9);
// https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/include/log/log_read.h;l=39;drc=82b5738732161dbaafb2e2f25cce19cd26b9157d // https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/include/log/log_read.h;l=39;drc=82b5738732161dbaafb2e2f25cce19cd26b9157d
export const LoggerEntry = new Struct( export const LoggerEntry = struct(
{ {
payloadSize: u16, payloadSize: u16,
headerSize: u16, headerSize: u16,

View file

@ -1,7 +1,7 @@
import type { StructInit } from "@yume-chan/struct"; import type { StructInit } from "@yume-chan/struct";
import { Struct, u8 } from "@yume-chan/struct"; import { struct, u8 } from "@yume-chan/struct";
export const EmptyControlMessage = new Struct( export const EmptyControlMessage = struct(
{ type: u8 }, { type: u8 },
{ littleEndian: false }, { littleEndian: false },
); );

View file

@ -1,5 +1,5 @@
import type { StructInit } from "@yume-chan/struct"; import type { StructInit } from "@yume-chan/struct";
import { Struct, u32, u8 } from "@yume-chan/struct"; import { struct, u32, u8 } from "@yume-chan/struct";
export enum AndroidKeyEventAction { export enum AndroidKeyEventAction {
Down = 0, Down = 0,
@ -206,7 +206,7 @@ export enum AndroidKeyCode {
AndroidPaste, AndroidPaste,
} }
export const ScrcpyInjectKeyCodeControlMessage = new Struct( export const ScrcpyInjectKeyCodeControlMessage = struct(
{ {
type: u8, type: u8,
action: u8.as<AndroidKeyEventAction>(), action: u8.as<AndroidKeyEventAction>(),

View file

@ -1,7 +1,7 @@
import type { StructInit } from "@yume-chan/struct"; import type { StructInit } from "@yume-chan/struct";
import { string, Struct, u32, u8 } from "@yume-chan/struct"; import { string, struct, u32, u8 } from "@yume-chan/struct";
export const ScrcpyInjectTextControlMessage = new Struct( export const ScrcpyInjectTextControlMessage = struct(
{ type: u8, text: string(u32) }, { type: u8, text: string(u32) },
{ littleEndian: false }, { littleEndian: false },
); );

View file

@ -1,5 +1,5 @@
import type { StructInit } from "@yume-chan/struct"; import type { StructInit } from "@yume-chan/struct";
import { Struct, u8 } from "@yume-chan/struct"; import { struct, u8 } from "@yume-chan/struct";
// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/SurfaceControl.java;l=659;drc=20303e05bf73796124ab70a279cf849b61b97905 // https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/SurfaceControl.java;l=659;drc=20303e05bf73796124ab70a279cf849b61b97905
export const AndroidScreenPowerMode = { export const AndroidScreenPowerMode = {
@ -10,7 +10,7 @@ export const AndroidScreenPowerMode = {
export type AndroidScreenPowerMode = export type AndroidScreenPowerMode =
(typeof AndroidScreenPowerMode)[keyof typeof AndroidScreenPowerMode]; (typeof AndroidScreenPowerMode)[keyof typeof AndroidScreenPowerMode];
export const ScrcpySetScreenPowerModeControlMessage = new Struct( export const ScrcpySetScreenPowerModeControlMessage = struct(
{ type: u8, mode: u8.as<AndroidScreenPowerMode>() }, { type: u8, mode: u8.as<AndroidScreenPowerMode>() },
{ littleEndian: false }, { littleEndian: false },
); );

View file

@ -1,11 +1,6 @@
import type { CodecOptions } from "./codec-options.js"; import type { CodecOptions } from "./codec-options.js";
export enum ScrcpyLogLevel1_16 { export type ScrcpyLogLevel1_16 = "debug" | "info" | "warn" | "error";
Debug = "debug",
Info = "info",
Warn = "warn",
Error = "error",
}
export enum ScrcpyVideoOrientation1_16 { export enum ScrcpyVideoOrientation1_16 {
Unlocked = -1, Unlocked = -1,

View file

@ -1,5 +1,5 @@
import type { StructInit } from "@yume-chan/struct"; import type { StructInit } from "@yume-chan/struct";
import { Struct, buffer, string, u16, u32, u64, u8 } from "@yume-chan/struct"; import { buffer, string, struct, u16, u32, u64, u8 } from "@yume-chan/struct";
import type { AndroidMotionEventAction } from "../../control/index.js"; import type { AndroidMotionEventAction } from "../../control/index.js";
import { import {
@ -24,14 +24,14 @@ export const SCRCPY_CONTROL_MESSAGE_TYPES_1_16: readonly ScrcpyControlMessageTyp
/* 10 */ ScrcpyControlMessageType.RotateDevice, /* 10 */ ScrcpyControlMessageType.RotateDevice,
]; ];
export const ScrcpyMediaStreamRawPacket = new Struct( export const ScrcpyMediaStreamRawPacket = struct(
{ pts: u64, data: buffer(u32) }, { pts: u64, data: buffer(u32) },
{ littleEndian: false }, { littleEndian: false },
); );
export const SCRCPY_MEDIA_PACKET_FLAG_CONFIG = 1n << 63n; export const SCRCPY_MEDIA_PACKET_FLAG_CONFIG = 1n << 63n;
export const ScrcpyInjectTouchControlMessage1_16 = new Struct( export const ScrcpyInjectTouchControlMessage1_16 = struct(
{ {
type: u8, type: u8,
action: u8.as<AndroidMotionEventAction>(), action: u8.as<AndroidMotionEventAction>(),
@ -52,7 +52,7 @@ export type ScrcpyInjectTouchControlMessage1_16 = StructInit<
export const ScrcpyBackOrScreenOnControlMessage1_16 = EmptyControlMessage; export const ScrcpyBackOrScreenOnControlMessage1_16 = EmptyControlMessage;
export const ScrcpySetClipboardControlMessage1_15 = new Struct( export const ScrcpySetClipboardControlMessage1_15 = struct(
{ type: u8, content: string(u32) }, { type: u8, content: string(u32) },
{ littleEndian: false }, { littleEndian: false },
); );
@ -61,7 +61,7 @@ export type ScrcpySetClipboardControlMessage1_15 = StructInit<
typeof ScrcpySetClipboardControlMessage1_15 typeof ScrcpySetClipboardControlMessage1_15
>; >;
export const ScrcpyClipboardDeviceMessage = new Struct( export const ScrcpyClipboardDeviceMessage = struct(
{ content: string(u32) }, { content: string(u32) },
{ littleEndian: false }, { littleEndian: false },
); );

View file

@ -37,7 +37,7 @@ import {
import { CodecOptions } from "./codec-options.js"; import { CodecOptions } from "./codec-options.js";
import type { ScrcpyOptionsInit1_16 } from "./init.js"; import type { ScrcpyOptionsInit1_16 } from "./init.js";
import { ScrcpyLogLevel1_16, ScrcpyVideoOrientation1_16 } from "./init.js"; import { ScrcpyVideoOrientation1_16 } from "./init.js";
import { import {
SCRCPY_CONTROL_MESSAGE_TYPES_1_16, SCRCPY_CONTROL_MESSAGE_TYPES_1_16,
SCRCPY_MEDIA_PACKET_FLAG_CONFIG, SCRCPY_MEDIA_PACKET_FLAG_CONFIG,
@ -52,7 +52,7 @@ import { ScrcpyScrollController1_16 } from "./scroll.js";
export class ScrcpyOptions1_16 extends ScrcpyOptions<ScrcpyOptionsInit1_16> { export class ScrcpyOptions1_16 extends ScrcpyOptions<ScrcpyOptionsInit1_16> {
static readonly DEFAULTS = { static readonly DEFAULTS = {
logLevel: ScrcpyLogLevel1_16.Debug, logLevel: "debug",
maxSize: 0, maxSize: 0,
bitRate: 8_000_000, bitRate: 8_000_000,
maxFps: 0, maxFps: 0,

View file

@ -1,4 +1,4 @@
import { s32, Struct, u16, u32, u8 } from "@yume-chan/struct"; import { s32, struct, u16, u32, u8 } from "@yume-chan/struct";
import type { ScrcpyInjectScrollControlMessage } from "../../control/index.js"; import type { ScrcpyInjectScrollControlMessage } from "../../control/index.js";
import { ScrcpyControlMessageType } from "../../control/index.js"; import { ScrcpyControlMessageType } from "../../control/index.js";
@ -9,7 +9,7 @@ export interface ScrcpyScrollController {
): Uint8Array | undefined; ): Uint8Array | undefined;
} }
export const ScrcpyInjectScrollControlMessage1_16 = new Struct( export const ScrcpyInjectScrollControlMessage1_16 = struct(
{ {
type: u8.as(ScrcpyControlMessageType.InjectScroll as const), type: u8.as(ScrcpyControlMessageType.InjectScroll as const),
pointerX: u32, pointerX: u32,

View file

@ -1,5 +1,5 @@
import type { StructInit } from "@yume-chan/struct"; import type { StructInit } from "@yume-chan/struct";
import { Struct, u8 } from "@yume-chan/struct"; import { struct, u8 } from "@yume-chan/struct";
import type { import type {
AndroidKeyEventAction, AndroidKeyEventAction,
@ -17,22 +17,24 @@ import { ScrcpyOptions1_17 } from "./1_17.js";
import type { ScrcpyEncoder } from "./types.js"; import type { ScrcpyEncoder } from "./types.js";
import { ScrcpyOptions } from "./types.js"; import { ScrcpyOptions } from "./types.js";
export enum ScrcpyLogLevel1_18 { export type ScrcpyLogLevel1_18 =
Verbose = "verbose", | "verbose"
Debug = "debug", | "debug"
Info = "info", | "info"
Warn = "warn", | "warn"
Error = "error", | "error";
}
export enum ScrcpyVideoOrientation1_18 { export const ScrcpyVideoOrientation1_18 = {
Initial = -2, Initial: -2,
Unlocked = -1, Unlocked: -1,
Portrait = 0, Portrait: 0,
Landscape = 1, Landscape: 1,
PortraitFlipped = 2, PortraitFlipped: 2,
LandscapeFlipped = 3, LandscapeFlipped: 3,
} };
export type ScrcpyVideoOrientation1_18 =
(typeof ScrcpyVideoOrientation1_18)[keyof typeof ScrcpyVideoOrientation1_18];
export interface ScrcpyOptionsInit1_18 export interface ScrcpyOptionsInit1_18
extends Omit<ScrcpyOptionsInit1_17, "logLevel" | "lockVideoOrientation"> { extends Omit<ScrcpyOptionsInit1_17, "logLevel" | "lockVideoOrientation"> {
@ -43,11 +45,11 @@ export interface ScrcpyOptionsInit1_18
powerOffOnClose?: boolean; powerOffOnClose?: boolean;
} }
export const ScrcpyBackOrScreenOnControlMessage1_18 = new Struct( export const ScrcpyBackOrScreenOnControlMessage1_18 = struct(
{ /* #__PURE__ */ (() => ({
...ScrcpyBackOrScreenOnControlMessage1_16.fields, ...ScrcpyBackOrScreenOnControlMessage1_16.fields,
action: u8.as<AndroidKeyEventAction>(), action: u8.as<AndroidKeyEventAction>(),
}, }))(),
{ littleEndian: false }, { littleEndian: false },
); );
@ -55,18 +57,16 @@ export type ScrcpyBackOrScreenOnControlMessage1_18 = StructInit<
typeof ScrcpyBackOrScreenOnControlMessage1_18 typeof ScrcpyBackOrScreenOnControlMessage1_18
>; >;
export const SCRCPY_CONTROL_MESSAGE_TYPES_1_18 = export const SCRCPY_CONTROL_MESSAGE_TYPES_1_18 = /* #__PURE__ */ (() => {
SCRCPY_CONTROL_MESSAGE_TYPES_1_16.slice(); const result = SCRCPY_CONTROL_MESSAGE_TYPES_1_16.slice();
SCRCPY_CONTROL_MESSAGE_TYPES_1_18.splice( result.splice(6, 0, ScrcpyControlMessageType.ExpandSettingPanel);
6, return result;
0, })();
ScrcpyControlMessageType.ExpandSettingPanel,
);
export class ScrcpyOptions1_18 extends ScrcpyOptions<ScrcpyOptionsInit1_18> { export class ScrcpyOptions1_18 extends ScrcpyOptions<ScrcpyOptionsInit1_18> {
static readonly DEFAULTS = { static readonly DEFAULTS = {
...ScrcpyOptions1_17.DEFAULTS, ...ScrcpyOptions1_17.DEFAULTS,
logLevel: ScrcpyLogLevel1_18.Debug, logLevel: "debug",
lockVideoOrientation: ScrcpyVideoOrientation1_18.Unlocked, lockVideoOrientation: ScrcpyVideoOrientation1_18.Unlocked,
powerOffOnClose: false, powerOffOnClose: false,
} as const satisfies Required<ScrcpyOptionsInit1_18>; } as const satisfies Required<ScrcpyOptionsInit1_18>;

View file

@ -2,7 +2,7 @@
import { PromiseResolver } from "@yume-chan/async"; import { PromiseResolver } from "@yume-chan/async";
import type { AsyncExactReadable, StructInit } from "@yume-chan/struct"; import type { AsyncExactReadable, StructInit } from "@yume-chan/struct";
import { Struct, string, u32, u64, u8 } from "@yume-chan/struct"; import { string, struct, u32, u64, u8 } from "@yume-chan/struct";
import type { ScrcpySetClipboardControlMessage } from "../control/index.js"; import type { ScrcpySetClipboardControlMessage } from "../control/index.js";
@ -11,7 +11,7 @@ import type { ScrcpyOptionsInit1_18 } from "./1_18.js";
import { ScrcpyOptions1_18 } from "./1_18.js"; import { ScrcpyOptions1_18 } from "./1_18.js";
import { ScrcpyOptions, toScrcpyOptionValue } from "./types.js"; import { ScrcpyOptions, toScrcpyOptionValue } from "./types.js";
export const ScrcpyAckClipboardDeviceMessage = new Struct( export const ScrcpyAckClipboardDeviceMessage = struct(
{ sequence: u64 }, { sequence: u64 },
{ littleEndian: false }, { littleEndian: false },
); );
@ -24,7 +24,7 @@ function toSnakeCase(input: string): string {
return input.replace(/([A-Z])/g, "_$1").toLowerCase(); return input.replace(/([A-Z])/g, "_$1").toLowerCase();
} }
export const ScrcpySetClipboardControlMessage1_21 = new Struct( export const ScrcpySetClipboardControlMessage1_21 = struct(
{ {
type: u8, type: u8,
sequence: u64, sequence: u64,

View file

@ -1,13 +1,16 @@
import type { StructInit } from "@yume-chan/struct"; import type { StructInit } from "@yume-chan/struct";
import { s32, Struct } from "@yume-chan/struct"; import { s32, struct } from "@yume-chan/struct";
import { import {
ScrcpyInjectScrollControlMessage1_16, ScrcpyInjectScrollControlMessage1_16,
ScrcpyScrollController1_16, ScrcpyScrollController1_16,
} from "../1_16/index.js"; } from "../1_16/index.js";
export const ScrcpyInjectScrollControlMessage1_22 = new Struct( export const ScrcpyInjectScrollControlMessage1_22 = struct(
{ ...ScrcpyInjectScrollControlMessage1_16.fields, buttons: s32 }, /* #__PURE__ */ (() => ({
...ScrcpyInjectScrollControlMessage1_16.fields,
buttons: s32,
}))(),
{ littleEndian: false }, { littleEndian: false },
); );

View file

@ -1,6 +1,6 @@
import { getInt16, setInt16 } from "@yume-chan/no-data-view"; import { getInt16, setInt16 } from "@yume-chan/no-data-view";
import type { Field, StructInit } from "@yume-chan/struct"; import type { Field, StructInit } from "@yume-chan/struct";
import { bipedal, Struct, u16, u32, u8 } from "@yume-chan/struct"; import { bipedal, struct, u16, u32, u8 } from "@yume-chan/struct";
import type { ScrcpyInjectScrollControlMessage } from "../../control/index.js"; import type { ScrcpyInjectScrollControlMessage } from "../../control/index.js";
import { ScrcpyControlMessageType } from "../../control/index.js"; import { ScrcpyControlMessageType } from "../../control/index.js";
@ -23,7 +23,7 @@ export const ScrcpySignedFloat: Field<number, never, never> = {
}), }),
}; };
export const ScrcpyInjectScrollControlMessage1_25 = new Struct( export const ScrcpyInjectScrollControlMessage1_25 = struct(
{ {
type: u8.as(ScrcpyControlMessageType.InjectScroll as const), type: u8.as(ScrcpyControlMessageType.InjectScroll as const),
pointerX: u32, pointerX: u32,

View file

@ -5,7 +5,7 @@ import {
PushReadableStream, PushReadableStream,
} from "@yume-chan/stream-extra"; } from "@yume-chan/stream-extra";
import type { MaybePromiseLike, StructInit } from "@yume-chan/struct"; import type { MaybePromiseLike, StructInit } from "@yume-chan/struct";
import { Struct, u16, u32, u64, u8 } from "@yume-chan/struct"; import { struct, u16, u32, u64, u8 } from "@yume-chan/struct";
import type { import type {
AndroidMotionEventAction, AndroidMotionEventAction,
@ -30,7 +30,7 @@ import type {
} from "./types.js"; } from "./types.js";
import { ScrcpyOptions } from "./types.js"; import { ScrcpyOptions } from "./types.js";
export const ScrcpyInjectTouchControlMessage2_0 = new Struct( export const ScrcpyInjectTouchControlMessage2_0 = struct(
{ {
type: u8, type: u8,
action: u8.as<AndroidMotionEventAction>(), action: u8.as<AndroidMotionEventAction>(),

View file

@ -1,8 +1,8 @@
import { ScrcpyLogLevel1_18, ScrcpyVideoOrientation1_18 } from "./1_18.js"; import type { ScrcpyLogLevel1_18 } from "./1_18.js";
import { ScrcpyVideoOrientation1_18 } from "./1_18.js";
import type { ScrcpyOptionsInit2_3 } from "./2_3.js"; import type { ScrcpyOptionsInit2_3 } from "./2_3.js";
import { ScrcpyOptions2_3 } from "./2_3.js"; import { ScrcpyOptions2_3 } from "./2_3.js";
export const ScrcpyLogLevel = ScrcpyLogLevel1_18;
export type ScrcpyLogLevel = ScrcpyLogLevel1_18; export type ScrcpyLogLevel = ScrcpyLogLevel1_18;
export const ScrcpyVideoOrientation = ScrcpyVideoOrientation1_18; export const ScrcpyVideoOrientation = ScrcpyVideoOrientation1_18;

View file

@ -25,9 +25,9 @@ $ npm i @yume-chan/struct
## Quick Start ## Quick Start
```ts ```ts
import { Struct, u8, u16, s32, buffer, string } from "@yume-chan/struct"; import { struct, u8, u16, s32, buffer, string } from "@yume-chan/struct";
const Message = new Struct( const Message = struct(
{ {
a: u8, a: u8,
b: u16, b: u16,
@ -94,7 +94,7 @@ const MyField: Field<number, never, never> = {
}, },
}; };
const Message2 = new Struct({ const Message2 = struct({
a: u8, a: u8,
b: MyField, b: MyField,
}); });

View file

@ -4,15 +4,12 @@ import { describe, it } from "node:test";
import { buffer } from "./buffer.js"; import { buffer } from "./buffer.js";
import type { ExactReadable } from "./readable.js"; import type { ExactReadable } from "./readable.js";
import { ExactReadableEndedError } from "./readable.js"; import { ExactReadableEndedError } from "./readable.js";
import { Struct } from "./struct.js"; import { struct } from "./struct.js";
describe("buffer", () => { describe("buffer", () => {
describe("fixed size", () => { describe("fixed size", () => {
it("should deserialize", () => { it("should deserialize", () => {
const A = new Struct( const A = struct({ value: buffer(10) }, { littleEndian: false });
{ value: buffer(10) },
{ littleEndian: false },
);
const reader: ExactReadable = { const reader: ExactReadable = {
position: 0, position: 0,
readExactly() { readExactly() {
@ -25,10 +22,7 @@ describe("buffer", () => {
}); });
it("should throw for not enough data", () => { it("should throw for not enough data", () => {
const A = new Struct( const A = struct({ value: buffer(10) }, { littleEndian: false });
{ value: buffer(10) },
{ littleEndian: false },
);
const reader: ExactReadable = { const reader: ExactReadable = {
position: 0, position: 0,
readExactly() { readExactly() {
@ -43,10 +37,7 @@ describe("buffer", () => {
}); });
it("should throw for no data", () => { it("should throw for no data", () => {
const A = new Struct( const A = struct({ value: buffer(10) }, { littleEndian: false });
{ value: buffer(10) },
{ littleEndian: false },
);
const reader: ExactReadable = { const reader: ExactReadable = {
position: 0, position: 0,
readExactly() { readExactly() {

View file

@ -42,14 +42,16 @@ export interface BufferLike {
export const EmptyUint8Array = new Uint8Array(0); export const EmptyUint8Array = new Uint8Array(0);
export const buffer: BufferLike = (( // This is required for Rollup tree-shaking to work.
/* #__NO_SIDE_EFFECTS__ */
function _buffer(
lengthOrField: lengthOrField:
| string | string
| number | number
| Field<number, never, unknown> | Field<number, never, unknown>
| BufferLengthConverter<string, unknown>, | BufferLengthConverter<string, unknown>,
converter?: Converter<Uint8Array, unknown>, converter?: Converter<Uint8Array, unknown>,
): Field<unknown, string, Record<string, unknown>> => { ): Field<unknown, string, Record<string, unknown>> {
if (typeof lengthOrField === "number") { if (typeof lengthOrField === "number") {
if (converter) { if (converter) {
return { return {
@ -226,4 +228,6 @@ export const buffer: BufferLike = ((
return reader.readExactly(length); return reader.readExactly(length);
}, },
}; };
}) as never; }
export const buffer: BufferLike = _buffer as never;

View file

@ -1,12 +1,12 @@
import * as assert from "node:assert"; import * as assert from "node:assert";
import { describe, it } from "node:test"; import { describe, it } from "node:test";
import { Struct } from "./index.js"; import { struct } from "./index.js";
describe("Struct", () => { describe("Struct", () => {
describe("Index", () => { describe("Index", () => {
it("should export default Struct", () => { it("should export default Struct", () => {
assert.ok(Struct); assert.ok(struct);
}); });
}); });
}); });

View file

@ -25,15 +25,19 @@ export interface String {
): Field<string, KOmitInit, KS>; ): Field<string, KOmitInit, KS>;
} }
export const string: String = (( // This is required for Rollup tree-shaking to work.
/* #__NO_SIDE_EFFECTS__ */
function _string(
lengthOrField: string | number | BufferLengthConverter<string, unknown>, lengthOrField: string | number | BufferLengthConverter<string, unknown>,
): Field<string, string, Record<string, unknown>> & { ): Field<string, string, Record<string, unknown>> & {
as: <T>(infer: T) => Field<T, string, Record<string, unknown>>; as: <T>(infer: T) => Field<T, string, Record<string, unknown>>;
} => { } {
const field = buffer(lengthOrField as never, { const field = buffer(lengthOrField as never, {
convert: decodeUtf8, convert: decodeUtf8,
back: encodeUtf8, back: encodeUtf8,
}); });
(field as never as { as: unknown }).as = () => field; (field as never as { as: unknown }).as = () => field;
return field as never; return field as never;
}) as never; }
export const string: String = _string as never;

View file

@ -2,11 +2,11 @@ import * as assert from "node:assert";
import { describe, it } from "node:test"; import { describe, it } from "node:test";
import { u8 } from "./number.js"; import { u8 } from "./number.js";
import { Struct } from "./struct.js"; import { struct } from "./struct.js";
describe("Struct", () => { describe("Struct", () => {
it("serialize", () => { it("serialize", () => {
const A = new Struct({ id: u8 }, { littleEndian: true }); const A = struct({ id: u8 }, { littleEndian: true });
assert.deepStrictEqual(A.serialize({ id: 10 }), new Uint8Array([10])); assert.deepStrictEqual(A.serialize({ id: 10 }), new Uint8Array([10]));
}); });
}); });

View file

@ -29,7 +29,7 @@ export type StructInit<
export type StructValue< export type StructValue<
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
T extends Struct<any, any, any>, T extends Struct<any, any, any>,
> = ReturnType<Exclude<T["postDeserialize"], undefined>>; > = T extends Struct<any, any, infer P> ? P : never;
export class StructDeserializeError extends Error { export class StructDeserializeError extends Error {
constructor(message: string) { constructor(message: string) {
@ -55,26 +55,29 @@ export class StructEmptyError extends StructDeserializeError {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
export type StructLike<T> = Struct<any, any, T>; export type StructLike<T> = Struct<any, any, T>;
export class Struct< export interface Struct<
T extends Record<string, Field<unknown, string, Partial<FieldsType<T>>>>, T extends Record<string, Field<unknown, string, Partial<FieldsType<T>>>>,
// eslint-disable-next-line @typescript-eslint/no-empty-object-type Extra extends Record<PropertyKey, unknown> | undefined = undefined,
Extra extends Record<PropertyKey, unknown> = {},
PostDeserialize = FieldsType<T> & Extra, PostDeserialize = FieldsType<T> & Extra,
> { > {
fields: T; fields: T;
size: number; size: number;
#fieldList: [string, Field<unknown, string, unknown>][] = [];
littleEndian: boolean;
extra: Extra; extra: Extra;
postDeserialize?: serialize(runtimeStruct: StructInit<this>): Uint8Array;
| ((fields: FieldsType<T> & Extra) => PostDeserialize) serialize(runtimeStruct: StructInit<this>, buffer: Uint8Array): number;
| undefined;
constructor( deserialize(reader: ExactReadable): PostDeserialize;
deserialize(reader: AsyncExactReadable): MaybePromiseLike<PostDeserialize>;
}
/* #__NO_SIDE_EFFECTS__ */
export function struct<
T extends Record<string, Field<unknown, string, Partial<FieldsType<T>>>>,
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
Extra extends Record<PropertyKey, unknown> = {},
PostDeserialize = FieldsType<T> & Extra,
>(
fields: T, fields: T,
options: { options: {
littleEndian?: boolean; littleEndian?: boolean;
@ -84,37 +87,36 @@ export class Struct<
fields: FieldsType<T> & Extra, fields: FieldsType<T> & Extra,
) => PostDeserialize; ) => PostDeserialize;
}, },
) { ): Struct<T, Extra, PostDeserialize> {
this.#fieldList = Object.entries(fields); const fieldList = Object.entries(fields);
this.fields = fields; const size = fieldList.reduce((sum, [, field]) => sum + field.size, 0);
this.size = this.#fieldList.reduce(
(sum, [, field]) => sum + field.size,
0,
);
this.littleEndian = !!options.littleEndian; const littleEndian = !!options.littleEndian;
this.extra = options.extra!; const extra = options.extra
this.postDeserialize = options.postDeserialize; ? Object.getOwnPropertyDescriptors(options.extra)
} : undefined;
serialize(runtimeStruct: StructInit<this>): Uint8Array; return {
serialize(runtimeStruct: StructInit<this>, buffer: Uint8Array): number; fields,
size,
extra: options.extra,
serialize( serialize(
runtimeStruct: StructInit<this>, runtimeStruct: StructInit<Struct<T, Extra, PostDeserialize>>,
buffer?: Uint8Array, buffer?: Uint8Array,
): Uint8Array | number { ): Uint8Array | number {
for (const [key, field] of this.#fieldList) { for (const [key, field] of fieldList) {
if (key in runtimeStruct) { if (key in runtimeStruct) {
field.preSerialize?.( field.preSerialize?.(
runtimeStruct[key as never], runtimeStruct[key as never],
runtimeStruct, runtimeStruct as never,
); );
} }
} }
const sizes = this.#fieldList.map( const sizes = fieldList.map(
([key, field]) => ([key, field]) =>
field.dynamicSize?.(runtimeStruct[key as never]) ?? field.size, field.dynamicSize?.(runtimeStruct[key as never]) ??
field.size,
); );
const size = sizes.reduce((sum, size) => sum + size, 0); const size = sizes.reduce((sum, size) => sum + size, 0);
@ -132,9 +134,9 @@ export class Struct<
const context: SerializeContext = { const context: SerializeContext = {
buffer, buffer,
index: 0, index: 0,
littleEndian: this.littleEndian, littleEndian,
}; };
for (const [index, [key, field]] of this.#fieldList.entries()) { for (const [index, [key, field]] of fieldList.entries()) {
field.serialize(runtimeStruct[key as never], context); field.serialize(runtimeStruct[key as never], context);
context.index += sizes[index]!; context.index += sizes[index]!;
} }
@ -144,12 +146,8 @@ export class Struct<
} else { } else {
return buffer; return buffer;
} }
} },
deserialize: bipedal(function* (
deserialize: {
(reader: ExactReadable): PostDeserialize;
(reader: AsyncExactReadable): MaybePromiseLike<PostDeserialize>;
} = bipedal(function* (
this: Struct<T, Extra, PostDeserialize>, this: Struct<T, Extra, PostDeserialize>,
then, then,
reader: AsyncExactReadable, reader: AsyncExactReadable,
@ -157,15 +155,17 @@ export class Struct<
const startPosition = reader.position; const startPosition = reader.position;
const runtimeStruct = {} as Record<string, unknown>; const runtimeStruct = {} as Record<string, unknown>;
const context: DeserializeContext<unknown> = { const context: DeserializeContext<Partial<FieldsType<T>>> = {
reader, reader,
runtimeStruct, runtimeStruct: runtimeStruct as never,
littleEndian: this.littleEndian, littleEndian: littleEndian,
}; };
try { try {
for (const [key, field] of this.#fieldList) { for (const [key, field] of fieldList) {
runtimeStruct[key] = yield* then(field.deserialize(context)); runtimeStruct[key] = yield* then(
field.deserialize(context),
);
} }
} catch (e) { } catch (e) {
if (!(e instanceof ExactReadableEndedError)) { if (!(e instanceof ExactReadableEndedError)) {
@ -179,20 +179,18 @@ export class Struct<
} }
} }
if (this.extra) { if (extra) {
Object.defineProperties( Object.defineProperties(runtimeStruct, extra);
runtimeStruct,
Object.getOwnPropertyDescriptors(this.extra),
);
} }
if (this.postDeserialize) { if (options.postDeserialize) {
return this.postDeserialize.call( return options.postDeserialize.call(
runtimeStruct, runtimeStruct as never,
runtimeStruct as never, runtimeStruct as never,
); );
} else { } else {
return runtimeStruct as never; return runtimeStruct;
} }
}) as never; }),
} as never;
} }