quickwin 3.0.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/lib/abort.d.ts +21 -0
  2. package/lib/assert.d.ts +2 -0
  3. package/lib/bitmap.d.ts +7 -0
  4. package/lib/cache-utils.d.ts +6 -0
  5. package/lib/certs.d.ts +1 -0
  6. package/lib/dpi.d.ts +9 -0
  7. package/lib/dumpRects.d.ts +2 -0
  8. package/lib/exec-log.d.ts +23 -0
  9. package/lib/fetch.d.ts +4 -0
  10. package/lib/ffi/bind.d.ts +70 -0
  11. package/lib/ffi/ctype.d.ts +117 -0
  12. package/lib/ffi/index.d.ts +2 -0
  13. package/lib/ffi/struct.d.ts +174 -0
  14. package/lib/http-common.d.ts +143 -0
  15. package/lib/http-server.d.ts +59 -0
  16. package/lib/polyfill.d.ts +22 -0
  17. package/lib/react-qw/components/Button.d.ts +9 -0
  18. package/lib/react-qw/components/CheckBox.d.ts +11 -0
  19. package/lib/react-qw/components/ComboBox.d.ts +11 -0
  20. package/lib/react-qw/components/DateTimePicker.d.ts +15 -0
  21. package/lib/react-qw/components/Input.d.ts +15 -0
  22. package/lib/react-qw/components/Link.d.ts +12 -0
  23. package/lib/react-qw/components/ListBox.d.ts +13 -0
  24. package/lib/react-qw/components/ListView.d.ts +37 -0
  25. package/lib/react-qw/components/PathPicker.d.ts +23 -0
  26. package/lib/react-qw/components/ProgressBar.d.ts +9 -0
  27. package/lib/react-qw/components/RadioButton.d.ts +11 -0
  28. package/lib/react-qw/components/ScrollView.d.ts +9 -0
  29. package/lib/react-qw/components/Slider.d.ts +12 -0
  30. package/lib/react-qw/components/Tab.d.ts +14 -0
  31. package/lib/react-qw/components/Tooltip.d.ts +7 -0
  32. package/lib/react-qw/components/TreeView.d.ts +18 -0
  33. package/lib/react-qw/components/index.d.ts +16 -0
  34. package/lib/react-qw/index.d.ts +24 -0
  35. package/lib/react-qw/layout.d.ts +29 -0
  36. package/lib/react-qw/nmhdr.d.ts +1 -0
  37. package/lib/react-qw/props.d.ts +2 -0
  38. package/lib/react-qw/reconciler.d.ts +21 -0
  39. package/lib/stream.d.ts +20 -0
  40. package/lib/text-codec.d.ts +39 -0
  41. package/lib/text-measure.d.ts +14 -0
  42. package/lib/url.d.ts +54 -0
  43. package/lib/websocket.d.ts +73 -0
  44. package/lib/windows/{advapi32.ts → advapi32.d.ts} +6 -21
  45. package/lib/windows/comctl32.d.ts +14 -0
  46. package/lib/windows/comdlg32.d.ts +4 -0
  47. package/lib/windows/{gdi32.ts → gdi32.d.ts} +161 -98
  48. package/lib/windows/{kernel32.ts → kernel32.d.ts} +138 -115
  49. package/lib/windows/{msimg32.ts → msimg32.d.ts} +11 -14
  50. package/lib/windows/ntdll.d.ts +5 -0
  51. package/lib/windows/ole32.d.ts +3 -0
  52. package/lib/windows/{psapi.ts → psapi.d.ts} +9 -18
  53. package/lib/windows/{shell32.ts → shell32.d.ts} +25 -24
  54. package/lib/windows/structs.d.ts +3141 -0
  55. package/lib/windows/{user32.ts → user32.d.ts} +196 -193
  56. package/lib/windows/version.d.ts +27 -0
  57. package/lib/windows/{winspool.ts → winspool.d.ts} +18 -32
  58. package/package.json +15 -6
  59. package/qwin-nowasm-x86.exe +0 -0
  60. package/qwin-nowasm.exe +0 -0
  61. package/qwin-x86.exe +0 -0
  62. package/qwin.exe +0 -0
  63. package/lib/abort.ts +0 -69
  64. package/lib/assert.ts +0 -7
  65. package/lib/bitmap.ts +0 -39
  66. package/lib/cache-utils.ts +0 -40
  67. package/lib/certs.ts +0 -258
  68. package/lib/dpi.ts +0 -31
  69. package/lib/dumpRects.ts +0 -34
  70. package/lib/exec-log.ts +0 -30
  71. package/lib/fetch.ts +0 -589
  72. package/lib/ffi/bind.ts +0 -540
  73. package/lib/ffi/ctype.ts +0 -371
  74. package/lib/ffi/index.ts +0 -6
  75. package/lib/ffi/struct.ts +0 -780
  76. package/lib/http-common.ts +0 -441
  77. package/lib/http-server.ts +0 -604
  78. package/lib/polyfill.ts +0 -171
  79. package/lib/react-qw/components/index.ts +0 -17
  80. package/lib/react-qw/index.ts +0 -96
  81. package/lib/react-qw/layout.ts +0 -111
  82. package/lib/react-qw/nmhdr.ts +0 -7
  83. package/lib/react-qw/props.ts +0 -41
  84. package/lib/react-qw/reconciler.ts +0 -379
  85. package/lib/stream.ts +0 -22
  86. package/lib/text-codec.ts +0 -171
  87. package/lib/text-measure.ts +0 -44
  88. package/lib/url.ts +0 -201
  89. package/lib/websocket.ts +0 -600
  90. package/lib/windows/comctl32.ts +0 -20
  91. package/lib/windows/comdlg32.ts +0 -10
  92. package/lib/windows/ntdll.ts +0 -11
  93. package/lib/windows/ole32.ts +0 -9
  94. package/lib/windows/structs.ts +0 -807
  95. package/lib/windows/version.ts +0 -35
