koffi 2.7.4 → 2.8.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,13 @@
2
2
 
3
3
  ## Version history
4
4
 
5
+ ### Koffi 2.8
6
+
7
+ #### Koffi 2.8.0 (2024-02-12)
8
+
9
+ - Support pushing pointers for string arguments
10
+ - Add `koffi.alloc()` for [stable pointers](output.md#stable-pointers)
11
+
5
12
  ### Koffi 2.7
6
13
 
7
14
  #### Koffi 2.7.4 (2024-02-04)
@@ -130,7 +137,7 @@ Pre-built binaries don't work correctly in Koffi 2.5.13 to 2.5.15, skip those ve
130
137
 
131
138
  #### Koffi 2.5.11 (2023-08-03)
132
139
 
133
- - Support casting function pointers with [koffi.as()](polymorphism.md#input-polymorphism)
140
+ - Support casting function pointers with [koffi.as()](pointers.md#handling-void-pointers)
134
141
  - Build in C++20 mode
135
142
 
136
143
  #### Koffi 2.5.10 (2023-08-01)
@@ -338,7 +345,7 @@ Pre-built binaries don't work correctly in Koffi 2.5.13 to 2.5.15, skip those ve
338
345
  **Main changes:**
339
346
 
340
347
  - Allow buffers (TypedArray or ArrayBuffer) values for input and/or output pointer arguments (for polymorphic arguments)
341
- - Support opaque buffers (TypedArray or ArrayBuffer) values in `koffi.decode()` to [decode output buffers](polymorphism.md#output-buffers)
348
+ - Support opaque buffers (TypedArray or ArrayBuffer) values in `koffi.decode()` to [decode output buffers](output.md#output-buffers)
342
349
  - Decode non-string types as arrays when an [explicit length is passed to koffi.decode()](callbacks.md#decoding-pointer-arguments)
343
350
 
344
351
  **Other changes:**
@@ -431,7 +438,7 @@ Pre-built binaries don't work correctly in Koffi 2.5.13 to 2.5.15, skip those ve
431
438
 
432
439
  **Main changes:**
433
440
 
434
- - Add [koffi.as()](polymorphism.md#input-polymorphism) to support polymorphic APIs based on `void *` parameters
441
+ - Add [koffi.as()](pointers.md#handling-void-pointers) to support polymorphic APIs based on `void *` parameters
435
442
  - Add [endian-sensitive integer types](input.md#endian-sensitive-integers): `intX_le_t`, `intX_be_t`, `uintX_le_t`, `uintX_be_t`
436
443
  - Accept typed arrays for `void *` parameters
437
444
  - Introduce `koffi.opaque()` to replace `koffi.handle()` (which remains supported until Koffi 3.0)
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/functions.md CHANGED
@@ -251,4 +251,3 @@ Among other thing, in the the following pages you will learn more about:
251
251
  - How Koffi translates [input parameters](input.md) to C
252
252
  - How you can [define and use pointers](pointers.md)
253
253
  - How to deal with [output parameters](output.md)
254
- - How to handle [polymorphic API](polymorphism.md)
package/doc/index.rst CHANGED
@@ -29,7 +29,6 @@ Table of contents
29
29
  input
30
30
  pointers
31
31
  output
32
- polymorphism
33
32
  unions
34
33
  variables
35
34
  callbacks
package/doc/input.md CHANGED
@@ -393,7 +393,7 @@ The reverse case is also true, Koffi can convert a C fixed-size buffer to a JS s
393
393
 
394
394
  ### Dynamic arrays (pointers)
395
395
 
396
- 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.
396
+ In C, dynamically-sized arrays are usually passed around as pointers. Read more about [array pointers](pointers.md#dynamic-arrays) in the relevant section.
397
397
 
398
398
  ## Union types
399
399
 
package/doc/output.md CHANGED
@@ -167,3 +167,111 @@ ConcatToBuffer(str1, str2, out);
167
167
 
168
168
  console.log(out[0]);
169
169
  ```
170
+
171
+ ## Output buffers
172
+
173
+ 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.
174
+
175
+ ```{warning}
176
+ 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.
177
+
178
+ If you need to provide a pointer that will be kept around, allocate memory with [koffi.alloc()](#stable-pointers) instead.
179
+ ```
180
+
181
+ ### Transient pointers
182
+
183
+ *New in Koffi 2.3*
184
+
185
+ 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.
186
+
187
+ Once the native function returns, you can decode the content with `koffi.decode(value, type)` as in the following example:
188
+
189
+ ```js
190
+ // ES6 syntax: import koffi from 'koffi';
191
+ const koffi = require('koffi');
192
+
193
+ const lib = koffi.load('libc.so.6');
194
+
195
+ const Vec3 = koffi.struct('Vec3', {
196
+ x: 'float32',
197
+ y: 'float32',
198
+ z: 'float32'
199
+ })
200
+
201
+ const memcpy = lib.func('void *memcpy(_Out_ void *dest, const void *src, size_t size)');
202
+
203
+ let vec1 = { x: 1, y: 2, z: 3 };
204
+ let vec2 = null;
205
+
206
+ // Copy the vector in a convoluted way through memcpy
207
+ {
208
+ let src = koffi.as(vec1, 'Vec3 *');
209
+ let dest = Buffer.allocUnsafe(koffi.sizeof(Vec3));
210
+
211
+ memcpy(dest, src, koffi.sizeof(Vec3));
212
+
213
+ vec2 = koffi.decode(dest, Vec3);
214
+ }
215
+
216
+ // CHange vector1, leaving copy alone
217
+ [vec1.x, vec1.y, vec1.z] = [vec1.z, vec1.y, vec1.x];
218
+
219
+ console.log(vec1); // { x: 3, y: 2, z: 1 }
220
+ console.log(vec2); // { x: 1, y: 2, z: 3 }
221
+ ```
222
+
223
+ See [decoding variables](variables.md#decode-to-js-values) for more information about the decode function.
224
+
225
+ ### Stable pointers
226
+
227
+ *New in Koffi 2.8*
228
+
229
+ 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.
230
+
231
+ In this case, it is **not safe to use buffers or typed arrays**!
232
+
233
+ 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.
234
+
235
+ The example below sets up some memory to be used as an output buffer where a concatenation function appends a string on each call.
236
+
237
+ ```c
238
+ #include <assert.h>
239
+ #include <stddef.h>
240
+
241
+ static char *buf_ptr;
242
+ static size_t buf_len;
243
+ static size_t buf_size;
244
+
245
+ void reset_buffer(char *buf, size_t size)
246
+ {
247
+ assert(size > 1);
248
+
249
+ buf_ptr = buf;
250
+ buf_len = 0;
251
+ buf_size = size - 1; // Keep space for trailing NUL
252
+
253
+ buf_ptr[0] = 0;
254
+ }
255
+
256
+ void append_str(const char *str)
257
+ {
258
+ for (size_t i = 0; str[i] && buf_len < buf_size; i++, buf_len++) {
259
+ buf_ptr[buf_len] = str[i];
260
+ }
261
+ buf_ptr[buf_len] = 0;
262
+ }
263
+ ```
264
+
265
+ ```js
266
+ const reset_buffer = lib.func('void reset_buffer(char *buf, size_t size)');
267
+ const append_str = lib.func('void append_str(const char *str)');
268
+
269
+ let output = koffi.alloc('char', 64);
270
+ reset_buffer(output, 64);
271
+
272
+ append_str('Hello');
273
+ console.log(koffi.decode(output, 'char', -1)); // Prints Hello
274
+
275
+ append_str(' World!');
276
+ console.log(koffi.decode(output, 'char', -1)); // Prints Hello World!
277
+ ```
package/doc/pointers.md CHANGED
@@ -88,7 +88,7 @@ AddInt(sum, 6);
88
88
  console.log(sum[0]); // Prints 42
89
89
  ```
90
90
 
91
- ### Array pointers (dynamic arrays)
91
+ ### Dynamic arrays
92
92
 
93
93
  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).
94
94
 
@@ -132,6 +132,69 @@ console.log(total); // Prints 14
132
132
 
133
133
  By default, just like for objects, array arguments are copied from JS to C but not vice-versa. You can however change the direction as documented in the section on [output parameters](output.md).
134
134
 
135
+ ## Handling void pointers
136
+
137
+ *New in Koffi 2.1*
138
+
139
+ 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.
140
+
141
+ Koffi provides two features to deal with this:
142
+
143
+ - You can use `koffi.as(value, type)` to tell Koffi what kind of type is actually expected, as shown in the example below.
144
+ - 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.
145
+
146
+ The example below shows the use of `koffi.as()` to read the header of a PNG file with `fread()` directly to a JS object.
147
+
148
+ ```js
149
+ // ES6 syntax: import koffi from 'koffi';
150
+ const koffi = require('koffi');
151
+
152
+ const lib = koffi.load('libc.so.6');
153
+
154
+ const FILE = koffi.opaque('FILE');
155
+
156
+ const PngHeader = koffi.pack('PngHeader', {
157
+ signature: koffi.array('uint8_t', 8),
158
+ ihdr: koffi.pack({
159
+ length: 'uint32_be_t',
160
+ chunk: koffi.array('char', 4),
161
+ width: 'uint32_be_t',
162
+ height: 'uint32_be_t',
163
+ depth: 'uint8_t',
164
+ color: 'uint8_t',
165
+ compression: 'uint8_t',
166
+ filter: 'uint8_t',
167
+ interlace: 'uint8_t',
168
+ crc: 'uint32_be_t'
169
+ })
170
+ });
171
+
172
+ const fopen = lib.func('FILE *fopen(const char *path, const char *mode)');
173
+ const fclose = lib.func('int fclose(FILE *fp)');
174
+ const fread = lib.func('size_t fread(_Out_ void *ptr, size_t size, size_t nmemb, FILE *fp)');
175
+
176
+ let filename = process.argv[2];
177
+ if (filename == null)
178
+ throw new Error('Usage: node png.js <image.png>');
179
+
180
+ let hdr = {};
181
+ {
182
+ let fp = fopen(filename, 'rb');
183
+ if (!fp)
184
+ throw new Error(`Failed to open '${filename}'`);
185
+
186
+ try {
187
+ let len = fread(koffi.as(hdr, 'PngHeader *'), 1, koffi.sizeof(PngHeader), fp);
188
+ if (len < koffi.sizeof(PngHeader))
189
+ throw new Error('Failed to read PNG header');
190
+ } finally {
191
+ fclose(fp);
192
+ }
193
+ }
194
+
195
+ console.log('PNG header:', hdr);
196
+ ```
197
+
135
198
  ## Disposable types
136
199
 
137
200
  *New in Koffi 2.0*
package/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.7.4",
382
- stable: "2.7.4",
381
+ version: "2.8.0",
382
+ stable: "2.8.0",
383
383
  description: "Fast and simple C FFI (foreign function interface) for Node.js",
384
384
  keywords: [
385
385
  "foreign",
package/indirect.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.7.4",
382
- stable: "2.7.4",
381
+ version: "2.8.0",
382
+ stable: "2.8.0",
383
383
  description: "Fast and simple C FFI (foreign function interface) for Node.js",
384
384
  keywords: [
385
385
  "foreign",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "2.7.4",
4
- "stable": "2.7.4",
3
+ "version": "2.8.0",
4
+ "stable": "2.8.0",
5
5
  "description": "Fast and simple C FFI (foreign function interface) for Node.js",
6
6
  "keywords": [
7
7
  "foreign",
@@ -3142,10 +3142,10 @@ static inline void Log(LogLevel level, const char *ctx, const char *fmt, Args...
3142
3142
  #ifdef RG_DEBUG
3143
3143
  const char *DebugLogContext(const char *filename, int line);
3144
3144
 
3145
- #define LogDebug(...) Log(LogLevel::Debug, DebugLogContext(__FILE__, __LINE__), __VA_ARGS__)
3146
- #define LogInfo(...) Log(LogLevel::Info, nullptr, __VA_ARGS__)
3147
- #define LogWarning(...) Log(LogLevel::Warning, DebugLogContext(__FILE__, __LINE__), __VA_ARGS__)
3148
- #define LogError(...) Log(LogLevel::Error, DebugLogContext(__FILE__, __LINE__), __VA_ARGS__)
3145
+ #define LogDebug(...) Log(LogLevel::Debug, DebugLogContext(__FILE__, __LINE__) __VA_OPT__(,) __VA_ARGS__)
3146
+ #define LogInfo(...) Log(LogLevel::Info, nullptr __VA_OPT__(,) __VA_ARGS__)
3147
+ #define LogWarning(...) Log(LogLevel::Warning, DebugLogContext(__FILE__, __LINE__) __VA_OPT__(,) __VA_ARGS__)
3148
+ #define LogError(...) Log(LogLevel::Error, DebugLogContext(__FILE__, __LINE__) __VA_OPT__(,) __VA_ARGS__)
3149
3149
  #else
3150
3150
  template <typename... Args>
3151
3151
  static inline void LogDebug(Args...) {}
@@ -30,7 +30,7 @@ include(CheckCXXCompilerFlag)
30
30
  set(CMAKE_CXX_STANDARD 20)
31
31
  if(MSVC)
32
32
  set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded)
33
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus /W4 /wd4200 /wd4201 /wd4127 /wd4458 /wd4706 /wd4702 /wd4324")
33
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus /Zc:preprocessor /W4 /wd4200 /wd4201 /wd4127 /wd4458 /wd4706 /wd4702 /wd4324")
34
34
 
35
35
  # ASM_MASM does not (yet) work on Windows ARM64
36
36
  if(NOT CMAKE_GENERATOR_PLATFORM MATCHES "ARM64")
@@ -209,8 +209,7 @@ bool CallData::PushString(Napi::Value value, int directions, const char **out_st
209
209
 
210
210
  return true;
211
211
  } else {
212
- ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected string", GetValueType(instance, value));
213
- return false;
212
+ return PushPointer(value, instance->str_type, directions, (void **)out_str);
214
213
  }
215
214
  }
216
215
 
@@ -311,8 +310,7 @@ bool CallData::PushString16(Napi::Value value, int directions, const char16_t **
311
310
 
312
311
  return true;
313
312
  } else {
314
- ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected string", GetValueType(instance, value));
315
- return false;
313
+ return PushPointer(value, instance->str16_type, directions, (void **)out_str16);
316
314
  }
317
315
  }
318
316
 
@@ -983,7 +981,9 @@ bool CallData::PushPointer(Napi::Value value, const TypeInfo *type, int directio
983
981
  } break;
984
982
 
985
983
  case napi_external: {
986
- RG_ASSERT(type->primitive == PrimitiveKind::Pointer);
984
+ RG_ASSERT(type->primitive == PrimitiveKind::Pointer ||
985
+ type->primitive == PrimitiveKind::String ||
986
+ type->primitive == PrimitiveKind::String16);
987
987
 
988
988
  if (!CheckValueTag(instance, value, type->ref.marker) &&
989
989
  !CheckValueTag(instance, value, instance->void_type) &&
@@ -770,6 +770,55 @@ static inline bool GetExternalPointer(Napi::Env env, Napi::Value value, void **o
770
770
  }
771
771
  }
772
772
 
773
+ static Napi::Value CallAlloc(const Napi::CallbackInfo &info)
774
+ {
775
+ Napi::Env env = info.Env();
776
+ InstanceData *instance = env.GetInstanceData<InstanceData>();
777
+
778
+ if (info.Length() < 2) {
779
+ ThrowError<Napi::TypeError>(env, "Expected 2 arguments, got %1", info.Length());
780
+ return env.Null();
781
+ }
782
+ if (!info[1].IsNumber()) {
783
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for length, expected number", GetValueType(instance, info[1]));
784
+ return env.Null();
785
+ }
786
+
787
+ const TypeInfo *type = ResolveType(info[0]);
788
+ if (!type)
789
+ return env.Null();
790
+
791
+ if (!type->size) [[unlikely]] {
792
+ ThrowError<Napi::TypeError>(env, "Cannot allocate memory for zero-sized type %1", type->name);
793
+ return env.Null();
794
+ }
795
+
796
+ int32_t len = info[1].As<Napi::Number>();
797
+
798
+ if (len <= 0) [[unlikely]] {
799
+ ThrowError<Napi::Error>(env, "Size must be greater than 0");
800
+ return env.Null();
801
+ }
802
+ if (len > INT32_MAX / type->size) [[unlikely]] {
803
+ ThrowError<Napi::Error>(env, "Cannot allocate more than %1 objects of type %2", INT32_MAX / type->size, type->name);
804
+ return env.Null();
805
+ }
806
+
807
+ void *ptr = calloc((size_t)len, (size_t)type->size);
808
+
809
+ if (!ptr) [[unlikely]] {
810
+ Size size = (Size)(len * type->size);
811
+
812
+ ThrowError<Napi::Error>(env, "Failed to allocate %1 of memory", FmtMemSize((Size)size));
813
+ return env.Null();
814
+ }
815
+
816
+ Napi::External<void> external = Napi::External<void>::New(env, ptr);
817
+ SetValueTag(instance, external, type);
818
+
819
+ return external;
820
+ }
821
+
773
822
  static Napi::Value CallFree(const Napi::CallbackInfo &info)
774
823
  {
775
824
  Napi::Env env = info.Env();
@@ -2241,6 +2290,7 @@ static Napi::Object InitModule(Napi::Env env, Napi::Object exports)
2241
2290
  exports.Set("inout", Napi::Function::New(env, MarkInOut, "inout"));
2242
2291
 
2243
2292
  exports.Set("disposable", Napi::Function::New(env, CreateDisposableType, "disposable"));
2293
+ exports.Set("alloc", Napi::Function::New(env, CallAlloc, "alloc"));
2244
2294
  exports.Set("free", Napi::Function::New(env, CallFree, "free"));
2245
2295
 
2246
2296
  exports.Set("register", Napi::Function::New(env, RegisterCallback, "register"));
@@ -2327,6 +2377,8 @@ static Napi::Object InitModule(Napi::Env env, Napi::Object exports)
2327
2377
  instance->void_type = instance->types_map.FindValue("void", nullptr);
2328
2378
  instance->char_type = instance->types_map.FindValue("char", nullptr);
2329
2379
  instance->char16_type = instance->types_map.FindValue("char16", nullptr);
2380
+ instance->str_type = instance->types_map.FindValue("char *", nullptr);
2381
+ instance->str16_type = instance->types_map.FindValue("char16_t *", nullptr);
2330
2382
 
2331
2383
  instance->active_symbol = Napi::Symbol::New(env, "active");
2332
2384
 
@@ -277,6 +277,8 @@ struct InstanceData {
277
277
  const TypeInfo *void_type;
278
278
  const TypeInfo *char_type;
279
279
  const TypeInfo *char16_type;
280
+ const TypeInfo *str_type;
281
+ const TypeInfo *str16_type;
280
282
 
281
283
  Napi::Symbol active_symbol;
282
284
 
@@ -1,108 +0,0 @@
1
- # Polymorphic arguments
2
-
3
- ## Input polymorphism
4
-
5
- *New in Koffi 2.1*
6
-
7
- 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.
8
-
9
- Koffi provides two features to deal with this:
10
-
11
- - Buffers and typed JS arrays can be used as values in place everywhere a pointer is expected. See [dynamic arrays](pointers.md#array-pointers-dynamic-arrays) for more information, for input or output.
12
- - You can use `koffi.as(value, type)` to tell Koffi what kind of type is actually expected.
13
-
14
- The example below shows the use of `koffi.as()` to read the header of a PNG file with `fread()`.
15
-
16
- ```js
17
- // ES6 syntax: import koffi from 'koffi';
18
- const koffi = require('koffi');
19
-
20
- const lib = koffi.load('libc.so.6');
21
-
22
- const FILE = koffi.opaque('FILE');
23
-
24
- const PngHeader = koffi.pack('PngHeader', {
25
- signature: koffi.array('uint8_t', 8),
26
- ihdr: koffi.pack({
27
- length: 'uint32_be_t',
28
- chunk: koffi.array('char', 4),
29
- width: 'uint32_be_t',
30
- height: 'uint32_be_t',
31
- depth: 'uint8_t',
32
- color: 'uint8_t',
33
- compression: 'uint8_t',
34
- filter: 'uint8_t',
35
- interlace: 'uint8_t',
36
- crc: 'uint32_be_t'
37
- })
38
- });
39
-
40
- const fopen = lib.func('FILE *fopen(const char *path, const char *mode)');
41
- const fclose = lib.func('int fclose(FILE *fp)');
42
- const fread = lib.func('size_t fread(_Out_ void *ptr, size_t size, size_t nmemb, FILE *fp)');
43
-
44
- let filename = process.argv[2];
45
- if (filename == null)
46
- throw new Error('Usage: node png.js <image.png>');
47
-
48
- let hdr = {};
49
- {
50
- let fp = fopen(filename, 'rb');
51
- if (!fp)
52
- throw new Error(`Failed to open '${filename}'`);
53
-
54
- try {
55
- let len = fread(koffi.as(hdr, 'PngHeader *'), 1, koffi.sizeof(PngHeader), fp);
56
- if (len < koffi.sizeof(PngHeader))
57
- throw new Error('Failed to read PNG header');
58
- } finally {
59
- fclose(fp);
60
- }
61
- }
62
-
63
- console.log('PNG header:', hdr);
64
- ```
65
-
66
- ## Output buffers
67
-
68
- *New in Koffi 2.3*
69
-
70
- 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.
71
-
72
- Once the native function returns, you can decode the content with `koffi.decode(value, type)` as in the following example:
73
-
74
- ```js
75
- // ES6 syntax: import koffi from 'koffi';
76
- const koffi = require('koffi');
77
-
78
- const lib = koffi.load('libc.so.6');
79
-
80
- const Vec3 = koffi.struct('Vec3', {
81
- x: 'float32',
82
- y: 'float32',
83
- z: 'float32'
84
- })
85
-
86
- const memcpy = lib.func('void *memcpy(_Out_ void *dest, const void *src, size_t size)');
87
-
88
- let vec1 = { x: 1, y: 2, z: 3 };
89
- let vec2 = null;
90
-
91
- // Copy the vector in a convoluted way through memcpy
92
- {
93
- let src = koffi.as(vec1, 'Vec3 *');
94
- let dest = Buffer.allocUnsafe(koffi.sizeof(Vec3));
95
-
96
- memcpy(dest, src, koffi.sizeof(Vec3));
97
-
98
- vec2 = koffi.decode(dest, Vec3);
99
- }
100
-
101
- // CHange vector1, leaving copy alone
102
- [vec1.x, vec1.y, vec1.z] = [vec1.z, vec1.y, vec1.x];
103
-
104
- console.log(vec1); // { x: 3, y: 2, z: 1 }
105
- console.log(vec2); // { x: 1, y: 2, z: 3 }
106
- ```
107
-
108
- See [decoding variables](variables.md#decode-to-js-values) for more information about the decode function.