node-ctypes 1.2.0 → 1.3.0
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.
- package/README.md +125 -10
- package/build/Release/ctypes-darwin-arm64.node +0 -0
- package/build/Release/ctypes-darwin-x64.node +0 -0
- package/build/Release/ctypes-linux-arm64.node +0 -0
- package/build/Release/ctypes-linux-x64.node +0 -0
- package/build/Release/ctypes-win32-arm64.node +0 -0
- package/build/Release/ctypes-win32-x64.node +0 -0
- package/lib/core/Library.js +222 -214
- package/lib/core/types.js +7 -0
- package/lib/index.d.ts +23 -1
- package/lib/index.js +56 -59
- package/lib/memory/pointer.js +243 -25
- package/lib/structures/Structure.js +261 -230
- package/lib/structures/Union.js +10 -2
- package/lib/structures/helpers/common.js +9 -1
- package/lib/structures/helpers/struct.js +6 -1
- package/lib/structures/helpers/union.js +9 -19
- package/lib/types/primitives.js +1 -1
- package/package.json +1 -1
|
@@ -69,10 +69,11 @@
|
|
|
69
69
|
*
|
|
70
70
|
* @param {Function} alloc - Memory allocation function
|
|
71
71
|
* @param {Function} struct - Struct definition function
|
|
72
|
+
* @param {Function} bitfield - Bitfield helper function for Python-style [name, type, bits] syntax
|
|
72
73
|
* @returns {Class} Structure base class
|
|
73
74
|
* @private
|
|
74
75
|
*/
|
|
75
|
-
export function createStructureClass(alloc, struct) {
|
|
76
|
+
export function createStructureClass(alloc, struct, bitfield) {
|
|
76
77
|
/**
|
|
77
78
|
* Structure base class for creating C-style structures.
|
|
78
79
|
*
|
|
@@ -135,280 +136,310 @@ export function createStructureClass(alloc, struct) {
|
|
|
135
136
|
* c.radius = 5;
|
|
136
137
|
* ```
|
|
137
138
|
*/
|
|
138
|
-
class Structure {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
139
|
+
class Structure {
|
|
140
|
+
constructor(...args) {
|
|
141
|
+
const Ctor = this.constructor;
|
|
142
|
+
const def = Ctor._structDef || Ctor._buildStruct();
|
|
143
|
+
// Allocate buffer for instance
|
|
144
|
+
let buf = alloc(def.size);
|
|
145
|
+
buf.fill(0);
|
|
146
|
+
|
|
147
|
+
// Support positional args: new Point(10,20)
|
|
148
|
+
if (args.length === 1 && typeof args[0] === "object" && !Array.isArray(args[0]) && !Buffer.isBuffer(args[0])) {
|
|
149
|
+
const initial = args[0];
|
|
150
|
+
for (const [k, v] of Object.entries(initial)) {
|
|
151
|
+
def.set(buf, k, v);
|
|
152
|
+
}
|
|
153
|
+
} else if (args.length === 1 && Buffer.isBuffer(args[0])) {
|
|
154
|
+
// new Point(buffer) - wrap existing buffer
|
|
155
|
+
buf = args[0];
|
|
156
|
+
// Validate buffer size matches struct size
|
|
157
|
+
if (buf.length < def.size) {
|
|
158
|
+
throw new RangeError(`Buffer size (${buf.length}) is smaller than struct size (${def.size})`);
|
|
159
|
+
}
|
|
160
|
+
} else if (args.length > 0) {
|
|
161
|
+
// Positional mapping to non-anonymous declared fields order
|
|
162
|
+
const ordered = def.fields.filter((f) => !f.isAnonymous);
|
|
163
|
+
for (let i = 0; i < Math.min(args.length, ordered.length); i++) {
|
|
164
|
+
def.set(buf, ordered[i].name, args[i]);
|
|
165
|
+
}
|
|
164
166
|
}
|
|
165
|
-
}
|
|
166
167
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
168
|
+
// Store internal references on this (needed for methods like toObject)
|
|
169
|
+
this.__buffer = buf;
|
|
170
|
+
this.__structDef = def;
|
|
171
|
+
|
|
172
|
+
// Build field map for O(1) lookup
|
|
173
|
+
const fieldMap = new Map();
|
|
174
|
+
const anonFieldNames = new Set();
|
|
175
|
+
for (const field of def.fields) {
|
|
176
|
+
fieldMap.set(field.name, field);
|
|
177
|
+
if (field.isAnonymous && field.type && Array.isArray(field.type.fields)) {
|
|
178
|
+
for (const subField of field.type.fields) {
|
|
179
|
+
anonFieldNames.add(subField.name);
|
|
180
|
+
}
|
|
179
181
|
}
|
|
180
182
|
}
|
|
181
|
-
}
|
|
182
183
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (typeof target[prop] === "function") {
|
|
196
|
-
return target[prop].bind(target);
|
|
197
|
-
}
|
|
184
|
+
// Return Proxy instead of using Object.defineProperty for each field
|
|
185
|
+
// Pre-compute fast lookup set for all field names (including anonymous)
|
|
186
|
+
const allFieldNames = new Set(fieldMap.keys());
|
|
187
|
+
for (const name of anonFieldNames) allFieldNames.add(name);
|
|
188
|
+
|
|
189
|
+
return new Proxy(this, {
|
|
190
|
+
get(target, prop, receiver) {
|
|
191
|
+
// FAST PATH: struct field access (most common case)
|
|
192
|
+
// Check string type first, then Set lookup - both are O(1)
|
|
193
|
+
if (typeof prop === "string" && allFieldNames.has(prop)) {
|
|
194
|
+
return def.get(buf, prop);
|
|
195
|
+
}
|
|
198
196
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
197
|
+
// SLOW PATH: special properties and methods
|
|
198
|
+
// Symbol check (Symbol.toStringTag, Symbol.iterator)
|
|
199
|
+
if (typeof prop === "symbol") {
|
|
200
|
+
if (prop === Symbol.toStringTag) return Ctor.name || "Structure";
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
203
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
204
|
+
// Internal buffer access
|
|
205
|
+
if (prop === "_buffer" || prop === "__buffer") return buf;
|
|
206
|
+
if (prop === "_structDef" || prop === "__structDef") return def;
|
|
208
207
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
if (typeof prop === "string" && fieldMap.has(prop)) {
|
|
215
|
-
def.set(buf, prop, value);
|
|
216
|
-
return true;
|
|
217
|
-
}
|
|
208
|
+
// Methods defined on the prototype
|
|
209
|
+
const method = target[prop];
|
|
210
|
+
if (typeof method === "function") {
|
|
211
|
+
return method.bind(target);
|
|
212
|
+
}
|
|
218
213
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
214
|
+
// Fallback to target properties
|
|
215
|
+
return Reflect.get(target, prop, receiver);
|
|
216
|
+
},
|
|
217
|
+
set(target, prop, value, receiver) {
|
|
218
|
+
// FAST PATH: struct field access (most common case)
|
|
219
|
+
if (typeof prop === "string" && allFieldNames.has(prop)) {
|
|
220
|
+
def.set(buf, prop, value);
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
224
223
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
224
|
+
// Fallback
|
|
225
|
+
return Reflect.set(target, prop, value, receiver);
|
|
226
|
+
},
|
|
227
|
+
has(target, prop) {
|
|
228
|
+
if (typeof prop === "string" && allFieldNames.has(prop)) return true;
|
|
229
|
+
if (prop === "_buffer" || prop === "_structDef" || prop === "toObject") return true;
|
|
230
|
+
return Reflect.has(target, prop);
|
|
231
|
+
},
|
|
232
|
+
ownKeys(target) {
|
|
233
|
+
const keys = [];
|
|
234
|
+
for (const f of def.fields) {
|
|
235
|
+
if (f.isAnonymous && f.type && Array.isArray(f.type.fields)) {
|
|
236
|
+
for (const sf of f.type.fields) {
|
|
237
|
+
keys.push(sf.name);
|
|
238
|
+
}
|
|
239
|
+
} else {
|
|
240
|
+
keys.push(f.name);
|
|
240
241
|
}
|
|
241
|
-
} else {
|
|
242
|
-
keys.push(f.name);
|
|
243
242
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
243
|
+
return keys;
|
|
244
|
+
},
|
|
245
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
246
|
+
if (typeof prop === "string" && allFieldNames.has(prop)) {
|
|
247
|
+
return {
|
|
248
|
+
enumerable: true,
|
|
249
|
+
configurable: true,
|
|
250
|
+
writable: true,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
if (prop === "_buffer" || prop === "_structDef") {
|
|
254
|
+
return {
|
|
255
|
+
enumerable: false,
|
|
256
|
+
configurable: true,
|
|
257
|
+
writable: true,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
return Reflect.getOwnPropertyDescriptor(target, prop);
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
}
|
|
266
264
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
265
|
+
// Static getters for size, alignment, and fields (enables use as nested type)
|
|
266
|
+
static get size() {
|
|
267
|
+
const def = this._structDef || this._buildStruct();
|
|
268
|
+
return def.size;
|
|
269
|
+
}
|
|
272
270
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
271
|
+
static get alignment() {
|
|
272
|
+
const def = this._structDef || this._buildStruct();
|
|
273
|
+
return def.alignment;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
static get fields() {
|
|
277
|
+
const def = this._structDef || this._buildStruct();
|
|
278
|
+
return def.fields;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
static get create() {
|
|
282
|
+
const def = this._structDef || this._buildStruct();
|
|
283
|
+
return def.create.bind(def);
|
|
281
284
|
}
|
|
282
285
|
|
|
283
|
-
//
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
286
|
+
// Build the underlying struct definition and cache it on the constructor
|
|
287
|
+
static _buildStruct() {
|
|
288
|
+
// Accept either array _fields_ (Python style) or object map
|
|
289
|
+
const fields = this._fields_ || {};
|
|
290
|
+
let mapFields = {};
|
|
291
|
+
|
|
292
|
+
if (Array.isArray(fields)) {
|
|
293
|
+
for (const entry of fields) {
|
|
294
|
+
if (Array.isArray(entry) && entry.length >= 2) {
|
|
295
|
+
const name = entry[0];
|
|
296
|
+
const type = entry[1];
|
|
297
|
+
// Python-style bitfield: [name, type, bits]
|
|
298
|
+
if (entry.length >= 3 && typeof entry[2] === "number") {
|
|
299
|
+
mapFields[name] = bitfield(type, entry[2]);
|
|
300
|
+
} else {
|
|
301
|
+
mapFields[name] = type;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
291
304
|
}
|
|
305
|
+
} else if (typeof fields === "object" && fields !== null) {
|
|
306
|
+
mapFields = { ...fields };
|
|
292
307
|
}
|
|
293
|
-
}
|
|
294
308
|
|
|
295
|
-
|
|
296
|
-
|
|
309
|
+
// Handle anonymous fields list: convert to { anonymous: true, type: T }
|
|
310
|
+
if (Array.isArray(this._anonymous_)) {
|
|
311
|
+
for (const anonName of this._anonymous_) {
|
|
312
|
+
if (mapFields[anonName] !== undefined) {
|
|
313
|
+
let anonType = mapFields[anonName];
|
|
314
|
+
// Normalize type: if it's a Structure/Union class, get its _structDef
|
|
315
|
+
if (typeof anonType === "function" && typeof anonType._buildStruct === "function") {
|
|
316
|
+
anonType = anonType._structDef || anonType._buildStruct();
|
|
317
|
+
}
|
|
318
|
+
mapFields[anonName] = {
|
|
319
|
+
anonymous: true,
|
|
320
|
+
type: anonType,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
297
325
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
return def;
|
|
301
|
-
}
|
|
326
|
+
const options = {};
|
|
327
|
+
if (this._pack_ !== undefined) options.packed = !!this._pack_;
|
|
302
328
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
// If a Buffer provided, wrap it
|
|
307
|
-
if (Buffer.isBuffer(values)) {
|
|
308
|
-
const inst = new this(values);
|
|
309
|
-
return inst;
|
|
329
|
+
const def = struct(mapFields, options);
|
|
330
|
+
this._structDef = def;
|
|
331
|
+
return def;
|
|
310
332
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
333
|
+
|
|
334
|
+
// Create returns an instance of the JS class (not a plain object)
|
|
335
|
+
static create(values = {}) {
|
|
336
|
+
const def = this._structDef || this._buildStruct();
|
|
337
|
+
// If a Buffer provided, wrap it
|
|
338
|
+
if (Buffer.isBuffer(values)) {
|
|
339
|
+
const inst = new this(values);
|
|
340
|
+
return inst;
|
|
341
|
+
}
|
|
342
|
+
// If values is instance of this class, return it
|
|
343
|
+
if (values && values._buffer && values._structDef === def) {
|
|
344
|
+
return values;
|
|
345
|
+
}
|
|
346
|
+
return new this(values);
|
|
314
347
|
}
|
|
315
|
-
return new this(values);
|
|
316
|
-
}
|
|
317
348
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
349
|
+
// Create raw plain object without synchronization (for performance)
|
|
350
|
+
static createRaw(values = {}) {
|
|
351
|
+
const def = this._structDef || this._buildStruct();
|
|
352
|
+
return def.toObject(def.create(values)._buffer);
|
|
353
|
+
}
|
|
323
354
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
355
|
+
// Synchronize plain object into instance buffer
|
|
356
|
+
syncFromObject(obj) {
|
|
357
|
+
const plain = this.__structDef.toObject(this.__buffer);
|
|
358
|
+
Object.assign(plain, obj);
|
|
359
|
+
this.__structDef.fromObject(this.__buffer, plain);
|
|
360
|
+
}
|
|
330
361
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
362
|
+
// toObject: accept Buffer, instance or plain buffer
|
|
363
|
+
static toObject(bufOrInst) {
|
|
364
|
+
const def = this._structDef || this._buildStruct();
|
|
365
|
+
if (bufOrInst && bufOrInst._buffer) {
|
|
366
|
+
return def.toObject(bufOrInst._buffer);
|
|
367
|
+
}
|
|
368
|
+
return def.toObject(bufOrInst);
|
|
336
369
|
}
|
|
337
|
-
return def.toObject(bufOrInst);
|
|
338
|
-
}
|
|
339
370
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
371
|
+
// fromObject: write plain object into buffer
|
|
372
|
+
static fromObject(bufOrInst, obj) {
|
|
373
|
+
const def = this._structDef || this._buildStruct();
|
|
374
|
+
const buf = bufOrInst && bufOrInst._buffer ? bufOrInst._buffer : bufOrInst;
|
|
375
|
+
return def.fromObject(buf, obj);
|
|
376
|
+
}
|
|
346
377
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
378
|
+
// Instance convenience
|
|
379
|
+
get(fieldName) {
|
|
380
|
+
return this.__structDef.get(this.__buffer, fieldName);
|
|
381
|
+
}
|
|
351
382
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
383
|
+
set(fieldName, value) {
|
|
384
|
+
return this.__structDef.set(this.__buffer, fieldName, value);
|
|
385
|
+
}
|
|
355
386
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
387
|
+
// Bulk operations for better performance
|
|
388
|
+
setFields(fields) {
|
|
389
|
+
for (const [name, value] of Object.entries(fields)) {
|
|
390
|
+
this.__structDef.set(this.__buffer, name, value);
|
|
391
|
+
}
|
|
360
392
|
}
|
|
361
|
-
}
|
|
362
393
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
394
|
+
getFields(fieldNames) {
|
|
395
|
+
const result = {};
|
|
396
|
+
for (const name of fieldNames) {
|
|
397
|
+
result[name] = this.__structDef.get(this.__buffer, name);
|
|
398
|
+
}
|
|
399
|
+
return result;
|
|
367
400
|
}
|
|
368
|
-
return result;
|
|
369
|
-
}
|
|
370
401
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
402
|
+
// bulk operations (Proxy reads directly from buffer - no cache needed)
|
|
403
|
+
withBulkUpdate(callback) {
|
|
404
|
+
return callback(this);
|
|
405
|
+
}
|
|
375
406
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
407
|
+
// Direct typed array access for numeric fields (maximum performance)
|
|
408
|
+
getInt32Array(offset = 0, length) {
|
|
409
|
+
return new Int32Array(this.__buffer.buffer, this.__buffer.byteOffset + offset, length);
|
|
410
|
+
}
|
|
380
411
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
412
|
+
getFloat64Array(offset = 0, length) {
|
|
413
|
+
return new Float64Array(this.__buffer.buffer, this.__buffer.byteOffset + offset, length);
|
|
414
|
+
}
|
|
384
415
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
416
|
+
// Get raw buffer slice for external operations
|
|
417
|
+
getBufferSlice(offset = 0, size = this.__buffer.length - offset) {
|
|
418
|
+
return this.__buffer.subarray(offset, offset + size);
|
|
419
|
+
}
|
|
389
420
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
421
|
+
// Vectorized operations for arrays of structs
|
|
422
|
+
static createArray(count, initialValues = []) {
|
|
423
|
+
const instances = [];
|
|
424
|
+
for (let i = 0; i < count; i++) {
|
|
425
|
+
instances.push(this.create(initialValues[i] || {}));
|
|
426
|
+
}
|
|
427
|
+
return instances;
|
|
395
428
|
}
|
|
396
|
-
return instances;
|
|
397
|
-
}
|
|
398
429
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
430
|
+
// Bulk update all instances in array
|
|
431
|
+
static updateArray(instances, updates) {
|
|
432
|
+
for (let i = 0; i < instances.length && i < updates.length; i++) {
|
|
433
|
+
if (updates[i] && typeof updates[i] === "object") {
|
|
434
|
+
instances[i].setFields(updates[i]);
|
|
435
|
+
}
|
|
404
436
|
}
|
|
405
437
|
}
|
|
406
|
-
}
|
|
407
438
|
|
|
408
|
-
|
|
409
|
-
|
|
439
|
+
toObject() {
|
|
440
|
+
return this.__structDef.toObject(this.__buffer);
|
|
441
|
+
}
|
|
410
442
|
}
|
|
411
|
-
}
|
|
412
443
|
|
|
413
444
|
return Structure;
|
|
414
445
|
}
|
package/lib/structures/Union.js
CHANGED
|
@@ -45,10 +45,11 @@
|
|
|
45
45
|
*
|
|
46
46
|
* @param {Class} Structure - Structure base class to extend from
|
|
47
47
|
* @param {Function} union - Union definition function
|
|
48
|
+
* @param {Function} bitfield - Bitfield helper function for Python-style [name, type, bits] syntax
|
|
48
49
|
* @returns {Class} Union base class
|
|
49
50
|
* @private
|
|
50
51
|
*/
|
|
51
|
-
export function createUnionClass(Structure, union) {
|
|
52
|
+
export function createUnionClass(Structure, union, bitfield) {
|
|
52
53
|
/**
|
|
53
54
|
* Union base class for creating C-style unions.
|
|
54
55
|
*
|
|
@@ -86,7 +87,14 @@ export function createUnionClass(Structure, union) {
|
|
|
86
87
|
if (Array.isArray(fields)) {
|
|
87
88
|
for (const entry of fields) {
|
|
88
89
|
if (Array.isArray(entry) && entry.length >= 2) {
|
|
89
|
-
|
|
90
|
+
const name = entry[0];
|
|
91
|
+
const type = entry[1];
|
|
92
|
+
// Python-style bitfield: [name, type, bits]
|
|
93
|
+
if (entry.length >= 3 && typeof entry[2] === "number") {
|
|
94
|
+
mapFields[name] = bitfield(type, entry[2]);
|
|
95
|
+
} else {
|
|
96
|
+
mapFields[name] = type;
|
|
97
|
+
}
|
|
90
98
|
}
|
|
91
99
|
}
|
|
92
100
|
} else if (typeof fields === "object" && fields !== null) {
|
|
@@ -87,7 +87,15 @@ export function bitfield(baseType, bits, sizeof) {
|
|
|
87
87
|
* @private
|
|
88
88
|
*/
|
|
89
89
|
export function _isStruct(type) {
|
|
90
|
-
|
|
90
|
+
// Check for struct definition object (from struct() function)
|
|
91
|
+
if (typeof type === "object" && type !== null && typeof type.size === "number" && Array.isArray(type.fields) && typeof type.create === "function") {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
// Check for Structure/Union class (has _fields_ or _structDef and _buildStruct)
|
|
95
|
+
if (typeof type === "function" && (type._fields_ || type._structDef) && typeof type._buildStruct === "function") {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
return false;
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
/**
|
|
@@ -248,7 +248,12 @@ export function struct(fields, options = {}, sizeof, alloc, readValue, writeValu
|
|
|
248
248
|
currentBitFieldBase = null;
|
|
249
249
|
currentBitOffset = 0;
|
|
250
250
|
|
|
251
|
-
|
|
251
|
+
// Normalize type: if it's a Structure/Union class, get its _structDef
|
|
252
|
+
let nestedStruct = type;
|
|
253
|
+
if (typeof type === "function" && typeof type._buildStruct === "function") {
|
|
254
|
+
nestedStruct = type._structDef || type._buildStruct();
|
|
255
|
+
}
|
|
256
|
+
|
|
252
257
|
const size = nestedStruct.size;
|
|
253
258
|
const alignment = packed ? 1 : nestedStruct.alignment;
|
|
254
259
|
|
|
@@ -94,26 +94,16 @@ export function union(fields, sizeof, alloc, readValue, writeValue, _isStruct, _
|
|
|
94
94
|
let size, alignment;
|
|
95
95
|
let fieldDef = { name, type, offset: 0 };
|
|
96
96
|
|
|
97
|
-
// Nested struct (object form)
|
|
97
|
+
// Nested struct (object form or class form)
|
|
98
98
|
if (_isStruct(type)) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
alignment = nested.alignment;
|
|
108
|
-
fieldDef.type = nested;
|
|
109
|
-
fieldDef.isNested = true;
|
|
110
|
-
}
|
|
111
|
-
// Union class (declarata come `class X extends Union`)
|
|
112
|
-
else if (typeof type === "function" && type.prototype instanceof Union) {
|
|
113
|
-
const nested = type._unionDef || type._buildUnion();
|
|
114
|
-
size = nested.size;
|
|
115
|
-
alignment = nested.alignment;
|
|
116
|
-
fieldDef.type = nested;
|
|
99
|
+
// Normalize type: if it's a Structure/Union class, get its _structDef
|
|
100
|
+
let nestedStruct = type;
|
|
101
|
+
if (typeof type === "function" && typeof type._buildStruct === "function") {
|
|
102
|
+
nestedStruct = type._structDef || type._buildStruct();
|
|
103
|
+
fieldDef.type = nestedStruct;
|
|
104
|
+
}
|
|
105
|
+
size = nestedStruct.size;
|
|
106
|
+
alignment = nestedStruct.alignment;
|
|
117
107
|
fieldDef.isNested = true;
|
|
118
108
|
}
|
|
119
109
|
// Array type
|