Comparing version 2.7.4 to 2.8.0
@@ -5,2 +5,9 @@ # Changelog | ||
### Koffi 2.8 | ||
#### Koffi 2.8.0 (2024-02-12) | ||
- Support pushing pointers for string arguments | ||
- Add `koffi.alloc()` for [stable pointers](output.md#stable-pointers) | ||
### Koffi 2.7 | ||
@@ -134,3 +141,3 @@ | ||
- Support casting function pointers with [koffi.as()](polymorphism.md#input-polymorphism) | ||
- Support casting function pointers with [koffi.as()](pointers.md#handling-void-pointers) | ||
- Build in C++20 mode | ||
@@ -343,3 +350,3 @@ | ||
- Allow buffers (TypedArray or ArrayBuffer) values for input and/or output pointer arguments (for polymorphic arguments) | ||
- Support opaque buffers (TypedArray or ArrayBuffer) values in `koffi.decode()` to [decode output buffers](polymorphism.md#output-buffers) | ||
- Support opaque buffers (TypedArray or ArrayBuffer) values in `koffi.decode()` to [decode output buffers](output.md#output-buffers) | ||
- Decode non-string types as arrays when an [explicit length is passed to koffi.decode()](callbacks.md#decoding-pointer-arguments) | ||
@@ -437,3 +444,3 @@ | ||
- Add [koffi.as()](polymorphism.md#input-polymorphism) to support polymorphic APIs based on `void *` parameters | ||
- Add [koffi.as()](pointers.md#handling-void-pointers) to support polymorphic APIs based on `void *` parameters | ||
- Add [endian-sensitive integer types](input.md#endian-sensitive-integers): `intX_le_t`, `intX_be_t`, `uintX_le_t`, `uintX_be_t` | ||
@@ -440,0 +447,0 @@ - Accept typed arrays for `void *` parameters |
@@ -254,2 +254,1 @@ # Function calls | ||
- How to deal with [output parameters](output.md) | ||
- How to handle [polymorphic API](polymorphism.md) |
@@ -396,3 +396,3 @@ # Input parameters | ||
In C, dynamically-sized arrays are usually passed around as pointers. Read more about [array pointers](pointers.md#array-pointers-dynamic-arrays) in the relevant section. | ||
In C, dynamically-sized arrays are usually passed around as pointers. Read more about [array pointers](pointers.md#dynamic-arrays) in the relevant section. | ||
@@ -399,0 +399,0 @@ ## Union types |
@@ -170,1 +170,109 @@ # Output parameters | ||
``` | ||
## Output buffers | ||
In most cases, you can use buffers and typed arrays to provide output buffers. This works as long as the buffer only gets used while the native C function is being called. See [transient pointers](#transient-pointers) below for an example. | ||
```{warning} | ||
It is unsafe to keep the pointer around in the native code, or to change the contents outside of the function call where it is provided. | ||
If you need to provide a pointer that will be kept around, allocate memory with [koffi.alloc()](#stable-pointers) instead. | ||
``` | ||
### Transient pointers | ||
*New in Koffi 2.3* | ||
You can use buffers and typed arrays for output (and input/output) pointer parameters. Simply pass the buffer as an argument and the native function will receive a pointer to its contents. | ||
Once the native function returns, you can decode the content with `koffi.decode(value, type)` as in the following example: | ||
```js | ||
// ES6 syntax: import koffi from 'koffi'; | ||
const koffi = require('koffi'); | ||
const lib = koffi.load('libc.so.6'); | ||
const Vec3 = koffi.struct('Vec3', { | ||
x: 'float32', | ||
y: 'float32', | ||
z: 'float32' | ||
}) | ||
const memcpy = lib.func('void *memcpy(_Out_ void *dest, const void *src, size_t size)'); | ||
let vec1 = { x: 1, y: 2, z: 3 }; | ||
let vec2 = null; | ||
// Copy the vector in a convoluted way through memcpy | ||
{ | ||
let src = koffi.as(vec1, 'Vec3 *'); | ||
let dest = Buffer.allocUnsafe(koffi.sizeof(Vec3)); | ||
memcpy(dest, src, koffi.sizeof(Vec3)); | ||
vec2 = koffi.decode(dest, Vec3); | ||
} | ||
// CHange vector1, leaving copy alone | ||
[vec1.x, vec1.y, vec1.z] = [vec1.z, vec1.y, vec1.x]; | ||
console.log(vec1); // { x: 3, y: 2, z: 1 } | ||
console.log(vec2); // { x: 1, y: 2, z: 3 } | ||
``` | ||
See [decoding variables](variables.md#decode-to-js-values) for more information about the decode function. | ||
### Stable pointers | ||
*New in Koffi 2.8* | ||
In some cases, the native code may need to change the output buffer at a later time, maybe during a later call or from another thread. | ||
In this case, it is **not safe to use buffers or typed arrays**! | ||
However, you can use `koffi.alloc(type, len)` to allocate memory and get a pointer that won't move, and can be safely used at any time by the native code. Use [koffi.decode()](variables.md#decode-to-js-values) to read data from the pointer when needed. | ||
The example below sets up some memory to be used as an output buffer where a concatenation function appends a string on each call. | ||
```c | ||
#include <assert.h> | ||
#include <stddef.h> | ||
static char *buf_ptr; | ||
static size_t buf_len; | ||
static size_t buf_size; | ||
void reset_buffer(char *buf, size_t size) | ||
{ | ||
assert(size > 1); | ||
buf_ptr = buf; | ||
buf_len = 0; | ||
buf_size = size - 1; // Keep space for trailing NUL | ||
buf_ptr[0] = 0; | ||
} | ||
void append_str(const char *str) | ||
{ | ||
for (size_t i = 0; str[i] && buf_len < buf_size; i++, buf_len++) { | ||
buf_ptr[buf_len] = str[i]; | ||
} | ||
buf_ptr[buf_len] = 0; | ||
} | ||
``` | ||
```js | ||
const reset_buffer = lib.func('void reset_buffer(char *buf, size_t size)'); | ||
const append_str = lib.func('void append_str(const char *str)'); | ||
let output = koffi.alloc('char', 64); | ||
reset_buffer(output, 64); | ||
append_str('Hello'); | ||
console.log(koffi.decode(output, 'char', -1)); // Prints Hello | ||
append_str(' World!'); | ||
console.log(koffi.decode(output, 'char', -1)); // Prints Hello World! | ||
``` |
@@ -91,3 +91,3 @@ # Data pointers | ||
### Array pointers (dynamic arrays) | ||
### Dynamic arrays | ||
@@ -136,2 +136,65 @@ In C, dynamically-sized arrays are usually passed around as pointers. The length is either passed as an additional argument, or inferred from the array content itself, for example with a terminating sentinel value (such as a NULL pointers in the case of an array of strings). | ||
## Handling void pointers | ||
*New in Koffi 2.1* | ||
Many C functions use `void *` parameters in order to pass polymorphic objects and arrays, meaning that the data format changes can change depending on one other argument, or on some kind of struct tag member. | ||
Koffi provides two features to deal with this: | ||
- You can use `koffi.as(value, type)` to tell Koffi what kind of type is actually expected, as shown in the example below. | ||
- Buffers and typed JS arrays can be used as values in place everywhere a pointer is expected. See [dynamic arrays](#dynamic-arrays) for more information, for input or output. | ||
The example below shows the use of `koffi.as()` to read the header of a PNG file with `fread()` directly to a JS object. | ||
```js | ||
// ES6 syntax: import koffi from 'koffi'; | ||
const koffi = require('koffi'); | ||
const lib = koffi.load('libc.so.6'); | ||
const FILE = koffi.opaque('FILE'); | ||
const PngHeader = koffi.pack('PngHeader', { | ||
signature: koffi.array('uint8_t', 8), | ||
ihdr: koffi.pack({ | ||
length: 'uint32_be_t', | ||
chunk: koffi.array('char', 4), | ||
width: 'uint32_be_t', | ||
height: 'uint32_be_t', | ||
depth: 'uint8_t', | ||
color: 'uint8_t', | ||
compression: 'uint8_t', | ||
filter: 'uint8_t', | ||
interlace: 'uint8_t', | ||
crc: 'uint32_be_t' | ||
}) | ||
}); | ||
const fopen = lib.func('FILE *fopen(const char *path, const char *mode)'); | ||
const fclose = lib.func('int fclose(FILE *fp)'); | ||
const fread = lib.func('size_t fread(_Out_ void *ptr, size_t size, size_t nmemb, FILE *fp)'); | ||
let filename = process.argv[2]; | ||
if (filename == null) | ||
throw new Error('Usage: node png.js <image.png>'); | ||
let hdr = {}; | ||
{ | ||
let fp = fopen(filename, 'rb'); | ||
if (!fp) | ||
throw new Error(`Failed to open '${filename}'`); | ||
try { | ||
let len = fread(koffi.as(hdr, 'PngHeader *'), 1, koffi.sizeof(PngHeader), fp); | ||
if (len < koffi.sizeof(PngHeader)) | ||
throw new Error('Failed to read PNG header'); | ||
} finally { | ||
fclose(fp); | ||
} | ||
} | ||
console.log('PNG header:', hdr); | ||
``` | ||
## Disposable types | ||
@@ -138,0 +201,0 @@ |
@@ -381,4 +381,4 @@ "use strict"; | ||
name: "koffi", | ||
version: "2.7.4", | ||
stable: "2.7.4", | ||
version: "2.8.0", | ||
stable: "2.8.0", | ||
description: "Fast and simple C FFI (foreign function interface) for Node.js", | ||
@@ -385,0 +385,0 @@ keywords: [ |
@@ -381,4 +381,4 @@ "use strict"; | ||
name: "koffi", | ||
version: "2.7.4", | ||
stable: "2.7.4", | ||
version: "2.8.0", | ||
stable: "2.8.0", | ||
description: "Fast and simple C FFI (foreign function interface) for Node.js", | ||
@@ -385,0 +385,0 @@ keywords: [ |
{ | ||
"name": "koffi", | ||
"version": "2.7.4", | ||
"stable": "2.7.4", | ||
"version": "2.8.0", | ||
"stable": "2.8.0", | ||
"description": "Fast and simple C FFI (foreign function interface) for Node.js", | ||
@@ -6,0 +6,0 @@ "keywords": [ |
@@ -33,3 +33,3 @@ # Copyright 2023 Niels Martignène <niels.martignene@protonmail.com> | ||
set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded) | ||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus /W4 /wd4200 /wd4201 /wd4127 /wd4458 /wd4706 /wd4702 /wd4324") | ||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus /Zc:preprocessor /W4 /wd4200 /wd4201 /wd4127 /wd4458 /wd4706 /wd4702 /wd4324") | ||
@@ -36,0 +36,0 @@ # ASM_MASM does not (yet) work on Windows ARM64 |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
69770096
425