koffi 0.9.27 → 0.9.28

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
@@ -265,7 +265,7 @@ Once this is done, you can execute each implementation, e.g. `build/atoi_cc` or
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
271
  $ build/atoi_cc
@@ -278,7 +278,7 @@ Time: 1.10s
278
278
 
279
279
  $ ./atoi_koffi.js
280
280
  Iterations: 20000000
281
- Time: 2.34s
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
@@ -289,29 +289,29 @@ 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
295
  $ build\atoi_cc.exe
296
296
  Iterations: 20000000
297
- Time: 0.66s
297
+ Time: 0.25s
298
298
 
299
299
  $ node atoi_napi.js
300
300
  Iterations: 20000000
301
- Time: 3.23s
301
+ Time: 1.94s
302
302
 
303
303
  $ node atoi_koffi.js
304
304
  Iterations: 20000000
305
- Time: 4.81s
305
+ Time: 3.15s
306
306
 
307
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
317
  $ build/raylib_cc
@@ -327,18 +327,18 @@ 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
333
  $ build\raylib_cc.exe
334
334
  Iterations: 100
335
- Time: 10.53s
335
+ Time: 8.39s
336
336
 
337
337
  $ node raylib_koffi.js
338
338
  Iterations: 100
339
- Time: 14.60s
339
+ Time: 11.51s
340
340
 
341
341
  $ node raylib_node_ffi.js
342
342
  Iterations: 100
