koffi 0.9.28 → 0.9.29
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/package.json +1 -1
- package/src/ffi.cc +24 -11
- package/src/ffi.hh +1 -0
- package/src/util.cc +20 -24
- package/src/util.hh +1 -0
- package/test/tests/misc.c +47 -2
- package/test/tests/misc.js +55 -19
package/package.json
CHANGED
package/src/ffi.cc
CHANGED
|
@@ -75,20 +75,20 @@ static const TypeInfo *ResolveType(const InstanceData *instance, Napi::Value val
|
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
static Napi::Value CreateStructType(const Napi::CallbackInfo &info)
|
|
78
|
+
static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
|
|
79
79
|
{
|
|
80
80
|
Napi::Env env = info.Env();
|
|
81
81
|
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
82
82
|
|
|
83
|
-
if (info.Length() <
|
|
84
|
-
ThrowError<Napi::TypeError>(env, "Expected 2 arguments, got %1", info.Length());
|
|
83
|
+
if (info.Length() < 1) {
|
|
84
|
+
ThrowError<Napi::TypeError>(env, "Expected 1 or 2 arguments, got %1", info.Length());
|
|
85
85
|
return env.Null();
|
|
86
86
|
}
|
|
87
|
-
if (!info[0].IsString()) {
|
|
87
|
+
if (info.Length() > 1 && !info[0].IsString()) {
|
|
88
88
|
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for name, expected string", GetValueType(instance, info[0]));
|
|
89
89
|
return env.Null();
|
|
90
90
|
}
|
|
91
|
-
if (!IsObject(info[1])) {
|
|
91
|
+
if (!IsObject(info[info.Length() > 1])) {
|
|
92
92
|
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for members, expected object", GetValueType(instance, info[1]));
|
|
93
93
|
return env.Null();
|
|
94
94
|
}
|
|
@@ -96,8 +96,8 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info)
|
|
|
96
96
|
TypeInfo *type = instance->types.AppendDefault();
|
|
97
97
|
RG_DEFER_N(err_guard) { instance->types.RemoveLast(1); };
|
|
98
98
|
|
|
99
|
-
std::string name = info[0].As<Napi::String>();
|
|
100
|
-
Napi::Object obj = info[1].As<Napi::Object>();
|
|
99
|
+
std::string name = info.Length() > 1 ? info[0].As<Napi::String>() : std::string("<anonymous>");
|
|
100
|
+
Napi::Object obj = info[info.Length() > 1].As<Napi::Object>();
|
|
101
101
|
Napi::Array keys = obj.GetPropertyNames();
|
|
102
102
|
|
|
103
103
|
type->name = DuplicateString(name.c_str(), &instance->str_alloc).ptr;
|
|
@@ -116,8 +116,10 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info)
|
|
|
116
116
|
if (!member.type)
|
|
117
117
|
return env.Null();
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
member.align = pad ? member.type->align : 1;
|
|
120
|
+
|
|
121
|
+
type->size = (int16_t)(AlignLen(type->size, member.align) + member.type->size);
|
|
122
|
+
type->align = std::max(type->align, member.align);
|
|
121
123
|
|
|
122
124
|
type->members.Append(member);
|
|
123
125
|
}
|
|
@@ -125,7 +127,7 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info)
|
|
|
125
127
|
type->size = (int16_t)AlignLen(type->size, type->align);
|
|
126
128
|
|
|
127
129
|
// If the insert succeeds, we cannot fail anymore
|
|
128
|
-
if (!instance->types_map.TrySet(type).second) {
|
|
130
|
+
if (info.Length() > 1 && !instance->types_map.TrySet(type).second) {
|
|
129
131
|
ThrowError<Napi::Error>(env, "Duplicate type name '%1'", type->name);
|
|
130
132
|
return env.Null();
|
|
131
133
|
}
|
|
@@ -137,6 +139,16 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info)
|
|
|
137
139
|
return external;
|
|
138
140
|
}
|
|
139
141
|
|
|
142
|
+
static Napi::Value CreatePaddedStructType(const Napi::CallbackInfo &info)
|
|
143
|
+
{
|
|
144
|
+
return CreateStructType(info, true);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
static Napi::Value CreatePackedStructType(const Napi::CallbackInfo &info)
|
|
148
|
+
{
|
|
149
|
+
return CreateStructType(info, false);
|
|
150
|
+
}
|
|
151
|
+
|
|
140
152
|
static Napi::Value CreateHandleType(const Napi::CallbackInfo &info)
|
|
141
153
|
{
|
|
142
154
|
Napi::Env env = info.Env();
|
|
@@ -564,7 +576,8 @@ InstanceData::InstanceData()
|
|
|
564
576
|
template <typename Func>
|
|
565
577
|
static void SetExports(Napi::Env env, Func func)
|
|
566
578
|
{
|
|
567
|
-
func("struct", Napi::Function::New(env,
|
|
579
|
+
func("struct", Napi::Function::New(env, CreatePaddedStructType));
|
|
580
|
+
func("pack", Napi::Function::New(env, CreatePackedStructType));
|
|
568
581
|
func("handle", Napi::Function::New(env, CreateHandleType));
|
|
569
582
|
func("pointer", Napi::Function::New(env, CreatePointerType));
|
|
570
583
|
func("load", Napi::Function::New(env, LoadSharedLibrary));
|
package/src/ffi.hh
CHANGED
package/src/util.cc
CHANGED
|
@@ -81,36 +81,32 @@ const char *CallData::PushString(const Napi::Value &value)
|
|
|
81
81
|
{
|
|
82
82
|
RG_ASSERT(value.IsString());
|
|
83
83
|
|
|
84
|
-
const Size SmallSize = 32;
|
|
85
|
-
|
|
86
84
|
Napi::Env env = value.Env();
|
|
87
|
-
napi_status status;
|
|
88
85
|
|
|
89
86
|
Span<char> buf;
|
|
90
87
|
size_t len = 0;
|
|
88
|
+
napi_status status;
|
|
91
89
|
|
|
92
|
-
|
|
93
|
-
buf.len =
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
ThrowError<Napi::Error>(env, "Failed to convert string to UTF-8");
|
|
98
|
-
return nullptr;
|
|
99
|
-
}
|
|
90
|
+
buf.ptr = (char *)heap_mem->ptr;
|
|
91
|
+
buf.len = std::max((Size)0, heap_mem->len - Kibibytes(32));
|
|
92
|
+
|
|
93
|
+
status = napi_get_value_string_utf8(env, value, buf.ptr, (size_t)buf.len, &len);
|
|
94
|
+
RG_ASSERT(status == napi_ok);
|
|
100
95
|
|
|
101
|
-
|
|
102
|
-
|
|
96
|
+
len++;
|
|
97
|
+
|
|
98
|
+
if (RG_LIKELY(len < (size_t)buf.len)) {
|
|
99
|
+
heap_mem->ptr += (Size)len;
|
|
100
|
+
heap_mem->len -= (Size)len;
|
|
101
|
+
} else {
|
|
103
102
|
status = napi_get_value_string_utf8(env, value, nullptr, 0, &len);
|
|
104
103
|
RG_ASSERT(status == napi_ok);
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
buf.len = (Size)len
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
ThrowError<Napi::Error>(env, "Failed to convert string to UTF-8");
|
|
112
|
-
return nullptr;
|
|
113
|
-
}
|
|
104
|
+
|
|
105
|
+
buf.ptr = (char *)Allocator::Allocate(&big_alloc, (Size)len);
|
|
106
|
+
buf.len = (Size)len;
|
|
107
|
+
|
|
108
|
+
status = napi_get_value_string_utf8(env, value, buf.ptr, (size_t)buf.len, &len);
|
|
109
|
+
RG_ASSERT(status == napi_ok);
|
|
114
110
|
}
|
|
115
111
|
|
|
116
112
|
return buf.ptr;
|
|
@@ -132,7 +128,7 @@ bool CallData::PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t
|
|
|
132
128
|
return false;
|
|
133
129
|
}
|
|
134
130
|
|
|
135
|
-
dest = AlignUp(dest, member.
|
|
131
|
+
dest = AlignUp(dest, member.align);
|
|
136
132
|
|
|
137
133
|
switch (member.type->primitive) {
|
|
138
134
|
case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
|
|
@@ -229,7 +225,7 @@ void PopObject(Napi::Object obj, const uint8_t *ptr, const TypeInfo *type)
|
|
|
229
225
|
RG_ASSERT(type->primitive == PrimitiveKind::Record);
|
|
230
226
|
|
|
231
227
|
for (const RecordMember &member: type->members) {
|
|
232
|
-
ptr = AlignUp(ptr, member.
|
|
228
|
+
ptr = AlignUp(ptr, member.align);
|
|
233
229
|
|
|
234
230
|
switch (member.type->primitive) {
|
|
235
231
|
case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
|
package/src/util.hh
CHANGED
package/test/tests/misc.c
CHANGED
|
@@ -53,6 +53,19 @@ typedef struct BFG {
|
|
|
53
53
|
double g;
|
|
54
54
|
} inner;
|
|
55
55
|
} BFG;
|
|
56
|
+
#pragma pack(push, 1)
|
|
57
|
+
typedef struct PackedBFG {
|
|
58
|
+
int8_t a;
|
|
59
|
+
int64_t b;
|
|
60
|
+
signed char c;
|
|
61
|
+
const char *d;
|
|
62
|
+
short e;
|
|
63
|
+
struct {
|
|
64
|
+
float f;
|
|
65
|
+
double g;
|
|
66
|
+
} inner;
|
|
67
|
+
} PackedBFG;
|
|
68
|
+
#pragma pack(pop)
|
|
56
69
|
|
|
57
70
|
EXPORT void FillPack3(int a, int b, int c, Pack3 *p)
|
|
58
71
|
{
|
|
@@ -135,14 +148,36 @@ EXPORT const char *ConcatenateToStr8(int64_t a, int64_t b, int64_t c, int64_t d,
|
|
|
135
148
|
return buf;
|
|
136
149
|
}
|
|
137
150
|
|
|
138
|
-
EXPORT BFG STDCALL MakeBFG(int x, double y,
|
|
151
|
+
EXPORT BFG STDCALL MakeBFG(BFG *p, int x, double y, const char *str)
|
|
139
152
|
{
|
|
140
153
|
BFG bfg;
|
|
141
154
|
|
|
155
|
+
char buf[64];
|
|
156
|
+
snprintf(buf, sizeof(buf), "X/%s/X", str);
|
|
157
|
+
|
|
158
|
+
bfg.a = x;
|
|
159
|
+
bfg.b = x * 2;
|
|
160
|
+
bfg.c = x - 27;
|
|
161
|
+
bfg.d = buf;
|
|
162
|
+
bfg.e = x * 27;
|
|
163
|
+
bfg.inner.f = (float)y * x;
|
|
164
|
+
bfg.inner.g = (double)y - x;
|
|
165
|
+
*p = bfg;
|
|
166
|
+
|
|
167
|
+
return bfg;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
EXPORT PackedBFG FASTCALL MakePackedBFG(int x, double y, PackedBFG *p, const char *str)
|
|
171
|
+
{
|
|
172
|
+
PackedBFG bfg;
|
|
173
|
+
|
|
174
|
+
char buf[64];
|
|
175
|
+
snprintf(buf, sizeof(buf), "X/%s/X", str);
|
|
176
|
+
|
|
142
177
|
bfg.a = x;
|
|
143
178
|
bfg.b = x * 2;
|
|
144
179
|
bfg.c = x - 27;
|
|
145
|
-
bfg.d =
|
|
180
|
+
bfg.d = buf;
|
|
146
181
|
bfg.e = x * 27;
|
|
147
182
|
bfg.inner.f = (float)y * x;
|
|
148
183
|
bfg.inner.g = (double)y - x;
|
|
@@ -150,3 +185,13 @@ EXPORT BFG STDCALL MakeBFG(int x, double y, BFG *p)
|
|
|
150
185
|
|
|
151
186
|
return bfg;
|
|
152
187
|
}
|
|
188
|
+
|
|
189
|
+
EXPORT const char *ReturnBigString(const char *str)
|
|
190
|
+
{
|
|
191
|
+
static char buf[1 * 1024 * 1024];
|
|
192
|
+
|
|
193
|
+
size_t len = strlen(str);
|
|
194
|
+
memcpy(buf, str, len + 1);
|
|
195
|
+
|
|
196
|
+
return buf;
|
|
197
|
+
}
|
package/test/tests/misc.js
CHANGED
|
@@ -29,7 +29,18 @@ const BFG = koffi.struct('BFG', {
|
|
|
29
29
|
c: 'char',
|
|
30
30
|
d: 'string',
|
|
31
31
|
e: 'short',
|
|
32
|
-
inner: koffi.struct(
|
|
32
|
+
inner: koffi.struct({
|
|
33
|
+
f: 'float',
|
|
34
|
+
g: 'double'
|
|
35
|
+
})
|
|
36
|
+
});
|
|
37
|
+
const PackedBFG = koffi.pack('PackedBFG', {
|
|
38
|
+
a: 'int8_t',
|
|
39
|
+
b: 'int64_t',
|
|
40
|
+
c: 'char',
|
|
41
|
+
d: 'string',
|
|
42
|
+
e: 'short',
|
|
43
|
+
inner: koffi.pack({
|
|
33
44
|
f: 'float',
|
|
34
45
|
g: 'double'
|
|
35
46
|
})
|
|
@@ -60,28 +71,53 @@ async function test() {
|
|
|
60
71
|
const ConcatenateToStr1 = lib.cdecl('ConcatenateToStr1', 'string', [...Array(8).fill('int8_t'), koffi.struct('IJK1', {i: 'int8_t', j: 'int8_t', k: 'int8_t'}), 'int8_t']);
|
|
61
72
|
const ConcatenateToStr4 = lib.cdecl('ConcatenateToStr4', 'string', [...Array(8).fill('int32_t'), koffi.pointer(koffi.struct('IJK4', {i: 'int32_t', j: 'int32_t', k: 'int32_t'})), 'int32_t']);
|
|
62
73
|
const ConcatenateToStr8 = lib.cdecl('ConcatenateToStr8', 'string', [...Array(8).fill('int64_t'), koffi.struct('IJK8', {i: 'int64_t', j: 'int64_t', k: 'int64_t'}), 'int64_t']);
|
|
63
|
-
const MakeBFG = lib.stdcall('MakeBFG', BFG, [
|
|
74
|
+
const MakeBFG = lib.stdcall('MakeBFG', BFG, [koffi.out(koffi.pointer(BFG)), 'int', 'double', 'string']);
|
|
75
|
+
const MakePackedBFG = lib.stdcall('MakePackedBFG', PackedBFG, ['int', 'double', koffi.out(koffi.pointer(PackedBFG)), 'string']);
|
|
76
|
+
const ReturnBigString = lib.stdcall('ReturnBigString', 'string', ['string']);
|
|
77
|
+
|
|
78
|
+
// Simple tests
|
|
79
|
+
{
|
|
80
|
+
let p = {};
|
|
64
81
|
|
|
65
|
-
|
|
82
|
+
FillPack3(1, 2, 3, p);
|
|
83
|
+
assert.deepEqual(p, { a: 1, b: 2, c: 3 });
|
|
66
84
|
|
|
67
|
-
|
|
68
|
-
|
|
85
|
+
let q = RetPack3(6, 9, -12);
|
|
86
|
+
assert.deepEqual(q, { a: 6, b: 9, c: -12 });
|
|
87
|
+
|
|
88
|
+
AddPack3(6, 9, -12, p);
|
|
89
|
+
assert.deepEqual(p, { a: 7, b: 11, c: -9 });
|
|
90
|
+
}
|
|
69
91
|
|
|
70
|
-
|
|
71
|
-
|
|
92
|
+
// Many parameters
|
|
93
|
+
{
|
|
94
|
+
assert.equal(ConcatenateToInt1(5, 6, 1, 2, 3, 9, 4, 4, 0, 6, 8, 7), 561239440687n);
|
|
95
|
+
assert.equal(ConcatenateToInt4(5, 6, 1, 2, 3, 9, 4, 4, 0, 6, 8, 7), 561239440687n);
|
|
96
|
+
assert.equal(ConcatenateToInt8(5, 6, 1, 2, 3, 9, 4, 4, 0, 6, 8, 7), 561239440687n);
|
|
97
|
+
assert.equal(ConcatenateToStr1(5, 6, 1, 2, 3, 9, 4, 4, {i: 0, j: 6, k: 8}, 7), '561239440687');
|
|
98
|
+
assert.equal(ConcatenateToStr4(5, 6, 1, 2, 3, 9, 4, 4, {i: 0, j: 6, k: 8}, 7), '561239440687');
|
|
99
|
+
assert.equal(ConcatenateToStr8(5, 6, 1, 2, 3, 9, 4, 4, {i: 0, j: 6, k: 8}, 7), '561239440687');
|
|
100
|
+
}
|
|
72
101
|
|
|
73
|
-
|
|
74
|
-
|
|
102
|
+
// Big struct
|
|
103
|
+
{
|
|
104
|
+
let out = {};
|
|
105
|
+
let bfg = MakeBFG(out, 2, 7, '__Hello123456789++++foobarFOOBAR!__');
|
|
106
|
+
assert.deepEqual(bfg, { a: 2, b: 4, c: -25, d: 'X/__Hello123456789++++foobarFOOBAR!__/X', e: 54, inner: { f: 14, g: 5 } });
|
|
107
|
+
assert.deepEqual(out, bfg);
|
|
108
|
+
}
|
|
75
109
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
110
|
+
// Packed struct
|
|
111
|
+
{
|
|
112
|
+
let out = {};
|
|
113
|
+
let bfg = MakePackedBFG(2, 7, out, '__Hello123456789++++foobarFOOBAR!__');
|
|
114
|
+
assert.deepEqual(bfg, { a: 2, b: 4, c: -25, d: 'X/__Hello123456789++++foobarFOOBAR!__/X', e: 54, inner: { f: 14, g: 5 } });
|
|
115
|
+
assert.deepEqual(out, bfg);
|
|
116
|
+
}
|
|
82
117
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
118
|
+
// Big string
|
|
119
|
+
{
|
|
120
|
+
let str = 'fooBAR!'.repeat(1024 * 1024);
|
|
121
|
+
assert.equal(ReturnBigString(str), str);
|
|
122
|
+
}
|
|
87
123
|
}
|