bridge
Bridge between FatScript and external C libraries
The bridge
library allows FatScript to interface with external C libraries by providing dynamic linking capabilities and foreign function interface (FFI) bindings. This is useful for leveraging performance advantages of C and using existing C libraries in FatScript applications.
Import
_ <- fat.bridge
Types
The bridge
library introduces two primary types for handling dynamic linking of external libraries and calling foreign functions: DLL
and FFI
.
DLL
The DLL
type represents a handle to a dynamically loaded library.
Constructor
Name | Signature | Brief |
---|---|---|
DLL | (filename) | Loads a dynamic library |
The DLL
constructor takes the following argument:
- filename: The path to the shared object (.so/.dll) file to be loaded.
FFI
The FFI
type allows binding to external functions from the dynamically loaded library, using FatScript's CType
system to match the function's expected input and output types.
Constructor
Name | Signature | Brief |
---|---|---|
FFI | (lib, name, in, out) | Binds to an external function |
The FFI
constructor takes the following arguments:
- lib: A
DLL
instance representing the loaded library. - name: A name (
Text
) of the function to bind within the library. - in: A
List/Ctype
of argument types expected by the function. - out: The
CType
return type of the function.
Prototype members
Name | Signature | Brief |
---|---|---|
call | (args...): Any | Calls the bound function |
Aliases
- CPointer: Represents a memory pointer (
void*
-like), base type isChunk
. - CType: Represents C data types in FatScript, base type is
Number
.
CType
The CType
system maps common C types to corresponding FatScript types, allowing safe interaction with C libraries.The following types are made available in the ctype
scope and implement automatic correspondence with FatScript types:
Name | C type | Correspondence |
---|---|---|
sint | int | Number |
sintP | int* | Number |
uint | unsigned int | Number |
uintP | unsigned int* | Number |
float | float | Number |
floatP | float* | Number |
double | double | Number |
doubleP | double* | Number |
schar | char | Chunk |
scharP | char* | Chunk |
uchar | unsigned char | Chunk |
ucharP | unsigned char* | Chunk |
sshort | short | Number |
sshortP | short* | Number |
ushort | unsigned short | Number |
ushortP | unsigned short* | Number |
slong | long | Number |
slongP | long* | Number |
ulong | unsigned long | Number |
ulongP | unsigned long* | Number |
string | char* (return type) | Text |
void | void (return type) | Void |
voidP | void* | Chunk |
string
must be a dynamically allocated, null-terminated char pointer.
Standalone methods
Name | Signature | Brief |
---|---|---|
unsafePeek | (ptr: CPointer, offset: Number, len: Number): Chunk | Reads from ptr considering offset and length |
detachNode | (node: Any): Void | Releases ownership of memory |
marshal | (val: Any, type: CType): Chunk | Marshals a FatScript value to a raw C type |
unmarshal | (raw: Chunk, type: CType): Any | Unmarshals from a C type back to FatScript |
getErrno | (): Number | Returns the errno from the last FFI call |
sizeOf | (type: CType): Number | Gets the number of bytes for a given CType |
unsafePeek
Allows direct reading of raw memory, which can be used to interface with C data structures. Warning: This method performs no bounds checking and relies on correct parameters. Misuse can cause system crashes or security vulnerabilities.
detachNode
Relinquishes ownership of memory pointed to by Text
or Chunk
to prevent double freeing of memory. Consult external library documentation to understand memory ownership before using detachNode
, as it's not always necessary.
marshal
Converts a FatScript value to a raw memory chunk using a specific CType
. Useful for composing C structs. Only string
and voidP
are valid for marshaling Text
and Chunk
as pointers types respectively. Warning: Ensure proper handling of buffer pointers to avoid double freeing of memory.
unmarshal
Casts raw memory chunks to specific FatScript types based on CType
. Useful for interpreting data returned from C structs. Warning: Incorrect usage or incorrect CType
can result in undefined behavior or data corruption.
getErrno
The errno
from the last FFI call is cached and can be retrieved through this method.
sizeOf
Determines the memory size (in bytes) of a given CType
. This is can be useful for safely using functions like unsafePeek
.
Example Usage
Loading a library
To load a dynamic library, use the DLL
type:
zlibDLL = DLL('libz.so')
This will attempt to load the libz.so
shared object library (in this example, the zlib compression library).
Binding to a function
To bind to a function within the loaded library, use the FFI
type:
compressFFI = FFI(zlibDLL, 'compress', [ucharP, slongP, ucharP, slong], sint)
This binds to the compress
function in the zlib library. The argument types and return type are specified using CType
.
Calling the function
Once bound, you can call the function using the call
method:
compressedData = compressFFI.call(destBuff, destSize, source, sourceSize)
This calls the compress
function and returns the result.
Full Example: compressing data with zlib
_ <- fat.type._
bridge <- fat.bridge
zlibDLL = DLL('libz.so')
{ ucharP, slong, slongP, sint } = bridge.ctype
compressFFI = FFI(zlibDLL, 'compress', [ucharP, slongP, ucharP, slong], sint)
# Compress data
source = 'Hello, zlib compression!'.toChunk
destSize = 256
destBuff = Chunk(256)
compressedData = compressFFI.call(destBuff, destSize, source, source.size)
Note that destSize
uses the slongP
type mapping, and while it is considered immutable in FatScript, it may be mutated through the function call. This is expected behavior and is way of interfacing FatScript with C.
Advanced raw data manipulation
To have a better understanding of how bridge
works, you can study the FFI test case and the sample implementation projects zlib.fat and qrcode.fat.
Bridge in Web Build
When using fry
built with Emscripten (for example, when using FatScript Playground), there is no support for this library.