343
- Time: 44.97s
343
+ Time: 32.47s
344
344
  ```
@@ -16,7 +16,10 @@ 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
+
21
+ set(THREADS_PREFER_PTHREAD_FLAG ON)
22
+ find_package(Threads REQUIRED)
20
23
 
21
24
  if(NOT TARGET koffi)
22
25
  add_subdirectory(.. koffi)
@@ -27,6 +30,7 @@ add_subdirectory(../test test)
27
30
 
28
31
  add_executable(atoi_cc atoi_cc.cc ../vendor/libcc/libcc.cc)
29
32
  target_include_directories(atoi_cc PRIVATE ..)
33
+ target_link_libraries(atoi_cc PRIVATE Threads::Threads)
30
34
 
31
35
  if(WIN32)
32
36
  target_compile_definitions(atoi_cc PRIVATE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE)
@@ -35,19 +39,24 @@ endif()
35
39
 
36
40
  add_node_addon(NAME atoi_napi SOURCES atoi_napi.cc ../vendor/libcc/libcc.cc)
37
41
  target_include_directories(atoi_napi PRIVATE .. ../vendor/node-addon-api)
42
+ target_link_libraries(atoi_napi PRIVATE Threads::Threads)
38
43
 
39
44
  if(WIN32)
40
45
  target_compile_definitions(atoi_napi PRIVATE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE)
41
46
  target_link_libraries(atoi_napi PRIVATE ws2_32)
47
+ else()
48
+ target_link_libraries(atoi_napi PRIVATE dl)
42
49
  endif()
43
50
 
44
51
  # ---- Raylib ----
45
52
 
46
- add_executable(raylib_cc atoi_cc.cc ../vendor/libcc/libcc.cc)
53
+ add_executable(raylib_cc raylib_cc.cc ../vendor/libcc/libcc.cc)
47
54
  target_include_directories(raylib_cc PRIVATE ..)
48
- add_dependencies(raylib_cc raylib)
55
+ target_link_libraries(raylib_cc PRIVATE Threads::Threads raylib)
49
56
 
50
57
  if(WIN32)
51
58
  target_compile_definitions(raylib_cc PRIVATE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE)
52
59
  target_link_libraries(raylib_cc PRIVATE ws2_32)
60
+ else()
61
+ target_link_libraries(raylib_cc PRIVATE dl)
53
62
  endif()
@@ -36,7 +36,7 @@ int Main(int argc, char **argv)
36
36
  int64_t start = GetMonotonicTime();
37
37
 
38
38
  for (int i = 0; i < iterations; i++) {
39
- sum += (uint64_t)atoi(strings[i % RG_LEN(strings)]);
39
+ sum = sum + (uint64_t)atoi(strings[i % RG_LEN(strings)]);
40
40
  }
41
41
 
42
42
  // Help prevent optimisation of loop
@@ -31,13 +31,13 @@ int Main(int argc, char **argv)
31
31
  SetWindowState(FLAG_WINDOW_HIDDEN);
32
32
  InitWindow(640, 480, "Raylib Test");
33
33
 
34
- 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 });
35
35
  Font font = GetFontDefault();
36
36
 
37
37
  int64_t start = GetMonotonicTime();
38
38
 
39
39
  for (int i = 0; i < iterations; i++) {
40
- ImageClearBackground(&img, (Color){ .r = 0, .g = 0, .b = 0, .a = 255 });
40
+ ImageClearBackground(&img, Color { .r = 0, .g = 0, .b = 0, .a = 255 });
41
41
 
42
42
  for (int j = 0; j < 3600; j++) {
43
43
  const char *text = "Hello World!";
@@ -82,7 +82,7 @@ function main() {
82
82
  }
83
83
  console.log('Iterations:', iterations);
84
84
 
85
- let lib_filename = path.dirname(__filename) + '/test/build/raylib' + koffi.extension;
85
+ let lib_filename = path.dirname(__filename) + '/build/raylib' + koffi.extension;
86
86
  let lib = koffi.load(lib_filename);
87
87
 
88
88
  const InitWindow = lib.cdecl('InitWindow', 'void', ['int', 'int', 'string']);
@@ -98,14 +98,7 @@ function main() {
98
98
  }
99
99
  console.log('Iterations:', iterations);
100
100
 
101
- let iterations = parseInt(process.argv[2], 10);
102
- if (Number.isNaN(iterations))
103
- throw new Error('Not a valid number');
104
- if (iterations < 1)
105
- throw new Error('Value must be positive');
106
- console.log('Iterations:', iterations);
107
-
108
- let lib_filename = path.dirname(__filename) + '/test/build/raylib' + koffi.extension;
101
+ let lib_filename = path.dirname(__filename) + '/build/raylib' + koffi.extension;
109
102
 
110
103
  const r = ffi.Library(lib_filename, {
111
104
  InitWindow: ['void', ['int', 'int', 'string']],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "0.9.27",
3
+ "version": "0.9.28",
4
4
  "description": "Fast and simple FFI (foreign function interface) for Node.js",
5
5
  "keywords": [
6
6
  "foreign",
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/util.cc CHANGED
@@ -81,23 +81,38 @@ const char *CallData::PushString(const Napi::Value &value)
81
81
  {
82
82
  RG_ASSERT(value.IsString());
83
83
 
84
+ const Size SmallSize = 32;
85
+
84
86
  Napi::Env env = value.Env();
85
87
  napi_status status;
86
88
 
89
+ Span<char> buf;
87
90
  size_t len = 0;
88
- status = napi_get_value_string_utf8(env, value, nullptr, 0, &len);
89
- RG_ASSERT(status == napi_ok);
90
91
 
91
- Span<char> buf;
92
- buf.len = (Size)len + 1;
92
+ // Optimize for small strings
93
+ buf.len = SmallSize;
93
94
  if (RG_UNLIKELY(!AllocHeap(buf.len, 1, &buf.ptr)))
94
95
  return nullptr;
95
-
96
96
  if (RG_UNLIKELY(napi_get_value_string_utf8(env, value, buf.ptr, (size_t)buf.len, &len) != napi_ok)) {
97
97
  ThrowError<Napi::Error>(env, "Failed to convert string to UTF-8");
98
98
  return nullptr;
99
99
  }
100
100
 
101
+ // Slow path for bigger strings
102
+ if (len >= SmallSize - 1) {
103
+ status = napi_get_value_string_utf8(env, value, nullptr, 0, &len);
104
+ RG_ASSERT(status == napi_ok);
105
+ RG_ASSERT(len >= SmallSize);
106
+
107
+ buf.len = (Size)len + 1;
108
+ if (RG_UNLIKELY(!AllocHeap(buf.len - SmallSize, 1)))
109
+ return nullptr;
110
+ if (RG_UNLIKELY(napi_get_value_string_utf8(env, value, buf.ptr, (size_t)buf.len, &len) != napi_ok)) {
111
+ ThrowError<Napi::Error>(env, "Failed to convert string to UTF-8");
112
+ return nullptr;
113
+ }
114
+ }
115
+
101
116
  return buf.ptr;
102
117
  }
103
118
 
package/src/util.hh CHANGED
@@ -101,8 +101,8 @@ public:
101
101
 
102
102
  template <typename T = void>
103
103
  bool AllocStack(Size size, Size align, T **out_ptr = nullptr);
104
- template <typename T>
105
- bool AllocHeap(Size size, Size align, T **out_ptr);
104
+ template <typename T = void>
105
+ bool AllocHeap(Size size, Size align, T **out_ptr = nullptr);
106
106
 
107
107
  const char *PushString(const Napi::Value &value);
108
108
  bool PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t *dest);
@@ -151,7 +151,9 @@ bool CallData::AllocHeap(Size size, Size align, T **out_ptr)
151
151
  heap_mem->ptr += delta;
152
152
  heap_mem->len -= delta;
153
153
 
154
- *out_ptr = (T *)ptr;
154
+ if (out_ptr) {
155
+ *out_ptr = (T *)ptr;
156
+ }
155
157
  return true;
156
158
  }
157
159
 
@@ -7964,9 +7964,14 @@ static int zipfileFilter(
7964
7964
  zipfileCursorErr(pCsr, "zipfile() function requires an argument");
7965
7965
  return SQLITE_ERROR;
7966
7966
  }else if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){
7967
+ static const u8 aEmptyBlob = 0;
7967
7968
  const u8 *aBlob = (const u8*)sqlite3_value_blob(argv[0]);
7968
7969
  int nBlob = sqlite3_value_bytes(argv[0]);
7969
7970
  assert( pTab->pFirstEntry==0 );
7971
+ if( aBlob==0 ){
7972
+ aBlob = &aEmptyBlob;
7973
+ nBlob = 0;
7974
+ }
7970
7975
  rc = zipfileLoadDirectory(pTab, aBlob, nBlob);
7971
7976
  pCsr->pFreeEntry = pTab->pFirstEntry;
7972
7977
  pTab->pFirstEntry = pTab->pLastEntry = 0;
@@ -19906,7 +19911,7 @@ static int do_meta_command(char *zLine, ShellState *p){
19906
19911
 
19907
19912
  if( c=='i' && strncmp(azArg[0], "import", n)==0 ){
19908
19913
  char *zTable = 0; /* Insert data into this table */
19909
- char *zSchema = "main"; /* within this schema */
19914
+ char *zSchema = 0; /* within this schema (may default to "main") */
19910
19915
  char *zFile = 0; /* Name of file to extra content from */
19911
19916
  sqlite3_stmt *pStmt = NULL; /* A statement */
19912
19917
  int nCol; /* Number of columns in the table */
@@ -19915,11 +19920,13 @@ static int do_meta_command(char *zLine, ShellState *p){
19915
19920
  int needCommit; /* True to COMMIT or ROLLBACK at end */
19916
19921
  int nSep; /* Number of bytes in p->colSeparator[] */
19917
19922
  char *zSql; /* An SQL statement */
19923
+ char *zFullTabName; /* Table name with schema if applicable */
19918
19924
  ImportCtx sCtx; /* Reader context */
19919
19925
  char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */
19920
19926
  int eVerbose = 0; /* Larger for more console output */
19921
19927
  int nSkip = 0; /* Initial lines to skip */
19922
19928
  int useOutputMode = 1; /* Use output mode to determine separators */
19929
+ char *zCreate = 0; /* CREATE TABLE statement text */
19923
19930
 
19924
19931
  failIfSafeMode(p, "cannot run .import in safe mode");
19925
19932
  memset(&sCtx, 0, sizeof(sCtx));
@@ -20042,7 +20049,6 @@ static int do_meta_command(char *zLine, ShellState *p){
20042
20049
  import_cleanup(&sCtx);
20043
20050
  goto meta_command_exit;
20044
20051
  }
20045
- /* Below, resources must be freed before exit. */
20046
20052
  if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){
20047
20053
  char zSep[2];
20048
20054
  zSep[1] = 0;
@@ -20054,11 +20060,17 @@ static int do_meta_command(char *zLine, ShellState *p){
20054
20060
  output_c_string(p->out, zSep);
20055
20061
  utf8_printf(p->out, "\n");
20056
20062
  }
20063
+ /* Below, resources must be freed before exit. */
20057
20064
  while( (nSkip--)>0 ){
20058
20065
  while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){}
20059
20066
  }
20060
- zSql = sqlite3_mprintf("SELECT * FROM \"%w\".\"%w\"", zSchema, zTable);
20061
- if( zSql==0 ){
20067
+ if( zSchema!=0 ){
20068
+ zFullTabName = sqlite3_mprintf("\"%w\".\"%w\"", zSchema, zTable);
20069
+ }else{
20070
+ zFullTabName = sqlite3_mprintf("\"%w\"", zTable);
20071
+ }
20072
+ zSql = sqlite3_mprintf("SELECT * FROM %s", zFullTabName);
20073
+ if( zSql==0 || zFullTabName==0 ){
20062
20074
  import_cleanup(&sCtx);
20063
20075
  shell_out_of_memory();
20064
20076
  }
@@ -20066,11 +20078,10 @@ static int do_meta_command(char *zLine, ShellState *p){
20066
20078
  rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
20067
20079
  import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */
20068
20080
  if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(p->db))==0 ){
20069
- char *zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"",
20070
- zSchema, zTable);
20071
20081
  sqlite3 *dbCols = 0;
20072
20082
  char *zRenames = 0;
20073
20083
  char *zColDefs;
20084
+ zCreate = sqlite3_mprintf("CREATE TABLE %s", zFullTabName);
20074
20085
  while( xRead(&sCtx) ){
20075
20086
  zAutoColumn(sCtx.z, &dbCols, 0);
20076
20087
  if( sCtx.cTerm!=sCtx.cColSep ) break;
@@ -20084,9 +20095,12 @@ static int do_meta_command(char *zLine, ShellState *p){
20084
20095
  }
20085
20096
  assert(dbCols==0);
20086
20097
  if( zColDefs==0 ){
20098
+ utf8_printf(stderr,"%s: empty file\n", sCtx.zFile);
20099
+ import_fail:
20087
20100
  sqlite3_free(zCreate);
20101
+ sqlite3_free(zSql);
20102
+ sqlite3_free(zFullTabName);
20088
20103
  import_cleanup(&sCtx);
20089
- utf8_printf(stderr,"%s: empty file\n", sCtx.zFile);
20090
20104
  rc = 1;
20091
20105
  goto meta_command_exit;
20092
20106
  }
@@ -20097,22 +20111,18 @@ static int do_meta_command(char *zLine, ShellState *p){
20097
20111
  rc = sqlite3_exec(p->db, zCreate, 0, 0, 0);
20098
20112
  if( rc ){
20099
20113
  utf8_printf(stderr, "%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db));
20100
- sqlite3_free(zCreate);
20101
- import_cleanup(&sCtx);
20102
- rc = 1;
20103
- goto meta_command_exit;
20114
+ goto import_fail;
20104
20115
  }
20105
20116
  sqlite3_free(zCreate);
20117
+ zCreate = 0;
20106
20118
  rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
20107
20119
  }
20108
- sqlite3_free(zSql);
20109
20120
  if( rc ){
20110
20121
  if (pStmt) sqlite3_finalize(pStmt);
20111
20122
  utf8_printf(stderr,"Error: %s\n", sqlite3_errmsg(p->db));
20112
- import_cleanup(&sCtx);
20113
- rc = 1;
20114
- goto meta_command_exit;
20123
+ goto import_fail;
20115
20124
  }
20125
+ sqlite3_free(zSql);
20116
20126
  nCol = sqlite3_column_count(pStmt);
20117
20127
  sqlite3_finalize(pStmt);
20118
20128
  pStmt = 0;
@@ -20122,8 +20132,7 @@ static int do_meta_command(char *zLine, ShellState *p){
20122
20132
  import_cleanup(&sCtx);
20123
20133
  shell_out_of_memory();
20124
20134
  }
20125
- sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\".\"%w\" VALUES(?",
20126
- zSchema, zTable);
20135
+ sqlite3_snprintf(nByte+20, zSql, "INSERT INTO %s VALUES(?", zFullTabName);
20127
20136
  j = strlen30(zSql);
20128
20137
  for(i=1; i<nCol; i++){
20129
20138
  zSql[j++] = ',';
@@ -20135,14 +20144,13 @@ static int do_meta_command(char *zLine, ShellState *p){
20135
20144
  utf8_printf(p->out, "Insert using: %s\n", zSql);
20136
20145
  }
20137
20146
  rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
20138
- sqlite3_free(zSql);
20139
20147
  if( rc ){
20140
20148
  utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
20141
20149
  if (pStmt) sqlite3_finalize(pStmt);
20142
- import_cleanup(&sCtx);
20143
- rc = 1;
20144
- goto meta_command_exit;
20150
+ goto import_fail;
20145
20151
  }
20152
+ sqlite3_free(zSql);
20153
+ sqlite3_free(zFullTabName);
20146
20154
  needCommit = sqlite3_get_autocommit(p->db);
20147
20155
  if( needCommit ) sqlite3_exec(p->db, "BEGIN", 0, 0, 0);
20148
20156
  do{
@@ -22636,7 +22644,8 @@ static int process_input(ShellState *p){
22636
22644
  qss = QSS_Start;
22637
22645
  }
22638
22646
  }
22639
- if( nSql && QSS_PLAINDARK(qss) ){
22647
+ if( nSql ){
22648
+ /* This may be incomplete. Let the SQL parser deal with that. */
22640
22649
  errCnt += runOneSqlLine(p, zSql, p->in, startline);
22641
22650
  }
22642
22651
  free(zSql);