zhangyuang / node-ffi-rs

Implement ffi in Node.js by Rust and NAPI
MIT License
191 stars 7 forks source link

How to use struct pointer parameter to c function? #39

Closed achenging closed 5 months ago

achenging commented 6 months ago

Current ffi-rs version

$ cat node_modules/ffi-rs/package.json

{
  "name": "ffi-rs",
  "version": "1.0.76",
  "main": "index.js",
  "types": "index.d.ts",
  "description": "A module written in Rust and N-API provides interface (FFI) features for Node.js",
  "napi": {
    "name": "ffi-rs",
    "triples": {
      "additional": [
        "aarch64-apple-darwin",
        "aarch64-unknown-linux-gnu",
        "aarch64-unknown-linux-musl",
        "i686-pc-windows-msvc",
        "x86_64-unknown-linux-musl",
        "aarch64-pc-windows-msvc"
      ]
    }
  },
  "author": "zhangyuang",
  "homepage": "https://github.com/zhangyuang/node-ffi-rs#readme",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/zhangyuang/node-ffi-rs.git"
  },
  "keywords": [
    "ffi",
    "rust",
    "node.js",
    "napi"
  ],
  "files": [
    "index.js",
    "index.d.ts",
    "README.md"
  ],
  "license": "MIT",
  "dependencies": {},
  "devDependencies": {
    "@napi-rs/cli": "^2.15.2",
    "@types/node": "^20.8.7",
    "benny": "^3.7.1",
    "conventional-changelog-cli": "^4.1.0",
    "esno": "^4.0.0",
    "ffi-napi": "^4.0.3",
    "koa": "^2.14.2",
    "shelljs": "^0.8.5",
    "typescript": "^5.4.5"
  },
  "scripts": {
    "artifacts": "napi artifacts",
    "build:c": "node scripts/compile.js",
    "build:dev": "env=development node scripts/build.js",
    "build": "node scripts/build.js",
    "publish:npm": "node scripts/publish.js",
    "test": "esno ./test.ts",
    "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add . && git commit -m \"docs: update changelog.md\" && git push origin master",
    "pub": "npm version patch && git push origin master --tags && npm run changelog"
  },
  "optionalDependencies": {
    "@yuuang/ffi-rs-darwin-arm64": "1.0.76",
    "@yuuang/ffi-rs-darwin-x64": "1.0.76",
    "@yuuang/ffi-rs-linux-arm64-gnu": "1.0.76",
    "@yuuang/ffi-rs-linux-arm64-musl": "1.0.76",
    "@yuuang/ffi-rs-linux-x64-gnu": "1.0.76",
    "@yuuang/ffi-rs-linux-x64-musl": "1.0.76",
    "@yuuang/ffi-rs-win32-arm64-msvc": "1.0.76",
    "@yuuang/ffi-rs-win32-ia32-msvc": "1.0.76",
    "@yuuang/ffi-rs-win32-x64-msvc": "1.0.76"
  }
}

$ ls node_modules/@yuuang

ffi-rs-darwin-x64

Current Node.js arch

Print current Node.js info with the following code

$ node -e "console.log(process.arch, process.platform)"

v16.20.2 x64 darwin

Descibe your problem in detail

//test.c
#include <stdio.h>

typedef struct Person {
    int no;
} Person;

int createPerson(Person *person) {
    printf("origin no = %d\r\n", person->no);
    person->no = 200;
    printf("modify person, no = %d\r\n", person->no);
    return 1;
}
//main.js
import {createPointer, DataType, load, open, restorePointer, unwrapPointer} from 'ffi-rs';

open({
    library: 'test',
    path: './libtest.dylib'
});

const PersonType = {
    no: DataType.I32
};

const person = {
    no: 100
}

const pType = createPointer({
    paramsType: [PersonType],
    paramsValue: [person]
});

const s = restorePointer({
    retType: [PersonType],
    paramsValue: pType
})

const result1 = load({
    library: 'test',
    funcName: 'createPerson',
    paramsType: [DataType.External],
    paramsValue: pType,
    retType: DataType.I32
});

console.log(`in js result1 person = ${JSON.stringify(person)}`)
console.log(`result 1 = ${result1}`)
console.log("---------------------------\r\n");

const result2 = load({
    library: 'test',
    funcName: 'createPerson',
    paramsType: [PersonType],
    paramsValue: [person],
    retType: DataType.I32
});
console.log(`in js result2 person = ${JSON.stringify(person)}`)
console.log(`result 2 = ${result2}`)
// output
origin no = 49348832
modify person, no = 200
---------------------------
in js result1 person = {"no":100}
result 1 = 1
---------------------------

