koffi 2.3.13 → 2.3.15

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.
Files changed (48) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/build/2.3.15/koffi_darwin_arm64/koffi.node +0 -0
  3. package/build/2.3.15/koffi_darwin_x64/koffi.node +0 -0
  4. package/build/2.3.15/koffi_freebsd_arm64/koffi.node +0 -0
  5. package/build/2.3.15/koffi_freebsd_ia32/koffi.node +0 -0
  6. package/build/2.3.15/koffi_freebsd_x64/koffi.node +0 -0
  7. package/build/2.3.15/koffi_linux_arm32hf/koffi.node +0 -0
  8. package/build/2.3.15/koffi_linux_arm64/koffi.node +0 -0
  9. package/build/2.3.15/koffi_linux_ia32/koffi.node +0 -0
  10. package/build/2.3.15/koffi_linux_riscv64hf64/koffi.node +0 -0
  11. package/build/2.3.15/koffi_linux_x64/koffi.node +0 -0
  12. package/build/2.3.15/koffi_openbsd_ia32/koffi.node +0 -0
  13. package/build/2.3.15/koffi_openbsd_x64/koffi.node +0 -0
  14. package/build/2.3.15/koffi_win32_arm64/koffi.node +0 -0
  15. package/build/2.3.15/koffi_win32_ia32/koffi.node +0 -0
  16. package/build/2.3.15/koffi_win32_x64/koffi.node +0 -0
  17. package/doc/calls.md +54 -0
  18. package/doc/misc.md +22 -0
  19. package/package.json +2 -2
  20. package/src/core/libcc/libcc.cc +15 -0
  21. package/src/core/libcc/libcc.hh +7 -1
  22. package/src/index.d.ts +9 -2
  23. package/src/koffi/src/errno.inc +480 -0
  24. package/src/koffi/src/ffi.cc +58 -4
  25. package/src/koffi/src/ffi.hh +6 -0
  26. package/src/koffi/src/util.cc +35 -12
  27. package/src/koffi/src/util.hh +2 -2
  28. package/build/2.3.13/koffi_darwin_arm64/koffi.node +0 -0
  29. package/build/2.3.13/koffi_darwin_x64/koffi.node +0 -0
  30. package/build/2.3.13/koffi_freebsd_arm64/koffi.node +0 -0
  31. package/build/2.3.13/koffi_freebsd_ia32/koffi.node +0 -0
  32. package/build/2.3.13/koffi_freebsd_x64/koffi.node +0 -0
  33. package/build/2.3.13/koffi_linux_arm32hf/koffi.node +0 -0
  34. package/build/2.3.13/koffi_linux_arm64/koffi.node +0 -0
  35. package/build/2.3.13/koffi_linux_ia32/koffi.node +0 -0
  36. package/build/2.3.13/koffi_linux_riscv64hf64/koffi.node +0 -0
  37. package/build/2.3.13/koffi_linux_x64/koffi.node +0 -0
  38. package/build/2.3.13/koffi_openbsd_ia32/koffi.node +0 -0
  39. package/build/2.3.13/koffi_openbsd_x64/koffi.node +0 -0
  40. package/build/2.3.13/koffi_win32_arm64/koffi.node +0 -0
  41. package/build/2.3.13/koffi_win32_ia32/koffi.node +0 -0
  42. package/build/2.3.13/koffi_win32_x64/koffi.node +0 -0
  43. /package/build/{2.3.13 → 2.3.15}/koffi_win32_arm64/koffi.exp +0 -0
  44. /package/build/{2.3.13 → 2.3.15}/koffi_win32_arm64/koffi.lib +0 -0
  45. /package/build/{2.3.13 → 2.3.15}/koffi_win32_ia32/koffi.exp +0 -0
  46. /package/build/{2.3.13 → 2.3.15}/koffi_win32_ia32/koffi.lib +0 -0
  47. /package/build/{2.3.13 → 2.3.15}/koffi_win32_x64/koffi.exp +0 -0
  48. /package/build/{2.3.13 → 2.3.15}/koffi_win32_x64/koffi.lib +0 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,20 @@
4
4
 
5
5
  ### Koffi 2.3
6
6
 
7
+ #### Koffi 2.3.15
8
+
9
+ **Main changes:**
10
+
11
+ - Improve manual decoding of 0-terminated strings
12
+ - Add checks around array conversion hints
13
+
14
+ #### Koffi 2.3.14
15
+
16
+ **Main changes:**
17
+
18
+ - Add `koffi.errno()` function to get and set current errno value
19
+ - Add `koffi.os.errno` object with valid errno codes
20
+
7
21
  #### Koffi 2.3.13
8
22
 
9
23
  **Main changes:**
package/doc/calls.md CHANGED
@@ -68,6 +68,60 @@ The same can be done when declaring a function with a C-like prototype string, w
68
68
  - `_Out_` for output parameters
69
69
  - `_Inout_` for dual input/output parameters
70
70
 
