koffi 0.9.26 → 0.9.29

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/README.md CHANGED
@@ -261,84 +261,84 @@ At this stage, two benchmarks are implemented:
261
261
 
262
262
  In order to run it, go to `koffi/benchmark` and run `../../cnoke/cnoke.js` (or `node ..\..\cnoke\cnoke.js` on Windows) before doing anything else.
263
263
 
264
- Once this is done, you can execute each implementation, e.g. `build/atoi_cc 20000000` or `./atoi_koffi.js 20000000`.
264
+ Once this is done, you can execute each implementation, e.g. `build/atoi_cc` or `./atoi_koffi.js`. You can optionally define a custom number of iterations, e.g. `./atoi_koffi.js 10000000`.
265
265
 
266
266
  ## atoi results
267
267
 
268
- Here are some results from 2022-04-24 on my Linux machine (AMD® Ryzen™ 7 5800H 16G):
268
+ Here are some results from 2022-04-24 on Linux on my machine (AMD® Ryzen™ 7 5800H 16G):
269
269
 
270
270
  ```sh
271
- $ build/atoi_cc 20000000
271
+ $ build/atoi_cc
272
272
  Iterations: 20000000
273
273
  Time: 0.24s
274
274
 
275
- $ ./atoi_napi.js 20000000
275
+ $ ./atoi_napi.js
276
276
  Iterations: 20000000
277
- Time: 1.56s
277
+ Time: 1.10s
278
278
 
279
- $ ./atoi_koffi.js 20000000
279
+ $ ./atoi_koffi.js
280
280
  Iterations: 20000000
281
- Time: 2.41s
281
+ Time: 1.91s
282
282
 
283
283
  # Note: the Node-FFI version does a few setTimeout calls to force the GC to run (around 20
284
284
  # for the example below), without which Node will consume all memory because the GC never appears
285
285
  # to run, or not enough. It's not ideal but on the other hand it counts as another limitation
286
286
  # to Node-FFI performance.
287
- $ ./atoi_node_ffi.js 20000000
287
+ $ ./atoi_node_ffi.js
288
288
  Iterations: 20000000
289
289
  Time: 640.49s
290
290
  ```
291
291
 
292
- And on my Windows machine (Intel® Corei5-4460 16G):
292
+ And on Windows on the same machine (AMD® Ryzen7 5800H 16G):
293
293
 
294
294
  ```sh
295
- $ build\atoi_cc.exe 20000000
295
+ $ build\atoi_cc.exe
296
296
  Iterations: 20000000
297
- Time: 0.66s
297
+ Time: 0.25s
298
298
 
299
- $ node atoi_napi.js 20000000
299
+ $ node atoi_napi.js
300
300
  Iterations: 20000000
301
- Time: 3.52s
301
+ Time: 1.94s
302
302
 
303
- $ node atoi_koffi.js 20000000
303
+ $ node atoi_koffi.js
304
304
  Iterations: 20000000
305
- Time: 4.81s
305
+ Time: 3.15s
306
306
 
307
- $ node atoi_node_ffi.js 20000000
307
+ $ node atoi_node_ffi.js
308
308
  Iterations: 20000000
309
- Time: 491.99s
309
+ Time: 267.20s
310
310
  ```
311
311
 
312
312
  ## Raylib results
313
313
 
314
- Here are some results from 2022-04-24 on my Linux machine (AMD® Ryzen™ 7 5800H 16G):
314
+ Here are some results from 2022-04-24 on Linux on my machine (AMD® Ryzen™ 7 5800H 16G):
315
315
 
316
316
  ```sh
317
- $ build/raylib_cc 100
317
+ $ build/raylib_cc
318
318
  Iterations: 100
319
319
  Time: 4.14s
320
320
 
321
- $ ./raylib_koffi.js 100
321
+ $ ./raylib_koffi.js
322
322
  Iterations: 100
323
323
  Time: 6.25s
324
324
 
325
- $ ./raylib_node_ffi.js 100
325
+ $ ./raylib_node_ffi.js
326
326
  Iterations: 100
327
327
  Time: 27.13s
328
328
  ```
329
329
 
