libffi wrapper for quickjs

Libffi wrapper for QuickJS. Now supports almost every features of C including primitive types, structs, callbacks and so on

License

MIT License

Copyright (c) 2021 shajunxing

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Links

Intruduction

Although there's already one wrapper https://github.com/partnernetsoftware/qjs-ffi I found through duckduckgo, to be honest I'm not satisfied with this design. My idea is to keep C code as simple as possible, and put complex logic into JS. Existing library functions, variables, macro definitions and all the things exposed should keep their original look as much as possible.

So I wrote my own from scratch. My module has two layers, low layer is quickjs-ffi.c, compiled to quickjs-ffi.so, containing minimal necessary things from libc, libdl, libffi, and using it is almost the same as C, high layer quickjs-ffi.js makes low layer easy to use.

It's east to compile, just make, it will produce module quickjs-ffi.so, test lib test-lib.so and will run test.js.

High layer

Now it's quite a lot simplified, although I can still only promise to keep low layer unchanged, but not high layer.

Assume 3 C functions (defined in test-lib.c):

void test1();
double test2(float a, double b, const char *c);
typedef struct {
    int i;
    float f;
} s1;
typedef struct {
    long l;
    double d;
    s1 s;
} s2;
void test3(s2 s);

They can be invoked in JS like this:

import { CFunction } from './quickjs-ffi.js'
let test1 = new CFunction('test-lib.so', 'test1', null, 'void');
let test2 = new CFunction('test-lib.so', 'test2', null, 'double', 'float', 'double', 'string');
let test3 = new CFunction('test-lib.so', 'test3', null, 'void', ['long', 'double', ['int', 'float']]);
test1.invoke();
console.log(test2.invoke(3.141592654, 2.718281829, 'How do you do?'));
test3.invoke([123456789, 3.141592654, 54321, 2.718281829]);

CFunction's constructor definition is:

library name, function name, nfixedargs, return value type representation, ...arguments type representation

If C function arguments length is fixed, nfixedargs must be null, or be the number of fixed arguments.

Primitive types are representated by short string literals according to libffi's type definition, I renamed some of it to more C friendly, and also added some, see primitive_types in quickjs-ffi.js for details. Note: All C pointers are pointers, they are actually memory addresses. but char * can be represented by string, and will automatically inbox/outbox with JS string, inbox is not safe and may cause pointer oob, you know that, so do it at your own risk. C complex type is not yet supported.

Structure types are represented by array of primitive types. Nested structures must be defined by nested array, but putting/getting element values must be flattened, which means all structure's primitive elements, are in natural order, eg. 'depth first' order.

If function's return value is structure type, it will returns a flattened array.

High layer caches dlopen, dlsym and ffi_prep_cif results, including corresponding memory allocations. Same library and function will only load once, and also will be cif of same function definitions, eg. same arguments and return value representation.

Each CFunction instance shares dl and ffi cache, but keep it's own memory allocations for arguments and return value, this design is for possible multithread situation.

To invoke C functions with variadic arguments such as printf, if the function definition is fully dynamic, don't forget to free resources when no longer used. And Cautions: Be very carefully dealing with type promotions of C variadic function arguments. See C standards for details. Here is an example:

import { CFunction, freeCif } from './quickjs-ffi.js'
import { LIBC_SO } from './quickjs-ffi.so'
let printf = new CFunction(LIBC_SO, 'printf', 1, 'int', 'string', 'double', 'double', 'int');
printf.invoke('%g %g %d\n', 3.141592654, 2.718281829, 299792458);
freeCif(printf.cifcacheindex);
printf.free();
printf = new CFunction(LIBC_SO, 'printf', 1, 'int', 'string', 'string', 'string');
printf.invoke('%s %s\n', 'hello', 'world');
freeCif(printf.cifcacheindex);
printf.free();

C callbacks are fully supported by CCallback class, which will wrap a JS function using libffi closure mechanism, and returns a C function pointer which can be used as another C function's parameter. CCallback's constructor is:

JS function, nfixedargs, return value type representation, ...arguments type representation

Return value and arguments definitions are the same as CFunction.