71
+ ### Primitive value
72
+
73
+ This Windows example enumerate all Chrome windows along with their PID and their title. The `GetWindowThreadProcessId()` function illustrates how to get a primitive value from an output argument.
74
+
75
+ ```js
76
+ const koffi = require('koffi');
77
+ const user32 = koffi.load('user32.dll');
78
+
79
+ const DWORD = koffi.alias('DWORD', 'uint32_t');
80
+ const HANDLE = koffi.pointer(koffi.opaque('HANDLE'));
81
+ const HWND = koffi.alias('HWND', HANDLE);
82
+
83
+ const FindWindowEx = user32.func('HWND __stdcall FindWindowExW(HWND hWndParent, HWND hWndChildAfter, const char16_t *lpszClass, const char16_t *lpszWindow)');
84
+ const GetWindowThreadProcessId = user32.func('DWORD __stdcall GetWindowThreadProcessId(HWND hWnd, _Out_ DWORD *lpdwProcessId)');
85
+ const GetWindowText = user32.func('int __stdcall GetWindowTextA(HWND hWnd, _Out_ uint8_t *lpString, int nMaxCount)');
86
+
87
+ for (let hwnd = null;;) {
88
+ hwnd = FindWindowEx(0, hwnd, 'Chrome_WidgetWin_1', null);
89
+
90
+ if (!hwnd)
91
+ break;
92
+
93
+ // Get PID
94
+ let pid;
95
+ {
96
+ let ptr = [null];
97
+ let tid = GetWindowThreadProcessId(hwnd, ptr);
98
+
99
+ if (!tid) {
100
+ // Maybe the process ended in-between?
101
+ continue;
102
+ }
103
+
104
+ pid = ptr[0];
105
+ }
106
+
107
+ // Get window title
108
+ let title;
109
+ {
110
+ let buf = Buffer.allocUnsafe(1024);
111
+ let length = GetWindowText(hwnd, buf, buf.length);
112
+
113
+ if (!length) {
114
+ // Maybe the process ended in-between?
115
+ continue;
116
+ }
117
+
118
+ title = koffi.decode(buf, 'char', length);
119
+ }
120
+
121
+ console.log({ PID: pid, Title: title });
122
+ }
123
+ ```
124
+
71
125
  ### Struct example
72
126
 
73
127
  This example calls the POSIX function `gettimeofday()`, and uses the prototype-like syntax.