330
- And on my Windows machine (Intel® Corei5-4460 16G):
330
+ And on Windows on the same machine (AMD® Ryzen7 5800H 16G):
331
331
 
332
332
  ```sh
333
- $ build\raylib_cc.exe 100
333
+ $ build\raylib_cc.exe
334
334
  Iterations: 100
335
- Time: 10.53s
335
+ Time: 8.39s
336
336
 
337
- $ node raylib_koffi.js 100
337
+ $ node raylib_koffi.js
338
338
  Iterations: 100
339
- Time: 14.60s
339
+ Time: 11.51s
340
340
 
341
- $ node raylib_node_ffi.js 100
341
+ $ node raylib_node_ffi.js
342
342
  Iterations: 100
343
- Time: 44.97s
343
+ Time: 32.47s
344
344
  ```
@@ -16,15 +16,21 @@ project(koffi C CXX ASM)
16
16
 
17
17
  find_package(CNoke)
18
18
 
19
- set(CMAKE_CXX_STANDARD 17)
19
+ set(CMAKE_CXX_STANDARD 20)
20
20
 
21
- add_subdirectory(.. koffi)
21
+ set(THREADS_PREFER_PTHREAD_FLAG ON)
22
+ find_package(Threads REQUIRED)
23
+
24
+ if(NOT TARGET koffi)
25
+ add_subdirectory(.. koffi)
26
+ endif()
22
27
  add_subdirectory(../test test)
23
28
 
24
29
  # ---- atoi ----
25
30
 
26
31
  add_executable(atoi_cc atoi_cc.cc ../vendor/libcc/libcc.cc)
27
32
  target_include_directories(atoi_cc PRIVATE ..)
33
+ target_link_libraries(atoi_cc PRIVATE Threads::Threads)
28
34
 
29
35
  if(WIN32)
30
36
  target_compile_definitions(atoi_cc PRIVATE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE)
@@ -33,19 +39,24 @@ endif()
33
39
 
34
40
  add_node_addon(NAME atoi_napi SOURCES atoi_napi.cc ../vendor/libcc/libcc.cc)
35
41
  target_include_directories(atoi_napi PRIVATE .. ../vendor/node-addon-api)
42
+ target_link_libraries(atoi_napi PRIVATE Threads::Threads)
36
43
 
37
44
  if(WIN32)
38
45
  target_compile_definitions(atoi_napi PRIVATE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE)
39
46
  target_link_libraries(atoi_napi PRIVATE ws2_32)
47
+ else()
48
+ target_link_libraries(atoi_napi PRIVATE dl)
40
49
  endif()
41
50
 
42
51
  # ---- Raylib ----
43
52
 
44
- add_executable(raylib_cc atoi_cc.cc ../vendor/libcc/libcc.cc)
53
+ add_executable(raylib_cc raylib_cc.cc ../vendor/libcc/libcc.cc)
45
54
  target_include_directories(raylib_cc PRIVATE ..)
46
- add_dependencies(raylib_cc raylib)
55
+ target_link_libraries(raylib_cc PRIVATE Threads::Threads raylib)
47
56
 
48
57
  if(WIN32)
49
58
  target_compile_definitions(raylib_cc PRIVATE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE)
50
59
  target_link_libraries(raylib_cc PRIVATE ws2_32)
60
+ else()
61
+ target_link_libraries(raylib_cc PRIVATE dl)
51
62
  endif()
@@ -25,21 +25,18 @@ volatile uint64_t sum = 0;
25
25
 
26
26
  int Main(int argc, char **argv)
