Skip to content

Commit

Permalink
unify structure
Browse files Browse the repository at this point in the history
  • Loading branch information
xan105 committed Jan 27, 2024
1 parent e6bc21a commit 76080f6
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 64 deletions.
61 changes: 32 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -313,15 +313,15 @@ This is a class wrapper to the FFI library's callback function(s) inspired by De
##### Properties
- `pointer: unknown`
- `pointer: unknown` _(read only)_
The pointer to the callback.
- `address: number | BigInt | null`
- `address: number | BigInt | null` _(read only)_
The memory address of the pointer.
- `type: unknown`
- `type: unknown` _(read only)_
The type of the callback.
Expand Down Expand Up @@ -409,10 +409,33 @@ const dylib = dlopen("shell32.dll", {
}, { abi: "stdcall" });
```

#### `struct(schema: unknown): unknown`
#### `struct(schema: object): object`

Just a shorthand to define a structure.
💡 It is worth noting that while the goal of this lib is to write the same code with different FFI libraries;
when using Koffi you can just use Koffi's `struct()` function as Koffi converts JS objects to C structs, and vice-versa.

Define a structure. The returned object has 2 properties:

- `type: unknown`

The type of the struct.

- `create: ()=> Class instance`

Return an instance of a class wrapper to the FFI library's struct functions.

##### Class properties

- `pointer: unknown` _(read only)_

The pointer to the struct.

- `values: object`

Get or set the values of the struct.

##### Example

```js
import { dlopen, types, struct, pointer } from "@xan105/ffi/[ napi | koffi ]";