package/lib/ffi/struct.ts DELETED
@@ -1,780 +0,0 @@
1
- import * as ffi from 'ffi';
2
- import '../text-codec.js';
3
- import {
4
- C_BasicType_No_Void,
5
- C_TypeJsTypeMap,
6
- isCPtrToken,
7
- normToken,
8
- MaybePtr,
9
- PTR_SIZE,
10
- PtrArrayBuffer,
11
- readScalar,
12
- SizeAlign,
13
- writeScalar,
14
- type C_Array,
15
- type C_BasicType_Token,
16
- type C_Integer,
17
- type C_Number,
18
- type C_Struct, type C_Union,
19
- type Encoding,
20
- type Ptr,
21
- type SimpleMember,
22
- type SimpleToken,
23
- } from './ctype.js';
24
-
25
- // ============================================================
26
- // 聚合定义:唯一 API 是平铺语法(值词汇见 ctype.ts §4),签名与 pack 见末尾 struct()/union()。
27
- // layout 采用 MSVC 对齐语义:
28
- // i8/u8→1 i16/u16→2 i32/u32/f32→4 i64/u64/f64→8 '<>ptr'/'<T>ptr'→arch 宽(4/8)
29
- // struct 对齐 = 最大字段对齐,总尺寸末尾补齐;pack 压上限、键尾 '@N'(alignas)抬下限
30
- // 位域布局规则见下方 computeStructLayout 的状态机注释(mingw 实测 DCB/COMSTAT)。
31
- // ============================================================
32
-
33
- // ============================================================
34
- // 类型推导 — 值词汇 → decode/encode 形状
35
- // ============================================================
36
-
37
- // 指针成员:'<>ptr' → number(T='' 品牌退化);'<NAME>ptr' → MaybePtr<NAME>(NULL | 品牌 number)。
38
- // 该品牌 number 可直接喂 bind 的 <NAME>ptr 形参(裸地址透传);空位写 NULL。
39
-
40
- type N =
41
- | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
42
- | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19
43
- | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29
44
- | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39
45
- | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49
46
- | 50 | 51 | 52 | 53;
47
-
48
- // 规范 token(SimpleToken 守卫后,T 恒为规范形)→ JS 形状:'<T>ptr' → MaybePtr(0 保真,
49
- // 对齐 C 版);'<T>ptr!' → Ptr(非空担保:decode 直出非空品牌且运行时 0 断言,见 doDecode;
50
- // 下游 '! 消费位'由 bind 的 callPacked 兜底);'@E' → EnumMap 值域(与 bind 签名位同构:
51
- // decode 出值域收窄、encode 收同型 —— 异种枚举/域外字面量由枚举 nominal 拦,运行时值域
52
- // 校验按设计留 Phase 2);数字档 → JsType 映射。落不到的('void'、裸 'ptr'、拼错 token)
53
- // 塌 never。
54
- type ValOf<T> =
55
- T extends `<${infer B}>ptr!` ? Ptr<B>
56
- : T extends `<${string}>ptr@${infer E extends keyof EnumMap}` ? EnumMap[E]
57
- : T extends `<${infer B}>ptr` ? MaybePtr<B>
58
- : T extends `${infer U}@${infer E extends keyof EnumMap}` ? (U extends C_Number ? EnumMap[E] : never)
59
- : T extends C_Number ? C_TypeJsTypeMap[T]
60
- : never
61
-
62
- // 位域糖 → 形状:unit 非整数档(如 'f32:3';T 已被 SimpleToken 守卫限定为规范 token)
63
- // 塌 never(运行时同报错);width ≤53 可被 number 精确表示 → number,否则 bigint。
64
- type BitShape<T extends string, W extends number> =
65
- T extends C_Integer ? (W extends N ? number : bigint) : never
66
-
67
- // 值 → 形状。对象值一律按 '#' 分派(无「裸嵌套 map 默认 struct」—— 漏写 '#' 是错误)。
68
- // 字符串糖(值尾带 @encoding)在数组糖之前判别 —— 两形态本不相交(数组糖以 ] 收尾),
69
- // 特判在前更可读。注意 infer 出的 L 是字符串字面('8'),不能写 `L extends number`
70
- // (string 永不 extends number → 塌 never),要用 `L extends `${number}`` 检查;
71
- // U/L/E 都在条件里被引用,避免未使用的 infer 触发 TS6196。
72
- type ShapeOfValue<V> =
73
- V extends infer T extends SimpleToken ? ValOf<T>
74
- : V extends `${infer U}[${infer L}]@${infer E}`
75
- ? (U extends 'u8' | 'u16' ? (L extends `${number}` ? (E extends Encoding ? string : never) : never) : never)
76
- : V extends `${infer T extends SimpleToken}[${infer L extends number}]` ? Tuple<ShapeOfValue<T>, L>
77
- : V extends `${infer T extends SimpleToken}:${infer W extends number}` ? BitShape<T, W>
78
- : V extends { '#': 'array', element: infer E, length: infer L extends number } ? Tuple<ShapeOfValue<E>, L>
79
- : V extends { '#': 'struct' | 'union' } ? ShapeOfC<V>
80
- : never
81
-
82
- type Tuple<T, N extends number, R extends T[] = []> =
83
- number extends N ? T[]
84
- : R['length'] extends N ? R
85
- : R['length'] extends 64 ? T[]
86
- : Tuple<T, N, [...R, T]>
87
- type DollarKeys<T> = { [K in keyof T]: K extends `$${string}` ? K : never }[keyof T];
88
- type UnionToIntersection<U> =
89
- (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
90
- type Lift<T> =
91
- {
92
- [K in keyof T as K extends `$${string}` ? never : K]:
93
- T[K] extends readonly unknown[] ? T[K] :
94
- // 品牌 number(NULL / Ptr = 0&{brand} 或 number&{brand})经自己的对象半边
95
- // extends object,会被误当嵌套对象递归,把 number 榨成方法集对象(丧失
96
- // number 属性)——number 档先跳过。只递归进真正嵌套的结构体形状。
97
- T[K] extends number ? T[K] :
98
- T[K] extends object ? Lift<T[K]> : T[K];
99
- } & UnionToIntersection<
100
- { [K in DollarKeys<T>]: Lift<T[K]> }[DollarKeys<T>]
101
- >;
102
- // 形状层刻意不设约束:M 常是泛型实例化里的 C_Struct 交叉(在 struct()/union() 声明处
103
- // 无法证明满足模板索引签名),约束检查会在这里炸掉(TS2344/TS2589)。
104
- // 非法 M 落到 ShapeOfValue 的 never 分支,由具体实例化点暴露。
105
- // 指令键('#'/'#pack')不进形状;键尾 '@N'(alignas)拆掉 —— 对齐只影响布局、不影响读写形状。
106
- type FieldKey<K> =
107
- K extends `#${string}` ? never
108
- : K extends `${infer B}@${string}` ? B
109
- : K
110
- type _ShapeOfC<M> = {
111
- [K in keyof M as FieldKey<K>]: ShapeOfValue<M[K]>
112
- }
113
- type ShapeOfC<M> = Lift<_ShapeOfC<M>>
114
-
115
- // encode 入参的深度可选形态:缺省字段运行时跳过(doEncode 的 undefined-continue——
116
- // 不动 buffer 该字段,新建 buffer 上等价写 0),decode 返回仍是全字段 ShapeOfC。
117
- // 数组元组排除在外:writeArray 是整体写入语义,"省略第 N 个元素"没有意义——
118
- // 数组字段维持"要么给全、要么整个缺省"(整个缺省由外层 continue 拦下)。
119
- export type DeepPartial<T> =
120
- T extends readonly unknown[] ? T
121
- : T extends number | string | boolean | bigint | symbol ? T
122
- : { [K in keyof T]?: DeepPartial<T[K]> }
123
-
124
- // encode 物化入参:缺省字段运行时跳过(doEncode 的 undefined-continue —— 不动 buffer,
125
- // 新建槽等价写 0),decode 返回仍是全字段 ShapeOfC。
126
- // 必填集 = '!' 字段(非空担保,省略 = 槽留 0 违约)∪ '@' 值域字段中 0 ∉ 值域的(省略 =
127
- // 新槽写 0,0 不在枚举值域同样是静默违约;0 ∈ 值域或宽 number 值域可省 —— 省略产生的 0
128
- // 本身就是合法枚举值)。判定从 T 的**原始 token** 按 DFS 后序递归展开(子先判、父取 OR):
129
- // 原文 token 是标注的唯一权威,且 `<>ptr!` 的 '!' 在形上已塌进裸 number(Ptr<''> 空品牌
130
- // 归一),只有 token 能看见。数组(`{'#':'array'}` 与 `u32@E[N]` 糖)按**元素**穿透 ——
131
- // 整组省略 = 元素全 0,违约同理;嵌套 struct/union 递归字段 —— 子树含必填 ⇒ 本键必填,
132
- // 堵掉「encode 通过、decode 0 断言炸」的嵌套/数组缺口。
133
- // 必填键的值经 EncodeValOfT **后序合成**:嵌套 struct/union 再套同一 EncodeShapeOfC
134
- // (内层必填键强制、非必填兄弟维持 DeepPartial 可省 —— 与运行时逐字段对应:省略兄弟 =
135
- // 写 0,合法零值),标量/数组取全量形。其余字段维持 DeepPartial 缺省语义(省略 = 跳过,
136
- // 新建槽写 0 / 复用槽留旧值)。
137
- // 无必填字段时整体退化为纯 DeepPartial、**不进交集**:`{} & {全可选}` 会绕过 TS 的
138
- // weak-type 检查(TS2559 'no properties in common' 不再触发),裸 number/string 就此
139
- // 混进任何 codec 实参位(asctime(123) 回归实测)。元组包一层防 never 分发。
140
- type IsSubtreeMandatory<D> =
141
- // —— 结构形态:DFS 后序(先子后父)——
142
- D extends { '#': 'array', element: infer E } ? IsSubtreeMandatory<E>
143
- : D extends { '#': 'struct' | 'union' }
144
- ? (true extends { [K in keyof D]-?: IsSubtreeMandatory<D[K]> extends true ? true : false }[keyof D] ? true : false)
145
- // —— token 形态:分支序对齐 ShapeOfValue(字符串糖先于数组糖,编码 @ 先于枚举 @)——
146
- : D extends `${string}[${number}]@${string}` ? false // 字符串糖('u16[4]@utf-8'):'' 合法 → 可省
147
- : D extends `${infer TK extends SimpleToken}[${string}]` ? IsSubtreeMandatory<TK> // 数组糖穿透元素
148
- : D extends `${string}:${number}` ? false // 位域:数值零合法 → 可省
149
- : D extends `<${string}>ptr!` ? true // '!'(含裸 <>ptr! —— 形塌 number,token 是唯一判据)
150
- : D extends `<${string}>ptr@${infer E extends keyof EnumMap}` ? (0 extends EnumMap[E] ? false : true)
151
- : D extends `${infer U}@${infer E extends keyof EnumMap}`
152
- ? (U extends C_Number ? (0 extends EnumMap[E] ? false : true) : false)
153
- : false
154
- // 必填键 → 其**原始声明值**的映射(`as` 重映射一步完成过滤 + alignas 归一 + 剔 '#')。
155
- type MandatoryDeclKeys<T> = {
156
- [K in keyof T]-?: IsSubtreeMandatory<T[K]> extends true ? FieldKey<K> : never
157
- }[keyof T]
158
- // 必填键的值:嵌套 struct/union 后序递归同一形态,其余(标量/数组/糖)取全量形。
159
- type EncodeValOfT<D> =
160
- D extends { '#': 'struct' | 'union' } ? EncodeShapeOfC<D>
161
- : ShapeOfValue<D>
162
- type EncodeShapeOfC<T> =
163
- DeepPartial<ShapeOfC<T>> & {
164
- [K in keyof T as IsSubtreeMandatory<T[K]> extends true ? FieldKey<K> : never]-?: EncodeValOfT<T[K]>
165
- }
166
- type EncodeIn<T extends { '#': 'struct' | 'union' }> =
167
- [MandatoryDeclKeys<T>] extends [never]
168
- ? DeepPartial<ShapeOfC<T>>
169
- : EncodeShapeOfC<T>
170
-
171
-
172
- // ============================================================
173
- // 运行时类型
174
- // ============================================================
175
-
176
- // lower 后指针统一归一为内部 kind 'ptr'(用户面写 '<>ptr' 或 '<NAME>ptr')。
177
-
178
- // kind → size/align(不含指针:指针统一 PTR_SIZE,见 lower)。
179
-
180
-
181
- // 运行时 Field:IR 的 lowered 视图(独立于语法层,不保留 alignas)。
182
- // computeStructLayout 在 lowering 时把子布局内嵌进 FieldType(struct/union→fields,
183
- // array→elementType+elementSize,bitfield→bit 单元内偏移),运行期 read/write 零查表。
184
- // offset 相对本层起点;位域成员的 offset 是其存储单元的基址(不是位域自己的地址)。
185
- type FieldType =
186
- { tag: 'basic', kind: C_BasicType_No_Void }
187
- | { tag: 'bitfield', unit: C_Integer, bit: number, width: number }
188
- | { tag: 'string', encoding: Encoding }
189
- | { tag: 'struct' | 'union', fields: Field[] }
190
- | { tag: 'array', elementType: FieldType, elementSize: number }
191
- export type Field = {
192
- name: string
193
- offset: number
194
- size: number
195
- type: FieldType
196
- /** '!' 标注字段(原文 token `<X>ptr!`):decode 读出 0 当场 throw(后置兜底 ——
197
- * C 没填 / 拿未填充槽 decode 都在这里拦,见 doDecode)。 */
198
- nonnull?: true
199
- }
200
- export type Fields = Field[]
201
-
202
- // T 约束 = '#' 声明本身:struct()/union() 构造的 `{'#':'struct'} & M` 可证 —— M 是平铺
203
- // 字段表(SimpleMember 无 '#' 键),交叉的 '#' 取字面声明。形状层不设约束的理由见 ShapeOfC。
204
- // N = 布局品牌(struct(name, …) 传入):decode 入参与 alloc().ptr 同品牌。默认 '' ——
205
- // `Ptr<''>` 归一裸 number(与旧 never 守卫结果相同),品牌化 def 未来收窄时签名不用再动。
206
- export type StructDef<T extends { '#': 'struct' | 'union' }, N extends string = ''> = {
207
- // 布局 IR 直接暴露(原 __struct 成员读已删:成员读是可观察副作用,esbuild 无法把
208
- // 「PURE 调用 + X.__struct 实参」与 X 的定义一起摇掉 —— DECL 常量(对象字面量)走
209
- // 标识符引用则整链可摇。T 只活在 decode/encode 的类型签名里,运行时不挂成员)。
210
- readonly fields: Fields
211
- readonly size: number
212
- readonly structAlign: number
213
- decode(buf: ArrayBuffer | Ptr<N>): ShapeOfC<T>
214
- // alloc = 出参槽分配(全新全 0:'!' 字段的 0 是出参哨兵,decode 的 0 断言兜 C 没填);
215
- // encode = 物化(C 要读的对象),带品牌 .ptr 直接喂 <N>ptr 形参。'!' 字段在 EncodeIn
216
- // 里必填 —— 省略即编译错,不静默写 0 / 复用槽留旧值(DFS 后序穿透嵌套与数组:子树/
217
- // 数组元素含必填 ⇒ 该键必给,值后序合成到内层)。带 buf = 写入调用方既有
218
- // PtrArrayBuffer(返回其本身,供链式读回)——encode 出口一律 PAB,裸 ArrayBuffer
219
- // 不出 codec(与 CodecMap 同约束)。动态调用方无参 encode 由运行时 throw 兜底。
220
- /** n(默认 1)= 按本布局 size 步长连续分配 n 个元素的零填充缓冲(出参数组场景,
221
- * 品牌 .ptr 直接喂 <N>ptr 位)。n < 1 / 非整数运行时 throw ——
222
- * 任何路径都不产出 PAB(0)(其 .ptr 是"非 0 的 1 字节堆指针",会骗过 C 的 NULL
223
- * 防御;空位语义由调用方显式传 NULL)。 */
224
- alloc(n?: number): PtrArrayBuffer<N>
225
- encode(v: EncodeIn<T>): PtrArrayBuffer<N>
226
- // 带 buf = 写入调用方既有 PtrArrayBuffer(复用/预分配形态);带 offset = 写入点
227
- // 相对 buf 起点的字节偏移(数组物化/自定义内存管理的底层写入原语,内部即 doEncode
228
- // 的 base)。返回值仍是 buf 本身 —— offset 形态下其 .ptr 对不上写入点,写入点地址
229
- // = buf.ptr + offset(自算术,品牌随算术丢失同 CreatePipe 先例);越界写由 DataView
230
- // RangeError 兜底,无需额外校验。
231
- encode(v: EncodeIn<T>, buf: PtrArrayBuffer<any>, offset?: number): PtrArrayBuffer<any>
232
- offsetOf(name: string): number
233
- }
234
-
235
- // ============================================================
236
- // Layout IR —— computeStructLayout/lower 把 CType AST IR lower 到此。
237
- // 自包含:子布局在 lowering 时内嵌、offset 相对本层起点,运行期 read/write 不回查 AST IR。
238
- // ============================================================
239
-
240
- /** 向上对齐(a 必须是 2 的幂)。 */
241
- function alignUp(v: number, a: number): number {
242
- return (v + a - 1) & ~(a - 1)
243
- }
244
-
245
- type Layout = {
246
- size: number,
247
- maxEffectiveAlign: number,
248
- fields: Fields,
249
- }
250
-
251
- // 归一后 token 的档位检查:'void'/指针 token 查表为 undefined(SizeAlign 只收 C_Number)。
252
- const isCNumberToken = (t: C_BasicType_Token): t is C_Number =>
253
- SizeAlign[t as C_Number] !== undefined
254
-
255
- // 值 → { size, align, FieldType }:字符串先试字符串糖/数组糖/位域糖,再按 token 归一;
256
- // 对象值一律按 '#' 分派 —— 无「裸嵌套 map 默认 struct」,漏写 '#' 直接报错。
257
- // 字符串无 '#' 声明形式(词汇只有糖,见 ctype.ts §4):旧写法落到末尾 unknown '#' 报错。
258
- function lower(v: unknown): { size: number, align: number, type: FieldType } {
259
- if (typeof v === 'string') {
260
- const st = /^(u8|u16)\[(\d+)\]@(utf-8|utf-16le)$/.exec(v) // 'u16[128]@utf-16le' → 字符串糖
261
- if (st) {
262
- const length = Number(st[2])
263
- if (!Number.isInteger(length) || length < 1)
264
- throw new Error(`ffi-struct: string length must be an integer >= 1, got ${length}`)
265
- // 布局 = unit × length(与 encoding 无关),encoding 只是读写时的解释 ——
266
- // 'u8[128]@utf-16le'(128 字节按 utf-16le 解出 64 字符)之类的错配语义自洽。
267
- const s = st[1] === 'u8' ? 1 : 2
268
- return { size: s * length, align: s, type: { tag: 'string', encoding: st[3] as Encoding } }
269
- }
270
- const a = /^([^\[\]]+)\[(\d+)\]$/.exec(v) // 'u32[4]' / 'i3z[4]' → 数组糖(元素递归归一)
271
- if (a) {
272
- const length = Number(a[2])
273
- if (!Number.isInteger(length) || length < 1)
274
- throw new Error(`ffi-struct: array length must be an integer >= 1, got ${length}`)
275
- const el = lower(a[1])
276
- if (el.type.tag === 'bitfield')
277
- throw new Error('ffi-struct: bitfields cannot be array elements')
278
- return {
279
- size: el.size * length,
280
- align: el.align,
281
- type: { tag: 'array', elementType: el.type, elementSize: el.size },
282
- }
283
- }
284
- const b = /^([A-Za-z_]\w*):(\d+)$/.exec(v) // 'u32:3' → 位域糖('f32:3' 归一后非整数档被拒)
285
- if (b) {
286
- const rawUnit = b[1]!
287
- const unit = normToken(rawUnit)
288
- // 'void'/'<>ptr' 查表为 undefined、'float' 等别名 normToken 直接抛 —— 都不是合法位域单元。
289
- if (!isCNumberToken(unit) || unit === 'f32' || unit === 'f64')
290
- throw new Error(`ffi-struct: bitfield unit must be an integer type, got "${rawUnit}"`)
291
- const width = Number(b[2])
292
- const s = SizeAlign[unit]
293
- const cap = s * 8
294
- if (!Number.isInteger(width) || width < 1)
295
- throw new Error(`ffi-struct: bitfield width must be an integer >= 1, got ${width}`)
296
- if (width > cap)
297
- throw new Error(`ffi-struct: bitfield width ${width} exceeds unit width ${cap}`)
298
- // bit 是单元内偏移,由 computeStructLayout 的位域状态机填写;这里占位 0。
299
- return { size: s, align: s, type: { tag: 'bitfield', unit, bit: 0, width } }
300
- }
301
- // 规范 token / 指针 token(别名机制已移除:'DWORD' 等 normToken 直接抛)。
302
- // 归一后必须先判指针形,否则落 unknown kind;'void' 归一后无大小、
303
- // 裸 'ptr'/'i3z' 未知 —— 都在这里抛(原先由 SizeAlign 查表兜住,现归一并入)。
304
- const k = normToken(v)
305
- if (isCPtrToken(k))
306
- return { size: PTR_SIZE, align: PTR_SIZE, type: { tag: 'basic', kind: 'ptr' } }
307
- if (!isCNumberToken(k)) throw new Error(`ffi-struct: unknown kind "${v}"`)
308
- const s = SizeAlign[k]
309
- return { size: s, align: s, type: { tag: 'basic', kind: k } }
310
- }
311
- if (v && typeof v === 'object') {
312
- const o = v as Record<string, unknown>
313
- const tag = o['#']
314
- if (tag === undefined)
315
- throw new Error(`ffi-struct: nested object value requires a '#' key ('struct' | 'union' | 'array')`)
316
- if (tag === 'array' && '#pack' in o)
317
- throw new Error(`ffi-struct: '#pack' is only allowed on struct/union, got '${tag}'`)
318
- if (tag === 'array') {
319
- const length = o['length']
320
- if (!Number.isInteger(length) || (length as number) < 1)
321
- throw new Error(`ffi-struct: array length must be an integer >= 1, got ${String(length)}`)
322
- const el = lower(o['element'])
323
- if (el.type.tag === 'bitfield')
324
- throw new Error('ffi-struct: bitfields cannot be array elements')
325
- return {
326
- size: el.size * (length as number),
327
- align: el.align,
328
- type: { tag: 'array', elementType: el.type, elementSize: el.size },
329
- }
330
- }
331
- if (tag === 'struct' || tag === 'union') {
332
- const l = computeStructLayout(v as C_Struct | C_Union)
333
- return { size: l.size, align: l.maxEffectiveAlign, type: { tag, fields: l.fields } }
334
- }
335
- throw new Error(`ffi-struct: unknown '#' declaration ${JSON.stringify(String(tag))}`)
336
- }
337
- throw new Error(`ffi-struct: invalid member value: ${String(v)}`)
338
- }
339
-
340
- export function computeArray(t: C_Array): { align: number, size: number } {
341
- const { size, align } = lower(t)
342
- return { align, size }
343
- }
344
-
345
- export function computeStructLayout(t: C_Struct | C_Union): Layout {
346
- const isStruct = t['#'] === 'struct'
347
- const pack = t['#pack'] ?? 8
348
- let maxEffectiveAlign = 1
349
- let maxMemberSize = 0
350
- const fields: Fields = []
351
- let cursor = 0
352
-
353
- // 位域组状态:unitBase 是当前存储单元的字节基址(-1 = 不在位域组内),
354
- // unitBits 是单元内已用位数,unitSize 是单元宽(字节)。
355
- // MSVC 规则(mingw 交叉实测 DCB/COMSTAT 验证):
356
- // - 连续同宽位域共单元,LSB→MSB(签别不参与分组)
357
- // - 放不下的位域开新单元,单元剩余位废弃
358
- // - 非位域成员打断位域组,游标跳到完整单元边界(不是按字节截断)
359
- // 联合体位域不分组:每位域成员独立从 offset 0/bit 0 起算——完全别名
360
- // (mingw 16 x64+ia32 实测:a=1→b=1、溢出成员回 offset 0、sizeof=单元宽)。
361
- let unitBase = -1
362
- let unitBits = 0
363
- let unitSize = 0
364
-
365
- // 落定当前位域组:游标 = 单元起点 + 已用字节数,向上取整到单元宽。
366
- const flushUnit = () => {
367
- if (unitBase < 0) return
368
- cursor = alignUp(unitBase + Math.ceil(unitBits / 8), unitSize)
369
- unitBase = -1
370
- }
371
-
372
- for (const [rawName, v] of Object.entries(t as unknown as Record<string, unknown>)) {
373
- if (rawName.startsWith('#')) continue // '#' 指令键('#'/'#pack')不是字段
374
- const m = /^([^@]+)@(\d+)$/.exec(rawName) // 键尾 '@N' = alignas(字段名不含 '@')
375
- const name = m ? m[1]! : rawName
376
- const alignas = m ? Number(m[2]) : 0
377
- const { size, align: natural, type } = lower(v)
378
- // '!' 非空担保标记(原文 `<X>ptr!`):lower 里 normToken 已拒掉 'u32!'/'HANDLE!' 等
379
- // 非法尾缀,能走到这里的 '!' 尾必是指针 token —— 记到 Field 上供 decode 的 0 断言。
380
- const nonnull = typeof v === 'string' && v.endsWith('!')
381
- let align = natural
382
- if (pack > 0) align = Math.min(pack, align)
383
- if (alignas) align = Math.max(align, alignas)
384
-
385
- if (type.tag === 'bitfield') {
386
- if (name.startsWith('$')) throw new Error('ffi-struct: bitfield members must be named')
387
-
388
- // union:成员同址(C 标准),每位域成员独立从 offset 0/bit 0 起算。
389
- // 不顺序分组、不开新单元、不动游标(flushUnit 因 unitBase 恒 -1 而
390
- // no-op,普通/匿名成员的 offset 也因此保持 0)——曾因游标推进致
391
- // 溢出单元/后续成员落在 union 声明 size 之外(越界读写)。
392
- if (!isStruct) {
393
- fields.push({ name: name, offset: 0, size, type: { ...type, bit: 0 } })
394
- maxEffectiveAlign = Math.max(maxEffectiveAlign, align)
395
- maxMemberSize = Math.max(maxMemberSize, size)
396
- continue
397
- }
398
-
399
- const width = type.width
400
-
401
- // 新单元:无活动单元 / 单元宽不同 / 放不下。
402
- if (unitBase < 0 || size !== unitSize || unitBits + width > unitSize * 8) {
403
- // 单元宽变化或溢出时先把旧单元落定,新单元从落定点对齐。
404
- if (unitBase >= 0) flushUnit()
405
- const offset = alignUp(cursor, align)
406
- unitBase = offset
407
- unitBits = 0
408
- unitSize = size
409
- }
410
-
411
- fields.push({
412
- name: name,
413
- offset: unitBase, // 单元基址(不是位域自己的地址)
414
- size,
415
- type: { ...type, bit: unitBits },
416
- })
417
- unitBits += width
418
-
419
- maxEffectiveAlign = Math.max(maxEffectiveAlign, align)
420
- maxMemberSize = Math.max(maxMemberSize, size)
421
- continue
422
- }
423
-
424
- flushUnit()
425
- const offset = alignUp(cursor, align)
426
-
427
- if (!name.startsWith('$')) {
428
- fields.push({ name: name, offset, size, type, ...(nonnull ? { nonnull: true as const } : {}) })
429
- } else {
430
- // 匿名聚合:内嵌 fields 已 lower 好,offset 平移到本成员起点。
431
- if (type.tag !== 'struct' && type.tag !== 'union')
432
- throw new Error(`ffi-struct: anonymous member "${name}" must be a struct/union`)
433
- for (const cf of type.fields)
434
- fields.push({ ...cf, offset: cf.offset + offset })
435
- }
436
-
437
- if (isStruct) cursor = offset + size
438
- maxEffectiveAlign = Math.max(maxEffectiveAlign, align)
439
- maxMemberSize = Math.max(maxMemberSize, size)
440
- }
441
-
442
- flushUnit()
443
-
444
- return {
445
- size: isStruct ? alignUp(cursor, maxEffectiveAlign) : alignUp(maxMemberSize, maxEffectiveAlign),
446
- maxEffectiveAlign,
447
- fields,
448
- }
449
- }
450
-
451
- // ============================================================
452
- // 位域读写
453
- // ============================================================
454
- // 存储单元统一按无符号读(避免单元高位的符号位干扰提取),再用掩码取出位域。
455
- // 有符号单元在提取后做符号扩展。写是读-改-写:只改本位域占的位,其余位保留。
456
- // 64 位单元往返走 BigInt;读回时 width>53 返回 bigint、否则 number(见 readBitfield)。
457
- type BitfieldType = Extract<FieldType, { tag: 'bitfield' }>
458
-
459
- function readUnitRaw(dv: DataView, off: number, unit: C_Integer): bigint {
460
- const s = SizeAlign[unit]
461
- if (s === 1) return BigInt(dv.getUint8(off))
462
- if (s === 2) return BigInt(dv.getUint16(off, true))
463
- if (s === 4) return BigInt(dv.getUint32(off, true))
464
- return dv.getBigUint64(off, true)
465
- }
466
-
467
- function writeUnitRaw(dv: DataView, off: number, unit: C_Integer, v: bigint): void {
468
- const s = SizeAlign[unit]
469
- if (s === 1) { dv.setUint8(off, Number(v & 0xffn)); return }
470
- if (s === 2) { dv.setUint16(off, Number(v & 0xffffn), true); return }
471
- if (s === 4) { dv.setUint32(off, Number(v & 0xffffffffn), true); return }
472
- dv.setBigUint64(off, v & 0xffffffffffffffffn, true)
473
- }
474
-
475
- function readBitfield(dv: DataView, off: number, t: BitfieldType): number | bigint {
476
- const w = BigInt(t.width)
477
- let v = (readUnitRaw(dv, off, t.unit) >> BigInt(t.bit)) & ((1n << w) - 1n)
478
- if (t.unit[0] === 'i' && (v & (1n << (w - 1n)))) // 有符号单元且符号位为 1 → 先符号扩展
479
- v -= 1n << w
480
- if (t.width > 53) return v // 值域装不进安全整数 → bigint(与类型层 BitShape 同界)
481
- return Number(v)
482
- }
483
-
484
- function writeBitfield(dv: DataView, off: number, t: BitfieldType, val: number | bigint): void {
485
- const w = BigInt(t.width)
486
- const bit = BigInt(t.bit)
487
- const widthMask = (1n << w) - 1n
488
- const posMask = widthMask << bit
489
- const raw = readUnitRaw(dv, off, t.unit)
490
- const bits = (typeof val === 'bigint' ? val : BigInt(Math.trunc(val))) & widthMask
491
- writeUnitRaw(dv, off, t.unit, (raw & ~posMask) | (bits << bit))
492
- }
493
-
494
- // 字符串读写统一走 TextEncoder/TextDecoder(polyfill 见 lib/text-codec.js)。
495
- const stringEncoders: Record<Encoding, TextEncoder> = {
496
- 'utf-8': new TextEncoder('utf-8'),
497
- 'utf-16le': new TextEncoder('utf-16le'),
498
- }
499
-
500
- const stringDecoders: Record<Encoding, TextDecoder> = {
501
- 'utf-8': new TextDecoder('utf-8', { ignoreBOM: true }),
502
- 'utf-16le': new TextDecoder('utf-16le', { ignoreBOM: true }),
503
- }
504
-
505
- function readString(dv: DataView, off: number, size: number, enc: Encoding): string {
506
- const bytes = new Uint8Array(dv.buffer, dv.byteOffset + off, size)
507
- const unit = enc === 'utf-16le' ? 2 : 1
508
- let end = size
509
- for (let i = 0; i + unit <= size; i += unit) {
510
- if (bytes[i] === 0 && (unit === 1 || bytes[i + 1] === 0)) { end = i; break }
511
- }
512
- return stringDecoders[enc].decode(bytes.subarray(0, end))
513
- }
514
-
515
- function writeString(dv: DataView, off: number, size: number, enc: Encoding, v: unknown): void {
516
- const bytes = new Uint8Array(dv.buffer, dv.byteOffset + off, size)
517
- bytes.fill(0)
518
- // 始终给 NUL 终止符留一个存储单元(宽度由 encoding 决定,见 readString):
519
- // 末尾保持 0,payload 最多 size - unit 字节。这样 C 侧 strlen/wcslen 都不会越界。
520
- const unit = enc === 'utf-16le' ? 2 : 1
521
- const cap = size - unit
522
- if (cap <= 0) return
523
- const src = stringEncoders[enc].encode(String(v ?? ''))
524
- bytes.set(src.subarray(0, Math.min(src.length, cap)))
525
- }
526
-
527
- type ArrayFieldType = Extract<FieldType, { tag: 'array' }>
528
-
529
- function readArray(dv: DataView, off: number, size: number, t: ArrayFieldType): unknown[] {
530
- const count = t.elementSize > 0 ? size / t.elementSize : 0
531
- const out: unknown[] = []
532
- for (let i = 0; i < count; i++)
533
- out.push(readElement(dv, off + i * t.elementSize, t.elementType, t.elementSize))
534
- return out
535
- }
536
-
537
- function writeArray(dv: DataView, off: number, size: number, t: ArrayFieldType, v: unknown): void {
538
- const count = t.elementSize > 0 ? size / t.elementSize : 0
539
- const list = (v ?? []) as unknown[]
540
- for (let i = 0; i < count; i++)
541
- writeElement(dv, off + i * t.elementSize, t.elementType, t.elementSize, list[i])
542
- }
543
-
544
- function readElement(dv: DataView, off: number, t: FieldType, size: number): unknown {
545
- switch (t.tag) {
546
- case 'basic': return readScalar(dv, off, t.kind)
547
- case 'bitfield': return readBitfield(dv, off, t)
548
- case 'string': return readString(dv, off, size, t.encoding)
549
- case 'struct':
550
- case 'union': return doDecode(dv, off, t.fields)
551
- case 'array': return readArray(dv, off, size, t)
552
- }
553
- }
554
-
555
- function writeElement(dv: DataView, off: number, t: FieldType, size: number, val: unknown): void {
556
- switch (t.tag) {
557
- // TODO: remove this any
558
- case 'basic': writeScalar(dv, off, { k: t.kind, v: val as any }); break
559
- // 直传(不 Number()):writeBitfield 的 val 是 number | bigint,
560
- // 64 位单元走 bigint,先 Number() 会把 >2^53 的位域值截断。
561
- case 'bitfield': writeBitfield(dv, off, t, (val ?? 0) as number | bigint); break
562
- case 'string': writeString(dv, off, size, t.encoding, val); break
563
- case 'struct':
564
- case 'union': doEncode(dv, off, t.fields, (val ?? {}) as Record<string, unknown>); break
565
- case 'array': writeArray(dv, off, size, t, val); break
566
- }
567
- }
568
-
569
- function doDecode(dv: DataView, base: number, fields: Fields): Record<string, unknown> {
570
- const out: Record<string, unknown> = {}
571
- for (const f of fields) {
572
- const v = readElement(dv, base + f.offset, f.type, f.size)
573
- // '!' 非空担保的后置兜底:C 没填(忘判成功就 decode)或拿未填充槽(decode(alloc()))
574
- // 读出 0 —— 当场 throw,不让 0 带 Ptr 品牌流到下游 '! 消费位'。成功路径必非零,不触发。
575
- if (f.nonnull && v === 0)
576
- throw new Error(`ffi-struct: decode: '!' field "${f.name}" got 0 ` +
577
- `(C did not fill it, or decode was called on an unfilled slot)`)
578
- out[f.name] = v
579
- }
580
- return out
581
- }
582
-
583
- function doEncode(dv: DataView, base: number, fields: Fields, v: Record<string, unknown>): void {
584
- for (const f of fields) {
585
- // DeepPartial 缺省:不动该字段(新建 buffer 上保持 0、复用 buffer 上保留旧值);
586
- // 嵌套/数组字段整个缺失也在这里拦下(writeElement 的 struct 分支回到本函数)。
587
- if (v[f.name] === undefined) continue
588
- writeElement(dv, base + f.offset, f.type, f.size, v[f.name])
589
- }
590
- }
591
-
592
- // ============================================================
593
- // 内部构造器
594
- // ============================================================
595
-
596
- function createStruct(t: C_Struct | C_Union): any {
597
- const { fields, size, maxEffectiveAlign } = computeStructLayout(t)
598
- return {
599
- fields,
600
- size,
601
- structAlign: maxEffectiveAlign,
602
- decode: (p: ArrayBuffer | number) => {
603
- // number = native 拥有的品牌指针(Codec 的 decode(p) 形态):readBytes 一次
604
- // native memcpy 拷出 size 字节再解码(替代逐字节 readByte 循环,N 次跨语言
605
- // 调用降为 1 次);ArrayBuffer = 调用方持有的 out buffer 直读。
606
- // 顶层不收 offset——out 参数恒从起点读,嵌套偏移由字段布局(doDecode 的 base)承担。
607
- if (typeof p === 'number')
608
- return doDecode(new DataView(ffi.readBytes(p, size)), 0, fields)
609
- return doDecode(new DataView(p), 0, fields)
610
- },
611
- alloc: ((n?: number) => {
612
- // fail-loud:n < 1 / 非整数绝不静默产出 PAB(0)(其 .ptr 是"非 0 的 1 字节
613
- // 堆指针",会骗过 C 的 NULL 防御)—— 空位语义由调用方显式传 NULL。
614
- if (n !== undefined && (!Number.isInteger(n) || n < 1))
615
- throw new Error(`ffi-struct: alloc(n) requires an integer n >= 1 ` +
616
- `(PAB(0) never escapes — pass NULL for an empty pointer position)`)
617
- return new PtrArrayBuffer(size * (n ?? 1))
618
- }) as StructDef<any, any>['alloc'],
619
- encode: ((v: any, buf?: PtrArrayBuffer<any>, offset?: number) => {
620
- // fail-loud:无参 = 分配语义,走 alloc();这里绝不静默零填充 —— 类型层由
621
- // EncodeIn 拦 '!' 字段省略,动态/JS 调用方由本 throw 兜住。顶层默认从起点写,
622
- // offset 显式给写入点(数组物化原语,见接口注释)。
623
- if (v === undefined || v === null)
624
- throw new Error(`ffi-struct: encode(v) requires an argument; ` +
625
- `use alloc() for a zeroed out-param slot`)
626
- const out = buf ?? new PtrArrayBuffer(size)
627
- doEncode(new DataView(out), offset ?? 0, fields, v)
628
- return out
629
- }) as StructDef<any, any>['encode'],
630
- offsetOf: (name: string) => {
631
- for (const f of fields) if (f.name === name) return f.offset
632
- throw new Error(`ffi-struct: no field "${name}"`)
633
- },
634
- }
635
- }
636
-
637
- // ============================================================
638
- // 公共 API
639
- // ============================================================
640
-
641
- /** 聚合对齐上限(pack 压 maxAlign,语义同 MSVC #pragma pack)。 */
642
- export type AggOpts = { pack?: number }
643
-
644
- /** 平铺字段表 → '#' 声明:'#'/`#pack` 由构造器写入(顶层 kind 由函数名表明)。
645
- * member 里混进的其他 '#' 前缀键在此剔除 —— fresh literal 由 excess check 拒,这里
646
- * 运行时兜底,避免用户键覆盖构造器写入的 '#'/`#pack`。
647
- * 两处守卫(DECL 常量直传形态,`struct('X', X_DECL)`):
648
- * - member 自带 '#' 时须与函数名 kind 一致,否则 throw。TS 侧 overload 参数收
649
- * `M & { '#'?: 'struct' }`(union 镜像),'#' 与函数名 kind 不符在编译期就交叉成
650
- * never 报错;此 throw 是 JS / `as any` / 动态构造 DECL 调用方的运行时兜底 ——
651
- * union DECL 误传 struct() 会静默翻转成 struct 布局,是最难查的一类错;
652
- * - '#pack' 透传(剥键即丢,pack 布局静默变自然对齐):opts.pack 未给时取 member 的,
653
- * 两者都给且不等则 throw —— 跨参数的相等约束类型层表达不了,只能运行时校。 */
654
- function buildDecl(kind: 'struct' | 'union', member: Record<string, unknown>, pack?: number): C_Struct | C_Union {
655
- const declKind = member['#']
656
- if (declKind !== undefined && declKind !== kind)
657
- throw new Error(`ffi-struct: ${kind}() received a '${String(declKind)}' declaration ` +
658
- `(kind mismatch — use ${declKind}() for it)`)
659
- const declPack = member['#pack']
660
- if (pack === undefined && declPack !== undefined) pack = declPack as number
661
- else if (pack !== undefined && declPack !== undefined && pack !== declPack)
662
- throw new Error(`ffi-struct: #pack conflict: opts.pack=${pack} vs declaration ${String(declPack)}`)
663
- const decl: Record<string, unknown> = { '#': kind }
664
- if (pack !== undefined) decl['#pack'] = pack
665
- for (const [k, v] of Object.entries(member))
666
- if (!k.startsWith('#')) decl[k] = v
667
- return decl as unknown as C_Struct | C_Union
668
- }
669
-
670
- /** def 的类型经方法签名索引提取(StructDef 不再挂 __struct 成员 —— T 只活在签名里):
671
- * encode 重载末条首参 = EncodeIn<T>、decode 返回 = ShapeOfC<T>、alloc 返回 = PAB<N>、
672
- * decode 首参剥 ArrayBuffer = Ptr<N>(裸地址形)。 */
673
- type EncInOf<D extends StructDef<any, any>> = Parameters<D['encode']>[0]
674
- type DecOf<D extends StructDef<any, any>> = ReturnType<D['decode']>
675
- type PABOf<D extends StructDef<any, any>> = ReturnType<D['alloc']>
676
- type AddrOf<D extends StructDef<any, any>> = Exclude<Parameters<D['decode']>[0], ArrayBuffer>
677
-
678
- /** decode 双态重载:有 byteLength 的 buffer 形 count 可缺省(自算);裸地址无长度
679
- * 信息,count 必填(类型层拦,缺省调用编译红)。 */
680
- type StructArrayDecode<D extends StructDef<any, any>> = {
681
- (p: ArrayBuffer, count?: number): DecOf<D>[]
682
- (p: AddrOf<D>, count: number): DecOf<D>[]
683
- }
684
-
685
- /** 结构/联合体的数组编解码对工厂:返回 `{ encode, decode }`。
686
- * encode —— 单值 encoder 的数组版:收元素数组 → 按本布局 size 步长紧密排列
687
- * (stride = size,已含尾部对齐 padding),返回带品牌 .ptr 的 PAB —— 与 C 的
688
- * `const T apt[]` 同构。值域刻意收普通数组(调用方持有的 `const pts = [...]` 变量
689
- * 直传,非空元组会拒之);[] / 非数组 / 单对象运行时 throw —— 任何路径都不产出
690
- * PAB(0)(其 .ptr 是"非 0 的 1 字节堆指针",会骗过 C 的 NULL 防御;空位语义由
691
- * 调用方显式传 NULL)。注册进 bind 的 encoders 表(`{ POINT: structArray(POINT) }`)
692
- * 后,普通 `<POINT>ptr` 位的值域即元素数组(值域由 encoder 推导,token/引擎/类型层
693
- * 零数组概念);直接调用 `.encode(els).ptr` 亦可。
694
- * decode —— 出参数组读回(与 def.decode 的 ArrayBuffer/地址双态对称):
695
- * - buffer 形(含 PAB):count? 缺省自算 byteLength/size —— 要求整数倍,% ≠ 0
696
- * throw(混合缓冲如 EnumPrinters 的"结构 + 尾部字符串"必须显式 count);显式
697
- * count 校验 count*size ≤ byteLength(写错 count → 越界读的防线,buf 形可校才
698
- * 校;不验品牌,与 def.decode 的 buffer 形同款)。
699
- * - 裸地址形:count 必填(地址无长度信息),逐字节拷读(def.decode 的 number 形
700
- * 同机制);长度不可校 → 信任 C 的 count。
701
- * - count = 0 → [](读 0 个真空安全,与 encode 禁空的 PAB(0) 理由刻意不对称)。
702
- * 实现:encode 走纯公开 API(alloc + encode offset 循环);decode 直接读 def.fields
703
- * (createStruct 暴露的布局 IR,与 def.decode 同一份,无需重算)。 */
704
- export function structArray<D extends StructDef<any, any>>(def: D) {
705
- const { fields, size } = def
706
- const readN = (dv: DataView, n: number): unknown[] => {
707
- const out: unknown[] = []
708
- for (let i = 0; i < n; i++) out.push(doDecode(dv, i * size, fields))
709
- return out
710
- }
711
- return {
712
- encode: (els: readonly EncInOf<D>[]): PABOf<D> => {
713
- if (!Array.isArray(els) || els.length === 0)
714
- throw new Error(`ffi-struct: structArray(def).encode(els) requires a ` +
715
- `non-empty element array (pass NULL for an empty pointer position)`)
716
- const out = def.alloc(els.length)
717
- for (let i = 0; i < els.length; i++) def.encode(els[i], out, i * def.size)
718
- return out as PABOf<D>
719
- },
720
- decode: ((p: ArrayBuffer | number, count?: number): unknown[] => {
721
- // fail-loud 校验矩阵(见 JSDoc):显式 count 信调用方但拦越界;缺省只对
722
- // 有 byteLength 的 buffer 形自算;裸地址必带 count。
723
- if (count !== undefined && (!Number.isInteger(count) || count < 0))
724
- throw new Error(`ffi-struct: structArray.decode(p, count) requires a ` +
725
- `non-negative integer count, got ${count}`)
726
- if (typeof p === 'number') {
727
- if (count === undefined)
728
- throw new Error(`ffi-struct: structArray.decode(address, count) ` +
729
- `requires an explicit count (a bare address carries no length)`)
730
- if (count === 0) return []
731
- // readBytes 一次 native memcpy 拷出整段再解码(def.decode 的 number 形同机制)。
732
- return readN(new DataView(ffi.readBytes(p, count * size)), count)
733
- }
734
- let n: number
735
- if (count !== undefined) {
736
- n = count
737
- // 越界防线:写错 count 是越界读的唯一入口,buf 形有 byteLength 就校死。
738
- if (n * size > p.byteLength)
739
- throw new Error(`ffi-struct: structArray.decode: count ${n} × ` +
740
- `size ${size} = ${n * size} exceeds buffer byteLength ${p.byteLength}`)
741
- } else {
742
- // 自算形态:byteLength 必须是纯元素序列(size = 0 的退化布局同样落此
743
- // throw —— 0 % 0 = NaN),混合缓冲显式传 count。
744
- if (p.byteLength % size !== 0)
745
- throw new Error(`ffi-struct: structArray.decode: byteLength ` +
746
- `${p.byteLength} is not a multiple of size ${size} ` +
747
- `(mixed buffer? pass count explicitly)`)
748
- n = p.byteLength / size
749
- }
750
- if (n === 0) return []
751
- return readN(new DataView(p), n)
752
- }) as StructArrayDecode<D>,
753
- }
754
- }
755
-
756
- /** 平铺字段表定义结构体(token/`X[n]`/`unit:width`/`'#'` 声明均可,见 ctype.ts §4)。
757
- * 给 name 则 alloc().ptr 与 decode 入参带 Ptr<name> 品牌。DECL 常量可直传(
758
- * `struct('X', X_DECL)`)—— 参数交叉 `{ '#'?: 'struct' }` 锁 kind:传 union/array
759
- * DECL 时 '#' 字面类型交叉成 never,编译期即报错。 */
760
- type StructOf<M, N extends string = ''> = StructDef<{ '#': 'struct', '#pack'?: number } & M, N>
761
- type UnionOf<M, N extends string = ''> = StructDef<{ '#': 'union', '#pack'?: number } & M, N>
762
- export function struct<const M extends SimpleMember>(member: M & { '#'?: 'struct' }, opts?: AggOpts): StructOf<M>
763
- export function struct<const N extends string, const M extends SimpleMember>(name: N, member: M & { '#'?: 'struct' }, opts?: AggOpts): StructOf<M, N>
764
- export function struct(a: string | SimpleMember, b?: SimpleMember | AggOpts, c?: AggOpts): any {
765
- const isNamed = typeof a === 'string'
766
- const raw = (isNamed ? b : a) as Record<string, unknown>
767
- const opts = (isNamed ? c : b) as AggOpts | undefined
768
- return createStruct(buildDecl('struct', raw, opts?.pack))
769
- }
770
-
771
- /** 平铺字段表定义联合体;布局/读写与 struct 相同,仅成员偏移重叠。DECL 常量可直传,
772
- * 参数交叉 `{ '#'?: 'union' }` 锁 kind(struct 侧镜像,理由见上)。 */
773
- export function union<const M extends SimpleMember>(member: M & { '#'?: 'union' }, opts?: AggOpts): UnionOf<M>
774
- export function union<const N extends string, const M extends SimpleMember>(name: N, member: M & { '#'?: 'union' }, opts?: AggOpts): UnionOf<M, N>
775
- export function union(a: string | SimpleMember, b?: SimpleMember | AggOpts, c?: AggOpts): any {
776
- const isNamed = typeof a === 'string'
777
- const raw = (isNamed ? b : a) as Record<string, unknown>
778
- const opts = (isNamed ? c : b) as AggOpts | undefined
779
- return createStruct(buildDecl('union', raw, opts?.pack))
780
- }