bridge
Bridge between FatScript and external C libraries
the
bridgelibrary 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) | Load 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) | Bind to an external function |
The FFI constructor takes the following arguments:
- lib: A
DLLinstance representing the loaded library. - name: A name (
Text) of the function to bind within the library. - in: A
List/Ctypeof argument types expected by the function. - out: The
CTypereturn type of the function.
Prototype members
| Name | Signature | Brief |
|---|---|---|
| call | (args...): Any | Call 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* | Text |
| void | void (return type) | Void |
| voidP | void* | Chunk |
stringmust be a dynamically allocated, null-terminated char pointer
Standalone methods
| Name | Signature | Brief |
|---|---|---|
| unsafeCStr | (ptr: CPointer): Text | Read ptr as null-terminated C-string |
| unsafePeek | (ptr: CPointer, offset: Number, len: Number): Chunk | Read from ptr considering offset and length |
| detachNode | (node: Any): Void | Release ownership of memory |
| marshal | (val: Any, type: CType): Chunk | Marshal a FatScript value to a raw C type |
| unmarshal | (raw: Chunk, type: CType): Any | Unmarshal from a C type back to FatScript |
| getErrno | <> Number | Return the errno from the last FFI call |
| sizeOf | (type: CType): Number | Get the number of bytes for a given C Type |
unsafeCStr
Reads a CPointer as a null-terminated C-string and converts it into a FatScript Text. This method is useful for interfacing with libraries that return C-strings without explicit length information, taking a managed copy. Unlike unsafePeek, unsafeCStr automatically determines the string's length using strlen. Warning: Ensure the pointer points to a null-terminated string to avoid undefined behavior.
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.