origin no = 100
modify person, no = 200
---------------------------
in js result2 person = {"no":100}
result 2 = 1

What's your expect result

it can change person no properties in c function it may be output


origin no = 100
modify person, no = 200
---------------------------
in js result2 person = {"no":200}
result 1 = 1
``

## The reproduction repo address
zhangyuang commented 6 months ago

The person struct in c environment has different memory address with person object which defined in javascript.

If you want to modify data in situ modification. You can use createPointer to create a pointer pointed to person struct.

And then, pass pointer to c function. After ffi call, restore person struct data by restorePointer

achenging commented 6 months ago

thanks for your reply, i try to use createPointer to create person struct pointer ,but i don't known it correctly in my case like above up.

//create person pointer ?
const pType = createPointer({
    paramsType: [PersonType],
    paramsValue: [person]
});

//call c function
const result1 = load({
    library: 'test',
    funcName: 'createPerson',
    paramsType: [DataType.External],
    paramsValue: pType,
    retType: DataType.I32
});

but it output person->no = 49348832 in C function , not a 100

can you give me a simple sample to test.

zhangyuang commented 6 months ago
const p = createPointer({
    paramsType: [{
      no: DataType.I32
    }],
    paramsValue: [{
      no: 0
    }]
  })
  load({
    library: "libsum",
    funcName: "createPerson",
    retType: DataType.I32,
    paramsType: [DataType.External],
    paramsValue: unwrapPointer(p)
  })
  console.log(
    restorePointer({
      retType: [{
        no: DataType.I32
      }],
      paramsValue: p
    })
  )
achenging commented 6 months ago

this is ok! thanks.

achenging commented 6 months ago

HI,i have some problem to use,I can't get value from complex struct value.

typedef struct Test {
    int test;
} Test;
typedef struct Person {
    struct Test test;
    char *name;
    int no;
} Person;

int createPerson(Person *person) {
    printf("origin no = %d\r\n", person->no);
    printf("origin name = %s\r\n", person->name);
    printf("origin test = %d\r\n", person->test.test);
    printf("--------------------------\r\n");
    person->no = 200;
    person->name = "ffi-rs-test";
    person->test.test = 10086;
    printf("modify person, no = %d\r\n", person->no);
    printf("modify person, name = %s\r\n", person->name);
    printf("modify person, test = %d\r\n", person->test.test);
    return 1;
}
const TestType = {
    test: DataType.I32
}
const testObj = {test: 10}

const PersonType = {
    test: TestType,
    name: DataType.String,
    no: DataType.I32
};

const person = {
    test: testObj,
    name: 'ffi-rs',
    no: 100
}

const pType = createPointer({
    paramsType: [PersonType],
    paramsValue: [person]
});

const result1 = load({
    library: 'test',
    funcName: 'createPerson',
    paramsType: [DataType.External],
    paramsValue: unwrapPointer(pType),
    retType: DataType.I32
});

const a = restorePointer({
    retType: [PersonType],
    paramsValue: pType
})

console.log(`in js result1 person = ${JSON.stringify(a)}`)
console.log(`result 1 = ${JSON.stringify(person)}`)
console.log("---------------------------\r\n");

output result:

origin no = 100
origin name = ffi-rs
origin test = 57590592  // why is the test value not  10 ? 
modify person, no = 200
modify person, name = ffi-rs-test
modify person, test = 10086
in js result1 person = [{"test":{"test":0},"name":"ffi-rs-test","no":200}]
result 1 = {"test":{"test":10},"name":"ffi-rs","no":100}
zhangyuang commented 6 months ago

Use struct pointer if you want to set field of struct is another struct.

typedef struct Test {
    int test;
} Test;
typedef struct Person2 {
    Test* test;
    char *name;
    int no;
} Person2;
achenging commented 6 months ago

this struct is not define by me, this define by third part library, i can't change struct Test to struct Test* type. 😂😂😂

zhangyuang commented 6 months ago

ffi-rs has not support this type at present,i will implememt this in the future

zhangyuang commented 6 months ago

ffi-rs support stack struct in 1.0.78, developer can set ffiTypeTag field to DataType.StackStruct to indicate that the struct memory is allocated in stack. ref test.ts

achenging commented 5 months ago

it works good, thanks.