For example, there are test4 in test-lib.c:

char *test4(s2 (*fn)(float, double, const char *)) {
    puts("test4 begins");
    s2 ret = fn(3.141592654, 2.718281829, "Hi there");
    printf("callback returns %ld %f %d %f\n", ret.l, ret.d, ret.s.i, ret.s.f);
    puts("test4 ends");
    return "greetings";
}

Define and execute a JS callback is:

import * as ffi from './quickjs-ffi.js'
let test4 = new ffi.CFunction('test-lib.so', 'test4', null, 'string', 'pointer');
let cb = new ffi.CCallback((a, b, c) => {
    console.log('callback begins');
    console.log('arguments are', a, b, c);
    console.log('callback ends');
    return [1, 2, 3, 4];
}, null, ['long', 'double', ['int', 'float']], 'float', 'double', 'string');
console.log('test4 returns', test4.invoke(cb.cfuncptr));

Which will output:

test4 begins
callback begins
arguments are 3.1415927410125732 2.718281829 Hi there
callback ends
callback returns 1 2.000000 3 4.000000
test4 ends
test4 returns greetings

Low layer

This is an example of importing this module and print all exports:

import * as ffi from './quickjs-ffi.so'

for (let k in ffi) {
    console.log(k, '=', ffi[k].toString());
}

Some rules:

  • Any C numeric types such as int, float, are all number in JS.
  • All C pointer types are actually uintptr_t in C, and number in JS, the value are exactly memory addresses.
  • C char * string can be string in JS.
  • JS bool is C bool, in C99 there are bool definitions although they are actually integers.
  • C functions which have no return value, will return undefined in JS.

The module exposes many constant values, which varies by C or machine implementation or different compilations, it's necessary. For example: C int size varies, so i exposed sizeof_int. Any sizeof_xxx is actually value of sizeof(xxx). Also in order to properly operate structure members, I exposed some offsetof_xxx_yyy values which means offsetof(xxx, yyy) in C.

I will do my best checking function arguments, including their count and types, although I know it's not enough.

C needs lots of memory operations, so I exposed necessary libc functions as below:

  • pointer malloc(number size)
  • undefined free(pointer ptr)
  • pointer memset(pointer s, number c, number n)
  • pointer memcpy(pointer dest, pointer src, number n)
  • number strlen(pointer s)

And I added my own functions below. Also I will check out-of-bound error in them, yet still not enough, so do it at your own risk.

  • undefined fprinthex(pointer stream, pointer data, number size)
    print a memory block like hexdump -C format.
  • undefined printhex(pointer data, number size)
    print to stdout.
  • number memreadint(pointer buf, number buflen, number offset, bool issigned, number bytewidth)
    read specified width integer from offset of buf, buflen is for oob checking, bytewidth can only be 1, 2, 4 or 8.
  • undefined memwriteint(pointer buf, number buflen, number offset, number bytewidth, number val)
    write integer val to offset, signed/unsigned is not necessary to speciy.
  • number memreadfloat(pointer buf, number buflen, number offset, bool isdouble)
    read float/double from offset, in C float is always 4 bytes and double is 8.
  • undefined memwritefloat(pointer buf, number buflen, number offset, bool isdouble, double val)
    write float/double val to offset.
  • string memreadstring(pointer buf, number buflen, number offset, number len)
    read len bytes from offset, returns JS string.
  • undefined memwritestring(pointer buf, number buflen, number offset, string str)
    write str to offset.
  • pointer tocstring(string str)
    wrapper of JS_ToCString().
  • undefined freecstring(pointer cstr)
    wrapper of JS_FreeCString().
  • string newstring(pointer cstr)
    wrapper of JS_NewString(). Unsafe!

Here is an example:

let buflen = 8;
let buf = ffi.malloc(buflen);
ffi.printhex(buf, buflen);
ffi.memset(buf, 0xff, buflen);
ffi.printhex(buf, buflen);
console.log(
    ffi.memreadint(buf, buflen, 0, true, 1),
    ffi.memreadint(buf, buflen, 0, true, 2),
    ffi.memreadint(buf, buflen, 0, true, 4),
    ffi.memreadint(buf, buflen, 0, true, 8),
    ffi.memreadint(buf, buflen, 0, false, 1),
    ffi.memreadint(buf, buflen, 0, false, 2),
    ffi.memreadint(buf, buflen, 0, false, 4),
    ffi.memreadint(buf, buflen, 0, false, 8)
);
ffi.memwriteint(buf, buflen, 6, 2, 0x1234);
ffi.printhex(buf, buflen);
console.log(ffi.memreadint(buf, buflen, 6, false, 2).toString(16));
ffi.memwritestring(buf, buflen, 2, "hello");
ffi.printhex(buf, buflen);
print(ffi.memreadstring(buf, buflen, 2, 5));
ffi.memwritefloat(buf, buflen, 0, true, 6.66666666666666666666666666);
ffi.printhex(buf, buflen);
console.log(ffi.memreadfloat(buf, buflen, 0, true));
ffi.free(buf);

Functions in libdl and libffi are almost the same as it's C style:

  • pointer dlopen(string/null filename, number flags)
  • number dlclose(pointer handle)
  • pointer dlsym(pointer handle, string symbol)
  • string/null dlerror()
  • number ffi_prep_cif(pointer cif, number abi, number nargs, pointer rtype, pointer atypes)
  • number ffi_prep_cif_var(pointer cif, number abi, number nfixedargs, number ntotalargs, pointer rtype, pointer atypes)
  • undefined ffi_call(pointer cif, pointer fn, pointer rvalue, pointer avalues)
  • number ffi_get_struct_offsets(number abi, pointer struct_type, pointer offsets)
  • number ffi_closure_alloc(number, number)
  • undefined ffi_closure_free(number)
  • number ffi_prep_closure_loc(number, number, number, number, number)

And many necessary constant values or addresses such as RTLD_XXX FFI_XXX ffi_type_xxx are exposed.

Cautions:

  • Since there is no way to define C numeric variables in JS, only dynamic creation using malloc is reasonable, so don't forget to free them when no longer used.
  • ffi_type_xxx are pointers, eg. memory addresses.

Here is a simple example invoking test1:

let handle = ffi.dlopen("test-lib.so", ffi.RTLD_NOW);
let test1 = ffi.dlsym(handle, 'test1');
let cif = ffi.malloc(ffi.sizeof_ffi_cif);
ffi.ffi_prep_cif(cif, ffi.FFI_DEFAULT_ABI, 0, ffi.ffi_type_void, 0);
ffi.ffi_call(cif, test1, 0, 0)
ffi.free(cif);
ffi.dlclose(handle);

And here is a slightly complex example invoking test2:

let handle = ffi.dlopen('test-lib.so', ffi.RTLD_NOW);
if (handle != ffi.NULL) {
    let test2 = ffi.dlsym(handle, 'test2');
    if (test2 != ffi.NULL) {
        let cif = ffi.malloc(ffi.sizeof_ffi_cif);
        let nargs = 3;
        let rtype = ffi.ffi_type_double;
        let atypes_size = ffi.sizeof_uintptr_t * 3;
        let atypes = ffi.malloc(atypes_size);
        ffi.memwriteint(atypes, atypes_size, ffi.sizeof_uintptr_t * 0, ffi.sizeof_uintptr_t, ffi.ffi_type_float);
        ffi.memwriteint(atypes, atypes_size, ffi.sizeof_uintptr_t * 1, ffi.sizeof_uintptr_t, ffi.ffi_type_double);
        ffi.memwriteint(atypes, atypes_size, ffi.sizeof_uintptr_t * 2, ffi.sizeof_uintptr_t, ffi.ffi_type_pointer);
        if (ffi.ffi_prep_cif(cif, ffi.FFI_DEFAULT_ABI, nargs, rtype, atypes) == ffi.FFI_OK) {
            let arg1 = ffi.malloc(4);
            ffi.memwritefloat(arg1, 4, 0, false, 3.141592654);
            let arg2 = ffi.malloc(8);
            ffi.memwritefloat(arg2, 8, 0, true, 2.718281829);
            let str = ffi.malloc(1000);
            ffi.memwritestring(str, 1000, 0, "How do you do?");
            let arg3 = ffi.malloc(ffi.sizeof_uintptr_t);
            ffi.memwriteint(arg3, ffi.sizeof_uintptr_t, 0, ffi.sizeof_uintptr_t, str);
            let avalues_size = ffi.sizeof_uintptr_t * 3;
            let avalues = ffi.malloc(avalues_size);
            ffi.memwriteint(avalues, avalues_size, ffi.sizeof_uintptr_t * 0, ffi.sizeof_uintptr_t, arg1);
            ffi.memwriteint(avalues, avalues_size, ffi.sizeof_uintptr_t * 1, ffi.sizeof_uintptr_t, arg2);
            ffi.memwriteint(avalues, avalues_size, ffi.sizeof_uintptr_t * 2, ffi.sizeof_uintptr_t, arg3);
            let rvalue = ffi.malloc(8);
            ffi.ffi_call(cif, test2, rvalue, avalues);
            console.log(ffi.memreadfloat(rvalue, 8, 0, true));
            ffi.free(rvalue);
            ffi.free(avalues);
            ffi.free(arg3);
            ffi.free(str);
            ffi.free(arg2);
            ffi.free(arg1);
        }
        ffi.free(atypes);
        ffi.free(cif);
    }
    ffi.dlclose(handle);
}

