koffi 2.5.21-beta.3 → 2.6.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/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  ## Version history
4
4
 
5
+ ### Koffi 2.6
6
+
7
+ #### Koffi 2.6.0 (2023-09-13)
8
+
9
+ **New features:**
10
+
11
+ - Use [koffi.symbol()](variables.md#variable-definitions) to make pointers to exported variables (or other symbols)
12
+ - Use [koffi.encode()](variables.md#encode-to-c-memory) to explictly encode data from JS to C memory
13
+ - Use shared library [lazy-loading](functions.md#loading-options) (RTLD_LAZY) on POSIX platforms
14
+
15
+ **Other changes:**
16
+
17
+ - Print more helpful error for Win32/Win64 DLL mismatches
18
+ - Add missing 'Union' primitive value in TS definition file
19
+
5
20
  ### Koffi 2.5
6
21
 
7
22
  #### Koffi 2.5.20 (2023-08-31)
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/doc/callbacks.md CHANGED
@@ -153,14 +153,9 @@ let cb2 = koffi.register(store, store.get, 'IntCallback *'); // However in this
153
153
 
154
154
  Koffi does not have enough information to convert callback pointer arguments to an appropriate JS value. In this case, your JS function will receive an opaque *External* object.
155
155
 
156
- You can pass this value through to another C function that expects a pointer of the same type, or you can use `koffi.decode()` to decode it into something you can use in Javascript.
156
+ You can pass this value through to another C function that expects a pointer of the same type, or you can use [koffi.decode()](variables.md#decode-to-js-values) function to decode pointer arguments.
157
157
 
158
- Some arguments are optional and this function can be called in several ways:
159
-
160
- - `koffi.decode(value, type)`: no offset, expect NUL-terminated strings
161
- - `koffi.decode(value, offset, type)`: explicit offset to add to the pointer before decoding
162
-
163
- The following example sorts an array of strings (in-place) with `qsort()`:
158
+ The following examples uses it to sort an array of strings in-place with the standard C function `qsort()`:
164
159
 
165
160
  ```js
166
161
  // ES6 syntax: import koffi from 'koffi';
@@ -183,15 +178,6 @@ qsort(koffi.as(array, 'char **'), array.length, koffi.sizeof('void *'), (ptr1, p
183
178
  console.log(array); // Prints ['123', 'bar', 'foo', 'foobar']
184
179
  ```
185
180
 
186
- There is also an optional ending `length` argument that you can use in two cases:
187
-
188
- - Use it to give the number of bytes to decode in non-NUL terminated strings: `koffi.decode(value, 'char *', 5)`
189
- - Decode consecutive values into an array. For example, here is how you can decode an array with 3 float values: `koffi.decode(value, 'float', 3)`. This is equivalent to `koffi.decode(value, koffi.array('float', 3))`.
190
-
191
- ```{note}
192
- In Koffi 2.2 and earlier versions, the length argument is only used to decode strings and is ignored otherwise.
193
- ```
194
-
195
181
  ### Asynchronous callbacks
196
182
 
197
183
  *New in Koffi 2.2.2*
package/doc/functions.md CHANGED
@@ -19,6 +19,22 @@ Starting with *Koffi 2.3.20*, you can explicitly unload a library by calling `li
19
19
  On some platforms (such as with the [musl C library on Linux](https://wiki.musl-libc.org/functional-differences-from-glibc.html#Unloading-libraries)), shared libraries cannot be unloaded, so the library will remain loaded and memory mapped after the call to `lib.unload()`.
20
20
  ```
21
21
 
22
+ ## Loading options
23
+
24
+ *New in Koffi 2.6*
25
+
26
+ The `load` function can take an optional object argument, with the following options:
27
+
28
+ ```js
29
+ const options = {
30
+ lazy: true // Use RTLD_LAZY (lazy-binding) on POSIX platforms (by default, use RTLD_NOW)
31
+ };
32
+
33
+ const lib = koffi.load('/path/to/shared/library.so', options);
34
+ ```
35
+
36
+ More options may be added if needed.
37
+
22
38
  ## Function definitions
23
39
 
24
40
  ### Definition syntax
package/doc/index.rst CHANGED
@@ -31,6 +31,7 @@ Table of contents
31
31
  output
32
32
  polymorphism
33
33
  unions
34
+ variables
34
35
  callbacks
35
36
  misc
36
37
  packaging
@@ -105,4 +105,4 @@ console.log(vec1); // { x: 3, y: 2, z: 1 }
105
105
  console.log(vec2); // { x: 1, y: 2, z: 3 }
106
106
  ```
107
107
 
108
- See [pointer arguments](callbacks.md#decoding-pointer-arguments) for another example with the decode function.
108
+ See [decoding variables](variables.md#decode-to-js-values) for more information about the decode function.
@@ -0,0 +1,100 @@
1
+ # Exported variables
2
+
3
+ ## Variable definitions
4
+
5
+ *New in Koffi 2.6*
6
+
7
+ To find an exported and declare a variable, use `lib.symbol(name, type)`. You need to specify its name and its type.
8
+
9
+ ```c
10
+ int my_int = 42;
11
+ const char *my_string = NULL;
12
+ ```
13
+
14
+ ```js
15
+ const my_int = lib.symbol('my_int', 'int');
16
+ const my_string = lib.symbol('my_string', 'const char *');
17
+ ```
18
+
19
+ You cannot directly manipulate these variables, use:
20
+
21
+ - [koffi.decode()](#decode-to-js-values) to read their value
22
+ - [koffi.encode()](#encode-to-c-memory) to change their valuèe
23
+
24
+ ## Decode to JS values
25
+
26
+ *New in Koffi 2.2, changed in Koffi 2.3*
27
+
28
+ Use `koffi.decode()` to decode C pointers, wrapped as external objects or as simple numbers.
29
+
30
+ Some arguments are optional and this function can be called in several ways:
31
+
32
+ - `koffi.decode(value, type)`: no offset
33
+ - `koffi.decode(value, offset, type)`: explicit offset to add to the pointer before decoding
34
+
35
+ By default, Koffi expects NUL terminated strings when decoding them. See below if you need to specify the string length.
36
+
37
+ The following example illustrates how to decode an integer and a C string variable.
38
+
39
+ ```c
40
+ int my_int = 42;
41
+ const char *my_string = "foobar";
42
+ ```
43
+
44
+ ```js
45
+ const my_int = lib.symbol('my_int', 'int');
46
+ const my_string = lib.symbol('my_string', 'const char *');
47
+
48
+ console.log(koffi.decode(my_int, 'int')) // Prints 42
49
+ console.log(koffi.decode(my_string, 'const char *')) // Prints "foobar"
50
+ ```
51
+
52
+ There is also an optional ending `length` argument that you can use in two cases:
53
+
54
+ - Use it to give the number of bytes to decode in non-NUL terminated strings: `koffi.decode(value, 'char *', 5)`
55
+ - Decode consecutive values into an array. For example, here is how you can decode an array with 3 float values: `koffi.decode(value, 'float', 3)`. This is equivalent to `koffi.decode(value, koffi.array('float', 3))`.
56
+
57
+ Thge example below will decode the symbol `my_string` defined above but only the first three bytes.
58
+
59
+ ```js
60
+ // Only decode 3 bytes from the C string my_string
61
+ console.log(koffi.decode(my_string, 'const char *', 3)) // Prints "foo"
62
+ ```
63
+
64
+ ```{note}
65
+ In Koffi 2.2 and earlier versions, the length argument is only used to decode strings and is ignored otherwise.
66
+ ```
67
+
68
+ ## Encode to C memory
69
+
70
+ *New in Koffi 2.6*
71
+
72
+ Use `koffi.encode()` to encode C pointers, wrapped as external objects or as simple numbers.
73
+
74
+ Some arguments are optional and this function can be called in several ways:
75
+
76
+ - `koffi.encode(ref, type, value)`: no offset
77
+ - `koffi.encode(ref, offset, type, value)`: explicit offset to add to the pointer before encoding
78
+
79
+ We'll reuse the examples shown above and change the variable values with `koffi.encode()`.
80
+
81
+ ```c
82
+ int my_int = 42;
83
+ const char *my_string = NULL;
84
+ ```
85
+
86
+ ```js
87
+ const my_int = lib.symbol('my_int', 'int');
88
+ const my_string = lib.symbol('my_string', 'const char *');
89
+
90
+ console.log(koffi.decode(my_int, 'int')) // Prints 42
91
+ console.log(koffi.decode(my_string, 'const char *')) // Prints null
92
+
93
+ koffi.encode(my_int, 'int', -1);
94
+ koffi.encode(my_string, 'const char *', 'Hello World!');
95
+
96
+ console.log(koffi.decode(my_int, 'int')) // Prints -1
97
+ console.log(koffi.decode(my_string, 'const char *')) // Prints "Hello World!"
98
+ ```
99
+
100
+ When encoding strings (either directly or embedded in arrays or structs), the memory will be bound to the raw pointer value and managed by Koffi. You can assign to the same string again and again with any leak or risk of use-after-free.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "2.5.21-beta.3",
4
- "stable": "2.5.20",
3
+ "version": "2.6.0",
4
+ "stable": "2.6.0",
5
5
  "description": "Fast and simple C FFI (foreign function interface) for Node.js",
6
6
  "keywords": [
7
7
  "foreign",
package/src/index.js CHANGED
@@ -378,8 +378,8 @@ var require_package = __commonJS({
378
378
  "build/dist/src/koffi/package.json"(exports2, module2) {
379
379
  module2.exports = {
380
380
  name: "koffi",
381
- version: "2.5.21-beta.3",
382
- stable: "2.5.20",
381
+ version: "2.6.0",
382
+ stable: "2.6.0",
383
383
  description: "Fast and simple C FFI (foreign function interface) for Node.js",
384
384
  keywords: [
385
385
  "foreign",
@@ -1644,6 +1644,18 @@ static Napi::Value LoadSharedLibrary(const Napi::CallbackInfo &info)
1644
1644
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for filename, expected string or null", GetValueType(instance, info[0]));
1645
1645
  return env.Null();
1646
1646
  }
1647
+ if (info.Length() >= 2 && !IsObject(info[1])) {
1648
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for options, expected object", GetValueType(instance, info[1]));
1649
+ return env.Null();
1650
+ }
1651
+
1652
+ #ifndef _WIN32
1653
+ bool lazy = false;
1654
+ if (info.Length() >= 2) {
1655
+ Napi::Object options = info[1].As<Napi::Object>();
1656
+ lazy = options.Get("lazy").ToBoolean();
1657
+ }
1658
+ #endif
1647
1659
 
1648
1660
  if (!instance->memories.len) {
1649
1661
  AllocateMemory(instance, instance->config.sync_stack_size, instance->config.sync_heap_size);
@@ -1665,8 +1677,10 @@ static Napi::Value LoadSharedLibrary(const Napi::CallbackInfo &info)
1665
1677
  }
1666
1678
  #else
1667
1679
  if (info[0].IsString()) {
1680
+ int flags = lazy ? RTLD_LAZY : RTLD_NOW;
1681
+
1668
1682
  std::string filename = info[0].As<Napi::String>();
1669
- module = dlopen(filename.c_str(), RTLD_NOW);
1683
+ module = dlopen(filename.c_str(), flags);
1670
1684
 
1671
1685
  if (!module) {
1672
1686
  const char *msg = dlerror();