koffi 0.9.24 → 0.9.27

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
@@ -7,6 +7,8 @@
7
7
  - [Get started](#get-started)
8
8
  - [Tests](#tests)
9
9
  - [Benchmarks](#benchmarks)
10
+ * [atoi results](#atoi-results)
11
+ * [Raylib results](#raylib-results)
10
12
 
11
13
  # Introduction
12
14
 
@@ -253,33 +255,90 @@ node test.js info debian_x64
253
255
 
254
256
  # Benchmarks
255
257
 
256
- A basic benchmark based around Raylib is available, in three implementations: with Koffi, with node-ffi and with C code using Raylib (as a shared library).
258
+ At this stage, two benchmarks are implemented:
259
+ * The first one is based around repeated calls to atoi, and has four implementations: one in C++, one calling atoi through an NAPI module, one using Koffi, and one with node-ffi-napi. This is a simple function, thus the JS and FFI overhead is clearly visible.
260
+ * The second one is based around Raylib, and will execute much more heavier functions repeatdly. Also in three versions: Koffi, node-ffi-napi and C code.
257
261
 
258
- In order to run it, go to `koffi/benchmark` and run `build.sh` before doing anything else.
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.
259
263
 
260
- Once this is done, you can execute each implementation with `time`, e.g. `time ./raylib_c 2000`.
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`.
261
265
 
262
- Here are some results from 2022-04-15 on my machine (AMD Ryzen™ 7 5800H 16G):
266
+ ## atoi results
267
+
268
+ Here are some results from 2022-04-24 on my Linux machine (AMD® Ryzen™ 7 5800H 16G):
269
+
270
+ ```sh
271
+ $ build/atoi_cc
272
+ Iterations: 20000000
273
+ Time: 0.24s
274
+
275
+ $ ./atoi_napi.js
276
+ Iterations: 20000000
277
+ Time: 1.10s
278
+
279
+ $ ./atoi_koffi.js
280
+ Iterations: 20000000
281
+ Time: 2.34s
282
+
283
+ # Note: the Node-FFI version does a few setTimeout calls to force the GC to run (around 20
284
+ # for the example below), without which Node will consume all memory because the GC never appears
285
+ # to run, or not enough. It's not ideal but on the other hand it counts as another limitation
286
+ # to Node-FFI performance.
287
+ $ ./atoi_node_ffi.js
288
+ Iterations: 20000000
289
+ Time: 640.49s
290
+ ```
291
+
292
+ And on my Windows machine (Intel® Core™ i5-4460 16G):
293
+
294
+ ```sh
295
+ $ build\atoi_cc.exe
296
+ Iterations: 20000000
297
+ Time: 0.66s
298
+
299
+ $ node atoi_napi.js
300
+ Iterations: 20000000
301
+ Time: 3.23s
302
+
303
+ $ node atoi_koffi.js
304
+ Iterations: 20000000
305
+ Time: 4.81s
306
+
307
+ $ node atoi_node_ffi.js
308
+ Iterations: 20000000
309
+ Time: 491.99s
310
+ ```
311
+
312
+ ## Raylib results
313
+
314
+ Here are some results from 2022-04-24 on my Linux machine (AMD® Ryzen™ 7 5800H 16G):
263
315
 
264
316
  ```sh
265
- $ time ./raylib_c 200
266
- Iterations: 200
317
+ $ build/raylib_cc
318
+ Iterations: 100
319
+ Time: 4.14s
320
+
321
+ $ ./raylib_koffi.js
322
+ Iterations: 100
323
+ Time: 6.25s
267
324
 
268
- real 0m8,871s
269
- user 0m8,792s
270
- sys 0m0,016s
325
+ $ ./raylib_node_ffi.js
326
+ Iterations: 100
327
+ Time: 27.13s
328
+ ```
271
329
 
272
- $ time ./raylib_koffi.js 200
273
- Iterations: 200
330
+ And on my Windows machine (Intel® Core™ i5-4460 16G):
274
331
 
275
- real 0m13,011s
276
- user 0m12,923s
277
- sys 0m0,032s
332
+ ```sh
333
+ $ build\raylib_cc.exe
334
+ Iterations: 100
335
+ Time: 10.53s
278
336
 
279
- $ time ./raylib_node_ffi.js 200
280
- Iterations: 200
337
+ $ node raylib_koffi.js
338
+ Iterations: 100
339
+ Time: 14.60s
281
340
 
282
- real 1m41,523s
283
- user 1m56,623s
284
- sys 0m3,731s
341
+ $ node raylib_node_ffi.js
342
+ Iterations: 100
343
+ Time: 44.97s
285
344
  ```
@@ -0,0 +1,53 @@
1
+ # This program is free software: you can redistribute it and/or modify
2
+ # it under the terms of the GNU Affero General Public License as published by
3
+ # the Free Software Foundation, either version 3 of the License, or
4
+ # (at your option) any later version.
5
+ #
6
+ # This program is distributed in the hope that it will be useful,
7
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
8
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
+ # GNU Affero General Public License for more details.
10
+ #
11
+ # You should have received a copy of the GNU Affero General Public License
12
+ # along with this program. If not, see https://www.gnu.org/licenses/.
13
+
14
+ cmake_minimum_required(VERSION 3.12)
15
+ project(koffi C CXX ASM)
16
+
17
+ find_package(CNoke)
18
+
19
+ set(CMAKE_CXX_STANDARD 17)
20
+
21
+ if(NOT TARGET koffi)
22
+ add_subdirectory(.. koffi)
23
+ endif()
24
+ add_subdirectory(../test test)
25
+
26
+ # ---- atoi ----
27
+
28
+ add_executable(atoi_cc atoi_cc.cc ../vendor/libcc/libcc.cc)
29
+ target_include_directories(atoi_cc PRIVATE ..)
30
+
31
+ if(WIN32)
32
+ target_compile_definitions(atoi_cc PRIVATE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE)
33
+ target_link_libraries(atoi_cc PRIVATE ws2_32)
34
+ endif()
35
+
36
+ add_node_addon(NAME atoi_napi SOURCES atoi_napi.cc ../vendor/libcc/libcc.cc)
37
+ target_include_directories(atoi_napi PRIVATE .. ../vendor/node-addon-api)
38
+
39
+ if(WIN32)
40
+ target_compile_definitions(atoi_napi PRIVATE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE)
41
+ target_link_libraries(atoi_napi PRIVATE ws2_32)
42
+ endif()
43
+
44
+ # ---- Raylib ----
45
+
46
+ add_executable(raylib_cc atoi_cc.cc ../vendor/libcc/libcc.cc)
47
+ target_include_directories(raylib_cc PRIVATE ..)
48
+ add_dependencies(raylib_cc raylib)
49
+
50
+ if(WIN32)
51
+ target_compile_definitions(raylib_cc PRIVATE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE)
52
+ target_link_libraries(raylib_cc PRIVATE ws2_32)
53
+ endif()
@@ -0,0 +1,59 @@
1
+ // This program is free software: you can redistribute it and/or modify
2
+ // it under the terms of the GNU Affero General Public License as published by
3
+ // the Free Software Foundation, either version 3 of the License, or
4
+ // (at your option) any later version.
5
+ //
6
+ // This program is distributed in the hope that it will be useful,
7
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
8
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
+ // GNU Affero General Public License for more details.
10
+ //
11
+ // You should have received a copy of the GNU Affero General Public License
12
+ // along with this program. If not, see https://www.gnu.org/licenses/.
13
+
14
+ #include "../vendor/libcc/libcc.hh"
15
+
16
+ namespace RG {
17
+
18
+ static const char * strings[] = {
19
+ "424242",
20
+ "foobar",
21
+ "123456789",
22
+ };
23
+
24
+ volatile uint64_t sum = 0;
25
+
26
+ int Main(int argc, char **argv)
27
+ {
28
+ int iterations = 20000000;
29
+
30
+ if (argc >= 2) {
31
+ if (!ParseInt(argv[1], &iterations))
32
+ return 1;
33
+ }
34
+ LogInfo("Iterations: %1", iterations);
35
+
36
+ int64_t start = GetMonotonicTime();
37
+
38
+ for (int i = 0; i < iterations; i++) {
39
+ sum += (uint64_t)atoi(strings[i % RG_LEN(strings)]);
40
+ }
41
+
42
+ // Help prevent optimisation of loop
43
+ {
44
+ PushLogFilter([](LogLevel, const char *, const char *, FunctionRef<LogFunc>) {});
45
+ RG_DEFER { PopLogFilter(); };
46
+
47
+ LogInfo("Sum = %1", sum);
48
+ }
49
+
50
+ int64_t time = GetMonotonicTime() - start;
51
+ LogInfo("Time: %1s", FmtDouble((double)time / 1000.0, 2));
52
+
53
+ return 0;
54
+ }
55
+
56
+ }
57
+
58
+ // C++ namespaces are stupid
59
+ int main(int argc, char **argv) { return RG::Main(argc, argv); }
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+
3
+ // This program is free software: you can redistribute it and/or modify
4
+ // it under the terms of the GNU Affero General Public License as published by
5
+ // the Free Software Foundation, either version 3 of the License, or
6
+ // (at your option) any later version.
7
+ //
8
+ // This program is distributed in the hope that it will be useful,
9
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ // GNU Affero General Public License for more details.
12
+ //
13
+ // You should have received a copy of the GNU Affero General Public License
14
+ // along with this program. If not, see https://www.gnu.org/licenses/.
15
+
16
+ const koffi = require('./build/koffi.node');
17
+
18
+ const strings = [
19
+ '424242',
20
+ 'foobar',
21
+ '123456789'
22
+ ];
23
+
24
+ let sum = 0;
25
+
26
+ main();
27
+
28
+ function main() {
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
+ }
38
+ console.log('Iterations:', iterations);
39
+
40
+ let lib = koffi.load(process.platform == 'win32' ? 'msvcrt.dll' : null);
41
+
42
+ const atoi = lib.cdecl('atoi', 'int', ['string']);
43
+
44
+ let start = performance.now();
45
+
46
+ for (let i = 0; i < iterations; i++) {
47
+ sum += atoi(strings[i % strings.length]);
48
+ }
49
+
50
+ let time = performance.now()- start;
51
+ console.log('Time:', (time / 1000.0).toFixed(2) + 's');
52
+ }
@@ -0,0 +1,68 @@
1
+ // This program is free software: you can redistribute it and/or modify
2
+ // it under the terms of the GNU Affero General Public License as published by
3
+ // the Free Software Foundation, either version 3 of the License, or
4
+ // (at your option) any later version.
5
+ //
6
+ // This program is distributed in the hope that it will be useful,
7
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
8
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
+ // GNU Affero General Public License for more details.
10
+ //
11
+ // You should have received a copy of the GNU Affero General Public License
12
+ // along with this program. If not, see https://www.gnu.org/licenses/.
13
+
14
+ #include "vendor/libcc/libcc.hh"
15
+ #include <napi.h>
16
+
17
+ namespace RG {
18
+
19
+ template <typename T, typename... Args>
20
+ void ThrowError(Napi::Env env, const char *msg, Args... args)
21
+ {
22
+ char buf[1024];
23
+ Fmt(buf, msg, args...);
24
+
25
+ auto err = T::New(env, buf);
26
+ err.ThrowAsJavaScriptException();
27
+ }
28
+
29
+ static Napi::Value RunAtoi(const Napi::CallbackInfo &info)
30
+ {
31
+ Napi::Env env = info.Env();
32
+
33
+ if (RG_UNLIKELY(info.Length() < 1)) {
34
+ ThrowError<Napi::TypeError>(env, "Expected 1 argument, got %1", info.Length());
35
+ return env.Null();
36
+ }
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
+ }
50
+ }
51
+
52
+ int value = atoi(str);
53
+
54
+ return Napi::Number::New(env, value);
55
+ }
56
+
57
+ }
58
+
59
+ static Napi::Object InitModule(Napi::Env env, Napi::Object exports)
60
+ {
61
+ using namespace RG;
62
+
63
+ exports.Set("atoi", Napi::Function::New(env, RunAtoi));
64
+
65
+ return exports;
66
+ }
67
+
68
+ NODE_API_MODULE(koffi, InitModule);
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+
3
+ // This program is free software: you can redistribute it and/or modify
4
+ // it under the terms of the GNU Affero General Public License as published by
5
+ // the Free Software Foundation, either version 3 of the License, or
6
+ // (at your option) any later version.
7
+ //
8
+ // This program is distributed in the hope that it will be useful,
9
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ // GNU Affero General Public License for more details.
12
+ //
13
+ // You should have received a copy of the GNU Affero General Public License
14
+ // along with this program. If not, see https://www.gnu.org/licenses/.
15
+
16
+ const atoi = require('./build/atoi_napi.node');
17
+
18
+ const strings = [
19
+ '424242',
20
+ 'foobar',
21
+ '123456789'
22
+ ];
23
+
24
+ let sum = 0;
25
+
26
+ main();
27
+
28
+ function main() {
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
+ }
38
+ console.log('Iterations:', iterations);
39
+
40
+ let start = performance.now();
41
+
42
+ for (let i = 0; i < iterations; i++) {
43
+ sum += atoi.atoi(strings[i % strings.length]);
44
+ }
45
+
46
+ let time = performance.now()- start;
47
+ console.log('Time:', (time / 1000.0).toFixed(2) + 's');
48
+ }
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+
3
+ // This program is free software: you can redistribute it and/or modify
4
+ // it under the terms of the GNU Affero General Public License as published by
5
+ // the Free Software Foundation, either version 3 of the License, or
6
+ // (at your option) any later version.
7
+ //
8
+ // This program is distributed in the hope that it will be useful,
9
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ // GNU Affero General Public License for more details.
12
+ //
13
+ // You should have received a copy of the GNU Affero General Public License
14
+ // along with this program. If not, see https://www.gnu.org/licenses/.
15
+
16
+ const ref = require('ref-napi');
17
+ const ffi = require('ffi-napi');
18
+ const struct = require('ref-struct-di')(ref);
19
+
20
+ const strings = [
21
+ '424242',
22
+ 'foobar',
23
+ '123456789'
24
+ ];
25
+
26
+ let sum = 0;
27
+
28
+ main();
29
+
30
+ async function main() {
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
+ console.log('Iterations:', iterations);
41
+
42
+ const lib = ffi.Library(process.platform == 'win32' ? 'msvcrt.dll' : null, {
43
+ atoi: ['int', ['string']]
44
+ });
45
+
46
+ let start = performance.now();
47
+
48
+ for (let i = 0; i < iterations; i++) {
49
+ if (i % 1000000 == 0)
50
+ await new Promise(resolve => setTimeout(resolve, 0));
51
+
52
+ sum += lib.atoi(strings[i % strings.length]);
53
+ }
54
+
55
+ let time = performance.now()- start;
56
+ console.log('Time:', (time / 1000.0).toFixed(2) + 's');
57
+ }
@@ -11,51 +11,31 @@
11
11
  // You should have received a copy of the GNU Affero General Public License
12
12
  // along with this program. If not, see https://www.gnu.org/licenses/.
13
13
 
14
- #include <math.h>
15
- #include <stdio.h>
16
- #include <stdlib.h>
17
- #include <limits.h>
14
+ #include "../vendor/libcc/libcc.hh"
18
15
  #include "../vendor/raylib/src/raylib.h"
19
16
 
20
- #define STRINGIFY(Value) # Value
17
+ namespace RG {
21
18
 
22
- static int ParseInt(const char *str)
19
+ int Main(int argc, char **argv)
23
20
  {
24
- char *end;
25
- long long value = strtoll(str, &end, 10);
21
+ int iterations = 100;
26
22
 
27
- if (end == str || *end) {
28
- fprintf(stderr, "Not a valid integer number\n");
29
- return -1;
30
- }
31
- if (value < 1 || value == LLONG_MAX) {
32
- fprintf(stderr, "Value must be between 1 and " STRINGIFY(LLONG_MAX) "\n");
33
- return -1;
34
- }
35
-
36
- return (int)value;
37
- }
38
-
39
- int main(int argc, char **argv)
40
- {
41
- if (argc < 2) {
42
- fprintf(stderr, "Missing number of iterations\n");
43
- return 1;
23
+ if (argc >= 2) {
24
+ if (!ParseInt(argv[1], &iterations))
25
+ return 1;
44
26
  }
45
-
46
- int iterations = ParseInt(argv[1]);
47
- if (iterations < 0)
48
- return 1;
49
- printf("Iterations: %d\n", iterations);
27
+ LogInfo("Iterations: %1", iterations);
50
28
 
51
29
  // We need to call InitWindow before using anything else (such as fonts)
52
- SetTraceLogLevel(4); // Warnings
53
- SetWindowState(0x80); // Hidden
30
+ SetTraceLogLevel(LOG_WARNING);
31
+ SetWindowState(FLAG_WINDOW_HIDDEN);
54
32
  InitWindow(640, 480, "Raylib Test");
55
33
 
56
34
  Image img = GenImageColor(800, 600, (Color){ .r = 0, .g = 0, .b = 0, .a = 255 });
57
35
  Font font = GetFontDefault();
58
36
 
37
+ int64_t start = GetMonotonicTime();
38
+
59
39
  for (int i = 0; i < iterations; i++) {
60
40
  ImageClearBackground(&img, (Color){ .r = 0, .g = 0, .b = 0, .a = 255 });
61
41
 
@@ -65,19 +45,27 @@ int main(int argc, char **argv)
65
45
 
66
46
  double angle = (j * 7) * PI / 180;
67
47
  Color color = {
68
- .r = 127.5 + 127.5 * sin(angle),
69
- .g = 127.5 + 127.5 * sin(angle + PI / 2),
70
- .b = 127.5 + 127.5 * sin(angle + PI),
48
+ .r = (unsigned char)(127.5 + 127.5 * sin(angle)),
49
+ .g = (unsigned char)(127.5 + 127.5 * sin(angle + PI / 2)),
50
+ .b = (unsigned char)(127.5 + 127.5 * sin(angle + PI)),
71
51
  .a = 255
72
52
  };
73
53
  Vector2 pos = {
74
- .x = (img.width / 2 - text_width / 2) + j * 0.1 * cos(angle - PI / 2),
75
- .y = (img.height / 2 - 16) + j * 0.1 * sin(angle - PI / 2)
54
+ .x = (float)((img.width / 2 - text_width / 2) + j * 0.1 * cos(angle - PI / 2)),
55
+ .y = (float)((img.height / 2 - 16) + j * 0.1 * sin(angle - PI / 2))
76
56
  };
77
57
 
78
58
  ImageDrawTextEx(&img, font, text, pos, 10, 1, color);
79
59
  }
80
60
  }
81
61
 
62
+ int64_t time = GetMonotonicTime() - start;
63
+ LogInfo("Time: %1s", FmtDouble((double)time / 1000.0, 2));
64
+
82
65
  return 0;
83
66
  }
67
+
68
+ }
69
+
70
+ // C++ namespaces are stupid
71
+ int main(int argc, char **argv) { return RG::Main(argc, argv); }
@@ -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) + '/test/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']);
@@ -102,6 +103,8 @@ function main() {
102
103
  let img = GenImageColor(800, 600, { r: 0, g: 0, b: 0, a: 255 });
103
104
  let font = GetFontDefault();
104
105
 
106
+ let start = performance.now();
107
+
105
108
  for (let i = 0; i < iterations; i++) {
106
109
  ImageClearBackground(img, { r: 0, g: 0, b: 0, a: 255 });
107
110
 
@@ -124,4 +127,7 @@ function main() {
124
127
  ImageDrawTextEx(img, font, text, pos, 10, 1, color);
125
128
  }
126
129
  }
130
+
131
+ let time = performance.now()- start;
132
+ console.log('Time:', (time / 1000.0).toFixed(2) + 's');
127
133
  }
@@ -16,6 +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
20
  const path = require('path');
20
21
 
21
22
  const Color = struct({
@@ -86,8 +87,16 @@ const Font = struct({
86
87
  main();
87
88
 
88
89
  function main() {
89
- if (process.argv.length < 3)
90
- throw new Error('Missing number of iterations');
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
+ }
99
+ console.log('Iterations:', iterations);
91
100
 
92
101
  let iterations = parseInt(process.argv[2], 10);
93
102
  if (Number.isNaN(iterations))
@@ -96,7 +105,7 @@ function main() {
96
105
  throw new Error('Value must be positive');
97
106
  console.log('Iterations:', iterations);
98
107
 
99
- let lib_filename = path.dirname(__filename) + '/../test/build/raylib' + koffi.extension;
108
+ let lib_filename = path.dirname(__filename) + '/test/build/raylib' + koffi.extension;
100
109
 
101
110
  const r = ffi.Library(lib_filename, {
102
111
  InitWindow: ['void', ['int', 'int', 'string']],
@@ -119,6 +128,8 @@ function main() {
119
128
  let imgp = img.ref();
120
129
  let font = r.GetFontDefault();
121
130
 
131
+ let start = performance.now();
132
+
122
133
  for (let i = 0; i < iterations; i++) {
123
134
  r.ImageClearBackground(imgp, new Color({ r: 0, g: 0, b: 0, a: 255 }));
124
135
 
@@ -141,4 +152,7 @@ function main() {
141
152
  r.ImageDrawTextEx(imgp, font, text, pos, 10, 1, color);
142
153
  }
143
154
  }
155
+
156
+ let time = performance.now()- start;
157
+ console.log('Time:', (time / 1000.0).toFixed(2) + 's');
144
158
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "0.9.24",
3
+ "version": "0.9.27",
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",
@@ -35,11 +35,13 @@
35
35
  },
36
36
  "files": [
37
37
  "src",
38
- "benchmark",
39
- "test/files",
38
+ "benchmark/CMakeLists.txt",
39
+ "benchmark/atoi_*",
40
+ "benchmark/raylib_*",
41
+ "test/CMakeLists.txt",
42
+ "test/test.js",
40
43
  "test/registry",
41
44
  "test/tests",
42
- "test/test.js",
43
45
  "vendor",
44
46
  "LICENSE.txt",
45
47
  "README.md",
package/src/ffi.cc CHANGED
@@ -116,7 +116,7 @@ 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;
119
+ type->size = (int16_t)(AlignLen(type->size, member.type->align) + member.type->size);
120
120
  type->align = std::max(type->align, member.type->align);
121
121
 
122
122
  type->members.Append(member);
@@ -0,0 +1,115 @@
1
+ # This program is free software: you can redistribute it and/or modify
2
+ # it under the terms of the GNU Affero General Public License as published by
3
+ # the Free Software Foundation, either version 3 of the License, or
4
+ # (at your option) any later version.
5
+ #
6
+ # This program is distributed in the hope that it will be useful,
7
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
8
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
+ # GNU Affero General Public License for more details.
10
+ #
11
+ # You should have received a copy of the GNU Affero General Public License
12
+ # along with this program. If not, see https://www.gnu.org/licenses/.
13
+
14
+ cmake_minimum_required(VERSION 3.12)
15
+ project(koffi C CXX)
16
+
17
+ if(NOT TARGET koffi)
18
+ add_subdirectory(.. koffi)
19
+ endif()
20
+
21
+ # ---- Raylib ----
22
+
23
+ add_library(raylib SHARED
24
+ ../vendor/raylib/src/rcore.c
25
+ ../vendor/raylib/src/rshapes.c
26
+ ../vendor/raylib/src/rtextures.c
27
+ ../vendor/raylib/src/rtext.c
28
+ ../vendor/raylib/src/rmodels.c
29
+ ../vendor/raylib/src/utils.c
30
+ ../vendor/raylib/src/rglfw.c
31
+ ../vendor/raylib/src/raudio.c
32
+ )
33
+ set_target_properties(raylib PROPERTIES PREFIX "")
34
+ target_include_directories(raylib PRIVATE ../vendor/raylib/src/external/glfw/include)
35
+ target_compile_definitions(raylib PRIVATE PLATFORM_DESKTOP GRAPHICS_API_OPENGL_21
36
+ BUILD_LIBTYPE_SHARED NDEBUG)
37
+
38
+ if(MSVC)
39
+ target_compile_options(raylib PRIVATE /wd4244 /wd4305)
40
+ else()
41
+ target_compile_options(raylib PRIVATE -Wno-sign-compare -Wno-old-style-declaration
42
+ -Wno-unused-function -Wno-missing-field-initializers
43
+ -Wno-unused-value -Wno-implicit-fallthrough -Wno-stringop-overflow
44
+ -Wno-unused-result)
45
+ endif()
46
+
47
+ if(WIN32)
48
+ target_compile_definitions(raylib PRIVATE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE)
49
+ target_link_libraries(raylib PRIVATE winmm)
50
+ endif()
51
+
52
+ if(CMAKE_SYSTEM_NAME MATCHES "BSD")
53
+ target_include_directories(raylib PRIVATE /usr/local/include)
54
+ endif()
55
+
56
+ if(APPLE)
57
+ target_compile_options(raylib PRIVATE -Wno-unknown-warning-option -Wno-macro-redefined)
58
+ target_compile_definitions(raylib PRIVATE GL_SILENCE_DEPRECATION)
59
+ set_source_files_properties(../vendor/raylib/src/rglfw.c PROPERTIES COMPILE_FLAGS "-x objective-c")
60
+ target_link_libraries(raylib PRIVATE "-framework Cocoa" "-framework IOKit" "-framework CoreFoundation" "-framework OpenGL")
61
+ endif()
62
+
63
+ if(UNIX AND NOT APPLE)
64
+ set(missing_xlib "")
65
+
66
+ find_path(XLIB_INCLUDE_DIRS X11/Xlib.h)
67
+ if(NOT XLIB_INCLUDE_DIRS)
68
+ list(APPEND missing_xlib Xlib)
69
+ endif()
70
+ find_path(XCURSOR_INCLUDE_DIRS X11/Xcursor/Xcursor.h)
71
+ if(NOT XCURSOR_INCLUDE_DIRS)
72
+ list(APPEND missing_xlib Xcursor)
73
+ endif()
74
+ find_path(XRANDR_INCLUDE_DIRS X11/extensions/Xrandr.h)
75
+ if(NOT XRANDR_INCLUDE_DIRS)
76
+ list(APPEND missing_xlib Xrandr)
77
+ endif()
78
+ find_path(XKB_INCLUDE_DIRS X11/XKBlib.h)
79
+ if(NOT XKB_INCLUDE_DIRS)
80
+ list(APPEND missing_xlib Xkbcommon)
81
+ endif()
82
+ find_path(XINERAMA_INCLUDE_DIRS X11/extensions/Xinerama.h)
83
+ if(NOT XINERAMA_INCLUDE_DIRS)
84
+ list(APPEND missing_xlib Xinerama)
85
+ endif()
86
+ find_path(XINPUT_INCLUDE_DIRS X11/extensions/XInput2.h)
87
+ if(NOT XINPUT_INCLUDE_DIRS)
88
+ list(APPEND missing_xlib XInput2)
89
+ endif()
90
+
91
+ if(missing_xlib)
92
+ list(JOIN missing_xlib ", " missing_xlib_str)
93
+ message(FATAL_ERROR "Missing X11 development files: ${missing_xlib_str}")
94
+ endif()
95
+
96
+ target_include_directories(raylib PRIVATE ${XLIB_INCLUDE_DIRS})
97
+ endif()
98
+
99
+ # ---- SQLite ----
100
+
101
+ add_library(sqlite3 SHARED ../vendor/sqlite3/sqlite3.c)
102
+ set_target_properties(sqlite3 PROPERTIES PREFIX "")
103
+
104
+ if(WIN32)
105
+ target_compile_definitions(sqlite3 PRIVATE SQLITE_API=__declspec\(dllexport\))
106
+ endif()
107
+
108
+ # ---- Misc ----
109
+
110
+ add_library(misc SHARED tests/misc.c)
111
+ set_target_properties(misc PROPERTIES PREFIX "")
112
+
113
+ if(MSVC)
114
+ target_compile_options(misc PRIVATE /wd4116)
115
+ endif()
@@ -25,9 +25,7 @@
25
25
  "Linux ARM32": {
26
26
  "directory": "/home/debian/luigi",
27
27
  "build": {
28
- "Install": "npm install --production --ignore-scripts",
29
- "Build Koffi": "node ../cnoke/cnoke.js",
30
- "Build dependencies": "node ../cnoke/cnoke.js -C test"
28
+ "Build": "node ../cnoke/cnoke.js -C test"
31
29
  },
32
30
  "commands": {
33
31
  "Test Raylib": "xvfb-run node test/tests/raylib.js",
@@ -64,9 +62,7 @@
64
62
  "Linux ARM64": {
65
63
  "directory": "/home/debian/luigi",
66
64
  "build": {
67
- "Install": "npm install --production --ignore-scripts",
68
- "Build Koffi": "node ../cnoke/cnoke.js",
69
- "Build dependencies": "node ../cnoke/cnoke.js -C test"
65
+ "Build": "node ../cnoke/cnoke.js -C test"
70
66
  },
71
67
  "commands": {
72
68
  "Test Raylib": "xvfb-run node test/tests/raylib.js",
@@ -103,9 +99,7 @@
103
99
  "Linux i386": {
104
100
  "directory": "/home/debian/luigi",
105
101
  "build": {
106
- "Install": "npm install --production --ignore-scripts",
107
- "Build Koffi": "node ../cnoke/cnoke.js",
108
- "Build dependencies": "node ../cnoke/cnoke.js -C test"
102
+ "Build": "node ../cnoke/cnoke.js -C test"
109
103
  },
110
104
  "commands": {
111
105
  "Test Raylib": "xvfb-run node test/tests/raylib.js",
@@ -142,9 +136,7 @@
142
136
  "Linux x64": {
143
137
  "directory": "/home/debian/luigi",
144
138
  "build": {
145
- "Install": "npm install --production --ignore-scripts",
146
- "Build Koffi": "node ../cnoke/cnoke.js",
147
- "Build dependencies": "node ../cnoke/cnoke.js -C test"
139
+ "Build": "node ../cnoke/cnoke.js -C test"
148
140
  },
149
141
  "commands": {
150
142
  "Test Raylib": "xvfb-run node test/tests/raylib.js",
@@ -181,9 +173,7 @@
181
173
  "Windows i386": {
182
174
  "directory": "C:/Users/windows/Desktop/luigi32",
183
175
  "build": {
184
- "Install": "C:\\Node32\\node32.cmd npm install --production --ignore-scripts",
185
- "Build Koffi": "C:\\Node32\\node32.cmd node ../cnoke/cnoke.js",
186
- "Build dependencies": "C:\\Node32\\node32.cmd node ../cnoke/cnoke.js -C test"
176
+ "Build": "C:\\Node32\\node32.cmd node ../cnoke/cnoke.js -C test"
187
177
  },
188
178
  "commands": {
189
179
  "Test Raylib": "seatsh C:\\Node32\\node32.cmd node test/tests/raylib.js",
@@ -195,9 +185,7 @@
195
185
  "Windows x64": {
196
186
  "directory": "C:/Users/windows/Desktop/luigi64",
197
187
  "build": {
198
- "Install": "C:\\Node64\\node64.cmd npm install --production --ignore-scripts",
199
- "Build Koffi": "C:\\Node64\\node64.cmd node ../cnoke/cnoke.js",
200
- "Build dependencies": "C:\\Node64\\node64.cmd node ../cnoke/cnoke.js -C test"
188
+ "Build": "C:\\Node64\\node64.cmd node ../cnoke/cnoke.js -C test"
201
189
  },
202
190
  "commands": {
203
191
  "Test Raylib": "seatsh C:\\Node64\\node64.cmd node test/tests/raylib.js",
@@ -234,9 +222,7 @@
234
222
  "FreeBSD x64": {
235
223
  "directory": "/home/freebsd/luigi",
236
224
  "build": {
237
- "Install": "npm install --production --ignore-scripts",
238
- "Build Koffi": "node ../cnoke/cnoke.js",
239
- "Build dependencies": "node ../cnoke/cnoke.js -C test"
225
+ "Build": "node ../cnoke/cnoke.js -C test"
240
226
  },
241
227
  "commands": {
242
228
  "Test Raylib": "xvfb-run node test/tests/raylib.js",
@@ -273,9 +259,7 @@
273
259
  "FreeBSD i386": {
274
260
  "directory": "/home/freebsd/luigi",
275
261
  "build": {
276
- "Install": "npm install --production --ignore-scripts",
277
- "Build Koffi": "node ../cnoke/cnoke.js",
278
- "Build dependencies": "node ../cnoke/cnoke.js -C test"
262
+ "Build": "node ../cnoke/cnoke.js -C test"
279
263
  },
280
264
  "commands": {
281
265
  "Test Raylib": "xvfb-run node test/tests/raylib.js",
@@ -312,9 +296,7 @@
312
296
  "FreeBSD ARM64": {
313
297
  "directory": "/home/freebsd/luigi",
314
298
  "build": {
315
- "Install": "npm install --production --ignore-scripts",
316
- "Build Koffi": "node ../cnoke/cnoke.js",
317
- "Build dependencies": "node ../cnoke/cnoke.js -C test"
299
+ "Build": "node ../cnoke/cnoke.js -C test"
318
300
  },
319
301
  "commands": {
320
302
  "Test Raylib": "xvfb-run node test/tests/raylib.js",
@@ -351,9 +333,7 @@
351
333
  "macOS x64": {
352
334
  "directory": "/Users/macos/luigi",
353
335
  "build": {
354
- "Install": "PATH=/usr/local/bin:/usr/bin:/bin npm install --production --ignore-scripts",
355
- "Build Koffi": "PATH=/usr/local/bin:/usr/bin:/bin SDKROOT=/Library/Developer/CommandLineTools/SDKs/MacOSX11.3.sdk node ../cnoke/cnoke.js",
356
- "Build dependencies": "PATH=/usr/local/bin:/usr/bin:/bin SDKROOT=/Library/Developer/CommandLineTools/SDKs/MacOSX11.3.sdk node ../cnoke/cnoke.js -C test"
336
+ "Build": "PATH=/usr/local/bin:/usr/bin:/bin SDKROOT=/Library/Developer/CommandLineTools/SDKs/MacOSX11.3.sdk node ../cnoke/cnoke.js -C test"
357
337
  },
358
338
  "commands": {
359
339
  "Test Misc": "PATH=/usr/local/bin:/usr/bin:/bin node test/tests/misc.js",
package/test/tests/misc.c CHANGED
@@ -61,6 +61,17 @@ EXPORT void FillPack3(int a, int b, int c, Pack3 *p)
61
61
  p->c = c;
62
62
  }
63
63
 
64
+ EXPORT Pack3 RetPack3(int a, int b, int c)
65
+ {
66
+ Pack3 p;
67
+
68
+ p.a = a;
69
+ p.b = b;
70
+ p.c = c;
71
+
72
+ return p;
73
+ }
74
+
64
75
  EXPORT void FASTCALL AddPack3(int a, int b, int c, Pack3 *p)
65
76
  {
66
77
  p->a += a;
@@ -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 assert = require('assert');
18
18
  const path = require('path');
19
19
 
@@ -52,6 +52,7 @@ async function test() {
52
52
  let lib = koffi.load(lib_filename);
53
53
 
54
54
  const FillPack3 = lib.cdecl('FillPack3', 'void', ['int', 'int', 'int', koffi.out(koffi.pointer(Pack3))]);
55
+ const RetPack3 = lib.cdecl('RetPack3', Pack3, ['int', 'int', 'int']);
55
56
  const AddPack3 = lib.fastcall('AddPack3', 'void', ['int', 'int', 'int', koffi.inout(koffi.pointer(Pack3))]);
56
57
  const ConcatenateToInt1 = lib.cdecl('ConcatenateToInt1', 'int64_t', Array(12).fill('int8_t'));
57
58
  const ConcatenateToInt4 = lib.cdecl('ConcatenateToInt4', 'int64_t', Array(12).fill('int32_t'));
@@ -66,6 +67,9 @@ async function test() {
66
67
  FillPack3(1, 2, 3, p);
67
68
  assert.deepEqual(p, { a: 1, b: 2, c: 3 });
68
69
 
70
+ let q = RetPack3(6, 9, -12);
71
+ assert.deepEqual(q, { a: 6, b: 9, c: -12 });
72
+
69
73
  AddPack3(6, 9, -12, p);
70
74
  assert.deepEqual(p, { a: 7, b: 11, c: -9 });
71
75
 
@@ -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 crypto = require('crypto');
18
18
  const fs = require('fs');
19
19
  const os = require('os');
@@ -14,7 +14,7 @@
14
14
  // along with this program. If not, see https://www.gnu.org/licenses/.
15
15
 
16
16
  const crypto = require('crypto');
17
- const koffi = require('../../build/koffi.node');
17
+ const koffi = require('../build/koffi.node');
18
18
  const assert = require('assert');
19
19
  const fs = require('fs');
20
20
  const os = require('os');
@@ -1,13 +0,0 @@
1
- #!/bin/sh -e
2
-
3
- cd $(dirname $0)/..
4
-
5
- echo "Install all dependencies..."
6
- npm install
7
-
8
- echo "Building dependencies..."
9
- node ../cnoke/cnoke.js
10
- node ../cnoke/cnoke.js -C test
11
-
12
- echo "Building raylib_c..."
13
- gcc -O2 -std=gnu99 -Wall -Wl,-rpath,$(realpath $(pwd)/test/build) -fPIC benchmark/raylib_c.c -o benchmark/raylib_c test/build/raylib.so -lm -pthread -ldl