27
27
  {
28
- if (argc < 2) {
29
- LogError("Missing number of iterations");
30
- LogInfo("Usage: atoi_cc <iterations>");
31
- return 1;
32
- }
28
+ int iterations = 20000000;
33
29
 
34
- int iterations = 0;
35
- if (!ParseInt(argv[1], &iterations))
36
- return 1;
30
+ if (argc >= 2) {
31
+ if (!ParseInt(argv[1], &iterations))
32
+ return 1;
33
+ }
37
34
  LogInfo("Iterations: %1", iterations);
38
35
 
39
36
  int64_t start = GetMonotonicTime();
40
37
 
41
38
  for (int i = 0; i < iterations; i++) {
42
- sum += (uint64_t)atoi(strings[i % RG_LEN(strings)]);
39
+ sum = sum + (uint64_t)atoi(strings[i % RG_LEN(strings)]);
43
40
  }
44
41
 
45
42
  // Help prevent optimisation of loop
@@ -13,7 +13,7 @@
13
13
  // You should have received a copy of the GNU Affero General Public License
14
14
  // along with this program. If not, see https://www.gnu.org/licenses/.
15
15
 
16
- const koffi = require('../build/koffi.node');
16
+ const koffi = require('./build/koffi.node');
17
17
 
18
18
  const strings = [
19
19
  '424242',
@@ -26,14 +26,15 @@ let sum = 0;
26
26
  main();
27
27
 
28
28
  function main() {
29
- if (process.argv.length < 3)
30
- throw new Error('Missing number of iterations');
31
-
32
- let iterations = parseInt(process.argv[2], 10);
33
- if (Number.isNaN(iterations))
34
- throw new Error('Not a valid number');
35
- if (iterations < 1)
36
- throw new Error('Value must be positive');
29
+ let iterations = 20000000;
30
+
31
+ if (process.argv.length >= 3) {
32
+ iterations = parseInt(process.argv[2], 10);
33
+ if (Number.isNaN(iterations))
34
+ throw new Error('Not a valid number');
35
+ if (iterations < 1)
36
+ throw new Error('Value must be positive');
37
+ }
37
38
  console.log('Iterations:', iterations);
38
39
 
39
40
  let lib = koffi.load(process.platform == 'win32' ? 'msvcrt.dll' : null);
@@ -30,20 +30,26 @@ static Napi::Value RunAtoi(const Napi::CallbackInfo &info)
30
30
  {
31
31
  Napi::Env env = info.Env();
32
32
 
33
- // We want maximum performance here, this is a benchmark
34
- #if 0
35
- if (info.Length() < 1) {
33
+ if (RG_UNLIKELY(info.Length() < 1)) {
36
34
  ThrowError<Napi::TypeError>(env, "Expected 1 argument, got %1", info.Length());
37
35
  return env.Null();
38
36
  }
39
- if (!info[0].IsString()) {
40
- ThrowError<Napi::TypeError>(env, "Unexpected value for str, expected string");
41
- return env.Null();
37
+
38
+ char str[64];
39
+ {
40
+ napi_status status = napi_get_value_string_utf8(env, info[0], str, RG_SIZE(str), nullptr);
41
+
42
+ if (RG_UNLIKELY(status != napi_ok)) {
43
+ if (status == napi_string_expected) {
44
+ ThrowError<Napi::TypeError>(env, "Unexpected value for str, expected string");
45
+ } else {
46
+ ThrowError<Napi::TypeError>(env, "Failed to read JS string");
47
+ }
48
+ return env.Null();
49
+ }
42
50
  }
43
- #endif
44
51
 
45
- std::string name = info[0].As<Napi::String>();
46
- int value = atoi(name.c_str());
52
+ int value = atoi(str);
47
53
 
48
54
  return Napi::Number::New(env, value);
49
55
  }
@@ -26,14 +26,15 @@ let sum = 0;
26
26
  main();
27
27
 
28
28
  function main() {
29
- if (process.argv.length < 3)
30
- throw new Error('Missing number of iterations');
31
-
32
- let iterations = parseInt(process.argv[2], 10);
33
- if (Number.isNaN(iterations))
34
- throw new Error('Not a valid number');
35
- if (iterations < 1)
36
- throw new Error('Value must be positive');
29
+ let iterations = 20000000;
30
+
31
+ if (process.argv.length >= 3) {
32
+ iterations = parseInt(process.argv[2], 10);
33
+ if (Number.isNaN(iterations))
34
+ throw new Error('Not a valid number');
35
+ if (iterations < 1)
36
+ throw new Error('Value must be positive');
37
+ }
37
38
  console.log('Iterations:', iterations);
38
39
 
39
40
  let start = performance.now();
@@ -16,7 +16,6 @@
16
16
  const ref = require('ref-napi');
17
17
  const ffi = require('ffi-napi');
18
18
  const struct = require('ref-struct-di')(ref);
19
- const path = require('path');
20
19
 
21
20
  const strings = [
22
21
  '424242',
@@ -29,14 +28,15 @@ let sum = 0;
29
28
  main();
30
29
 
31
30
  async function main() {
32
- if (process.argv.length < 3)
33
- throw new Error('Missing number of iterations');
34
-
35
- let iterations = parseInt(process.argv[2], 10);
36
- if (Number.isNaN(iterations))
37
- throw new Error('Not a valid number');
38
- if (iterations < 1)
39
- throw new Error('Value must be positive');
31
+ let iterations = 20000000;
32
+
33
+ if (process.argv.length >= 3) {
34
+ iterations = parseInt(process.argv[2], 10);
35
+ if (Number.isNaN(iterations))
36
+ throw new Error('Not a valid number');
37
+ if (iterations < 1)
38
+ throw new Error('Value must be positive');
39
+ }
40
40
  console.log('Iterations:', iterations);
41
41
 
42
42
  const lib = ffi.Library(process.platform == 'win32' ? 'msvcrt.dll' : null, {
@@ -18,15 +18,12 @@ namespace RG {
18
18
 
19
19
  int Main(int argc, char **argv)
20
20
  {
21
- if (argc < 2) {
22
- LogError("Missing number of iterations");
23
- LogInfo("Usage: raylib_cc <iterations>");
24
- return 1;
25
- }
21
+ int iterations = 100;
26
22
 
27
- int iterations = 0;
28
- if (!ParseInt(argv[1], &iterations))
29
- return 1;
23
+ if (argc >= 2) {
24
+ if (!ParseInt(argv[1], &iterations))
25
+ return 1;
26
+ }
30
27
  LogInfo("Iterations: %1", iterations);
31
28
 
32
29
  // We need to call InitWindow before using anything else (such as fonts)
@@ -34,13 +31,13 @@ int Main(int argc, char **argv)
34
31
  SetWindowState(FLAG_WINDOW_HIDDEN);
35
32
  InitWindow(640, 480, "Raylib Test");
36
33
 
37
- Image img = GenImageColor(800, 600, (Color){ .r = 0, .g = 0, .b = 0, .a = 255 });
34
+ Image img = GenImageColor(800, 600, Color { .r = 0, .g = 0, .b = 0, .a = 255 });
38
35
  Font font = GetFontDefault();
39
36
 
40
37
  int64_t start = GetMonotonicTime();
41
38
 
42
39
  for (int i = 0; i < iterations; i++) {
43
- ImageClearBackground(&img, (Color){ .r = 0, .g = 0, .b = 0, .a = 255 });
40
+ ImageClearBackground(&img, Color { .r = 0, .g = 0, .b = 0, .a = 255 });
44
41
 
45
42
  for (int j = 0; j < 3600; j++) {
46
43
  const char *text = "Hello World!";
@@ -13,7 +13,7 @@
13
13
  // You should have received a copy of the GNU Affero General Public License
14
14
  // along with this program. If not, see https://www.gnu.org/licenses/.
15
15
 
16
- const koffi = require('../build/koffi.node');
16
+ const koffi = require('./build/koffi.node');
17
17
  const path = require('path');
18
18
 
19
19
  const Color = koffi.struct('Color', {
@@ -71,17 +71,18 @@ const Font = koffi.struct('Font', {
71
71
  main();
72
72
 
73
73
  function main() {
74
- if (process.argv.length < 3)
75
- throw new Error('Missing number of iterations');
76
-
77
- let iterations = parseInt(process.argv[2], 10);
78
- if (Number.isNaN(iterations))
79
- throw new Error('Not a valid number');
80
- if (iterations < 1)
81
- throw new Error('Value must be positive');
74
+ let iterations = 100;
75
+
76
+ if (process.argv.length >= 3) {
77
+ iterations = parseInt(process.argv[2], 10);
78
+ if (Number.isNaN(iterations))
79
+ throw new Error('Not a valid number');
80
+ if (iterations < 1)
81
+ throw new Error('Value must be positive');
82
+ }
82
83
  console.log('Iterations:', iterations);
83
84
 
84
- let lib_filename = path.dirname(__filename) + '/../test/build/raylib' + koffi.extension;
85
+ let lib_filename = path.dirname(__filename) + '/build/raylib' + koffi.extension;
85
86
  let lib = koffi.load(lib_filename);
86
87
 
87
88
  const InitWindow = lib.cdecl('InitWindow', 'void', ['int', 'int', 'string']);
@@ -16,7 +16,7 @@
16
16
  const ref = require('ref-napi');
17
17
  const ffi = require('ffi-napi');
18
18
  const struct = require('ref-struct-di')(ref);
19
- const koffi = require('../build/koffi.node');
19
+ const koffi = require('./build/koffi.node');
20
20
  const path = require('path');
21
21
 
22
22
  const Color = struct({
@@ -87,17 +87,18 @@ const Font = struct({
87
87
  main();
88
88
 
89
89
  function main() {
90
- if (process.argv.length < 3)
91
- throw new Error('Missing number of iterations');
92
-
93
- let iterations = parseInt(process.argv[2], 10);
94
- if (Number.isNaN(iterations))
95
- throw new Error('Not a valid number');
96
- if (iterations < 1)
97
- throw new Error('Value must be positive');
90
+ let iterations = 100;
91
+
92
+ if (process.argv.length >= 3) {
93
+ iterations = parseInt(process.argv[2], 10);
94
+ if (Number.isNaN(iterations))
95
+ throw new Error('Not a valid number');
96
+ if (iterations < 1)
97
+ throw new Error('Value must be positive');
98
+ }
98
99
  console.log('Iterations:', iterations);
99
100
 
100
- let lib_filename = path.dirname(__filename) + '/../test/build/raylib' + koffi.extension;
101
+ let lib_filename = path.dirname(__filename) + '/build/raylib' + koffi.extension;
101
102
 
102
103
  const r = ffi.Library(lib_filename, {
103
104
  InitWindow: ['void', ['int', 'int', 'string']],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "0.9.26",
3
+ "version": "0.9.29",
4
4
  "description": "Fast and simple FFI (foreign function interface) for Node.js",
5
5
  "keywords": [
6
6
  "foreign",
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "license": "AGPL-3.0",
25
25
  "dependencies": {
26
- "cnoke": "^0.9.26"
26
+ "cnoke": "^1.0.1"
27
27
  },
28
28
  "devDependencies": {
29
29
  "chalk": "^4.1.2",
package/src/call_arm32.cc CHANGED
@@ -233,10 +233,10 @@ static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
233
233
  // Return through registers unless it's too big
234
234
  if (RG_UNLIKELY(!call.AllocStack(func->args_size, 16, &args_ptr)))
235
235
  return env.Null();
236
- if (RG_UNLIKELY(!call.AllocStack(4 * 4, 8, &gpr_ptr)))
237
- return env.Null();
238
236
  if (RG_UNLIKELY(!call.AllocStack(8 * 8, 8, &vec_ptr)))
239
237
  return env.Null();
238
+ if (RG_UNLIKELY(!call.AllocStack(4 * 4, 8, &gpr_ptr)))
239
+ return env.Null();
240
240
  if (func->ret.use_memory) {
241
241
  if (RG_UNLIKELY(!call.AllocHeap(func->ret.type->size, 16, &return_ptr)))
242
242
  return env.Null();
@@ -56,22 +56,22 @@
56
56
 
57
57
  // Prepare general purpose argument registers from array passed by caller.
58
58
  .macro forward_int
59
- ldr r3, [r1, 76]
60
- ldr r2, [r1, 72]
61
- ldr r0, [r1, 64]
62
- ldr r1, [r1, 68]
59
+ ldr r3, [r1, 12]
60
+ ldr r2, [r1, 8]
61
+ ldr r0, [r1, 0]
62
+ ldr r1, [r1, 4]
63
63
  .endm
64
64
 
65
65
  // Prepare vector argument registers from array passed by caller.
66
66
  .macro forward_vec
67
- vldr d7, [r1, 56]
68
- vldr d6, [r1, 48]
69
- vldr d5, [r1, 40]
70
- vldr d4, [r1, 32]
71
- vldr d3, [r1, 24]
72
- vldr d2, [r1, 16]
73
- vldr d1, [r1, 8]
74
- vldr d0, [r1, 0]
67
+ vldr d7, [r1, 72]
68
+ vldr d6, [r1, 64]
69
+ vldr d5, [r1, 56]
70
+ vldr d4, [r1, 48]
71
+ vldr d3, [r1, 40]
72
+ vldr d2, [r1, 32]
73
+ vldr d1, [r1, 24]
74
+ vldr d0, [r1, 16]
75
75
  .endm
76
76
 
77
77
  ForwardCallGG:
package/src/ffi.cc CHANGED
@@ -75,20 +75,20 @@ static const TypeInfo *ResolveType(const InstanceData *instance, Napi::Value val
75
75
  }
76
76
  }
77
77
 
78
- static Napi::Value CreateStructType(const Napi::CallbackInfo &info)
78
+ static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
79
79
  {
80
80
  Napi::Env env = info.Env();
81
81
  InstanceData *instance = env.GetInstanceData<InstanceData>();
82
82
 
83
- if (info.Length() < 2) {
84
- ThrowError<Napi::TypeError>(env, "Expected 2 arguments, got %1", info.Length());
83
+ if (info.Length() < 1) {
84
+ ThrowError<Napi::TypeError>(env, "Expected 1 or 2 arguments, got %1", info.Length());
85
85
  return env.Null();
86
86
  }
87
- if (!info[0].IsString()) {
87
+ if (info.Length() > 1 && !info[0].IsString()) {
88
88
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for name, expected string", GetValueType(instance, info[0]));
89
89
  return env.Null();
90
90
  }
91
- if (!IsObject(info[1])) {
91
+ if (!IsObject(info[info.Length() > 1])) {
92
92
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for members, expected object", GetValueType(instance, info[1]));
93
93
  return env.Null();
94
94
  }
@@ -96,8 +96,8 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info)
96
96
  TypeInfo *type = instance->types.AppendDefault();
97
97
  RG_DEFER_N(err_guard) { instance->types.RemoveLast(1); };
98
98
 
99
- std::string name = info[0].As<Napi::String>();
100
- Napi::Object obj = info[1].As<Napi::Object>();
99
+ std::string name = info.Length() > 1 ? info[0].As<Napi::String>() : std::string("<anonymous>");
100
+ Napi::Object obj = info[info.Length() > 1].As<Napi::Object>();
101
101
  Napi::Array keys = obj.GetPropertyNames();
102
102
 
103
103
  type->name = DuplicateString(name.c_str(), &instance->str_alloc).ptr;
@@ -116,8 +116,10 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info)
116
116
  if (!member.type)
117
117
  return env.Null();
118
118
 
119
- type->size = AlignLen(type->size, member.type->align) + member.type->size;
120
- type->align = std::max(type->align, member.type->align);
119
+ member.align = pad ? member.type->align : 1;
120
+
121
+ type->size = (int16_t)(AlignLen(type->size, member.align) + member.type->size);
122
+ type->align = std::max(type->align, member.align);
121
123
 
122
124
  type->members.Append(member);
123
125
  }
@@ -125,7 +127,7 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info)
125
127
  type->size = (int16_t)AlignLen(type->size, type->align);
126
128
 
127
129
  // If the insert succeeds, we cannot fail anymore
128
- if (!instance->types_map.TrySet(type).second) {
130
+ if (info.Length() > 1 && !instance->types_map.TrySet(type).second) {
129
131
  ThrowError<Napi::Error>(env, "Duplicate type name '%1'", type->name);
130
132
  return env.Null();
131
133
  }
@@ -137,6 +139,16 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info)
137
139
  return external;
138
140
  }
139
141
 
142
+ static Napi::Value CreatePaddedStructType(const Napi::CallbackInfo &info)
143
+ {
144
+ return CreateStructType(info, true);
145
+ }
146
+
147
+ static Napi::Value CreatePackedStructType(const Napi::CallbackInfo &info)
148
+ {
149
+ return CreateStructType(info, false);
150
+ }
151
+
140
152
  static Napi::Value CreateHandleType(const Napi::CallbackInfo &info)
141
153
  {
142
154
  Napi::Env env = info.Env();
@@ -564,7 +576,8 @@ InstanceData::InstanceData()
564
576
  template <typename Func>
565
577
  static void SetExports(Napi::Env env, Func func)
566
578
  {
567
- func("struct", Napi::Function::New(env, CreateStructType));
579
+ func("struct", Napi::Function::New(env, CreatePaddedStructType));
580
+ func("pack", Napi::Function::New(env, CreatePackedStructType));
568
581
  func("handle", Napi::Function::New(env, CreateHandleType));
569
582
  func("pointer", Napi::Function::New(env, CreatePointerType));
570
583
  func("load", Napi::Function::New(env, LoadSharedLibrary));
package/src/ffi.hh CHANGED
@@ -89,6 +89,7 @@ struct TypeInfo {
89
89
  struct RecordMember {
90
90
  const char *name;
91
91
  const TypeInfo *type;
92
+ int16_t align;
92
93
  };
93
94
 
94
95
  struct LibraryHolder {
package/src/util.cc CHANGED
@@ -82,20 +82,31 @@ const char *CallData::PushString(const Napi::Value &value)
82
82
  RG_ASSERT(value.IsString());
83
83
 
84
84
  Napi::Env env = value.Env();
85
- napi_status status;
86
85
 
86
+ Span<char> buf;
87
87
  size_t len = 0;
88
- status = napi_get_value_string_utf8(env, value, nullptr, 0, &len);
88
+ napi_status status;
89
+
90
+ buf.ptr = (char *)heap_mem->ptr;
91
+ buf.len = std::max((Size)0, heap_mem->len - Kibibytes(32));
92
+
93
+ status = napi_get_value_string_utf8(env, value, buf.ptr, (size_t)buf.len, &len);
89
94
  RG_ASSERT(status == napi_ok);
90
95
 
91
- Span<char> buf;
92
- buf.len = (Size)len + 1;
93
- if (RG_UNLIKELY(!AllocHeap(buf.len, 1, &buf.ptr)))
94
- return nullptr;
96
+ len++;
97
+
98
+ if (RG_LIKELY(len < (size_t)buf.len)) {
99
+ heap_mem->ptr += (Size)len;
100
+ heap_mem->len -= (Size)len;
101
+ } else {
102
+ status = napi_get_value_string_utf8(env, value, nullptr, 0, &len);
103
+ RG_ASSERT(status == napi_ok);
104
+
105
+ buf.ptr = (char *)Allocator::Allocate(&big_alloc, (Size)len);
106
+ buf.len = (Size)len;
95
107
 
96
- if (RG_UNLIKELY(napi_get_value_string_utf8(env, value, buf.ptr, (size_t)buf.len, &len) != napi_ok)) {
97
- ThrowError<Napi::Error>(env, "Failed to convert string to UTF-8");
98
- return nullptr;
108
+ status = napi_get_value_string_utf8(env, value, buf.ptr, (size_t)buf.len, &len);
109
+ RG_ASSERT(status == napi_ok);
99
110
  }
100
111
 
101
112
  return buf.ptr;
@@ -117,7 +128,7 @@ bool CallData::PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t
117
128
  return false;
118
129
  }
119
130
 
120
- dest = AlignUp(dest, member.type->align);
131
+ dest = AlignUp(dest, member.align);
121
132
 
122
133
  switch (member.type->primitive) {
123
134
  case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
@@ -214,7 +225,7 @@ void PopObject(Napi::Object obj, const uint8_t *ptr, const TypeInfo *type)
214
225
  RG_ASSERT(type->primitive == PrimitiveKind::Record);
215
226
 
216
227
  for (const RecordMember &member: type->members) {
217
- ptr = AlignUp(ptr, member.type->align);
228
+ ptr = AlignUp(ptr, member.align);
218
229
 
219
230
  switch (member.type->primitive) {
220
231
  case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;