In libffi document there are detailed instructions on how to pass C structures. Here is how to invoke test3. JS code looks very similar to C, except almost all variables are dynamically created, so be very careful dealing memories and pointers.

let handle = ffi.dlopen('test-lib.so', ffi.RTLD_NOW);
if (handle != ffi.NULL) {
    let test3 = ffi.dlsym(handle, 'test3');
    if (test3 != ffi.NULL) {
        let cif = ffi.malloc(ffi.sizeof_ffi_cif);
        let atypes = ffi.malloc(ffi.sizeof_uintptr_t);

        let s1_elements = ffi.malloc(ffi.sizeof_uintptr_t * 3);
        ffi.memwriteint(s1_elements, ffi.sizeof_uintptr_t * 3, ffi.sizeof_uintptr_t * 0, ffi.sizeof_uintptr_t, ffi.ffi_type_sint);
        ffi.memwriteint(s1_elements, ffi.sizeof_uintptr_t * 3, ffi.sizeof_uintptr_t * 1, ffi.sizeof_uintptr_t, ffi.ffi_type_float);
        ffi.memwriteint(s1_elements, ffi.sizeof_uintptr_t * 3, ffi.sizeof_uintptr_t * 2, ffi.sizeof_uintptr_t, ffi.NULL);

        let s1 = ffi.malloc(ffi.sizeof_ffi_type);
        // I wrote C code, filled ffi_type members one by one with -1, and got it's structure is:
        // "size" 8 bytes, "alignment" 2 bytes, "type" 2 bytes, useless blank 4 bytes, "**elements" 8 bytes
        // I don't know why 4 bytes blank exists, C structure alignment rule?
        // In order to solve this problem, I added some "offsetof_xxx" members.
        ffi.memset(s1, 0, ffi.sizeof_ffi_type);
        ffi.memwriteint(s1, ffi.sizeof_ffi_type, ffi.offsetof_ffi_type_type, 2, ffi.FFI_TYPE_STRUCT);
        ffi.memwriteint(s1, ffi.sizeof_ffi_type, ffi.offsetof_ffi_type_elements, ffi.sizeof_uintptr_t, s1_elements);

        let s2_elements = ffi.malloc(ffi.sizeof_uintptr_t * 4);
        ffi.memwriteint(s2_elements, ffi.sizeof_uintptr_t * 4, ffi.sizeof_uintptr_t * 0, ffi.sizeof_uintptr_t, ffi.ffi_type_slong);
        ffi.memwriteint(s2_elements, ffi.sizeof_uintptr_t * 4, ffi.sizeof_uintptr_t * 1, ffi.sizeof_uintptr_t, ffi.ffi_type_double);
        ffi.memwriteint(s2_elements, ffi.sizeof_uintptr_t * 4, ffi.sizeof_uintptr_t * 2, ffi.sizeof_uintptr_t, s1);
        ffi.memwriteint(s2_elements, ffi.sizeof_uintptr_t * 4, ffi.sizeof_uintptr_t * 3, ffi.sizeof_uintptr_t, ffi.NULL);

        let s2 = ffi.malloc(ffi.sizeof_ffi_type);
        ffi.memset(s2, 0, ffi.sizeof_ffi_type);
        ffi.memwriteint(s2, ffi.sizeof_ffi_type, ffi.offsetof_ffi_type_type, 2, ffi.FFI_TYPE_STRUCT);
        ffi.memwriteint(s2, ffi.sizeof_ffi_type, ffi.offsetof_ffi_type_elements, ffi.sizeof_uintptr_t, s2_elements);

        ffi.printhex(s1, ffi.sizeof_ffi_type)
        ffi.printhex(s2, ffi.sizeof_ffi_type)

        ffi.memwriteint(atypes, ffi.sizeof_uintptr_t, 0, ffi.sizeof_uintptr_t, s2);

        if (ffi.ffi_prep_cif(cif, ffi.FFI_DEFAULT_ABI, 1, ffi.ffi_type_void, atypes) == ffi.FFI_OK) {
            let sizeof_struct_s2 = 24;
            let arg1 = ffi.malloc(sizeof_struct_s2);
            ffi.memwriteint(arg1, sizeof_struct_s2, 0, 8, 123456789);
            ffi.memwritefloat(arg1, sizeof_struct_s2, 8, true, 3.141592654);
            ffi.memwriteint(arg1, sizeof_struct_s2, 16, 4, 54321);
            ffi.memwritefloat(arg1, sizeof_struct_s2, 20, false, 2.718281829);

            let avalues_size = ffi.sizeof_uintptr_t;
            let avalues = ffi.malloc(avalues_size);
            ffi.memwriteint(avalues, avalues_size, 0, ffi.sizeof_uintptr_t, arg1);

            ffi.ffi_call(cif, test3, ffi.NULL, avalues);

            ffi.memset(arg1, 0xff, sizeof_struct_s2);
            ffi.ffi_call(cif, test3, ffi.NULL, avalues);

            ffi.free(avalues);
            ffi.free(arg1);
        }

        ffi.free(s2);
        ffi.free(s2_elements);
        ffi.free(s1);
        ffi.free(s1_elements);
        ffi.free(atypes);
        ffi.free(cif);
    }
    ffi.dlclose(handle);
}