Expand All @@ -424,33 +447,13 @@ const POINT = struct({ //define struct
const dylib = dlopen("user32.dll", { //lib loading
GetCursorPos: {
result: types.win32.BOOL,
parameters: [ pointer(POINT, "out") ] //struct pointer
parameters: [ pointer(POINT.type, "out") ] //struct pointer
}
}, { abi: "stdcall" });
```

⚠️ NB: Struct are use differently afterwards:

- Koffi

```js
const cursorPos = {};
GetCursorPos(cursorPos);
console.log(cursorPos)
//{ x: 0, y: 0 }
```

- ffi-napi

```js
const cursorPos = new POINT();
GetCursorPos(cursorPos.ref());

//access the properties directly
console.log({ x: cursorPos.x, y: cursorPos.y }); //{ x: 0, y: 0 }

//or call .toObject()/.toJSON() (alias) to get a JS Object
console.log(cursorPos.toObject()); //{ x: 0, y: 0 }
const cursorPos = POINT.create();
GetCursorPos(cursorPos.pointer);
console.log(cursorPos.values) //{ x: 0, y: 0 }
```

#### `alloc(type: unknown): { pointer: Buffer, get: ()=> unknown }`
Expand Down
38 changes: 36 additions & 2 deletions lib/ffi-napi/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,42 @@ function pointer(value){
return ref.refType(value);
}

class Struct{

#ref = null;
#value = null;
#members = null;

constructor(type){
this.#ref = type;
this.#value = new this.#ref();
this.#members = Object.keys(this.#value.toObject());
}

get pointer(){
return this.#value.ref();
}

set values(object){
shouldObj(object);
for (const [ name, value ] of Object.entries(object)){
if (this.#members.includes(name)) this.#value[name] = value;
}
}

get values(){
return this.#value.toObject();
}
}

function struct(schema){
return StructType(schema);
shouldObj(schema);
return Object.assign(Object.create(null),{
type: StructType(schema),
create: function(){
return new Struct(this.type);
}
});
}

function alloc(type){
Expand All @@ -95,7 +129,7 @@ function lastError(option = {}){

export {
Callback,
pointer,
pointer,
struct,
alloc,
lastError
Expand Down
45 changes: 42 additions & 3 deletions lib/koffi/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,47 @@ function pointer(value, direction = "in"){
}
}

class Struct{

#ref = null;
#value = {};
#members = null;

constructor(type){
this.#ref = type;
this.#members = Object.keys(koffi.introspect(this.#ref).members);
}

get pointer(){
return this.#value;
}

set values(object){
shouldObj(object);
for (const [ name, value ] of Object.entries(object)){
if (this.#members.includes(name)) this.#value[name] = value;
}
}

get values(){
//Workaround to mimic ffi-napi output when empty/non-init by FFI lib
if(Object.keys(this.#value).length === 0){
for (const { name, type } of Object.values(koffi.introspect(this.#ref).members)){
this.#value[name] = alloc(type).get();
}
}
return this.#value;
}
}

function struct(schema){
return koffi.struct(schema);
shouldObj(schema);
return Object.assign(Object.create(null),{
type: koffi.struct(schema),
create: function(){
return new Struct(this.type);
}
});
}

function alloc(type){
Expand All @@ -94,8 +133,8 @@ function lastError(option = {}){

export {
Callback,
pointer,
struct,
pointer,
struct,
alloc,
lastError
};
19 changes: 5 additions & 14 deletions test/dlopen.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,26 +31,17 @@ for (const [name, ffi] of Object.entries(APIs))
getCursorPos: {
symbol: "GetCursorPos",
result: ffi.types.win32.BOOL,
parameters: [ ffi.pointer(POINT, "out") ]
parameters: [ ffi.pointer(POINT.type, "out") ]
}
}, { abi: "stdcall" });

const expected = { x: 1, y: 1 };
setCursorPos(...Object.values(expected));

if(name === "ffi-napi")
{
const cursorPos = new POINT();
getCursorPos(cursorPos.ref());
const actual = cursorPos.toObject();
assert.deepEqual(actual, expected);
}
else
{
const actual = {};
getCursorPos(actual);
assert.deepEqual(actual, expected);
}
const cursorPos = POINT.create();
getCursorPos(cursorPos.pointer);
const actual = cursorPos.values;
assert.deepEqual(actual, expected);
});

};
19 changes: 5 additions & 14 deletions test/load.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,16 @@ for (const [name, ffi] of Object.entries(APIs))
});

const getCursorPos = lib("GetCursorPos", ffi.types.win32.BOOL, [
ffi.pointer(POINT, "out")
ffi.pointer(POINT.type, "out")
]);

const expected = { x: 0, y: 0 };
setCursorPos(...Object.values(expected));

if(name === "ffi-napi")
{
const cursorPos = new POINT();
getCursorPos(cursorPos.ref());
const actual = { x: cursorPos.x, y: cursorPos.y };
assert.deepEqual(actual, expected);
}
else
{
const actual = {};
getCursorPos(actual);
assert.deepEqual(actual, expected);
}
const cursorPos = POINT.create();
getCursorPos(cursorPos.pointer);
const actual = cursorPos.values;
assert.deepEqual(actual, expected);
});

};
40 changes: 40 additions & 0 deletions test/struct.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import test from "node:test";
import assert from "node:assert/strict";
import { isWindows } from "@xan105/is";

const APIs = {
koffi: await import("../lib/koffi/index.js"),
"ffi-napi": await import("../lib/ffi-napi/index.js")
};

for (const [name, ffi] of Object.entries(APIs))
{

test(`[${name}] Struct: no values`, () => {

const POINT = new ffi.struct({
x: ffi.types.i32,
y: ffi.types.i32
});

const expected = { x: 0, y: 0 };
const cursorPos = POINT.create();
const actual = cursorPos.values;
assert.deepEqual(actual, expected);
});

test(`[${name}] Struct: get/set values`, () => {

const POINT = new ffi.struct({
x: ffi.types.i32,
y: ffi.types.i32
});

const expected = { x: 1, y: 2 };
const cursorPos = POINT.create();
cursorPos.values = { x: 1, y: 2, z: 3 };
const actual = cursorPos.values;
assert.deepEqual(actual, expected);
});

};
9 changes: 8 additions & 1 deletion types/ffi-napi/helper.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ export class Callback {
#private;
}
export function pointer(value: unknown): ref.Type<ref.Pointer<unknown>>;
export function struct(schema: unknown): ref_struct.StructType<unknown>;
declare class Struct {
constructor(schema: object);
get pointer(): ref_struct.StructType<unknown>;
set values(object: object);
get values(): object;
#private;
}
export function struct(schema: object): { type: ref.Type<unknown>, create: () => Struct };
export function alloc(type: unknown): {
pointer: Buffer;
get: () => unknown;
Expand Down
9 changes: 8 additions & 1 deletion types/koffi/helper.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ export class Callback {
#private;
}
export function pointer(value: unknown, direction?: string): koffi.IKoffiCType;
export function struct(schema: unknown): koffi.IKoffiCType;
declare class Struct {
constructor(type: koffi.IKoffiCType);
get pointer(): object;
set values(object: object);
get values(): object;
#private;
}
export function struct(schema: object): { type: koffi.IKoffiCType, create: () => Struct };
export function alloc(type: unknown): {
pointer: Buffer;
get: () => unknown;
Expand Down

0 comments on commit 76080f6

Please sign in to comment.