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.

@@ -0,0 +1,261 @@
1
+ /**
2
+ * @file array.js
3
+ * @module structures/array
4
+ * @description Array type implementation for fixed-size arrays of C types.
5
+ *
6
+ * This module provides Python ctypes-compatible array types that support:
7
+ * - Fixed-size arrays of any C type (primitives, structs, unions)
8
+ * - Python-like indexing with bracket notation
9
+ * - Iteration support (for...of, spread operator)
10
+ * - String initialization for character arrays
11
+ *
12
+ * **Python ctypes Compatibility**:
13
+ * Similar to Python's `type * count` array syntax or explicit array types.
14
+ *
15
+ * @example Basic usage
16
+ * ```javascript
17
+ * import { array, c_int32 } from 'node-ctypes';
18
+ *
19
+ * const IntArray5 = array(c_int32, 5);
20
+ * const arr = IntArray5.create([1, 2, 3, 4, 5]);
21
+ * console.log(arr[0]); // 1
22
+ * arr[2] = 42;
23
+ * console.log(arr[2]); // 42
24
+ * ```
25
+ *
26
+ * @example Array of structs
27
+ * ```javascript
28
+ * class Point extends Structure {
29
+ * static _fields_ = [['x', c_int32], ['y', c_int32]];
30
+ * }
31
+ *
32
+ * const PointArray = array(Point, 3);
33
+ * const points = PointArray.create();
34
+ * points[0].x = 10;
35
+ * points[0].y = 20;
36
+ * ```
37
+ *
38
+ * @example Iteration
39
+ * ```javascript
40
+ * const arr = IntArray5.create([1, 2, 3, 4, 5]);
41
+ * for (const val of arr) {
42
+ * console.log(val);
43
+ * }
44
+ * const values = [...arr]; // Spread operator
45
+ * ```
46
+ */
47
+
48
+ /**
49
+ * Creates a fixed-size array type for a given element type.
50
+ *
51
+ * Returns an array type definition that can create instances of fixed-size
52
+ * arrays. The created arrays support Python-like indexing, iteration, and
53
+ * automatic memory management.
54
+ *
55
+ * **Python ctypes Compatibility**:
56
+ * Similar to Python's `type * count` syntax (e.g., `c_int * 5`).
57
+ *
58
+ * @param {Function|Object} elementType - Element type (SimpleCData class, Structure/Union class, or type object)
59
+ * @param {number} count - Number of elements in the array
60
+ * @param {Function} sizeof - sizeof function reference
61
+ * @param {Function} alloc - alloc function reference
62
+ * @param {Function} readValue - readValue function reference
63
+ * @param {Function} writeValue - writeValue function reference
64
+ * @param {Function} Structure - Structure class reference
65
+ * @param {Function} Union - Union class reference
66
+ * @param {Function} ArrayType - Native ArrayType class reference (if available)
67
+ * @returns {Object} Array type definition with create and wrap methods
68
+ * @throws {TypeError} If elementType is not a valid type
69
+ *
70
+ * @example Create integer array
71
+ * ```javascript
72
+ * import { array, c_int32 } from 'node-ctypes';
73
+ *
74
+ * const IntArray5 = array(c_int32, 5);
75
+ * const arr = IntArray5.create([1, 2, 3, 4, 5]);
76
+ * console.log(arr[0]); // 1
77
+ * arr[2] = 42;
78
+ * console.log(arr[2]); // 42
79
+ * ```
80
+ *
81
+ * @example Partial initialization
82
+ * ```javascript
83
+ * const arr = IntArray5.create([1, 2]); // Rest are zeros
84
+ * console.log(arr[0]); // 1
85
+ * console.log(arr[4]); // 0
86
+ * ```
87
+ *
88
+ * @example String initialization (character arrays)
89
+ * ```javascript
90
+ * const CharArray10 = array(c_char, 10);
91
+ * const arr = CharArray10.create("hello");
92
+ * // arr contains ASCII codes: [104, 101, 108, 108, 111, 0, 0, 0, 0, 0]
93
+ * ```
94
+ *
95
+ * @example Array of structs
96
+ * ```javascript
97
+ * class Point extends Structure {
98
+ * static _fields_ = [['x', c_int32], ['y', c_int32]];
99
+ * }
100
+ *
101
+ * const PointArray = array(Point, 3);
102
+ * const points = PointArray.create();
103
+ * points[0].x = 10;
104
+ * points[0].y = 20;
105
+ * ```
106
+ */
107
+ export function array(elementType, count, sizeof, alloc, readValue, writeValue, Structure, Union, ArrayType) {
108
+ // Validate elementType: accept SimpleCData classes, Structure/Union classes,
109
+ // or native CType-like objects.
110
+ const isSimple = typeof elementType === "function" && elementType._isSimpleCData;
111
+ const isStruct = typeof elementType === "function" && (elementType.prototype instanceof Structure || elementType.prototype instanceof Union);
112
+ const isNativeTypeObj = typeof elementType === "object" && elementType !== null && (typeof elementType.getSize === "function" || elementType.size !== undefined);
113
+ if (!isSimple && !isStruct && !isNativeTypeObj) {
114
+ throw new TypeError("array elementType must be a SimpleCData class or a Structure/Union class");
115
+ }
116
+
117
+ const elementSize = sizeof(elementType);
118
+ let nativeArray;
119
+ if (isNativeTypeObj && ArrayType) {
120
+ nativeArray = new ArrayType(elementType, count);
121
+ } else {
122
+ // Provide a small JS-side shim implementing the minimal ArrayType API
123
+ nativeArray = {
124
+ getSize: () => count * elementSize,
125
+ getLength: () => count,
126
+ getAlignment: () => elementSize,
127
+ create: (values) => {
128
+ // JS create is implemented below; this should not be invoked by JS code
129
+ const size = count * elementSize;
130
+ const buffer = alloc(size);
131
+ buffer.fill(0);
132
+ return buffer;
133
+ },
134
+ };
135
+ }
136
+
137
+ // Wrap function that returns a Proxy for array-like indexing
138
+ const wrap = function (buffer) {
139
+ return new Proxy(buffer, {
140
+ get(target, prop, receiver) {
141
+ // Special case for _buffer to return the underlying buffer
142
+ if (prop === "_buffer") {
143
+ return target;
144
+ }
145
+
146
+ // Special case for toString to show array contents
147
+ if (prop === "toString") {
148
+ return function () {
149
+ try {
150
+ const arr = Array.from(this);
151
+ return `[${arr.join(", ")}]`;
152
+ } catch (e) {
153
+ return "[error]";
154
+ }
155
+ }.bind(receiver);
156
+ }
157
+
158
+ // Intercept Symbol.iterator to iterate over elements, not bytes
159
+ if (prop === Symbol.iterator) {
160
+ return function* () {
161
+ for (let i = 0; i < count; i++) {
162
+ const off = i * elementSize;
163
+ // If elementType is a struct/union class, return an instance bound to the slice
164
+ if (typeof elementType === "function" && (elementType.prototype instanceof Structure || elementType.prototype instanceof Union)) {
165
+ yield new elementType(target.subarray(off, off + elementSize));
166
+ } else {
167
+ yield readValue(target, elementType, off);
168
+ }
169
+ }
170
+ };
171
+ }
172
+
173
+ // Ignore other Symbols
174
+ if (typeof prop === "symbol") {
175
+ const value = Reflect.get(target, prop, target);
176
+ if (typeof value === "function") {
177
+ return value.bind(target);
178
+ }
179
+ return value;
180
+ }
181
+
182
+ // If it's a number (index) - intercept BEFORE checking target
183
+ const index = Number(prop);
184
+ if (Number.isInteger(index) && !isNaN(index)) {
185
+ if (index >= 0 && index < count) {
186
+ const off = index * elementSize;
187
+ if (typeof elementType === "function" && (elementType.prototype instanceof Structure || elementType.prototype instanceof Union)) {
188
+ return new elementType(target.subarray(off, off + elementSize));
189
+ }
190
+ return readValue(target, elementType, off);
191
+ }
192
+ // Numeric index out of bounds -> undefined (JavaScript behavior)
193
+ return undefined;
194
+ }
195
+
196
+ // For everything else, use the normal buffer
197
+ const value = Reflect.get(target, prop, target);
198
+ // If it's a function, bind it to the target
199
+ if (typeof value === "function") {
200
+ return value.bind(target);
201
+ }
202
+ return value;
203
+ },
204
+ set(target, prop, value) {
205
+ // Ignore Symbols
206
+ if (typeof prop === "symbol") {
207
+ return Reflect.set(target, prop, value, target);
208
+ }
209
+
210
+ const index = Number(prop);
211
+ if (Number.isInteger(index) && !isNaN(index)) {
212
+ if (index >= 0 && index < count) {
213
+ writeValue(target, elementType, value, index * elementSize);
214
+ return true;
215
+ }
216
+ // Index out of bounds - don't write anything but return false
217
+ return false;
218
+ }
219
+ return Reflect.set(target, prop, value, target);
220
+ },
221
+ });
222
+ };
223
+
224
+ // Return a wrapper object that delegates to nativeArray but overrides create()
225
+ return {
226
+ // Type information
227
+ elementType,
228
+ length: count,
229
+
230
+ // Delegated methods
231
+ getSize: () => nativeArray.getSize(),
232
+ getLength: () => nativeArray.getLength(),
233
+ getAlignment: () => nativeArray.getAlignment(),
234
+
235
+ // create automatically returns the proxy
236
+ create: (values) => {
237
+ const size = count * sizeof(elementType);
238
+ const buffer = alloc(size);
239
+ buffer.fill(0);
240
+ if (Array.isArray(values)) {
241
+ for (let i = 0; i < Math.min(values.length, count); i++) {
242
+ writeValue(buffer, elementType, values[i], i * sizeof(elementType));
243
+ }
244
+ } else if (typeof values === "string") {
245
+ for (let i = 0; i < Math.min(values.length, count); i++) {
246
+ writeValue(buffer, elementType, values.charCodeAt(i), i * sizeof(elementType));
247
+ }
248
+ }
249
+ return wrap(buffer);
250
+ },
251
+
252
+ // Explicit wrap method
253
+ wrap: wrap,
254
+
255
+ // For compatibility, expose the native array (needed for StructType.addField)
256
+ _native: nativeArray,
257
+
258
+ // Helper to check if this is an ArrayType wrapper
259
+ _isArrayType: true,
260
+ };
261
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * @file bitfield-helpers.js
3
+ * @module structures/bitfield-helpers
4
+ * @description Helper functions for reading and writing bitfield values in structs.
5
+ *
6
+ * This module provides low-level bitfield manipulation functions used by struct
7
+ * and union implementations to pack multiple small integer values into single bytes.
8
+ *
9
+ * @example
10
+ * ```javascript
11
+ * import { _readBitField, _writeBitField } from './structures/bitfield-helpers.js';
12
+ *
13
+ * const buf = Buffer.alloc(4);
14
+ * _writeBitField(buf, 0, c_uint32, 0, 3, 5); // Write 5 to bits 0-2
15
+ * const value = _readBitField(buf, 0, c_uint32, 0, 3); // Read bits 0-2
16
+ * ```
17
+ */
18
+
19
+ /**
20
+ * Reads an unsigned integer value from a buffer.
21
+ *
22
+ * @param {Buffer} buf - Buffer to read from
23
+ * @param {number} offset - Byte offset in buffer
24
+ * @param {number} byteSize - Size in bytes (1, 2, 4, or 8)
25
+ * @param {Function} sizeof - sizeof function reference
26
+ * @returns {number|bigint} Value read from buffer
27
+ * @throws {Error} If byteSize is not supported
28
+ * @private
29
+ */
30
+ export function _readUintFromBuffer(buf, offset, byteSize) {
31
+ switch (byteSize) {
32
+ case 1:
33
+ return buf.readUInt8(offset);
34
+ case 2:
35
+ return buf.readUInt16LE(offset);
36
+ case 4:
37
+ return buf.readUInt32LE(offset);
38
+ case 8:
39
+ return buf.readBigUInt64LE(offset);
40
+ default:
41
+ throw new Error(`Unsupported byte size: ${byteSize}`);
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Writes an unsigned integer value to a buffer.
47
+ *
48
+ * @param {Buffer} buf - Buffer to write to
49
+ * @param {number} offset - Byte offset in buffer
50
+ * @param {number} byteSize - Size in bytes (1, 2, 4, or 8)
51
+ * @param {number|bigint} value - Value to write
52
+ * @throws {Error} If byteSize is not supported
53
+ * @private
54
+ */
55
+ export function _writeUintToBuffer(buf, offset, byteSize, value) {
56
+ switch (byteSize) {
57
+ case 1:
58
+ buf.writeUInt8(value, offset);
59
+ break;
60
+ case 2:
61
+ buf.writeUInt16LE(value, offset);
62
+ break;
63
+ case 4:
64
+ buf.writeUInt32LE(value, offset);
65
+ break;
66
+ case 8:
67
+ buf.writeBigUInt64LE(value, offset);
68
+ break;
69
+ default:
70
+ throw new Error(`Unsupported byte size: ${byteSize}`);
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Reads a bitfield value from a buffer.
76
+ *
77
+ * Extracts a specific range of bits from an integer value stored in memory.
78
+ * Used by struct/union implementations to support packed bitfields.
79
+ *
80
+ * @param {Buffer} buf - Buffer to read from
81
+ * @param {number} offset - Byte offset of the containing integer
82
+ * @param {Function|Object} baseType - Base integer type (c_uint8, c_uint16, c_uint32, c_uint64)
83
+ * @param {number} bitOffset - Starting bit position (0-based)
84
+ * @param {number} bitSize - Number of bits to read
85
+ * @param {Function} sizeof - sizeof function reference
86
+ * @returns {number} Extracted bitfield value
87
+ *
88
+ * @example
89
+ * ```javascript
90
+ * // Read 3-bit field starting at bit 5
91
+ * const value = _readBitField(buf, 0, c_uint32, 5, 3);
92
+ * ```
93
+ * @private
94
+ */
95
+ export function _readBitField(buf, offset, baseType, bitOffset, bitSize, sizeof) {
96
+ const baseSize = sizeof(baseType);
97
+ const value = _readUintFromBuffer(buf, offset, baseSize);
98
+
99
+ // Extract the bits
100
+ if (baseSize === 8) {
101
+ const mask = (1n << BigInt(bitSize)) - 1n;
102
+ return Number((value >> BigInt(bitOffset)) & mask);
103
+ } else {
104
+ const mask = (1 << bitSize) - 1;
105
+ return (value >> bitOffset) & mask;
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Writes a bitfield value to a buffer.
111
+ *
112
+ * Modifies a specific range of bits in an integer value stored in memory,
113
+ * preserving other bits. Used by struct/union implementations to support
114
+ * packed bitfields.
115
+ *
116
+ * @param {Buffer} buf - Buffer to write to
117
+ * @param {number} offset - Byte offset of the containing integer
118
+ * @param {Function|Object} baseType - Base integer type (c_uint8, c_uint16, c_uint32, c_uint64)
119
+ * @param {number} bitOffset - Starting bit position (0-based)
120
+ * @param {number} bitSize - Number of bits to write
121
+ * @param {number} newValue - Value to write (will be masked to bitSize)
122
+ * @param {Function} sizeof - sizeof function reference
123
+ *
124
+ * @example
125
+ * ```javascript
126
+ * // Write 5 to a 3-bit field starting at bit 2
127
+ * _writeBitField(buf, 0, c_uint32, 2, 3, 5);
128
+ * ```
129
+ * @private
130
+ */
131
+ export function _writeBitField(buf, offset, baseType, bitOffset, bitSize, newValue, sizeof) {
132
+ const baseSize = sizeof(baseType);
133
+ let value = _readUintFromBuffer(buf, offset, baseSize);
134
+
135
+ // Modify the bits
136
+ if (baseSize === 8) {
137
+ const mask = (1n << BigInt(bitSize)) - 1n;
138
+ const clearMask = ~(mask << BigInt(bitOffset));
139
+ value = (value & clearMask) | ((BigInt(newValue) & mask) << BigInt(bitOffset));
140
+ } else {
141
+ const mask = (1 << bitSize) - 1;
142
+ const clearMask = ~(mask << bitOffset);
143
+ value = (value & clearMask) | ((newValue & mask) << bitOffset);
144
+ }
145
+
146
+ _writeUintToBuffer(buf, offset, baseSize, value);
147
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * @file helpers.js
3
+ * @module structures/helpers
4
+ * @description Helper functions and type checks for structure and type system.
5
+ *
6
+ * This module provides utility functions for working with structures, arrays,
7
+ * and bitfields in the node-ctypes type system.
8
+ *
9
+ * @example
10
+ * ```javascript
11
+ * import { bitfield, _isStruct, _isArrayType } from './structures/helpers.js';
12
+ *
13
+ * // Create a bitfield
14
+ * const flags = bitfield(c_uint32, 8); // 8-bit field
15
+ *
16
+ * // Check if type is a struct
17
+ * if (_isStruct(myType)) {
18
+ * // Handle struct type
19
+ * }
20
+ * ```
21
+ */
22
+
23
+ /**
24
+ * Creates a bitfield definition.
25
+ *
26
+ * Bitfields allow packing multiple boolean or small integer values into a single
27
+ * integer type, useful for flags and compact data structures.
28
+ *
29
+ * **Python ctypes Compatibility**:
30
+ * Similar to Python's ctypes bitfield syntax in Structure _fields_.
31
+ *
32
+ * @param {Function|Object} baseType - Base integer type (c_uint8, c_uint16, c_uint32, c_uint64)
33
+ * @param {number} bits - Number of bits to allocate (1 to baseSize*8)
34
+ * @param {Function} sizeof - sizeof function reference
35
+ * @returns {Object} Bitfield definition object
36
+ * @throws {Error} If bits is out of valid range for base type
37
+ *
38
+ * @example 8-bit flags in uint32
39
+ * ```javascript
40
+ * import { struct, bitfield, c_uint32 } from 'node-ctypes';
41
+ *
42
+ * const Flags = struct({
43
+ * isActive: bitfield(c_uint32, 1), // 1 bit
44
+ * priority: bitfield(c_uint32, 3), // 3 bits
45
+ * category: bitfield(c_uint32, 4), // 4 bits
46
+ * });
47
+ * ```
48
+ *
49
+ * @example Using different base types
50
+ * ```javascript
51
+ * // Small bitfield (up to 8 bits)
52
+ * const smallFlags = bitfield(c_uint8, 5);
53
+ *
54
+ * // Large bitfield (up to 64 bits)
55
+ * const largeFlags = bitfield(c_uint64, 48);
56
+ * ```
57
+ */
58
+ export function bitfield(baseType, bits, sizeof) {
59
+ const baseSize = sizeof(baseType);
60
+ const maxBits = baseSize * 8;
61
+
62
+ if (bits < 1 || bits > maxBits) {
63
+ throw new Error(`Bit field size must be between 1 and ${maxBits} for ${baseType}`);
64
+ }
65
+
66
+ return {
67
+ _isBitField: true,
68
+ baseType: baseType,
69
+ bits: bits,
70
+ baseSize: baseSize,
71
+ };
72
+ }
73
+
74
+ /**
75
+ * Checks if a type is a struct definition.
76
+ *
77
+ * @param {*} type - Type to check
78
+ * @returns {boolean} True if type is a struct definition
79
+ *
80
+ * @example
81
+ * ```javascript
82
+ * const myStruct = struct({ x: c_int32, y: c_int32 });
83
+ * console.log(_isStruct(myStruct)); // true
84
+ * console.log(_isStruct(c_int32)); // false
85
+ * ```
86
+ *
87
+ * @private
88
+ */
89
+ export function _isStruct(type) {
90
+ return typeof type === "object" && type !== null && typeof type.size === "number" && Array.isArray(type.fields) && typeof type.create === "function";
91
+ }
92
+
93
+ /**
94
+ * Checks if a type is an array type definition.
95
+ *
96
+ * @param {*} type - Type to check
97
+ * @returns {boolean} True if type is an array type
98
+ *
99
+ * @example
100
+ * ```javascript
101
+ * const intArray = array(c_int32, 10);
102
+ * console.log(_isArrayType(intArray)); // true
103
+ * console.log(_isArrayType(c_int32)); // false
104
+ * ```
105
+ *
106
+ * @private
107
+ */
108
+ export function _isArrayType(type) {
109
+ return typeof type === "object" && type !== null && type._isArrayType === true;
110
+ }
111
+
112
+ /**
113
+ * Checks if a type is a bitfield definition.
114
+ *
115
+ * @param {*} type - Type to check
116
+ * @returns {boolean} True if type is a bitfield
117
+ *
118
+ * @example
119
+ * ```javascript
120
+ * const flags = bitfield(c_uint32, 8);
121
+ * console.log(_isBitField(flags)); // true
122
+ * console.log(_isBitField(c_int32)); // false
123
+ * ```
124
+ *
125
+ * @private
126
+ */
127
+ export function _isBitField(type) {
128
+ return typeof type === "object" && type !== null && type._isBitField === true;
129
+ }