Examples

More examples visit https://github.com/shajunxing/quickjs-misc

JS version of https://curl.se/libcurl/c/simple.html

import { CFunction } from 'quickjs-ffi.js'

const LIBCURL_SO = '/usr/lib/x86_64-linux-gnu/libcurl.so.4'
const curl_easy_init = new CFunction(LIBCURL_SO, 'curl_easy_init', null, 'pointer').invoke;
const CURLOPT_URL = 10000 + 2;
const CURLOPT_FOLLOWLOCATION = 0 + 52;
const curl_easy_perform = new CFunction(LIBCURL_SO, 'curl_easy_perform', null, 'int', 'pointer').invoke;
const CURLE_OK = 0;
const curl_easy_strerror = new CFunction(LIBCURL_SO, 'curl_easy_strerror', null, 'string', 'int').invoke;
const curl_easy_cleanup = new CFunction(LIBCURL_SO, 'curl_easy_cleanup', null, 'void', 'pointer').invoke;

let curl = curl_easy_init();
if (curl > 0) {
    let curl_easy_setopt = new CFunction(LIBCURL_SO, 'curl_easy_setopt', 2, 'int', 'pointer', 'int', 'string');
    curl_easy_setopt.invoke(curl, CURLOPT_URL, "https://example.com");
    curl_easy_setopt.free();
    curl_easy_setopt = new CFunction(LIBCURL_SO, 'curl_easy_setopt', 2, 'int', 'pointer', 'int', 'long');
    curl_easy_setopt.invoke(curl, CURLOPT_FOLLOWLOCATION, 1);
    curl_easy_setopt.free();
    let res = curl_easy_perform(curl);
    if (res != CURLE_OK) {
        console.log("curl_easy_perform() failed:", curl_easy_strerror(res));
    }
    curl_easy_cleanup(curl);
}
Owner
Retired from military university, former computer teacher & engineer. Now enjoying retirement salary. Life is boring, do something interesting.
null
Similar Resources

