koffi 2.3.14 → 2.3.16

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 (50) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/build/2.3.16/koffi_darwin_arm64/koffi.node +0 -0
  3. package/build/{2.3.14 → 2.3.16}/koffi_darwin_x64/koffi.node +0 -0
  4. package/build/2.3.16/koffi_freebsd_arm64/koffi.node +0 -0
  5. package/build/2.3.16/koffi_freebsd_ia32/koffi.node +0 -0
  6. package/build/2.3.16/koffi_freebsd_x64/koffi.node +0 -0
  7. package/build/2.3.16/koffi_linux_arm32hf/koffi.node +0 -0
  8. package/build/2.3.16/koffi_linux_arm64/koffi.node +0 -0
  9. package/build/2.3.16/koffi_linux_ia32/koffi.node +0 -0
  10. package/build/2.3.16/koffi_linux_riscv64hf64/koffi.node +0 -0
  11. package/build/2.3.16/koffi_linux_x64/koffi.node +0 -0
  12. package/build/2.3.16/koffi_openbsd_ia32/koffi.node +0 -0
  13. package/build/2.3.16/koffi_openbsd_x64/koffi.node +0 -0
  14. package/build/2.3.16/koffi_win32_arm64/koffi.exp +0 -0
  15. package/build/2.3.16/koffi_win32_arm64/koffi.lib +0 -0
  16. package/build/2.3.16/koffi_win32_arm64/koffi.node +0 -0
  17. package/build/{2.3.14 → 2.3.16}/koffi_win32_ia32/koffi.exp +0 -0
  18. package/build/{2.3.14 → 2.3.16}/koffi_win32_ia32/koffi.lib +0 -0
  19. package/build/2.3.16/koffi_win32_ia32/koffi.node +0 -0
  20. package/build/{2.3.14 → 2.3.16}/koffi_win32_x64/koffi.exp +0 -0
  21. package/build/{2.3.14/koffi_win32_arm64 → 2.3.16/koffi_win32_x64}/koffi.lib +0 -0
  22. package/build/2.3.16/koffi_win32_x64/koffi.node +0 -0
  23. package/doc/calls.md +54 -0
  24. package/package.json +2 -2
  25. package/src/cnoke/cnoke.js +1 -1
  26. package/src/cnoke/package.json +1 -1
  27. package/src/cnoke/src/builder.js +2 -2
  28. package/src/core/libcc/libcc.cc +15 -0
  29. package/src/core/libcc/libcc.hh +7 -1
  30. package/src/koffi/CMakeLists.txt +4 -0
  31. package/src/koffi/src/ffi.cc +40 -18
  32. package/src/koffi/src/ffi.hh +6 -0
  33. package/src/koffi/src/util.cc +35 -12
  34. package/src/koffi/src/util.hh +2 -2
  35. package/build/2.3.14/koffi_darwin_arm64/koffi.node +0 -0
  36. package/build/2.3.14/koffi_freebsd_arm64/koffi.node +0 -0
  37. package/build/2.3.14/koffi_freebsd_ia32/koffi.node +0 -0
  38. package/build/2.3.14/koffi_freebsd_x64/koffi.node +0 -0
  39. package/build/2.3.14/koffi_linux_arm32hf/koffi.node +0 -0
  40. package/build/2.3.14/koffi_linux_arm64/koffi.node +0 -0
  41. package/build/2.3.14/koffi_linux_ia32/koffi.node +0 -0
  42. package/build/2.3.14/koffi_linux_riscv64hf64/koffi.node +0 -0
  43. package/build/2.3.14/koffi_linux_x64/koffi.node +0 -0
  44. package/build/2.3.14/koffi_openbsd_ia32/koffi.node +0 -0
  45. package/build/2.3.14/koffi_openbsd_x64/koffi.node +0 -0
  46. package/build/2.3.14/koffi_win32_arm64/koffi.exp +0 -0
  47. package/build/2.3.14/koffi_win32_arm64/koffi.node +0 -0
  48. package/build/2.3.14/koffi_win32_ia32/koffi.node +0 -0
  49. package/build/2.3.14/koffi_win32_x64/koffi.lib +0 -0
  50. package/build/2.3.14/koffi_win32_x64/koffi.node +0 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,24 @@
4
4
 
5
5
  ### Koffi 2.3
6
6
 
7
+ #### Koffi 2.3.16
8
+
9
+ **Main changes:**
10
+
11
+ - Fix Windows ARM64 build to work with official Node.js version
12
+ - Compile Windows builds with Visual Studio 2022 17.5.3
13
+
14
+ **Other changes:**
15
+
16
+ - Support null in koffi.free() and koffi.address()
17
+
18
+ #### Koffi 2.3.15
19
+
20
+ **Main changes:**
21
+
22
+ - Improve manual decoding of 0-terminated strings
23
+ - Add checks around array conversion hints
24
+
7
25
  #### Koffi 2.3.14
