koffi 2.7.0 → 2.7.1
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/CHANGELOG.md +5 -0
- package/build/koffi/darwin_arm64/koffi.node +0 -0
- package/build/koffi/darwin_x64/koffi.node +0 -0
- package/build/koffi/freebsd_arm64/koffi.node +0 -0
- package/build/koffi/freebsd_ia32/koffi.node +0 -0
- package/build/koffi/freebsd_x64/koffi.node +0 -0
- package/build/koffi/linux_arm32hf/koffi.node +0 -0
- package/build/koffi/linux_arm64/koffi.node +0 -0
- package/build/koffi/linux_ia32/koffi.node +0 -0
- package/build/koffi/linux_riscv64hf64/koffi.node +0 -0
- package/build/koffi/linux_x64/koffi.node +0 -0
- package/build/koffi/openbsd_ia32/koffi.node +0 -0
- package/build/koffi/openbsd_x64/koffi.node +0 -0
- package/build/koffi/win32_arm64/koffi.node +0 -0
- package/build/koffi/win32_ia32/koffi.node +0 -0
- package/build/koffi/win32_x64/koffi.node +0 -0
- package/doc/functions.md +1 -1
- package/doc/input.md +19 -4
- package/index.d.ts +3 -2
- package/index.js +2 -2
- package/indirect.js +2 -2
- package/package.json +2 -2
- package/src/core/libcc/libcc.cc +23 -18
- package/src/core/libcc/libcc.hh +18 -4
- package/src/koffi/src/parser.cc +4 -0
- package/src/koffi/src/util.cc +64 -14
- package/src/koffi/src/util.hh +1 -4
- package/src/koffi/src/win32.cc +1 -0
- package/src/koffi/src/win32.hh +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
### Koffi 2.7
|
|
6
6
|
|
|
7
|
+
#### Koffi 2.7.1 (2024-01-02)
|
|
8
|
+
|
|
9
|
+
- Support C-like `int[3]` syntax for [fixed array types](input.md#fixed-size-c-arrays)
|
|
10
|
+
- Refuse type specifiers with invalid tokens at the end (previously ignored)
|
|
11
|
+
|
|
7
12
|
#### Koffi 2.7.0 (2023-12-21)
|
|
8
13
|
|
|
9
14
|
- Support alternative [callback calling convention](callbacks.md#callback-types) in classic syntax
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/doc/functions.md
CHANGED
|
@@ -9,7 +9,7 @@ To declare functions, start by loading the shared library with `koffi.load(filen
|
|
|
9
9
|
const koffi = require('koffi');
|
|
10
10
|
|
|
11
11
|
const lib = koffi.load('/path/to/shared/library'); // File extension depends on platforms: .so, .dll, .dylib, etc.
|
|
12
|
-
|
|
12
|
+
```
|
|
13
13
|
|
|
14
14
|
This library will be automatically unloaded once all references to it are gone (including all the functions that use it, as described below).
|
|
15
15
|
|
package/doc/input.md
CHANGED
|
@@ -336,6 +336,8 @@ try {
|
|
|
336
336
|
|
|
337
337
|
### Fixed-size C arrays
|
|
338
338
|
|
|
339
|
+
*Changed in Koffi 2.7.1*
|
|
340
|
+
|
|
339
341
|
Fixed-size arrays are declared with `koffi.array(type, length)`. Just like in C, they cannot be passed as functions parameters (they degenerate to pointers), or returned by value. You can however embed them in struct types.
|
|
340
342
|
|
|
341
343
|
Koffi applies the following conversion rules when passing arrays to/from C:
|
|
@@ -350,13 +352,13 @@ See the example below:
|
|
|
350
352
|
const koffi = require('koffi');
|
|
351
353
|
|
|
352
354
|
// Those two structs are exactly the same, only the array conversion hint is different
|
|
353
|
-
const Foo1 = koffi.struct('
|
|
355
|
+
const Foo1 = koffi.struct('Foo1', {
|
|
354
356
|
i: 'int',
|
|
355
|
-
a16: koffi.array('int16_t',
|
|
357
|
+
a16: koffi.array('int16_t', 2)
|
|
356
358
|
});
|
|
357
|
-
const Foo2 = koffi.struct('
|
|
359
|
+
const Foo2 = koffi.struct('Foo2', {
|
|
358
360
|
i: 'int',
|
|
359
|
-
a16: koffi.array('int16_t',
|
|
361
|
+
a16: koffi.array('int16_t', 2, 'Array')
|
|
360
362
|
});
|
|
361
363
|
|
|
362
364
|
// Uses an hypothetical C function that just returns the struct passed as a parameter
|
|
@@ -367,6 +369,19 @@ console.log(ReturnFoo1({ i: 5, a16: [6, 8] })) // Prints { i: 5, a16: Int16Array
|
|
|
367
369
|
console.log(ReturnFoo2({ i: 5, a16: [6, 8] })) // Prints { i: 5, a16: [6, 8] }
|
|
368
370
|
```
|
|
369
371
|
|
|
372
|
+
You can also declare arrays with the C-like short syntax in type declarations, as shown below:
|
|
373
|
+
|
|
374
|
+
```js
|
|
375
|
+
const StructType = koffi.struct('StructType', {
|
|
376
|
+
f8: 'float [8]',
|
|
377
|
+
self4: 'StructType *[4]'
|
|
378
|
+
});
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
```{note}
|
|
382
|
+
The short C-like syntax was introduced in Koffi 2.7.1, use `koffi.array()` for older versions.
|
|
383
|
+
```
|
|
384
|
+
|
|
370
385
|
### Fixed-size string buffers
|
|
371
386
|
|
|
372
387
|
Koffi can also convert JS strings to fixed-sized arrays in the following cases:
|
package/index.d.ts
CHANGED
|
@@ -104,8 +104,8 @@ declare module 'koffi' {
|
|
|
104
104
|
/** @deprecated */ export function handle(): IKoffiCType;
|
|
105
105
|
|
|
106
106
|
export function pointer(ref: TypeSpec): IKoffiCType;
|
|
107
|
-
export function pointer(ref: TypeSpec, asteriskCount
|
|
108
|
-
export function pointer(name: string, ref: TypeSpec, asteriskCount
|
|
107
|
+
export function pointer(ref: TypeSpec, asteriskCount?: number): IKoffiCType;
|
|
108
|
+
export function pointer(name: string, ref: TypeSpec, asteriskCount?: number): IKoffiCType;
|
|
109
109
|
|
|
110
110
|
export function out(type: TypeSpec): IKoffiCType;
|
|
111
111
|
export function inout(type: TypeSpec): IKoffiCType;
|
|
@@ -131,6 +131,7 @@ declare module 'koffi' {
|
|
|
131
131
|
export function decode(value: any, offset: number, type: TypeSpec): any;
|
|
132
132
|
export function decode(value: any, offset: number, type: TypeSpec, len: number): any;
|
|
133
133
|
export function address(value: any): bigint;
|
|
134
|
+
export function call(value: any, type: TypeSpec, ...args: any[]): any;
|
|
134
135
|
export function encode(ref: any, type: TypeSpec): void;
|
|
135
136
|
export function encode(ref: any, type: TypeSpec, value: any): void;
|
|
136
137
|
export function encode(ref: any, type: TypeSpec, value: any, len: number): void;
|
package/index.js
CHANGED
|
@@ -378,8 +378,8 @@ var require_package = __commonJS({
|
|
|
378
378
|
"build/dist/src/koffi/package.json"(exports2, module2) {
|
|
379
379
|
module2.exports = {
|
|
380
380
|
name: "koffi",
|
|
381
|
-
version: "2.7.
|
|
382
|
-
stable: "2.7.
|
|
381
|
+
version: "2.7.1",
|
|
382
|
+
stable: "2.7.1",
|
|
383
383
|
description: "Fast and simple C FFI (foreign function interface) for Node.js",
|
|
384
384
|
keywords: [
|
|
385
385
|
"foreign",
|
package/indirect.js
CHANGED
|
@@ -378,8 +378,8 @@ var require_package = __commonJS({
|
|
|
378
378
|
"build/dist/src/koffi/package.json"(exports2, module2) {
|
|
379
379
|
module2.exports = {
|
|
380
380
|
name: "koffi",
|
|
381
|
-
version: "2.7.
|
|
382
|
-
stable: "2.7.
|
|
381
|
+
version: "2.7.1",
|
|
382
|
+
stable: "2.7.1",
|
|
383
383
|
description: "Fast and simple C FFI (foreign function interface) for Node.js",
|
|
384
384
|
keywords: [
|
|
385
385
|
"foreign",
|
package/package.json
CHANGED
package/src/core/libcc/libcc.cc
CHANGED
|
@@ -3881,8 +3881,11 @@ bool CreateOverlappedPipe(bool overlap0, bool overlap1, PipeMode mode, HANDLE ou
|
|
|
3881
3881
|
|
|
3882
3882
|
void CloseHandleSafe(HANDLE *handle_ptr)
|
|
3883
3883
|
{
|
|
3884
|
-
|
|
3885
|
-
|
|
3884
|
+
HANDLE h = *handle_ptr;
|
|
3885
|
+
|
|
3886
|
+
if (h && h != INVALID_HANDLE_VALUE) {
|
|
3887
|
+
CancelIo(h);
|
|
3888
|
+
CloseHandle(h);
|
|
3886
3889
|
}
|
|
3887
3890
|
|
|
3888
3891
|
*handle_ptr = nullptr;
|
|
@@ -4021,6 +4024,7 @@ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
|
|
|
4021
4024
|
CloseHandleSafe(&si.hStdOutput);
|
|
4022
4025
|
CloseHandleSafe(&si.hStdError);
|
|
4023
4026
|
};
|
|
4027
|
+
|
|
4024
4028
|
if (in_func.IsValid() || out_func.IsValid()) {
|
|
4025
4029
|
if (!DuplicateHandle(GetCurrentProcess(), in_func.IsValid() ? in_pipe[0] : GetStdHandle(STD_INPUT_HANDLE),
|
|
4026
4030
|
GetCurrentProcess(), &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS)) {
|
|
@@ -4095,6 +4099,7 @@ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
|
|
|
4095
4099
|
if (proc_in.err && proc_in.err != ERROR_BROKEN_PIPE && proc_in.err != ERROR_NO_DATA) {
|
|
4096
4100
|
LogError("Failed to write to process: %1", GetWin32ErrorString(proc_in.err));
|
|
4097
4101
|
}
|
|
4102
|
+
|
|
4098
4103
|
proc_in.pending = true;
|
|
4099
4104
|
}
|
|
4100
4105
|
|
|
@@ -4111,30 +4116,23 @@ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
|
|
|
4111
4116
|
}
|
|
4112
4117
|
}
|
|
4113
4118
|
|
|
4114
|
-
if (proc_out.err
|
|
4115
|
-
|
|
4119
|
+
if (proc_out.err) {
|
|
4120
|
+
if (proc_out.err != ERROR_BROKEN_PIPE && proc_out.err != ERROR_NO_DATA) {
|
|
4121
|
+
LogError("Failed to read process output: %1", GetWin32ErrorString(proc_out.err));
|
|
4122
|
+
}
|
|
4123
|
+
break;
|
|
4116
4124
|
}
|
|
4125
|
+
|
|
4117
4126
|
proc_out.pending = true;
|
|
4118
4127
|
}
|
|
4119
4128
|
|
|
4120
|
-
|
|
4121
|
-
process_handle,
|
|
4122
|
-
console_ctrl_event
|
|
4123
|
-
};
|
|
4124
|
-
|
|
4125
|
-
running = (WaitForMultipleObjectsEx(RG_LEN(events), events, FALSE, INFINITE, TRUE) > WAIT_OBJECT_0 + 1);
|
|
4129
|
+
running = (WaitForSingleObjectEx(console_ctrl_event, INFINITE, TRUE) != WAIT_OBJECT_0);
|
|
4126
4130
|
}
|
|
4127
4131
|
}
|
|
4128
4132
|
|
|
4129
4133
|
// Terminate any remaining I/O
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
CloseHandleSafe(&in_pipe[1]);
|
|
4133
|
-
}
|
|
4134
|
-
if (out_pipe[0]) {
|
|
4135
|
-
CancelIo(out_pipe[0]);
|
|
4136
|
-
CloseHandleSafe(&out_pipe[0]);
|
|
4137
|
-
}
|
|
4134
|
+
CloseHandleSafe(&in_pipe[1]);
|
|
4135
|
+
CloseHandleSafe(&out_pipe[0]);
|
|
4138
4136
|
|
|
4139
4137
|
// Wait for process exit
|
|
4140
4138
|
{
|
|
@@ -7518,6 +7516,13 @@ void OptionParser::LogUnknownError() const
|
|
|
7518
7516
|
}
|
|
7519
7517
|
}
|
|
7520
7518
|
|
|
7519
|
+
void OptionParser::LogUnusedArguments() const
|
|
7520
|
+
{
|
|
7521
|
+
if (pos < args.len) {
|
|
7522
|
+
LogWarning("Unused command-line arguments");
|
|
7523
|
+
}
|
|
7524
|
+
}
|
|
7525
|
+
|
|
7521
7526
|
// ------------------------------------------------------------------------
|
|
7522
7527
|
// Console prompter (simplified readline)
|
|
7523
7528
|
// ------------------------------------------------------------------------
|
package/src/core/libcc/libcc.hh
CHANGED
|
@@ -1220,11 +1220,22 @@ public:
|
|
|
1220
1220
|
{
|
|
1221
1221
|
RG_ASSERT(len <= N - count);
|
|
1222
1222
|
|
|
1223
|
-
T *
|
|
1224
|
-
|
|
1225
|
-
|
|
1223
|
+
T *first = data + len;
|
|
1224
|
+
#if __cplusplus >= 201703L
|
|
1225
|
+
if constexpr(!std::is_trivial<T>::value) {
|
|
1226
|
+
#else
|
|
1227
|
+
if (true) {
|
|
1228
|
+
#endif
|
|
1229
|
+
for (Size i = 0; i < count; i++) {
|
|
1230
|
+
new (data + len) T();
|
|
1231
|
+
len++;
|
|
1232
|
+
}
|
|
1233
|
+
} else {
|
|
1234
|
+
memset_safe(first, 0, count * RG_SIZE(T));
|
|
1235
|
+
len += count;
|
|
1236
|
+
}
|
|
1226
1237
|
|
|
1227
|
-
return
|
|
1238
|
+
return first;
|
|
1228
1239
|
}
|
|
1229
1240
|
|
|
1230
1241
|
T *Append(const T &value)
|
|
@@ -1434,6 +1445,7 @@ public:
|
|
|
1434
1445
|
memset_safe(first, 0, count * RG_SIZE(T));
|
|
1435
1446
|
len += count;
|
|
1436
1447
|
}
|
|
1448
|
+
|
|
1437
1449
|
return first;
|
|
1438
1450
|
}
|
|
1439
1451
|
|
|
@@ -4887,7 +4899,9 @@ public:
|
|
|
4887
4899
|
{ return Test(test1, nullptr, type); }
|
|
4888
4900
|
|
|
4889
4901
|
bool TestHasFailed() const { return test_failed; }
|
|
4902
|
+
|
|
4890
4903
|
void LogUnknownError() const;
|
|
4904
|
+
void LogUnusedArguments() const;
|
|
4891
4905
|
};
|
|
4892
4906
|
|
|
4893
4907
|
template <typename T>
|
package/src/koffi/src/parser.cc
CHANGED
|
@@ -144,6 +144,10 @@ const TypeInfo *PrototypeParser::ParseType(int *out_directions)
|
|
|
144
144
|
while (++offset < tokens.len && (tokens[offset] == '*' ||
|
|
145
145
|
tokens[offset] == '!' ||
|
|
146
146
|
tokens[offset] == "const"));
|
|
147
|
+
if (offset < tokens.len && tokens[offset] == "[") [[unlikely]] {
|
|
148
|
+
MarkError("Array types decay to pointers in prototypes (C standard), use pointers");
|
|
149
|
+
return instance->void_type;
|
|
150
|
+
}
|
|
147
151
|
offset--;
|
|
148
152
|
|
|
149
153
|
while (offset >= start) {
|
package/src/koffi/src/util.cc
CHANGED
|
@@ -192,7 +192,8 @@ const TypeInfo *ResolveType(Napi::Env env, Span<const char> str, int *out_direct
|
|
|
192
192
|
{
|
|
193
193
|
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
194
194
|
|
|
195
|
-
|
|
195
|
+
// Each item can be > 0 for array or 0 for a pointer
|
|
196
|
+
LocalArray<Size, 8> arrays;
|
|
196
197
|
uint8_t disposables = 0;
|
|
197
198
|
|
|
198
199
|
// Consume parameter direction qualifier
|
|
@@ -223,18 +224,18 @@ const TypeInfo *ResolveType(Napi::Env env, Span<const char> str, int *out_direct
|
|
|
223
224
|
Span<const char> remain = str;
|
|
224
225
|
|
|
225
226
|
// Skip initial const qualifiers
|
|
226
|
-
remain =
|
|
227
|
+
remain = TrimStrLeft(remain);
|
|
227
228
|
while (SplitIdentifier(remain) == "const") {
|
|
228
229
|
remain = remain.Take(6, remain.len - 6);
|
|
229
|
-
remain =
|
|
230
|
+
remain = TrimStrLeft(remain);
|
|
230
231
|
}
|
|
231
|
-
remain =
|
|
232
|
+
remain = TrimStrLeft(remain);
|
|
232
233
|
|
|
233
234
|
after = remain;
|
|
234
235
|
|
|
235
236
|
// Consume one or more identifiers (e.g. unsigned int)
|
|
236
237
|
for (;;) {
|
|
237
|
-
after =
|
|
238
|
+
after = TrimStrLeft(after);
|
|
238
239
|
|
|
239
240
|
Span<const char> token = SplitIdentifier(after);
|
|
240
241
|
if (!token.len)
|
|
@@ -245,26 +246,57 @@ const TypeInfo *ResolveType(Napi::Env env, Span<const char> str, int *out_direct
|
|
|
245
246
|
name = TrimStr(MakeSpan(remain.ptr, after.ptr - remain.ptr));
|
|
246
247
|
}
|
|
247
248
|
|
|
248
|
-
// Consume pointer
|
|
249
|
+
// Consume type indirections (pointer, array, etc.)
|
|
249
250
|
while (after.len) {
|
|
250
251
|
if (after[0] == '*') {
|
|
251
252
|
after = after.Take(1, after.len - 1);
|
|
252
|
-
indirect++;
|
|
253
253
|
|
|
254
|
-
if (
|
|
255
|
-
ThrowError<Napi::Error>(env, "Too many
|
|
254
|
+
if (!arrays.Available()) [[unlikely]] {
|
|
255
|
+
ThrowError<Napi::Error>(env, "Too many type indirections");
|
|
256
256
|
return nullptr;
|
|
257
257
|
}
|
|
258
|
+
|
|
259
|
+
arrays.Append(0);
|
|
258
260
|
} else if (after[0] == '!') {
|
|
259
261
|
after = after.Take(1, after.len - 1);
|
|
260
|
-
disposables |= (1u <<
|
|
262
|
+
disposables |= (1u << arrays.len);
|
|
263
|
+
} else if (after[0] == '[') {
|
|
264
|
+
after = after.Take(1, after.len - 1);
|
|
265
|
+
|
|
266
|
+
Size len = 0;
|
|
267
|
+
|
|
268
|
+
after = TrimStrLeft(after);
|
|
269
|
+
if (!ParseInt(after, &len, 0, &after) || len < 0) [[unlikely]] {
|
|
270
|
+
ThrowError<Napi::Error>(env, "Invalid array length");
|
|
271
|
+
return nullptr;
|
|
272
|
+
}
|
|
273
|
+
after = TrimStrLeft(after);
|
|
274
|
+
if (!after.len || after[0] != ']') [[unlikely]] {
|
|
275
|
+
ThrowError<Napi::Error>(env, "Expected ']' after array length");
|
|
276
|
+
return nullptr;
|
|
277
|
+
}
|
|
278
|
+
after = after.Take(1, after.len - 1);
|
|
279
|
+
|
|
280
|
+
if (!arrays.Available()) [[unlikely]] {
|
|
281
|
+
ThrowError<Napi::Error>(env, "Too many type indirections");
|
|
282
|
+
return nullptr;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
arrays.Append(len);
|
|
261
286
|
} else if (SplitIdentifier(after) == "const") {
|
|
262
287
|
after = after.Take(6, after.len - 6);
|
|
263
288
|
} else {
|
|
289
|
+
after = TrimStrRight(after);
|
|
290
|
+
|
|
291
|
+
if (after.len) [[unlikely]] {
|
|
292
|
+
ThrowError<Napi::Error>(env, "Unexpected character '%1' in type specifier", after[0]);
|
|
293
|
+
return nullptr;
|
|
294
|
+
}
|
|
295
|
+
|
|
264
296
|
break;
|
|
265
297
|
}
|
|
266
298
|
|
|
267
|
-
after =
|
|
299
|
+
after = TrimStrLeft(after);
|
|
268
300
|
}
|
|
269
301
|
|
|
270
302
|
const TypeInfo *type = instance->types_map.FindValue(name, nullptr);
|
|
@@ -317,11 +349,29 @@ const TypeInfo *ResolveType(Napi::Env env, Span<const char> str, int *out_direct
|
|
|
317
349
|
type = copy;
|
|
318
350
|
}
|
|
319
351
|
|
|
320
|
-
if (i >=
|
|
352
|
+
if (i >= arrays.len)
|
|
321
353
|
break;
|
|
354
|
+
Size len = arrays[i];
|
|
322
355
|
|
|
323
|
-
|
|
324
|
-
|
|
356
|
+
if (len > 0) {
|
|
357
|
+
if (type->flags & (int)TypeFlag::IsIncomplete) [[unlikely]] {
|
|
358
|
+
ThrowError<Napi::TypeError>(env, "Cannot make array of incomplete type");
|
|
359
|
+
return nullptr;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (len > instance->config.max_type_size / type->size) {
|
|
363
|
+
ThrowError<Napi::TypeError>(env, "Array length is too high (max = %1)", instance->config.max_type_size / type->size);
|
|
364
|
+
return nullptr;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
type = MakeArrayType(instance, type, len);
|
|
368
|
+
RG_ASSERT(type);
|
|
369
|
+
} else {
|
|
370
|
+
RG_ASSERT(!len);
|
|
371
|
+
|
|
372
|
+
type = MakePointerType(instance, type);
|
|
373
|
+
RG_ASSERT(type);
|
|
374
|
+
}
|
|
325
375
|
}
|
|
326
376
|
|
|
327
377
|
if (type->flags & (int)TypeFlag::IsIncomplete) [[unlikely]] {
|
package/src/koffi/src/util.hh
CHANGED
|
@@ -22,15 +22,12 @@
|
|
|
22
22
|
#pragma once
|
|
23
23
|
|
|
24
24
|
#include "src/core/libcc/libcc.hh"
|
|
25
|
+
#include "ffi.hh"
|
|
25
26
|
|
|
26
27
|
#include <napi.h>
|
|
27
28
|
|
|
28
29
|
namespace RG {
|
|
29
30
|
|
|
30
|
-
struct InstanceData;
|
|
31
|
-
struct TypeInfo;
|
|
32
|
-
struct FunctionInfo;
|
|
33
|
-
|
|
34
31
|
extern const int TypeInfoMarker;
|
|
35
32
|
extern const int CastMarker;
|
|
36
33
|
extern const int MagicUnionMarker;
|
package/src/koffi/src/win32.cc
CHANGED