Implement yolov5 with Tensorrt C++ api, and integrate batchedNMSPlugin. A Python wrapper is also provided.

Implement yolov5 with Tensorrt C++ api, and integrate batchedNMSPlugin. A Python wrapper is also provided.

yolov5 Original codes from tensorrtx. I modified the yololayer and integrated batchedNMSPlugin. A yolov5s.wts is provided for fast demo. How to genera

Oct 21, 2022

The Ultimate Raylib gaming library wrapper for Nim

The Ultimate Raylib gaming library wrapper for Nim

NimraylibNow! - The Ultimate Raylib wrapper for Nim The most idiomatic and up-to-date wrapper for Raylib gaming C library. Use this library if you wan

Nov 19, 2022

cudnn_frontend provides a c++ wrapper for the cudnn backend API and samples on how to use it

cuDNN Frontend API Introduction The cuDNN Frontend API is a C++ header-only library that demonstrates how to use the cuDNN C backend API. The cuDNN C

Nov 20, 2022

SQLite3++ - C++ wrapper of SQLite3 API

ANNOUNCEMENTS Use files in headeronly_src directory. The files in src are exactly same but in the form of h/cpp files, which you need to compile and l

Nov 16, 2022

PhysFS++ is a C++ wrapper for the PhysicsFS library.

PhysFS++ PhysFS++ is a C++ wrapper for the excellent PhysicsFS library by Ryan C. Gordon and others. It is licensed under the zlib license - same as P

Oct 25, 2022

An Arduino wrapper to @sdima1357's usb_soft_host esp-idf example

An Arduino wrapper to @sdima1357's usb_soft_host esp-idf example

ESP32 USB Soft Host library for Arduino IDE This is mainly a wrapper around the excellent work of Dmitry Samsonov (@sdima1357) with esp32_usb_soft_hos

Nov 19, 2022

A cross-platform wrapper for using SDL2 with ImGui

ImSDL2 ImSDL2 is an open source "wrapper" of imgui backends available for SDL2. It aims to provide a backend-independent yet simple interface for inte

Feb 2, 2022

C Wrapper for skottie

Skottie can only be linked statically on the windows platform. If you use SkottieWrapper, you don’t need to directly link the Skottie library and allow C language projects to use Skottie indirectly

Apr 21, 2022

A simple wrapper for 'pacman' with a syntax similar to 'apt' to help people transitioning to Arch and Arch based distributions like Manjaro.

aptpac aptpac is a program which helps with the transition to Arch Linux and Arch based distros like Manjaro. It simplifies using pacman as it works l

Sep 26, 2022

CMakeLists wrapper around imgui

ImGui Wrappings This is a trifold wrapper for the Dear ImGui library. Ease integration with CMake, Provide an RAII mechanism for ImGui scopes, Provide

Nov 3, 2022

TouchEngine wrapper for openFrameworks

ofxTouchEngine TouchEngine wrapper for openFrameworks Caution This repository is in very rough and experimental stage.

Jan 22, 2022

A (very) simple notification wrapper for Dear ImGui

A (very) simple notification wrapper for Dear ImGui

imgui-notify Is a header-only wrapper made to create notifications with Dear ImGui. As I couldn't find any library for this I just decided to create m

Nov 19, 2022

curl4cpp - single header cURL wrapper for C++ around libcURL.

curl4cpp - single header cURL wrapper for C++ around libcURL.

Oct 13, 2022

A simple API wrapper that generates images & facts of any animal

animality.h A simple API wrapper that generates images & facts of any animal Required dependencies: libcurl for sending HTTPS requests. pthreads for t

Nov 10, 2022

Wrapper DLL for NieR Automata (PC ver.) to disable LODs & fix AO issues