package/doc/misc.md CHANGED
@@ -101,3 +101,25 @@ max_type_size | 64 MiB | Maximum size of Koffi types (for arrays and str
101
101
  *New in Koffi 2.3.2*
102
102
 
103
103
  You can use `koffi.stats()` to get a few statistics related to Koffi.
104
+
105
+ ## POSIX error codes
106
+
107
+ *New in Koffi 2.3.14*
108
+
109
+ You can use `koffi.errno()` to the current errno value, and `koffi.errno(value)` to change it.
110
+
111
+ The standard POSIX error codes are available in `koffi.os.errno`, as in the example below:
112
+
113
+ ```js
114
+ const assert = require('assert');
115
+ const koffi = require('koffi');
116
+
117
+ const lib = koffi.load('libc.so.6');
118
+
119
+ const close = lib.func('int close(int fd)');
120
+
121
+ close(-1);
122
+ assert.equal(koffi.errno(), koffi.os.errno.EBADF);
123
+
124
+ console.log('close() with invalid FD is POSIX compliant!');
125
+ ```
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "2.3.13",
4
- "stable": "2.3.13",
3
+ "version": "2.3.15",
4
+ "stable": "2.3.15",
5
5
  "description": "Fast and simple C FFI (foreign function interface) for Node.js",
6
6
  "keywords": [
7
7
  "foreign",
@@ -5891,6 +5891,8 @@ bool StreamReader::Open(Span<const uint8_t> buf, const char *filename,
5891
5891
  RG_DEFER_N(err_guard) { error = true; };
5892
5892
  error = false;
5893
5893
  raw_read = 0;
5894
+ read_total = 0;
5895
+ read_max = -1;
5894
5896
 
5895
5897
  this->filename = filename ? DuplicateString(filename, &str_alloc).ptr : "<memory>";
5896
5898
 
@@ -5912,6 +5914,8 @@ bool StreamReader::Open(FILE *fp, const char *filename, CompressionType compress
5912
5914
  RG_DEFER_N(err_guard) { error = true; };
5913
5915
  error = false;
5914
5916
  raw_read = 0;
5917
+ read_total = 0;
5918
+ read_max = -1;
5915
5919
 
5916
5920
  RG_ASSERT(fp);
5917
5921
  RG_ASSERT(filename);
@@ -5935,6 +5939,8 @@ OpenResult StreamReader::Open(const char *filename, CompressionType compression_
5935
5939
  RG_DEFER_N(err_guard) { error = true; };
5936
5940
  error = false;
5937
5941
  raw_read = 0;
5942
+ read_total = 0;
5943
+ read_max = -1;
5938
5944
 
5939
5945
  RG_ASSERT(filename);
5940
5946
  this->filename = DuplicateString(filename, &str_alloc).ptr;
@@ -5962,6 +5968,8 @@ bool StreamReader::Open(const std::function<Size(Span<uint8_t>)> &func, const ch
5962
5968
  RG_DEFER_N(err_guard) { error = true; };
5963
5969
  error = false;
5964
5970
  raw_read = 0;
5971
+ read_total = 0;
5972
+ read_max = -1;
5965
5973
 
5966
5974
  this->filename = filename ? DuplicateString(filename, &str_alloc).ptr : "<closure>";
5967
5975
 
@@ -6081,6 +6089,13 @@ Size StreamReader::Read(Span<uint8_t> out_buf)
6081
6089
  eof = source.eof;
6082
6090
  }
6083
6091
 
6092
+ if (RG_UNLIKELY(!error && read_max >= 0 && read_len > read_max - read_total)) {
6093
+ LogError("Exceeded max stream size of %1", FmtDiskSize(read_max));
6094
+ error = true;
6095
+ return -1;
6096
+ }
6097
+ read_total += read_len;
6098
+
6084
6099
  return read_len;
6085
6100
  }
6086
6101
 
@@ -4229,6 +4229,9 @@ class StreamReader {
4229
4229
  const char *filename = nullptr;
4230
4230
  bool error = true;
4231
4231
 
4232
+ int64_t read_total = 0;
4233
+ int64_t read_max = -1;
4234
+
4232
4235
  struct {
4233
4236
  SourceType type = SourceType::Memory;
4234
4237
  union U {
@@ -4289,15 +4292,18 @@ public:
4289
4292
 
4290
4293
  const char *GetFileName() const { return filename; }
4291
4294
  CompressionType GetCompressionType() const { return compression_type; }
4295
+ int64_t GetReadLimit() { return read_max; }
4292
4296
  bool IsValid() const { return filename && !error; }
4293
4297
  bool IsEOF() const { return eof; }
4294
4298
 
4295
4299
  FILE *GetFile() const;
4296
4300
  int GetDescriptor() const;
4297
4301
 
4302
+ void SetReadLimit(int64_t limit) { read_max = limit; }
4303
+
4298
4304
  Size Read(Span<uint8_t> out_buf);
4299
4305
  Size Read(Span<char> out_buf) { return Read(out_buf.As<uint8_t>()); }
4300
- Size Read(Size max_len, void *out_buf) { return Read(MakeSpan((uint8_t *)out_buf, max_len)); }
4306
+ Size Read(Size buf_len, void *out_buf) { return Read(MakeSpan((uint8_t *)out_buf, buf_len)); }
4301
4307
 
4302
4308
  Size ReadAll(Size max_len, HeapArray<uint8_t> *out_buf);
4303
4309
  Size ReadAll(Size max_len, HeapArray<char> *out_buf)
package/src/index.d.ts CHANGED
@@ -131,6 +131,13 @@ declare module 'koffi' {
131
131
  export function config(cfg: Record<string, unknown>): Record<string, unknown>;
132
132
  export function stats(): Record<string, unknown>;
133
133
 
134
- export let internal: Boolean;
135
- export let extension: String;
134
+ export function errno(): number;
135
+ export function errno(value: number): number;
136
+
137
+ export const internal: Boolean;
138
+ export const extension: String;
139
+
140
+ export const os: {
141
+ errno: Record<string, number>
142
+ };
136
143
  }
@@ -0,0 +1,480 @@
1
+ // Copyright 2023 Niels Martignène <niels.martignene@protonmail.com>
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ // this software and associated documentation files (the “Software”), to deal in
5
+ // the Software without restriction, including without limitation the rights to use,
6
+ // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
7
+ // Software, and to permit persons to whom the Software is furnished to do so,
8
+ // subject to the following conditions:
9
+ //
10
+ // The above copyright notice and this permission notice shall be included in all
11
+ // copies or substantial portions of the Software.
12
+ //
13
+ // THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
14
+ // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
15
+ // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16
+ // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
17
+ // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18
+ // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
+ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20
+ // OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ #include <errno.h>
23
+
24
+ struct ErrnoCodeInfo {
25
+ const char *name;
26
+ int value;
27
+ };
28
+
29
+ const ErrnoCodeInfo ErrnoCodes[] = {
30
+ #ifdef E2BIG
31
+ {"E2BIG", E2BIG },
32
+ #endif
33
+ #ifdef EACCES
34
+ {"EACCES", EACCES },
35
+ #endif
36
+ #ifdef EADDRINUSE
37
+ {"EADDRINUSE", EADDRINUSE },
38
+ #endif
39
+ #ifdef EADDRNOTAVAIL
40
+ {"EADDRNOTAVAIL", EADDRNOTAVAIL },
41
+ #endif
42
+ #ifdef EADV
43
+ {"EADV", EADV },
44
+ #endif
45
+ #ifdef EAFNOSUPPORT
46
+ {"EAFNOSUPPORT", EAFNOSUPPORT },
47
+ #endif
48
+ #ifdef EAGAIN
49
+ {"EAGAIN", EAGAIN },
50
+ #endif
51
+ #ifdef EALREADY
52
+ {"EALREADY", EALREADY },
53
+ #endif
54
+ #ifdef EAUTH
55
+ {"EAUTH", EAUTH },
56
+ #endif
57
+ #ifdef EBADE
58
+ {"EBADE", EBADE },
59
+ #endif
60
+ #ifdef EBADF
61
+ {"EBADF", EBADF },
62
+ #endif
63
+ #ifdef EBADFD
64
+ {"EBADFD", EBADFD },
65
+ #endif
66
+ #ifdef EBADMSG
67
+ {"EBADMSG", EBADMSG },
68
+ #endif
69
+ #ifdef EBADR
70
+ {"EBADR", EBADR },
71
+ #endif
72
+ #ifdef EBADRPC
73
+ {"EBADRPC", EBADRPC },
74
+ #endif
75
+ #ifdef EBADRQC
76
+ {"EBADRQC", EBADRQC },
77
+ #endif
78
+ #ifdef EBADSLT
79
+ {"EBADSLT", EBADSLT },
80
+ #endif
81
+ #ifdef EBFONT
82
+ {"EBFONT", EBFONT },
83
+ #endif
84
+ #ifdef EBUSY
85
+ {"EBUSY", EBUSY },
86
+ #endif
87
+ #ifdef ECANCELED
88
+ {"ECANCELED", ECANCELED },
89
+ #endif
90
+ #ifdef ECAPMODE
91
+ {"ECAPMODE", ECAPMODE },
92
+ #endif
93
+ #ifdef ECHILD
94
+ {"ECHILD", ECHILD },
95
+ #endif
96
+ #ifdef ECHRNG
97
+ {"ECHRNG", ECHRNG },
98
+ #endif
99
+ #ifdef ECOMM
100
+ {"ECOMM", ECOMM },
101
+ #endif
102
+ #ifdef ECONNABORTED
103
+ {"ECONNABORTED", ECONNABORTED },
104
+ #endif
105
+ #ifdef ECONNREFUSED
106
+ {"ECONNREFUSED", ECONNREFUSED },
107
+ #endif
108
+ #ifdef ECONNRESET
109
+ {"ECONNRESET", ECONNRESET },
110
+ #endif
111
+ #ifdef EDEADLK
112
+ {"EDEADLK", EDEADLK },
113
+ #endif
114
+ #ifdef EDEADLOCK
115
+ {"EDEADLOCK", EDEADLOCK },
116
+ #endif
117
+ #ifdef EDESTADDRREQ
118
+ {"EDESTADDRREQ", EDESTADDRREQ },
119
+ #endif
120
+ #ifdef EDOM
121
+ {"EDOM", EDOM },
122
+ #endif
123
+ #ifdef EDOOFUS
124
+ {"EDOOFUS", EDOOFUS },
125
+ #endif
126
+ #ifdef EDOTDOT
127
+ {"EDOTDOT", EDOTDOT },
128
+ #endif
129
+ #ifdef EDQUOT
130
+ {"EDQUOT", EDQUOT },
131
+ #endif
132
+ #ifdef EEXIST
133
+ {"EEXIST", EEXIST },
134
+ #endif
135
+ #ifdef EFAULT
136
+ {"EFAULT", EFAULT },
137
+ #endif
138
+ #ifdef EFBIG
139
+ {"EFBIG", EFBIG },
140
+ #endif
141
+ #ifdef EFTYPE
142
+ {"EFTYPE", EFTYPE },
143
+ #endif
144
+ #ifdef EHOSTDOWN
145
+ {"EHOSTDOWN", EHOSTDOWN },
146
+ #endif
147
+ #ifdef EHOSTUNREACH
148
+ {"EHOSTUNREACH", EHOSTUNREACH },
149
+ #endif
150
+ #ifdef EHWPOISON
151
+ {"EHWPOISON", EHWPOISON },
152
+ #endif
153
+ #ifdef EIDRM
154
+ {"EIDRM", EIDRM },
155
+ #endif
156
+ #ifdef EILSEQ
157
+ {"EILSEQ", EILSEQ },
158
+ #endif
159
+ #ifdef EINPROGRESS
160
+ {"EINPROGRESS", EINPROGRESS },
161
+ #endif
162
+ #ifdef EINTEGRITY
163
+ {"EINTEGRITY", EINTEGRITY },
164
+ #endif
165
+ #ifdef EINTR
166
+ {"EINTR", EINTR },
167
+ #endif
168
+ #ifdef EINVAL
169
+ {"EINVAL", EINVAL },
170
+ #endif
171
+ #ifdef EIO
172
+ {"EIO", EIO },
173
+ #endif
174
+ #ifdef EISCONN
175
+ {"EISCONN", EISCONN },
176
+ #endif
177
+ #ifdef EISDIR
178
+ {"EISDIR", EISDIR },
179
+ #endif
180
+ #ifdef EISNAM
181
+ {"EISNAM", EISNAM },
182
+ #endif
183
+ #ifdef EKEYEXPIRED
184
+ {"EKEYEXPIRED", EKEYEXPIRED },
185
+ #endif
186
+ #ifdef EKEYREJECTED
187
+ {"EKEYREJECTED", EKEYREJECTED },
188
+ #endif
189
+ #ifdef EKEYREVOKED
190
+ {"EKEYREVOKED", EKEYREVOKED },
191
+ #endif
192
+ #ifdef EL2HLT
193
+ {"EL2HLT", EL2HLT },
194
+ #endif
195
+ #ifdef EL2NSYNC
196
+ {"EL2NSYNC", EL2NSYNC },
197
+ #endif
198
+ #ifdef EL3HLT
199
+ {"EL3HLT", EL3HLT },
200
+ #endif
201
+ #ifdef EL3RST
202
+ {"EL3RST", EL3RST },
203
+ #endif
204
+ #ifdef ELIBACC
205
+ {"ELIBACC", ELIBACC },
206
+ #endif
207
+ #ifdef ELIBBAD
208
+ {"ELIBBAD", ELIBBAD },
209
+ #endif
210
+ #ifdef ELIBEXEC
211
+ {"ELIBEXEC", ELIBEXEC },
212
+ #endif
213
+ #ifdef ELIBMAX
214
+ {"ELIBMAX", ELIBMAX },
215
+ #endif
216
+ #ifdef ELIBSCN
217
+ {"ELIBSCN", ELIBSCN },
218
+ #endif
219
+ #ifdef ELNRNG
220
+ {"ELNRNG", ELNRNG },
221
+ #endif
222
+ #ifdef ELOOP
223
+ {"ELOOP", ELOOP },
224
+ #endif
225
+ #ifdef EMEDIUMTYPE
226
+ {"EMEDIUMTYPE", EMEDIUMTYPE },
227
+ #endif
228
+ #ifdef EMFILE
229
+ {"EMFILE", EMFILE },
230
+ #endif
231
+ #ifdef EMLINK
232
+ {"EMLINK", EMLINK },
233
+ #endif
234
+ #ifdef EMSGSIZE
235
+ {"EMSGSIZE", EMSGSIZE },
236
+ #endif
237
+ #ifdef EMULTIHOP
238
+ {"EMULTIHOP", EMULTIHOP },
239
+ #endif
240
+ #ifdef ENAMETOOLONG
241
+ {"ENAMETOOLONG", ENAMETOOLONG },
242
+ #endif
243
+ #ifdef ENAVAIL
244
+ {"ENAVAIL", ENAVAIL },
245
+ #endif
246
+ #ifdef ENEEDAUTH
247
+ {"ENEEDAUTH", ENEEDAUTH },
248
+ #endif
249
+ #ifdef ENETDOWN
250
+ {"ENETDOWN", ENETDOWN },
251
+ #endif
252
+ #ifdef ENETRESET
253
+ {"ENETRESET", ENETRESET },
254
+ #endif
255
+ #ifdef ENETUNREACH
256
+ {"ENETUNREACH", ENETUNREACH },
257
+ #endif
258
+ #ifdef ENFILE
259
+ {"ENFILE", ENFILE },
260
+ #endif
261
+ #ifdef ENOANO
262
+ {"ENOANO", ENOANO },
263
+ #endif
264
+ #ifdef ENOATTR
265
+ {"ENOATTR", ENOATTR },
266
+ #endif
267
+ #ifdef ENOBUFS
268
+ {"ENOBUFS", ENOBUFS },
269
+ #endif
270
+ #ifdef ENOCSI
271
+ {"ENOCSI", ENOCSI },
272
+ #endif
273
+ #ifdef ENODATA
274
+ {"ENODATA", ENODATA },
275
+ #endif
276
+ #ifdef ENODEV
277
+ {"ENODEV", ENODEV },
278
+ #endif
279
+ #ifdef ENOENT
280
+ {"ENOENT", ENOENT },
281
+ #endif
282
+ #ifdef ENOEXEC
283
+ {"ENOEXEC", ENOEXEC },
284
+ #endif
285
+ #ifdef ENOKEY
286
+ {"ENOKEY", ENOKEY },
287
+ #endif
288
+ #ifdef ENOLCK
289
+ {"ENOLCK", ENOLCK },
290
+ #endif
291
+ #ifdef ENOLINK
292
+ {"ENOLINK", ENOLINK },
293
+ #endif
294
+ #ifdef ENOMEDIUM
295
+ {"ENOMEDIUM", ENOMEDIUM },
296
+ #endif
297
+ #ifdef ENOMEM
298
+ {"ENOMEM", ENOMEM },
299
+ #endif
300
+ #ifdef ENOMSG
301
+ {"ENOMSG", ENOMSG },
302
+ #endif
303
+ #ifdef ENONET
304
+ {"ENONET", ENONET },
305
+ #endif
306
+ #ifdef ENOPKG
307
+ {"ENOPKG", ENOPKG },
308
+ #endif
309
+ #ifdef ENOPROTOOPT
310
+ {"ENOPROTOOPT", ENOPROTOOPT },
311
+ #endif
312
+ #ifdef ENOSPC
313
+ {"ENOSPC", ENOSPC },
314
+ #endif
315
+ #ifdef ENOSR
316
+ {"ENOSR", ENOSR },
317
+ #endif
318
+ #ifdef ENOSTR
319
+ {"ENOSTR", ENOSTR },
320
+ #endif
321
+ #ifdef ENOSYS
322
+ {"ENOSYS", ENOSYS },
323
+ #endif
324
+ #ifdef ENOTBLK
325
+ {"ENOTBLK", ENOTBLK },
326
+ #endif
327
+ #ifdef ENOTCAPABLE
328
+ {"ENOTCAPABLE", ENOTCAPABLE },
329
+ #endif
330
+ #ifdef ENOTCONN
331
+ {"ENOTCONN", ENOTCONN },
332
+ #endif
333
+ #ifdef ENOTDIR
334
+ {"ENOTDIR", ENOTDIR },
335
+ #endif
336
+ #ifdef ENOTEMPTY
337
+ {"ENOTEMPTY", ENOTEMPTY },
338
+ #endif
339
+ #ifdef ENOTNAM
340
+ {"ENOTNAM", ENOTNAM },
341
+ #endif
342
+ #ifdef ENOTRECOVERABLE
343
+ {"ENOTRECOVERABLE", ENOTRECOVERABLE },
344
+ #endif
345
+ #ifdef ENOTSOCK
346
+ {"ENOTSOCK", ENOTSOCK },
347
+ #endif
348
+ #ifdef ENOTSUP
349
+ {"ENOTSUP", ENOTSUP },
350
+ #endif
351
+ #ifdef ENOTTY
352
+ {"ENOTTY", ENOTTY },
353
+ #endif
354
+ #ifdef ENOTUNIQ
355
+ {"ENOTUNIQ", ENOTUNIQ },
356
+ #endif
357
+ #ifdef ENXIO
358
+ {"ENXIO", ENXIO },
359
+ #endif
360
+ #ifdef EOPNOTSUPP
361
+ {"EOPNOTSUPP", EOPNOTSUPP },
362
+ #endif
363
+ #ifdef EOTHER
364
+ {"EOTHER", EOTHER },
365
+ #endif
366
+ #ifdef EOVERFLOW
367
+ {"EOVERFLOW", EOVERFLOW },
368
+ #endif
369
+ #ifdef EOWNERDEAD
370
+ {"EOWNERDEAD", EOWNERDEAD },
371
+ #endif
372
+ #ifdef EPERM
373
+ {"EPERM", EPERM },
374
+ #endif
375
+ #ifdef EPFNOSUPPORT
376
+ {"EPFNOSUPPORT", EPFNOSUPPORT },
377
+ #endif
378
+ #ifdef EPIPE
379
+ {"EPIPE", EPIPE },
380
+ #endif
381
+ #ifdef EPROCLIM
382
+ {"EPROCLIM", EPROCLIM },
383
+ #endif
384
+ #ifdef EPROCUNAVAIL
385
+ {"EPROCUNAVAIL", EPROCUNAVAIL },
386
+ #endif
387
+ #ifdef EPROGMISMATCH
388
+ {"EPROGMISMATCH", EPROGMISMATCH },
389
+ #endif
390
+ #ifdef EPROGUNAVAIL
391
+ {"EPROGUNAVAIL", EPROGUNAVAIL },
392
+ #endif
393
+ #ifdef EPROTO
394
+ {"EPROTO", EPROTO },
395
+ #endif
396
+ #ifdef EPROTONOSUPPORT
397
+ {"EPROTONOSUPPORT", EPROTONOSUPPORT },
398
+ #endif
399
+ #ifdef EPROTOTYPE
400
+ {"EPROTOTYPE", EPROTOTYPE },
401
+ #endif
402
+ #ifdef ERANGE
403
+ {"ERANGE", ERANGE },
404
+ #endif
405
+ #ifdef EREMCHG
406
+ {"EREMCHG", EREMCHG },
407
+ #endif
408
+ #ifdef EREMOTE
409
+ {"EREMOTE", EREMOTE },
410
+ #endif
411
+ #ifdef EREMOTEIO
412
+ {"EREMOTEIO", EREMOTEIO },
413
+ #endif
414
+ #ifdef ERESTART
415
+ {"ERESTART", ERESTART },
416
+ #endif
417
+ #ifdef ERFKILL
418
+ {"ERFKILL", ERFKILL },
419
+ #endif
420
+ #ifdef EROFS
421
+ {"EROFS", EROFS },
422
+ #endif
423
+ #ifdef ERPCMISMATCH
424
+ {"ERPCMISMATCH", ERPCMISMATCH },
425
+ #endif
426
+ #ifdef ESHUTDOWN
427
+ {"ESHUTDOWN", ESHUTDOWN },
428
+ #endif
429
+ #ifdef ESOCKTNOSUPPORT
430
+ {"ESOCKTNOSUPPORT", ESOCKTNOSUPPORT },
431
+ #endif
432
+ #ifdef ESPIPE
433
+ {"ESPIPE", ESPIPE },
434
+ #endif
435
+ #ifdef ESRCH
436
+ {"ESRCH", ESRCH },
437
+ #endif
438
+ #ifdef ESRMNT
439
+ {"ESRMNT", ESRMNT },
440
+ #endif
441
+ #ifdef ESTALE
442
+ {"ESTALE", ESTALE },
443
+ #endif
444
+ #ifdef ESTRPIPE
445
+ {"ESTRPIPE", ESTRPIPE },
446
+ #endif
447
+ #ifdef ETIME
448
+ {"ETIME", ETIME },
449
+ #endif
450
+ #ifdef ETIMEDOUT
451
+ {"ETIMEDOUT", ETIMEDOUT },
452
+ #endif
453
+ #ifdef ETOOMANYREFS
454
+ {"ETOOMANYREFS", ETOOMANYREFS },
455
+ #endif
456
+ #ifdef ETXTBSY
457
+ {"ETXTBSY", ETXTBSY },
458
+ #endif
459
+ #ifdef EUCLEAN
460
+ {"EUCLEAN", EUCLEAN },
461
+ #endif
462
+ #ifdef EUNATCH
463
+ {"EUNATCH", EUNATCH },
464
+ #endif
465
+ #ifdef EUSERS
466
+ {"EUSERS", EUSERS },
467
+ #endif
468
+ #ifdef EWOULDBLOCK
469
+ {"EWOULDBLOCK", EWOULDBLOCK },
470
+ #endif
471
+ #ifdef EXDEV
472
+ {"EXDEV", EXDEV },
473
+ #endif
474
+ #ifdef EXFULL
475
+ {"EXFULL", EXFULL },
476
+ #endif
477
+ #ifdef STRUNCATE
478
+ {"STRUNCATE", STRUNCATE },
479
+ #endif
480
+ };
@@ -27,6 +27,7 @@
27
27
  #ifdef _WIN32
28
28
  #include "win32.hh"
29
29
  #endif
30
+ #include "errno.inc"
30
31
 
31
32
  #ifdef _WIN32
32
33
  #ifndef NOMINMAX
@@ -755,6 +756,26 @@ static Napi::Value CallFree(const Napi::CallbackInfo &info)
755
756
  return env.Undefined();
756
757
  }
757
758
 
759
+ static Napi::Value GetOrSetErrNo(const Napi::CallbackInfo &info)
760
+ {
761
+ Napi::Env env = info.Env();
762
+ InstanceData *instance = env.GetInstanceData<InstanceData>();
763
+
764
+ if (info.Length() >= 1) {
765
+ Napi::Number value = info[0].As<Napi::Number>();
766
+
767
+ if (!value.IsNumber()) {
768
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for errno, expected integer", GetValueType(instance, value));
769
+ return env.Null();
770
+ }
771
+
772
+ errno = value;
773
+ }
774
+
775
+ Napi::Number ret = Napi::Number::New(env, errno);
776
+ return ret;
777
+ }
778
+
758
779
  static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
759
780
  {
760
781
  Napi::Env env = info.Env();
@@ -795,6 +816,11 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
795
816
  ArrayHint hint = {};
796
817
 
797
818
  if (to == "Typed" || to == "typed") {
819
+ if (!(ref->flags & (int)TypeFlag::HasTypedArray)) {
820
+ ThrowError<Napi::Error>(env, "Array hint 'Typed' cannot be used with type %1", ref->name);
821
+ return env.Null();
822
+ }
823
+
798
824
  hint = ArrayHint::Typed;
799
825
  } else if (to == "Array" || to == "array") {
800
826
  hint = ArrayHint::Array;
@@ -1712,7 +1738,6 @@ void LibraryHolder::Unref() const
1712
1738
  }
1713
1739
  }
1714
1740
 
1715
-
1716
1741
  static void RegisterPrimitiveType(Napi::Env env, Napi::Object map, std::initializer_list<const char *> names,
1717
1742
  PrimitiveKind primitive, int32_t size, int16_t align, const char *ref = nullptr)
1718
1743
  {
@@ -1729,6 +1754,13 @@ static void RegisterPrimitiveType(Napi::Env env, Napi::Object map, std::initiali
1729
1754
  type->size = size;
1730
1755
  type->align = align;
1731
1756
 
1757
+ if (IsInteger(type) || IsFloat(type)) {
1758
+ type->flags |= (int)TypeFlag::HasTypedArray;
1759
+ }
1760
+ if (TestStr(type->name, "char") || TestStr(type->name, "char16") || TestStr(type->name, "char16_t")) {
1761
+ type->flags |= (int)TypeFlag::IsCharLike;
1762
+ }
1763
+
1732
1764
  if (ref) {
1733
1765
  const TypeInfo *marker = instance->types_map.FindValue(ref, nullptr);
1734
1766
  RG_ASSERT(marker);
@@ -1978,10 +2010,16 @@ static Napi::Value DecodeValue(const Napi::CallbackInfo &info)
1978
2010
 
1979
2011
  Napi::Value value = info[0];
1980
2012
  int64_t offset = has_offset ? info[1].As<Napi::Number>().Int64Value() : 0;
1981
- int64_t len = has_len ? info[2u + has_offset].As<Napi::Number>().Int64Value() : -1;
1982
2013
 
1983
- Napi::Value ret = Decode(value, offset, type, len);
1984
- return ret;
2014
+ if (has_len) {
2015
+ Size len = info[2u + has_offset].As<Napi::Number>();
2016
+
2017
+ Napi::Value ret = Decode(value, offset, type, &len);
2018
+ return ret;
2019
+ } else {
2020
+ Napi::Value ret = Decode(value, offset, type);
2021
+ return ret;
2022
+ }
1985
2023
  }
1986
2024
 
1987
2025
  static Napi::Value GetPointerAddress(const Napi::CallbackInfo &info)
@@ -2071,6 +2109,22 @@ static void SetExports(Napi::Env env, Func func)
2071
2109
  func("decode", Napi::Function::New(env, DecodeValue));
2072
2110
  func("address", Napi::Function::New(env, GetPointerAddress));
2073
2111
 
2112
+ func("errno", Napi::Function::New(env, GetOrSetErrNo));
2113
+
2114
+ Napi::Object os = Napi::Object::New(env);
2115
+ func("os", os);
2116
+
2117
+ // Init constants mapping
2118
+ {
2119
+ Napi::Object codes = Napi::Object::New(env);
2120
+
2121
+ for (const ErrnoCodeInfo &info: ErrnoCodes) {
2122
+ codes.Set(info.name, Napi::Number::New(env, info.value));
2123
+ }
2124
+
2125
+ os.Set("errno", codes);
2126
+ }
2127
+
2074
2128
  #if defined(_WIN32)
2075
2129
  func("extension", Napi::String::New(env, ".dll"));
2076
2130
  #elif defined(__APPLE__)
@@ -103,6 +103,11 @@ struct FunctionInfo;
103
103
 
104
104
  typedef void DisposeFunc (Napi::Env env, const TypeInfo *type, const void *ptr);
105
105
 
106
+ enum class TypeFlag {
107
+ HasTypedArray = 1 << 0,
108
+ IsCharLike = 1 << 1
109
+ };
110
+
106
111
  enum class ArrayHint {
107
112
  Array,
108
113
  Typed,
@@ -120,6 +125,7 @@ struct TypeInfo {
120
125
  PrimitiveKind primitive;
121
126
  int32_t size;
122
127
  int16_t align;
128
+ uint16_t flags;
123
129
 
124
130
  DisposeFunc *dispose;
125
131
  Napi::FunctionReference dispose_ref;
@@ -329,11 +329,12 @@ const TypeInfo *MakeArrayType(InstanceData *instance, const TypeInfo *ref, Size
329
329
  {
330
330
  ArrayHint hint = {};
331
331
 
332
- if (TestStr(ref->name, "char") || TestStr(ref->name, "char16") ||
333
- TestStr(ref->name, "char16_t")) {
332
+ if (ref->flags & (int)TypeFlag::IsCharLike) {
334
333
  hint = ArrayHint::String;
335
- } else {
334
+ } else if (ref->flags & (int)TypeFlag::HasTypedArray) {
336
335
  hint = ArrayHint::Typed;
336
+ } else {
337
+ hint = ArrayHint::Array;
337
338
  }
338
339
 
339
340
  return MakeArrayType(instance, ref, len, hint, true);
@@ -1002,7 +1003,7 @@ void DecodeBuffer(Span<uint8_t> buffer, const uint8_t *origin, const TypeInfo *r
1002
1003
  #undef SWAP
1003
1004
  }
1004
1005
 
1005
- Napi::Value Decode(Napi::Value value, Size offset, const TypeInfo *type, Size len)
1006
+ Napi::Value Decode(Napi::Value value, Size offset, const TypeInfo *type, const Size *len)
1006
1007
  {
1007
1008
  Napi::Env env = value.Env();
1008
1009
  InstanceData *instance = env.GetInstanceData<InstanceData>();
@@ -1035,13 +1036,35 @@ Napi::Value Decode(Napi::Value value, Size offset, const TypeInfo *type, Size le
1035
1036
  return ret;
1036
1037
  }
1037
1038
 
1038
- Napi::Value Decode(Napi::Env env, const uint8_t *ptr, const TypeInfo *type, Size len)
1039
+ Napi::Value Decode(Napi::Env env, const uint8_t *ptr, const TypeInfo *type, const Size *len)
1039
1040
  {
1040
1041
  InstanceData *instance = env.GetInstanceData<InstanceData>();
1041
1042
 
1042
- if (len >= 0 && type->primitive != PrimitiveKind::String &&
1043
- type->primitive != PrimitiveKind::String16) {
1044
- type = MakeArrayType(instance, type, len);
1043
+ if (len && type->primitive != PrimitiveKind::String &&
1044
+ type->primitive != PrimitiveKind::String16) {
1045
+ if (*len >= 0) {
1046
+ type = MakeArrayType(instance, type, *len);
1047
+ } else {
1048
+ if (RG_UNLIKELY(!(type->flags & (int)TypeFlag::IsCharLike))) {
1049
+ ThrowError<Napi::TypeError>(env, "Only char-like types can find their length automatically", type->name);
1050
+ return env.Null();
1051
+ }
1052
+
1053
+ switch (type->primitive) {
1054
+ case PrimitiveKind::Int8: {
1055
+ Size count = strlen((const char *)ptr);
1056
+ type = MakeArrayType(instance, type, count);
1057
+ } break;
1058
+ case PrimitiveKind::Int16: {
1059
+ Size count = WideStringLength((const char16_t *)ptr, RG_SIZE_MAX);
1060
+ type = MakeArrayType(instance, type, count);
1061
+ } break;
1062
+
1063
+ default: { RG_UNREACHABLE(); } break;
1064
+ }
1065
+
1066
+ }
1067
+
1045
1068
  }
1046
1069
 
1047
1070
  #define RETURN_INT(Type, NewCall) \
@@ -1080,18 +1103,18 @@ Napi::Value Decode(Napi::Env env, const uint8_t *ptr, const TypeInfo *type, Size
1080
1103
  case PrimitiveKind::UInt64: { RETURN_INT(uint64_t, NewBigInt); } break;
1081
1104
  case PrimitiveKind::UInt64S: { RETURN_INT_SWAP(uint64_t, NewBigInt); } break;
1082
1105
  case PrimitiveKind::String: {
1083
- if (len >= 0) {
1106
+ if (len) {
1084
1107
  const char *str = *(const char **)ptr;
1085
- return str ? Napi::String::New(env, str, len) : env.Null();
1108
+ return str ? Napi::String::New(env, str, *len) : env.Null();
1086
1109
  } else {
1087
1110
  const char *str = *(const char **)ptr;
1088
1111
  return str ? Napi::String::New(env, str) : env.Null();
1089
1112
  }
1090
1113
  } break;
1091
1114
  case PrimitiveKind::String16: {
1092
- if (len >= 0) {
1115
+ if (len) {
1093
1116
  const char16_t *str16 = *(const char16_t **)ptr;
1094
- return str16 ? Napi::String::New(env, str16, len) : env.Null();
1117
+ return str16 ? Napi::String::New(env, str16, *len) : env.Null();
1095
1118
  } else {
1096
1119
  const char16_t *str16 = *(const char16_t **)ptr;
1097
1120
  return str16 ? Napi::String::New(env, str16) : env.Null();
@@ -160,8 +160,8 @@ Napi::Value DecodeArray(Napi::Env env, const uint8_t *origin, const TypeInfo *ty
160
160
  void DecodeNormalArray(Napi::Array array, const uint8_t *origin, const TypeInfo *ref);
161
161
  void DecodeBuffer(Span<uint8_t> buffer, const uint8_t *origin, const TypeInfo *ref);
162
162
 
163
- Napi::Value Decode(Napi::Value value, Size offset, const TypeInfo *type, Size len = -1);
164
- Napi::Value Decode(Napi::Env env, const uint8_t *ptr, const TypeInfo *type, Size len = -1);
163
+ Napi::Value Decode(Napi::Value value, Size offset, const TypeInfo *type, const Size *len = nullptr);
164
+ Napi::Value Decode(Napi::Env env, const uint8_t *ptr, const TypeInfo *type, const Size *len = nullptr);
165
165
 
166
166
  static inline Napi::Value NewBigInt(Napi::Env env, int64_t value)
167
167
  {