node-ctypes 0.1.5 → 1.0.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.
Potentially problematic release.
This version of node-ctypes might be problematic. Click here for more details.
- package/README.md +14 -9
- 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 +312 -0
- package/lib/core/callback.js +275 -0
- package/lib/core/types.js +140 -0
- package/lib/index.d.ts +71 -124
- package/lib/index.js +215 -3096
- package/lib/memory/buffer.js +404 -0
- package/lib/memory/operations.js +295 -0
- package/lib/memory/pointer.js +358 -0
- package/lib/platform/constants.js +110 -0
- package/lib/platform/errors.js +403 -0
- package/lib/structures/Structure.js +414 -0
- package/lib/structures/Union.js +102 -0
- package/lib/structures/helpers/array.js +261 -0
- package/lib/structures/helpers/bitfield.js +147 -0
- package/lib/structures/helpers/common.js +129 -0
- package/lib/structures/helpers/struct.js +925 -0
- package/lib/structures/helpers/union.js +465 -0
- package/lib/types/SimpleCData.js +193 -0
- package/lib/types/primitives.js +392 -0
- package/lib/utils/cache.js +142 -0
- package/package.json +1 -1
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Structure.js
|
|
3
|
+
* @module structures/Structure
|
|
4
|
+
* @description Structure base class for Python ctypes-compatible structures.
|
|
5
|
+
*
|
|
6
|
+
* This module provides the `Structure` base class that users extend to create
|
|
7
|
+
* custom struct types with automatic field management, memory layout, and
|
|
8
|
+
* proxy-based field access.
|
|
9
|
+
*
|
|
10
|
+
* **Python ctypes Compatibility**:
|
|
11
|
+
* Direct equivalent to Python's `ctypes.Structure` class with `_fields_` attribute.
|
|
12
|
+
*
|
|
13
|
+
* @example Basic Structure subclass
|
|
14
|
+
* ```javascript
|
|
15
|
+
* import { Structure, c_int32, c_float } from 'node-ctypes';
|
|
16
|
+
*
|
|
17
|
+
* class Point extends Structure {
|
|
18
|
+
* static _fields_ = [
|
|
19
|
+
* ['x', c_int32],
|
|
20
|
+
* ['y', c_int32]
|
|
21
|
+
* ];
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* const pt = new Point({ x: 10, y: 20 });
|
|
25
|
+
* console.log(pt.x, pt.y); // 10 20
|
|
26
|
+
* pt.x = 42;
|
|
27
|
+
* console.log(pt.x); // 42
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @example Positional arguments
|
|
31
|
+
* ```javascript
|
|
32
|
+
* const pt = new Point(10, 20); // x=10, y=20
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @example Wrapping existing buffer
|
|
36
|
+
* ```javascript
|
|
37
|
+
* const buffer = Buffer.alloc(8);
|
|
38
|
+
* const pt = new Point(buffer);
|
|
39
|
+
* pt.x = 100;
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @example Array-style _fields_ (Python ctypes compatible)
|
|
43
|
+
* ```javascript
|
|
44
|
+
* class Rect extends Structure {
|
|
45
|
+
* static _fields_ = [
|
|
46
|
+
* ['x', c_int32],
|
|
47
|
+
* ['y', c_int32],
|
|
48
|
+
* ['width', c_int32],
|
|
49
|
+
* ['height', c_int32]
|
|
50
|
+
* ];
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @example Object-style _fields_ (alternative syntax)
|
|
55
|
+
* ```javascript
|
|
56
|
+
* class Rect extends Structure {
|
|
57
|
+
* static _fields_ = {
|
|
58
|
+
* x: c_int32,
|
|
59
|
+
* y: c_int32,
|
|
60
|
+
* width: c_int32,
|
|
61
|
+
* height: c_int32
|
|
62
|
+
* };
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Creates the Structure base class with required dependencies injected.
|
|
69
|
+
*
|
|
70
|
+
* @param {Function} alloc - Memory allocation function
|
|
71
|
+
* @param {Function} struct - Struct definition function
|
|
72
|
+
* @returns {Class} Structure base class
|
|
73
|
+
* @private
|
|
74
|
+
*/
|
|
75
|
+
export function createStructureClass(alloc, struct) {
|
|
76
|
+
/**
|
|
77
|
+
* Structure base class for creating C-style structures.
|
|
78
|
+
*
|
|
79
|
+
* Users extend this class and define static `_fields_` to specify the struct layout.
|
|
80
|
+
* The constructor handles memory allocation, field initialization, and returns a
|
|
81
|
+
* Proxy for transparent field access.
|
|
82
|
+
*
|
|
83
|
+
* **Key Features**:
|
|
84
|
+
* - Automatic memory layout based on `_fields_`
|
|
85
|
+
* - Proxy-based transparent field access
|
|
86
|
+
* - Support for positional arguments in constructor
|
|
87
|
+
* - Buffer wrapping for zero-copy operations
|
|
88
|
+
* - Anonymous field support via `_anonymous_`
|
|
89
|
+
* - Packed mode via `_pack_`
|
|
90
|
+
*
|
|
91
|
+
* **Python ctypes Compatibility**:
|
|
92
|
+
* Similar to Python's `ctypes.Structure` with:
|
|
93
|
+
* - `_fields_` attribute (array or object)
|
|
94
|
+
* - `_anonymous_` attribute for anonymous fields
|
|
95
|
+
* - `_pack_` attribute for packed structures
|
|
96
|
+
*
|
|
97
|
+
* @class Structure
|
|
98
|
+
*
|
|
99
|
+
* @example Extending Structure
|
|
100
|
+
* ```javascript
|
|
101
|
+
* class Vec3 extends Structure {
|
|
102
|
+
* static _fields_ = [
|
|
103
|
+
* ['x', c_float],
|
|
104
|
+
* ['y', c_float],
|
|
105
|
+
* ['z', c_float]
|
|
106
|
+
* ];
|
|
107
|
+
*
|
|
108
|
+
* // Add custom methods
|
|
109
|
+
* length() {
|
|
110
|
+
* return Math.sqrt(this.x**2 + this.y**2 + this.z**2);
|
|
111
|
+
* }
|
|
112
|
+
* }
|
|
113
|
+
*
|
|
114
|
+
* const v = new Vec3(1.0, 2.0, 3.0);
|
|
115
|
+
* console.log(v.length()); // 3.7416573867739413
|
|
116
|
+
* ```
|
|
117
|
+
*
|
|
118
|
+
* @example Anonymous fields
|
|
119
|
+
* ```javascript
|
|
120
|
+
* class Point extends Structure {
|
|
121
|
+
* static _fields_ = [['x', c_int32], ['y', c_int32]];
|
|
122
|
+
* }
|
|
123
|
+
*
|
|
124
|
+
* class Circle extends Structure {
|
|
125
|
+
* static _fields_ = [
|
|
126
|
+
* ['center', Point],
|
|
127
|
+
* ['radius', c_int32]
|
|
128
|
+
* ];
|
|
129
|
+
* static _anonymous_ = ['center'];
|
|
130
|
+
* }
|
|
131
|
+
*
|
|
132
|
+
* const c = new Circle();
|
|
133
|
+
* c.x = 10; // Access Point.x directly
|
|
134
|
+
* c.y = 20; // Access Point.y directly
|
|
135
|
+
* c.radius = 5;
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
class Structure {
|
|
139
|
+
constructor(...args) {
|
|
140
|
+
const Ctor = this.constructor;
|
|
141
|
+
const def = Ctor._structDef || Ctor._buildStruct();
|
|
142
|
+
// Allocate buffer for instance
|
|
143
|
+
let buf = alloc(def.size);
|
|
144
|
+
buf.fill(0);
|
|
145
|
+
|
|
146
|
+
// Support positional args: new Point(10,20)
|
|
147
|
+
if (args.length === 1 && typeof args[0] === "object" && !Array.isArray(args[0]) && !Buffer.isBuffer(args[0])) {
|
|
148
|
+
const initial = args[0];
|
|
149
|
+
for (const [k, v] of Object.entries(initial)) {
|
|
150
|
+
def.set(buf, k, v);
|
|
151
|
+
}
|
|
152
|
+
} else if (args.length === 1 && Buffer.isBuffer(args[0])) {
|
|
153
|
+
// new Point(buffer) - wrap existing buffer
|
|
154
|
+
buf = args[0];
|
|
155
|
+
// Validate buffer size matches struct size
|
|
156
|
+
if (buf.length < def.size) {
|
|
157
|
+
throw new RangeError(`Buffer size (${buf.length}) is smaller than struct size (${def.size})`);
|
|
158
|
+
}
|
|
159
|
+
} else if (args.length > 0) {
|
|
160
|
+
// Positional mapping to non-anonymous declared fields order
|
|
161
|
+
const ordered = def.fields.filter((f) => !f.isAnonymous);
|
|
162
|
+
for (let i = 0; i < Math.min(args.length, ordered.length); i++) {
|
|
163
|
+
def.set(buf, ordered[i].name, args[i]);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Store internal references on this (needed for methods like toObject)
|
|
168
|
+
this.__buffer = buf;
|
|
169
|
+
this.__structDef = def;
|
|
170
|
+
|
|
171
|
+
// Build field map for O(1) lookup
|
|
172
|
+
const fieldMap = new Map();
|
|
173
|
+
const anonFieldNames = new Set();
|
|
174
|
+
for (const field of def.fields) {
|
|
175
|
+
fieldMap.set(field.name, field);
|
|
176
|
+
if (field.isAnonymous && field.type && Array.isArray(field.type.fields)) {
|
|
177
|
+
for (const subField of field.type.fields) {
|
|
178
|
+
anonFieldNames.add(subField.name);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Return Proxy instead of using Object.defineProperty for each field
|
|
184
|
+
return new Proxy(this, {
|
|
185
|
+
get(target, prop, receiver) {
|
|
186
|
+
// Special properties that exist on the instance
|
|
187
|
+
if (prop === "_buffer") return buf;
|
|
188
|
+
if (prop === "_structDef") return def;
|
|
189
|
+
if (prop === "__buffer") return buf;
|
|
190
|
+
if (prop === "__structDef") return def;
|
|
191
|
+
if (prop === Symbol.toStringTag) return Ctor.name || "Structure";
|
|
192
|
+
if (prop === Symbol.iterator) return undefined;
|
|
193
|
+
|
|
194
|
+
// Methods defined on the prototype
|
|
195
|
+
if (typeof target[prop] === "function") {
|
|
196
|
+
return target[prop].bind(target);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Check if it's a known field (O(1) lookup)
|
|
200
|
+
if (typeof prop === "string" && fieldMap.has(prop)) {
|
|
201
|
+
return def.get(buf, prop);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Check anonymous fields
|
|
205
|
+
if (typeof prop === "string" && anonFieldNames.has(prop)) {
|
|
206
|
+
return def.get(buf, prop);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Fallback to target properties
|
|
210
|
+
return Reflect.get(target, prop, receiver);
|
|
211
|
+
},
|
|
212
|
+
set(target, prop, value, receiver) {
|
|
213
|
+
// Check if it's a known field (O(1) lookup)
|
|
214
|
+
if (typeof prop === "string" && fieldMap.has(prop)) {
|
|
215
|
+
def.set(buf, prop, value);
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Check anonymous fields
|
|
220
|
+
if (typeof prop === "string" && anonFieldNames.has(prop)) {
|
|
221
|
+
def.set(buf, prop, value);
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Fallback
|
|
226
|
+
return Reflect.set(target, prop, value, receiver);
|
|
227
|
+
},
|
|
228
|
+
has(target, prop) {
|
|
229
|
+
if (prop === "_buffer" || prop === "_structDef" || prop === "toObject") return true;
|
|
230
|
+
if (typeof prop === "string" && fieldMap.has(prop)) return true;
|
|
231
|
+
if (typeof prop === "string" && anonFieldNames.has(prop)) return true;
|
|
232
|
+
return Reflect.has(target, prop);
|
|
233
|
+
},
|
|
234
|
+
ownKeys(target) {
|
|
235
|
+
const keys = [];
|
|
236
|
+
for (const f of def.fields) {
|
|
237
|
+
if (f.isAnonymous && f.type && Array.isArray(f.type.fields)) {
|
|
238
|
+
for (const sf of f.type.fields) {
|
|
239
|
+
keys.push(sf.name);
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
keys.push(f.name);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return keys;
|
|
246
|
+
},
|
|
247
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
248
|
+
if (typeof prop === "string" && (fieldMap.has(prop) || anonFieldNames.has(prop))) {
|
|
249
|
+
return {
|
|
250
|
+
enumerable: true,
|
|
251
|
+
configurable: true,
|
|
252
|
+
writable: true,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
if (prop === "_buffer" || prop === "_structDef") {
|
|
256
|
+
return {
|
|
257
|
+
enumerable: false,
|
|
258
|
+
configurable: true,
|
|
259
|
+
writable: true,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
return Reflect.getOwnPropertyDescriptor(target, prop);
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Build the underlying struct definition and cache it on the constructor
|
|
268
|
+
static _buildStruct() {
|
|
269
|
+
// Accept either array _fields_ (Python style) or object map
|
|
270
|
+
const fields = this._fields_ || {};
|
|
271
|
+
let mapFields = {};
|
|
272
|
+
|
|
273
|
+
if (Array.isArray(fields)) {
|
|
274
|
+
for (const entry of fields) {
|
|
275
|
+
if (Array.isArray(entry) && entry.length >= 2) {
|
|
276
|
+
mapFields[entry[0]] = entry[1];
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
} else if (typeof fields === "object" && fields !== null) {
|
|
280
|
+
mapFields = { ...fields };
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Handle anonymous fields list: convert to { anonymous: true, type: T }
|
|
284
|
+
if (Array.isArray(this._anonymous_)) {
|
|
285
|
+
for (const anonName of this._anonymous_) {
|
|
286
|
+
if (mapFields[anonName] !== undefined) {
|
|
287
|
+
mapFields[anonName] = {
|
|
288
|
+
anonymous: true,
|
|
289
|
+
type: mapFields[anonName],
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const options = {};
|
|
296
|
+
if (this._pack_ !== undefined) options.packed = !!this._pack_;
|
|
297
|
+
|
|
298
|
+
const def = struct(mapFields, options);
|
|
299
|
+
this._structDef = def;
|
|
300
|
+
return def;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Create returns an instance of the JS class (not a plain object)
|
|
304
|
+
static create(values = {}) {
|
|
305
|
+
const def = this._structDef || this._buildStruct();
|
|
306
|
+
// If a Buffer provided, wrap it
|
|
307
|
+
if (Buffer.isBuffer(values)) {
|
|
308
|
+
const inst = new this(values);
|
|
309
|
+
return inst;
|
|
310
|
+
}
|
|
311
|
+
// If values is instance of this class, return it
|
|
312
|
+
if (values && values._buffer && values._structDef === def) {
|
|
313
|
+
return values;
|
|
314
|
+
}
|
|
315
|
+
return new this(values);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Create raw plain object without synchronization (for performance)
|
|
319
|
+
static createRaw(values = {}) {
|
|
320
|
+
const def = this._structDef || this._buildStruct();
|
|
321
|
+
return def.toObject(def.create(values)._buffer);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Synchronize plain object into instance buffer
|
|
325
|
+
syncFromObject(obj) {
|
|
326
|
+
const plain = this.__structDef.toObject(this.__buffer);
|
|
327
|
+
Object.assign(plain, obj);
|
|
328
|
+
this.__structDef.fromObject(this.__buffer, plain);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// toObject: accept Buffer, instance or plain buffer
|
|
332
|
+
static toObject(bufOrInst) {
|
|
333
|
+
const def = this._structDef || this._buildStruct();
|
|
334
|
+
if (bufOrInst && bufOrInst._buffer) {
|
|
335
|
+
return def.toObject(bufOrInst._buffer);
|
|
336
|
+
}
|
|
337
|
+
return def.toObject(bufOrInst);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// fromObject: write plain object into buffer
|
|
341
|
+
static fromObject(bufOrInst, obj) {
|
|
342
|
+
const def = this._structDef || this._buildStruct();
|
|
343
|
+
const buf = bufOrInst && bufOrInst._buffer ? bufOrInst._buffer : bufOrInst;
|
|
344
|
+
return def.fromObject(buf, obj);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Instance convenience
|
|
348
|
+
get(fieldName) {
|
|
349
|
+
return this.__structDef.get(this.__buffer, fieldName);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
set(fieldName, value) {
|
|
353
|
+
return this.__structDef.set(this.__buffer, fieldName, value);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Bulk operations for better performance
|
|
357
|
+
setFields(fields) {
|
|
358
|
+
for (const [name, value] of Object.entries(fields)) {
|
|
359
|
+
this.__structDef.set(this.__buffer, name, value);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
getFields(fieldNames) {
|
|
364
|
+
const result = {};
|
|
365
|
+
for (const name of fieldNames) {
|
|
366
|
+
result[name] = this.__structDef.get(this.__buffer, name);
|
|
367
|
+
}
|
|
368
|
+
return result;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// bulk operations (Proxy reads directly from buffer - no cache needed)
|
|
372
|
+
withBulkUpdate(callback) {
|
|
373
|
+
return callback(this);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Direct typed array access for numeric fields (maximum performance)
|
|
377
|
+
getInt32Array(offset = 0, length) {
|
|
378
|
+
return new Int32Array(this.__buffer.buffer, this.__buffer.byteOffset + offset, length);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
getFloat64Array(offset = 0, length) {
|
|
382
|
+
return new Float64Array(this.__buffer.buffer, this.__buffer.byteOffset + offset, length);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Get raw buffer slice for external operations
|
|
386
|
+
getBufferSlice(offset = 0, size = this.__buffer.length - offset) {
|
|
387
|
+
return this.__buffer.subarray(offset, offset + size);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Vectorized operations for arrays of structs
|
|
391
|
+
static createArray(count, initialValues = []) {
|
|
392
|
+
const instances = [];
|
|
393
|
+
for (let i = 0; i < count; i++) {
|
|
394
|
+
instances.push(this.create(initialValues[i] || {}));
|
|
395
|
+
}
|
|
396
|
+
return instances;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Bulk update all instances in array
|
|
400
|
+
static updateArray(instances, updates) {
|
|
401
|
+
for (let i = 0; i < instances.length && i < updates.length; i++) {
|
|
402
|
+
if (updates[i] && typeof updates[i] === "object") {
|
|
403
|
+
instances[i].setFields(updates[i]);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
toObject() {
|
|
409
|
+
return this.__structDef.toObject(this.__buffer);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return Structure;
|
|
414
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file UnionClass.js
|
|
3
|
+
* @module structures/UnionClass
|
|
4
|
+
* @description Union base class for Python ctypes-compatible unions.
|
|
5
|
+
*
|
|
6
|
+
* This module provides the `Union` base class that users extend to create
|
|
7
|
+
* custom union types where all fields share the same memory location.
|
|
8
|
+
*
|
|
9
|
+
* **Python ctypes Compatibility**:
|
|
10
|
+
* Direct equivalent to Python's `ctypes.Union` class with `_fields_` attribute.
|
|
11
|
+
*
|
|
12
|
+
* @example Basic Union subclass
|
|
13
|
+
* ```javascript
|
|
14
|
+
* import { Union, c_int32, c_float } from 'node-ctypes';
|
|
15
|
+
*
|
|
16
|
+
* class Value extends Union {
|
|
17
|
+
* static _fields_ = [
|
|
18
|
+
* ['as_int', c_int32],
|
|
19
|
+
* ['as_float', c_float]
|
|
20
|
+
* ];
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* const v = new Value();
|
|
24
|
+
* v.as_int = 0x40490FDB; // IEEE 754 for ~3.14159
|
|
25
|
+
* console.log(v.as_float); // ~3.14159
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @example Type reinterpretation
|
|
29
|
+
* ```javascript
|
|
30
|
+
* class ByteWord extends Union {
|
|
31
|
+
* static _fields_ = [
|
|
32
|
+
* ['bytes', array(c_uint8, 4)],
|
|
33
|
+
* ['word', c_uint32]
|
|
34
|
+
* ];
|
|
35
|
+
* }
|
|
36
|
+
*
|
|
37
|
+
* const bw = new ByteWord();
|
|
38
|
+
* bw.word = 0x12345678;
|
|
39
|
+
* console.log([...bw.bytes]); // [0x78, 0x56, 0x34, 0x12] (little-endian)
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Creates the Union base class with required dependencies injected.
|
|
45
|
+
*
|
|
46
|
+
* @param {Class} Structure - Structure base class to extend from
|
|
47
|
+
* @param {Function} union - Union definition function
|
|
48
|
+
* @returns {Class} Union base class
|
|
49
|
+
* @private
|
|
50
|
+
*/
|
|
51
|
+
export function createUnionClass(Structure, union) {
|
|
52
|
+
/**
|
|
53
|
+
* Union base class for creating C-style unions.
|
|
54
|
+
*
|
|
55
|
+
* Extends `Structure` but uses union layout where all fields share the
|
|
56
|
+
* same memory location (offset 0). The union size is the maximum of all
|
|
57
|
+
* field sizes.
|
|
58
|
+
*
|
|
59
|
+
* **Python ctypes Compatibility**:
|
|
60
|
+
* Similar to Python's `ctypes.Union` with:
|
|
61
|
+
* - `_fields_` attribute (array or object)
|
|
62
|
+
* - All fields at same memory location
|
|
63
|
+
* - Size equals largest field size
|
|
64
|
+
*
|
|
65
|
+
* @class Union
|
|
66
|
+
* @extends Structure
|
|
67
|
+
*
|
|
68
|
+
* @example RGB color union
|
|
69
|
+
* ```javascript
|
|
70
|
+
* class Color extends Union {
|
|
71
|
+
* static _fields_ = [
|
|
72
|
+
* ['rgba', c_uint32],
|
|
73
|
+
* ['components', array(c_uint8, 4)]
|
|
74
|
+
* ];
|
|
75
|
+
* }
|
|
76
|
+
*
|
|
77
|
+
* const color = new Color();
|
|
78
|
+
* color.rgba = 0xFF0080FF; // RGBA: 255, 0, 128, 255
|
|
79
|
+
* console.log([...color.components]); // [255, 128, 0, 255] (little-endian)
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
class Union extends Structure {
|
|
83
|
+
static _buildStruct() {
|
|
84
|
+
const fields = this._fields_ || {};
|
|
85
|
+
let mapFields = {};
|
|
86
|
+
if (Array.isArray(fields)) {
|
|
87
|
+
for (const entry of fields) {
|
|
88
|
+
if (Array.isArray(entry) && entry.length >= 2) {
|
|
89
|
+
mapFields[entry[0]] = entry[1];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
} else if (typeof fields === "object" && fields !== null) {
|
|
93
|
+
mapFields = fields;
|
|
94
|
+
}
|
|
95
|
+
const def = union(mapFields);
|
|
96
|
+
this._structDef = def;
|
|
97
|
+
return def;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return Union;
|
|
102
|
+
}
|