NieRAutomata-LodMod An XInput/DXGI wrapper DLL that hooks into NieR Automata (Steam ver.) and disables object LODs, improving visual quality & fixing

Jul 9, 2022

A C++ header-only ZLib wrapper

A C++ ZLib wrapper This C++ header-only library enables the use of C++ standard iostreams to access ZLib-compressed streams. For input access (decompr

Nov 11, 2022

Header-only, event based, tiny and easy to use libuv wrapper in modern C++ - now available as also shared/static library!

Do you have a question that doesn't require you to open an issue? Join the gitter channel. If you use uvw and you want to say thanks or support the pr

Nov 16, 2022

Standalone MinHook wrapper for Golang.

Standalone version of GoMinHook! Credit to https://github.com/NaniteFactory/gominhook and https://github.com/TsudaKageyu/minhook as almost all of the

Jun 4, 2022

WinINet wrapper - tiny windows HTTPS library, no dependencies.

WinINet wrapper - tiny windows HTTPS library, no dependencies.

WNetWrap A tiny, dependency-free wrapper around WinINet for developers targeting Windows only, who need a lightweight native solution. Inspired by the

Nov 4, 2022
Comments
  • error loading so file when running 'make'

    error loading so file when running 'make'

    test I got errers when running make, i found that quickjs-ffi.so and test-lib.so were generated, but could not be loaded, could you tell me how to solve the problem? Thank you

  • Is

    Is "bi-directional" possible?

    It's easy with your library for QuickJS to call into .dylib / .so / .dll functions (normal FFI)

    How would one of those DLLs for example, pass an event or variable back into QuickJS?

Sol3 (sol2 v3.0) - a C++ <-> Lua API wrapper with advanced features and top notch performance - is here, and it's great! Documentation:

sol2 sol2 is a C++ library binding to Lua. It currently supports all Lua versions 5.1+ (LuaJIT 2.0+ and MoonJIT included). sol2 aims to be easy to use

Nov 25, 2022
Fork of libffi-rs which corrects autotools usage

libffi-rs: Rust bindings for libffi The C libffi library provides two main facilities: assembling calls to functions dynamically, and creating closure

Jun 21, 2022
Android Bindings for QuickJS, A fine little javascript engine.

quickjs-android quickjs-android 是 QuickJS JavaScript 引擎的 Android 接口框架,整体基于面向对象设计,提供了自动GC功能,使用简单。armeabi-v7a 的大小仅 350KB,是 Google V8 不错的替代品,启动速度比 V8 快,内

Nov 14, 2022
Header-only, event based, tiny and easy to use libuv wrapper in modern C++ - now available as also shared/static library!

Do you have a question that doesn't require you to open an issue? Join the gitter channel. If you use uvw and you want to say thanks or support the pr

Nov 16, 2022
a simple RPC wrapper generator to C/C++ functions

This project initiated from the following practical problem. To control experimental equipment via computers, manufactures provide software drivers wi

Jan 25, 2022
An object oriented C++ wrapper for CURL (libcurl)

curlcpp An object-oriented C++ wrapper for cURL tool If you want to know a bit more about cURL and libcurl, you should go on the official website http

Nov 23, 2022
Openframework wrapper for box2d
Openframework wrapper for box2d

ofxBox2d Introduction This is a simple wrapper for box2d using Openframeworks. The examples below are still in progressive, but should be stable for t

Nov 14, 2022
Sol3 (sol2 v3.0) - a C++ <-> Lua API wrapper with advanced features and top notch performance - is here, and it's great! Documentation:

sol2 sol2 is a C++ library binding to Lua. It currently supports all Lua versions 5.1+ (LuaJIT 2.0+ and MoonJIT included). sol2 aims to be easy to use

Nov 25, 2022
A small C OpenCL wrapper

oclkit, plain and stupid OpenCL helper oclkit is a small set of C functions, to avoid writing the same OpenCL boiler plate over and over again, yet ke

Jul 22, 2022
Wrapper library for the BSD sockets API with a nicer C99 interface

A wrapper library for the BSD sockets API. Why? This library trades the series of getaddrinfo, socket, connect, bind, listen, etc. functions and their

Oct 28, 2022