8
26
 
9
27
  **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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "2.3.14",
4
- "stable": "2.3.14",
3
+ "version": "2.3.16",
4
+ "stable": "2.3.16",
5
5
  "description": "Fast and simple C FFI (foreign function interface) for Node.js",
6
6
  "keywords": [
7
7
  "foreign",
@@ -155,7 +155,7 @@ Options:
155
155
  (default: ${cnoke.DefaultOptions.mode})
156
156
  -D, --debug Shortcut for --config Debug
157
157
 
158
- --prebuild Use prebuilt binari if available
158
+ --prebuild Use prebuilt binary if available
159
159
 
160
160
  -a, --arch <ARCH> Change architecture and ABI
161
161
  (default: ${cnoke.determine_arch()})
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cnoke",
3
- "version": "3.3.1",
3
+ "version": "3.3.2",
4
4
  "description": "Build native Node addons based on CMake, without extra dependency",
5
5
  "keywords": [
6
6
  "native",
@@ -103,7 +103,7 @@ function Builder(config = {}) {
103
103
  let basename = `node-v${runtime_version}-headers.tar.gz`;
104
104
  let urls = [
105
105
  `https://nodejs.org/dist/v${runtime_version}/${basename}`,
106
- `https://unofficial-builds.nodejs.org/download/release/v${runtime_version}/${basename}`
106
+ // `https://unofficial-builds.nodejs.org/download/release/v${runtime_version}/${basename}`
107
107
  ];
108
108
  let destname = `${cache_dir}/${basename}`;
109
109
 
@@ -130,7 +130,7 @@ function Builder(config = {}) {
130
130
  if (!fs.existsSync(destname)) {
131
131
  let urls = [
132
132
  `https://nodejs.org/dist/v${runtime_version}/${dirname}/node.lib`,
133
- `https://unofficial-builds.nodejs.org/download/release/v${runtime_version}/${dirname}/node.lib`
133
+ // `https://unofficial-builds.nodejs.org/download/release/v${runtime_version}/${dirname}/node.lib`
134
134
  ];
135
135
  await tools.download_http(urls, destname);
136
136
  }
@@ -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)
@@ -20,6 +20,8 @@
20
20
  # OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
22
  cmake_minimum_required(VERSION 3.6)
23
+ cmake_policy(SET CMP0091 NEW)
24
+
23
25
  project(koffi C CXX ASM)
24
26
 
25
27
  include(CheckCXXCompilerFlag)
@@ -58,6 +60,8 @@ if(CMAKE_SIZEOF_VOID_P EQUAL 8)
58
60
  # CMAKE_SYSTEM_PROCESSOR is wrong on Windows ARM64
59
61
 
60
62
  if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch|arm" OR CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64" OR CMAKE_OSX_ARCHITECTURES MATCHES "arm")
63
+ set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded)
64
+
61
65
  if(WIN32)
62
66
  get_filename_component(cl_dir "${CMAKE_CXX_COMPILER}" DIRECTORY)
63
67
  file(TO_CMAKE_PATH "${cl_dir}/armasm64.exe" asm_compiler)
@@ -723,18 +723,25 @@ static Napi::Value CreateDisposableType(const Napi::CallbackInfo &info)
723
723
  return WrapType(env, instance, type);
724
724
  }
725
725
 
726
- static inline bool CheckExternalPointer(Napi::Env env, Napi::Value value)
726
+ static inline bool GetExternalPointer(Napi::Env env, Napi::Value value, void **out_ptr)
727
727
  {
728
728
  InstanceData *instance = env.GetInstanceData<InstanceData>();
729
729
 
730
- if (!value.IsExternal() || CheckValueTag(instance, value, &TypeInfoMarker) ||
731
- CheckValueTag(instance, value, &CastMarker) ||
732
- CheckValueTag(instance, value, &MagicUnionMarker)) {
730
+ if (IsNullOrUndefined(value)) {
731
+ *out_ptr = 0;
732
+ return true;
733
+ } else if (value.IsExternal() && !CheckValueTag(instance, value, &TypeInfoMarker) &&
734
+ !CheckValueTag(instance, value, &CastMarker) &&
735
+ !CheckValueTag(instance, value, &MagicUnionMarker)) {
736
+ Napi::External<void> external = value.As<Napi::External<void>>();
737
+ void *ptr = external.Data();
738
+
739
+ *out_ptr = ptr;
740
+ return true;
741
+ } else {
733
742
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for ptr, expected external pointer", GetValueType(instance, value));
734
743
  return false;
735
744
  }
736
-
737
- return true;
738
745
  }
739
746
 
740
747
  static Napi::Value CallFree(const Napi::CallbackInfo &info)
@@ -745,11 +752,10 @@ static Napi::Value CallFree(const Napi::CallbackInfo &info)
745
752
  ThrowError<Napi::TypeError>(env, "Expected 1 argument, got %1", info.Length());
746
753
  return env.Null();
747
754
  }
