node-ctypes 0.1.7 → 1.0.2

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.
@@ -0,0 +1,409 @@
1
+ /**
2
+ * @file primitives.js
3
+ * @module types/primitives
4
+ * @description Primitive C type definitions (integers, floats, chars, pointers).
5
+ *
6
+ * This module provides all primitive C types that extend SimpleCData. Each type
7
+ * defines its size, type name, and read/write functions for buffer operations.
8
+ *
9
+ * **Python ctypes Compatibility**:
10
+ * All types are compatible with Python's ctypes module equivalents.
11
+ *
12
+ * @example Using integer types
13
+ * ```javascript
14
+ * import { c_int32, c_uint64 } from 'node-ctypes';
15
+ *
16
+ * const x = new c_int32(42);
17
+ * console.log(x.value); // 42
18
+ * x.value = -100;
19
+ *
20
+ * const big = new c_uint64(9007199254740991n);
21
+ * console.log(big.value); // 9007199254740991n
22
+ * ```
23
+ *
24
+ * @example Using float types
25
+ * ```javascript
26
+ * import { c_float, c_double } from 'node-ctypes';
27
+ *
28
+ * const pi = new c_float(3.14159);
29
+ * console.log(pi.value); // ~3.1415898799896240 (float precision)
30
+ *
31
+ * const precise = new c_double(3.141592653589793);
32
+ * console.log(precise.value); // 3.141592653589793
33
+ * ```
34
+ *
35
+ * @example Using character types
36
+ * ```javascript
37
+ * import { c_char, c_wchar } from 'node-ctypes';
38
+ *
39
+ * const ch = new c_char('A'.charCodeAt(0));
40
+ * console.log(ch.value); // 65
41
+ *
42
+ * const wide = new c_wchar(0x4E2D); // Chinese character
43
+ * console.log(wide.value); // 20013
44
+ * ```
45
+ *
46
+ * @example Using pointer types
47
+ * ```javascript
48
+ * import { c_void_p, c_char_p } from 'node-ctypes';
49
+ *
50
+ * const ptr = new c_void_p(0x1234n);
51
+ * console.log(ptr.value); // 4660n
52
+ *
53
+ * const str = new c_char_p("Hello");
54
+ * console.log(str.value); // "Hello"
55
+ * ```
56
+ */
57
+
58
+ /**
59
+ * Creates all primitive type classes with required dependencies injected.
60
+ *
61
+ * @param {Class} SimpleCData - SimpleCData base class
62
+ * @param {Object} native - Native module with POINTER_SIZE
63
+ * @param {Function} addressOf - addressOf function for pointers
64
+ * @param {Function} cstring - cstring function for c_char_p
65
+ * @param {Function} wstring - wstring function for c_wchar_p
66
+ * @param {Function} readCString - readCString function for c_char_p
67
+ * @param {Function} readWString - readWString function for c_wchar_p
68
+ * @returns {Object} Object containing all primitive type classes
69
+ * @private
70
+ */
71
+ export function createPrimitiveTypes(SimpleCData, native, addressOf, cstring, wstring, readCString, readWString) {
72
+ // ============================================================================
73
+ // Void type
74
+ // ============================================================================
75
+
76
+ class c_void extends SimpleCData {
77
+ static _size = 0;
78
+ static _type = "void";
79
+ static _reader = (buf, off) => undefined;
80
+ static _writer = (buf, off, val) => {};
81
+ }
82
+
83
+ // ============================================================================
84
+ // Integer types - signed
85
+ // ============================================================================
86
+
87
+ class c_int8 extends SimpleCData {
88
+ static _size = 1;
89
+ static _type = "int8";
90
+ static _reader = (buf, off) => buf.readInt8(off);
91
+ static _writer = (buf, off, val) => buf.writeInt8(val, off);
92
+ }
93
+
94
+ class c_int16 extends SimpleCData {
95
+ static _size = 2;
96
+ static _type = "int16";
97
+ static _reader = (buf, off) => buf.readInt16LE(off);
98
+ static _writer = (buf, off, val) => buf.writeInt16LE(val, off);
99
+ }
100
+
101
+ class c_int32 extends SimpleCData {
102
+ static _size = 4;
103
+ static _type = "int32";
104
+ static _reader = (buf, off) => buf.readInt32LE(off);
105
+ static _writer = (buf, off, val) => {
106
+ const v = Number(val) | 0; // coerce to signed 32-bit
107
+ return buf.writeInt32LE(v, off);
108
+ };
109
+ }
110
+
111
+ class c_int64 extends SimpleCData {
112
+ static _size = 8;
113
+ static _type = "int64";
114
+ static _reader = (buf, off) => buf.readBigInt64LE(off);
115
+ static _writer = (buf, off, val) => buf.writeBigInt64LE(BigInt(val), off);
116
+ }
117
+
118
+ // ============================================================================
119
+ // Integer types - unsigned
120
+ // ============================================================================
121
+
122
+ class c_uint8 extends SimpleCData {
123
+ static _size = 1;
124
+ static _type = "uint8";
125
+ static _reader = (buf, off) => buf.readUInt8(off);
126
+ static _writer = (buf, off, val) => buf.writeUInt8(val, off);
127
+ }
128
+
129
+ class c_uint16 extends SimpleCData {
130
+ static _size = 2;
131
+ static _type = "uint16";
132
+ static _reader = (buf, off) => buf.readUInt16LE(off);
133
+ static _writer = (buf, off, val) => buf.writeUInt16LE(val, off);
134
+ }
135
+
136
+ class c_uint32 extends SimpleCData {
137
+ static _size = 4;
138
+ static _type = "uint32";
139
+ static _reader = (buf, off) => buf.readUInt32LE(off);
140
+ static _writer = (buf, off, val) => {
141
+ const v = Number(val) >>> 0; // coerce to unsigned 32-bit
142
+ return buf.writeUInt32LE(v, off);
143
+ };
144
+ }
145
+
146
+ class c_uint64 extends SimpleCData {
147
+ static _size = 8;
148
+ static _type = "uint64";
149
+ static _reader = (buf, off) => buf.readBigUInt64LE(off);
150
+ static _writer = (buf, off, val) => buf.writeBigUInt64LE(BigInt(val), off);
151
+ }
152
+
153
+ // ============================================================================
154
+ // Floating point types
155
+ // ============================================================================
156
+
157
+ class c_float extends SimpleCData {
158
+ static _size = 4;
159
+ static _type = "float";
160
+ static _reader = (buf, off) => buf.readFloatLE(off);
161
+ static _writer = (buf, off, val) => buf.writeFloatLE(val, off);
162
+ }
163
+
164
+ class c_double extends SimpleCData {
165
+ static _size = 8;
166
+ static _type = "double";
167
+ static _reader = (buf, off) => buf.readDoubleLE(off);
168
+ static _writer = (buf, off, val) => buf.writeDoubleLE(val, off);
169
+ }
170
+
171
+ // ============================================================================
172
+ // Boolean type
173
+ // ============================================================================
174
+
175
+ class c_bool extends SimpleCData {
176
+ static _size = 1;
177
+ static _type = "bool";
178
+ static _reader = (buf, off) => buf.readUInt8(off) !== 0;
179
+ static _writer = (buf, off, val) => buf.writeUInt8(val ? 1 : 0, off);
180
+ }
181
+
182
+ // ============================================================================
183
+ // Character types
184
+ // ============================================================================
185
+
186
+ class c_char extends SimpleCData {
187
+ static _size = 1;
188
+ static _type = "char";
189
+ static _reader = (buf, off) => buf.readInt8(off);
190
+ static _writer = (buf, off, val) => {
191
+ if (typeof val === "string") val = val.charCodeAt(0);
192
+ buf.writeInt8(val, off);
193
+ };
194
+ }
195
+
196
+ class c_wchar extends SimpleCData {
197
+ static _size = native.WCHAR_SIZE;
198
+ static _type = "wchar";
199
+ static _reader = (buf, off) => {
200
+ if (native.WCHAR_SIZE === 2) {
201
+ return String.fromCharCode(buf.readUInt16LE(off));
202
+ }
203
+ return String.fromCodePoint(buf.readUInt32LE(off));
204
+ };
205
+ static _writer = (buf, off, val) => {
206
+ const code = typeof val === "string" ? val.codePointAt(0) : val;
207
+ if (native.WCHAR_SIZE === 2) {
208
+ buf.writeUInt16LE(code, off);
209
+ } else {
210
+ buf.writeUInt32LE(code, off);
211
+ }
212
+ };
213
+ }
214
+
215
+ // ============================================================================
216
+ // Pointer types
217
+ // ============================================================================
218
+
219
+ class c_void_p extends SimpleCData {
220
+ static _size = native.POINTER_SIZE;
221
+ static _type = "pointer";
222
+ static _reader = (buf, off) => {
223
+ const ptr = native.POINTER_SIZE === 8 ? buf.readBigUInt64LE(off) : BigInt(buf.readUInt32LE(off));
224
+ if (!ptr) return null;
225
+ return ptr;
226
+ };
227
+ static _writer = (buf, off, val) => {
228
+ // Accept Buffers (pointer to memory) and struct proxies with _buffer
229
+ if (Buffer.isBuffer(val)) {
230
+ const addr = addressOf(val);
231
+ const v = typeof addr === "bigint" ? addr : BigInt(addr || 0);
232
+ if (native.POINTER_SIZE === 8) buf.writeBigUInt64LE(v, off);
233
+ else buf.writeUInt32LE(Number(v), off);
234
+ return;
235
+ }
236
+ if (val && typeof val === "object" && val._buffer && Buffer.isBuffer(val._buffer)) {
237
+ const addr = addressOf(val._buffer);
238
+ const v = typeof addr === "bigint" ? addr : BigInt(addr || 0);
239
+ if (native.POINTER_SIZE === 8) buf.writeBigUInt64LE(v, off);
240
+ else buf.writeUInt32LE(Number(v), off);
241
+ return;
242
+ }
243
+
244
+ // Fallback: accept BigInt or number
245
+ const v = typeof val === "bigint" ? val : BigInt(val || 0);
246
+ if (native.POINTER_SIZE === 8) {
247
+ buf.writeBigUInt64LE(v, off);
248
+ } else {
249
+ buf.writeUInt32LE(Number(v), off);
250
+ }
251
+ };
252
+ }
253
+
254
+ // ============================================================================
255
+ // Platform-specific types
256
+ // ============================================================================
257
+
258
+ class c_size_t extends SimpleCData {
259
+ static _size = native.POINTER_SIZE;
260
+ static _type = "size_t";
261
+ static _reader = (buf, off) => {
262
+ return native.POINTER_SIZE === 8 ? buf.readBigUInt64LE(off) : buf.readUInt32LE(off);
263
+ };
264
+ static _writer = (buf, off, val) => {
265
+ if (native.POINTER_SIZE === 8) {
266
+ buf.writeBigUInt64LE(BigInt(val), off);
267
+ } else {
268
+ buf.writeUInt32LE(Number(val), off);
269
+ }
270
+ };
271
+ }
272
+
273
+ // Get actual long size from native module (4 on Windows, 4 or 8 on Unix depending on arch)
274
+ const _longSize = native.sizeof ? native.sizeof("long") : 4;
275
+
276
+ class c_long extends SimpleCData {
277
+ static _size = _longSize;
278
+ static _type = "long";
279
+ static _reader = _longSize === 8 ? (buf, off) => buf.readBigInt64LE(off) : (buf, off) => buf.readInt32LE(off);
280
+ static _writer = _longSize === 8 ? (buf, off, val) => buf.writeBigInt64LE(BigInt(val), off) : (buf, off, val) => buf.writeInt32LE(val, off);
281
+ }
282
+
283
+ class c_ulong extends SimpleCData {
284
+ static _size = _longSize;
285
+ static _type = "ulong";
286
+ static _reader = _longSize === 8 ? (buf, off) => buf.readBigUInt64LE(off) : (buf, off) => buf.readUInt32LE(off);
287
+ static _writer = _longSize === 8 ? (buf, off, val) => buf.writeBigUInt64LE(BigInt(val), off) : (buf, off, val) => buf.writeUInt32LE(val, off);
288
+ }
289
+
290
+ // ============================================================================
291
+ // String pointer types (special handling)
292
+ // ============================================================================
293
+
294
+ class c_char_p extends SimpleCData {
295
+ static _size = native.POINTER_SIZE;
296
+ static _type = "char_p";
297
+ static _reader = (buf, off) => {
298
+ const ptr = native.POINTER_SIZE === 8 ? buf.readBigUInt64LE(off) : BigInt(buf.readUInt32LE(off));
299
+ if (!ptr) return null;
300
+ return readCString(ptr);
301
+ };
302
+ static _writer = (buf, off, val) => {
303
+ if (typeof val === "string") {
304
+ val = cstring(val);
305
+ }
306
+ if (Buffer.isBuffer(val)) {
307
+ const addr = addressOf(val);
308
+ const v = typeof addr === "bigint" ? addr : BigInt(addr || 0);
309
+ if (native.POINTER_SIZE === 8) {
310
+ buf.writeBigUInt64LE(v, off);
311
+ } else {
312
+ buf.writeUInt32LE(Number(v), off);
313
+ }
314
+ } else {
315
+ const v = typeof val === "bigint" ? val : BigInt(val || 0);
316
+ if (native.POINTER_SIZE === 8) {
317
+ buf.writeBigUInt64LE(v, off);
318
+ } else {
319
+ buf.writeUInt32LE(Number(v), off);
320
+ }
321
+ }
322
+ };
323
+ }
324
+
325
+ class c_wchar_p extends SimpleCData {
326
+ static _size = native.POINTER_SIZE;
327
+ static _type = "wchar_p";
328
+ static _reader = (buf, off) => {
329
+ const ptr = native.POINTER_SIZE === 8 ? buf.readBigUInt64LE(off) : BigInt(buf.readUInt32LE(off));
330
+ if (!ptr) return null;
331
+ return readWString(ptr);
332
+ };
333
+ static _writer = (buf, off, val) => {
334
+ if (typeof val === "string") {
335
+ val = wstring(val);
336
+ }
337
+ if (Buffer.isBuffer(val)) {
338
+ const addr = addressOf(val);
339
+ const v = typeof addr === "bigint" ? addr : BigInt(addr || 0);
340
+ if (native.POINTER_SIZE === 8) {
341
+ buf.writeBigUInt64LE(v, off);
342
+ } else {
343
+ buf.writeUInt32LE(Number(v), off);
344
+ }
345
+ } else {
346
+ const v = typeof val === "bigint" ? val : BigInt(val || 0);
347
+ if (native.POINTER_SIZE === 8) {
348
+ buf.writeBigUInt64LE(v, off);
349
+ } else {
350
+ buf.writeUInt32LE(Number(v), off);
351
+ }
352
+ }
353
+ };
354
+ }
355
+
356
+ // ============================================================================
357
+ // Python-compatible aliases
358
+ // ============================================================================
359
+
360
+ const c_byte = c_int8;
361
+ const c_ubyte = c_uint8;
362
+ const c_short = c_int16;
363
+ const c_ushort = c_uint16;
364
+ const c_int = c_int32;
365
+ const c_uint = c_uint32;
366
+ const c_longlong = c_int64;
367
+ const c_ulonglong = c_uint64;
368
+
369
+ // Return all types
370
+ return {
371
+ // Void
372
+ c_void,
373
+ // Signed integers
374
+ c_int8,
375
+ c_int16,
376
+ c_int32,
377
+ c_int64,
378
+ // Unsigned integers
379
+ c_uint8,
380
+ c_uint16,
381
+ c_uint32,
382
+ c_uint64,
383
+ // Floating point
384
+ c_float,
385
+ c_double,
386
+ // Boolean
387
+ c_bool,
388
+ // Characters
389
+ c_char,
390
+ c_wchar,
391
+ // Pointers
392
+ c_void_p,
393
+ c_char_p,
394
+ c_wchar_p,
395
+ // Platform-specific
396
+ c_size_t,
397
+ c_long,
398
+ c_ulong,
399
+ // Python-compatible aliases
400
+ c_byte,
401
+ c_ubyte,
402
+ c_short,
403
+ c_ushort,
404
+ c_int,
405
+ c_uint,
406
+ c_longlong,
407
+ c_ulonglong,
408
+ };
409
+ }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * @file cache.js
3
+ * @module utils/cache
4
+ * @description Simple LRU (Least Recently Used) cache implementation for function caching.
5
+ * Prevents unbounded memory growth in long-running applications.
6
+ *
7
+ * @example
8
+ * import { LRUCache } from './utils/cache.js';
9
+ *
10
+ * const cache = new LRUCache(1000); // Max 1000 entries
11
+ * cache.set('key1', 'value1');
12
+ * const value = cache.get('key1'); // 'value1'
13
+ */
14
+
15
+ /**
16
+ * LRU Cache implementation using Map for O(1) operations.
17
+ * When the cache reaches maxSize, the least recently used item is evicted.
18
+ *
19
+ * @class LRUCache
20
+ * @example
21
+ * const cache = new LRUCache(100);
22
+ * cache.set('user:1', { name: 'John' });
23
+ * cache.get('user:1'); // { name: 'John' }
24
+ * cache.has('user:1'); // true
25
+ * cache.size; // 1
26
+ */
27
+ export class LRUCache {
28
+ /**
29
+ * Creates a new LRU cache instance
30
+ * @param {number} [maxSize=1000] - Maximum number of entries before eviction
31
+ */
32
+ constructor(maxSize = 1000) {
33
+ /**
34
+ * Maximum cache size before LRU eviction
35
+ * @type {number}
36
+ * @private
37
+ */
38
+ this.maxSize = maxSize;
39
+
40
+ /**
41
+ * Internal Map storage for cache entries
42
+ * Map maintains insertion order, which we use for LRU tracking
43
+ * @type {Map<string, any>}
44
+ * @private
45
+ */
46
+ this.cache = new Map();
47
+ }
48
+
49
+ /**
50
+ * Retrieves a value from the cache and marks it as recently used.
51
+ * Moves the entry to the end of the Map (most recently used position).
52
+ *
53
+ * @param {string} key - Cache key to retrieve
54
+ * @returns {any|undefined} The cached value, or undefined if not found
55
+ *
56
+ * @example
57
+ * cache.set('key', 'value');
58
+ * cache.get('key'); // 'value'
59
+ * cache.get('missing'); // undefined
60
+ */
61
+ get(key) {
62
+ if (!this.cache.has(key)) {
63
+ return undefined;
64
+ }
65
+
66
+ // Move to end (most recently used) by deleting and re-inserting
67
+ const value = this.cache.get(key);
68
+ this.cache.delete(key);
69
+ this.cache.set(key, value);
70
+ return value;
71
+ }
72
+
73
+ /**
74
+ * Adds or updates a value in the cache.
75
+ * If the key exists, it's moved to the most recently used position.
76
+ * If the cache is full, the least recently used entry is evicted.
77
+ *
78
+ * @param {string} key - Cache key
79
+ * @param {any} value - Value to store
80
+ *
81
+ * @example
82
+ * cache.set('user:1', { name: 'John' });
83
+ * cache.set('user:1', { name: 'Jane' }); // Updates existing entry
84
+ */
85
+ set(key, value) {
86
+ // If key exists, remove it first (will re-add at end)
87
+ if (this.cache.has(key)) {
88
+ this.cache.delete(key);
89
+ }
90
+ // If at capacity, evict oldest entry (first in Map)
91
+ else if (this.cache.size >= this.maxSize) {
92
+ const firstKey = this.cache.keys().next().value;
93
+ this.cache.delete(firstKey);
94
+ }
95
+
96
+ // Add to end (most recently used position)
97
+ this.cache.set(key, value);
98
+ }
99
+
100
+ /**
101
+ * Checks if a key exists in the cache.
102
+ * Does NOT update the LRU position.
103
+ *
104
+ * @param {string} key - Cache key to check
105
+ * @returns {boolean} True if the key exists
106
+ *
107
+ * @example
108
+ * cache.set('key', 'value');
109
+ * cache.has('key'); // true
110
+ * cache.has('missing'); // false
111
+ */
112
+ has(key) {
113
+ return this.cache.has(key);
114
+ }
115
+
116
+ /**
117
+ * Removes all entries from the cache.
118
+ *
119
+ * @example
120
+ * cache.set('key1', 'value1');
121
+ * cache.set('key2', 'value2');
122
+ * cache.clear();
123
+ * cache.size; // 0
124
+ */
125
+ clear() {
126
+ this.cache.clear();
127
+ }
128
+
129
+ /**
130
+ * Returns the current number of entries in the cache.
131
+ *
132
+ * @returns {number} Number of cached entries
133
+ *
134
+ * @example
135
+ * cache.set('key1', 'value1');
136
+ * cache.set('key2', 'value2');
137
+ * cache.size; // 2
138
+ */
139
+ get size() {
140
+ return this.cache.size;
141
+ }
142
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-ctypes",
3
- "version": "0.1.7",
3
+ "version": "1.0.2",
4
4
  "description": "Python ctypes-like FFI for Node.js using libffi",
5
5
  "author": "Damiano Mazzella",
6
6
  "license": "MIT",