748
- if (!CheckExternalPointer(env, info[0]))
749
- return env.Null();
750
755
 
751
- Napi::External<void> external = info[0].As<Napi::External<void>>();
752
- void *ptr = external.Data();
756
+ void *ptr = nullptr;
757
+ if (!GetExternalPointer(env, info[0], &ptr))
758
+ return env.Null();
753
759
 
754
760
  free(ptr);
755
761
 
@@ -816,6 +822,11 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
816
822
  ArrayHint hint = {};
817
823
 
818
824
  if (to == "Typed" || to == "typed") {
825
+ if (!(ref->flags & (int)TypeFlag::HasTypedArray)) {
826
+ ThrowError<Napi::Error>(env, "Array hint 'Typed' cannot be used with type %1", ref->name);
827
+ return env.Null();
828
+ }
829
+
819
830
  hint = ArrayHint::Typed;
820
831
  } else if (to == "Array" || to == "array") {
821
832
  hint = ArrayHint::Array;
@@ -1733,7 +1744,6 @@ void LibraryHolder::Unref() const
1733
1744
  }
1734
1745
  }
1735
1746
 
1736
-
1737
1747
  static void RegisterPrimitiveType(Napi::Env env, Napi::Object map, std::initializer_list<const char *> names,
1738
1748
  PrimitiveKind primitive, int32_t size, int16_t align, const char *ref = nullptr)
1739
1749
  {
@@ -1750,6 +1760,13 @@ static void RegisterPrimitiveType(Napi::Env env, Napi::Object map, std::initiali
1750
1760
  type->size = size;
1751
1761
  type->align = align;
1752
1762
 
1763
+ if (IsInteger(type) || IsFloat(type)) {
1764
+ type->flags |= (int)TypeFlag::HasTypedArray;
1765
+ }
1766
+ if (TestStr(type->name, "char") || TestStr(type->name, "char16") || TestStr(type->name, "char16_t")) {
1767
+ type->flags |= (int)TypeFlag::IsCharLike;
1768
+ }
1769
+
1753
1770
  if (ref) {
1754
1771
  const TypeInfo *marker = instance->types_map.FindValue(ref, nullptr);
1755
1772
  RG_ASSERT(marker);
@@ -1999,10 +2016,16 @@ static Napi::Value DecodeValue(const Napi::CallbackInfo &info)
1999
2016
 
2000
2017
  Napi::Value value = info[0];
2001
2018
  int64_t offset = has_offset ? info[1].As<Napi::Number>().Int64Value() : 0;
2002
- int64_t len = has_len ? info[2u + has_offset].As<Napi::Number>().Int64Value() : -1;
2003
2019
 
2004
- Napi::Value ret = Decode(value, offset, type, len);
2005
- return ret;
2020
+ if (has_len) {
2021
+ Size len = info[2u + has_offset].As<Napi::Number>();
2022
+
2023
+ Napi::Value ret = Decode(value, offset, type, &len);
2024
+ return ret;
2025
+ } else {
2026
+ Napi::Value ret = Decode(value, offset, type);
2027
+ return ret;
2028
+ }
2006
2029
  }
2007
2030
 
2008
2031
  static Napi::Value GetPointerAddress(const Napi::CallbackInfo &info)
@@ -2013,11 +2036,10 @@ static Napi::Value GetPointerAddress(const Napi::CallbackInfo &info)
2013
2036
  ThrowError<Napi::TypeError>(env, "Expected 1 argument, got %1", info.Length());
2014
2037
  return env.Null();
2015
2038
  }
2016
- if (!CheckExternalPointer(env, info[0]))
2017
- return env.Null();
2018
2039
 
2019
- Napi::External<void> external = info[0].As<Napi::External<void>>();
2020
- void *ptr = external.Data();
2040
+ void *ptr = nullptr;
2041
+ if (!GetExternalPointer(env, info[0], &ptr))
2042
+ return env.Null();
2021
2043
 
2022
2044
  uint64_t ptr64 = (uint64_t)(uintptr_t)ptr;
2023
2045
  Napi::BigInt bigint = Napi::BigInt::New(env, ptr64);
@@ -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
  {