ffi. CData

Represents a C data object holding a value of a C type.

Example
const val = ctype(…);

val.get();
val.set(…);
val.ptr();
val.index(…);
val.deref(…);
val.size();
val.length();
val.itemsize(…);
val.slice(…);

Methods

cast(type) → {CData}

Cast a cdata to a different C type.

The .cast() method converts a cdata to a specified C type. This is equivalent to calling ffi.cast(type, cdata). It supports casts to numbers, enums, and pointers.

Parameters:
NameTypeDescription
typestring

The target C type declaration.

Throws: Error

Throws an exception if the cast is invalid.

Returns: CData

A cdata of the target type holding the cast value.

Examples
// Cast pointer to void*
let px = ffi.ctype('int *', ffi.ctype('int', 42).ptr());
let pv = px.cast('void *');
// Cast pointer to integer
let str = ffi.string("hello");
let addr = str.ptr().cast('uintptr_t');
print(addr.get());

copy(src, lenopt) → {undefined}

Copy memory to a cdata from a source.

The .copy() method copies memory from a source to this cdata. If the source is a ucode string, it copies the string including its null terminator. Otherwise, an explicit length can be provided.

Parameters:
NameTypeDescription
srcstring | CData

Source string or pointer.

lennumber(optional)

Number of bytes to copy. Required if src is not a string.

Returns: undefined

Returns undefined.

Examples
// Copy string into buffer
let buf = ffi.ctype('char[10]');
buf.copy("hello");
// Copy with explicit length
let src = ffi.ctype('char[5]', [1, 2, 3, 4, 5]);
let dst = ffi.ctype('char[5]');
dst.copy(src, 5);

deref(typeopt) → {*}

Read the value pointed to by a pointer cdata.

The deref() method dereferences a pointer cdata and reads the value at the pointed-to address. The target type can be specified explicitly or inferred from the pointer type.

Parameters:
NameTypeDescription
typestring(optional)

The C type to read. If omitted, the pointer's element type is used.

Throws: Error

Throws an exception if the pointer is NULL or the type is invalid.

Returns: *

The value at the pointer address, converted to a ucode type.

Examples
// Dereference int pointer
let x = ffi.ctype('int', 42);
let px = x.ptr();
print(px.deref('int'));  // => 42
// Read first byte of char*
ffi.cdef('char *strdup(const char *)');
let ptr = ffi.C.wrap('char *strdup(const char *)')("hello");
print(ptr.deref('char'));  // => 104 (ASCII for 'h')
ptr.deref();  // Also works, uses pointer's element type

get(keyopt) → {*}

Read a value from a C data object.

The get() method reads values from cdata objects and returns them converted to ucode types. It supports:

  • Scalar values: int.get() returns the scalar value directly
  • Array indexing: arr.get(n) returns element at position n
  • Struct fields: struct.get('field') returns field value
  • Path notation: struct.get('nested.field[0]') for deep access

For arrays and struct fields, get() behaves identically to index(). Use get() as the primary method for reading values due to its descriptive name.

Parameters:
NameTypeDescription
keystring | number(optional)

The field name, array index, or path to read. Omit for scalar types to get the value directly.

Throws: Error

Throws an exception if the key is invalid for the type.

Returns: *

The value at the specified location, converted to a ucode type. For structs without a key, returns an object with all field values.

Examples
// Read scalar value (no key needed)
let x = ffi.ctype('int', 42);
x.get();    // => 42 (number)
// Read struct field
ffi.cdef('struct point { int x; int y; };');
let p = ffi.ctype('struct point', 10, 20);
p.get('x');    // => 10 (number)
p.get('y');    // => 20 (number)
// Read entire struct as object
p.get();    // => {x: 10, y: 20} (ucode object)
// Read array element
let arr = ffi.ctype('int[5]', [1, 2, 3, 4, 5]);
arr.get(0);    // => 1 (number)
arr.get(4);    // => 5 (number)
// Path notation for nested access
ffi.cdef('struct rect { struct point min; struct point max; };');
let r = ffi.ctype('struct rect', {
    min: {x: 0, y: 0},
    max: {x: 100, y: 100}
});
r.get('min.x');        // => 0
r.get('max.y');        // => 100
See
  • index() - Equivalent for array/field access
  • set() - Write values to cdata

index(key) → {CData}

Access array elements, struct fields, or perform pointer arithmetic.

The index() method returns raw cdata references to the accessed location, without converting to ucode types. This allows further manipulation, pointer arithmetic, or explicit conversion.

Supports:

  • Array indexing: arr.index(n) returns cdata reference to element
  • Struct fields: struct.index('field') returns cdata reference
  • Pointer arithmetic: ptr.index(n) returns cdata at *(ptr + n)
  • Path notation: struct.index('nested.field[0]') for deep access

Key difference from get(): index() returns raw cdata (unconverted), while get() returns converted ucode values.

Parameters:
NameTypeDescription
keystring | number

The array index, field name, or path to access.

Throws: Error

Throws an exception if the key is invalid for the type.

Returns: CData

A cdata reference to the value at the specified location (unconverted). Call .get() on the result to convert to a ucode value.

Examples
// Array indexing - returns cdata, not number
let arr = ffi.ctype('int[5]', [10, 20, 30, 40, 50]);
arr.index(0);      // => cdata (int)
arr.index(0).get() // => 10 (number)
// Struct field access - returns cdata reference
ffi.cdef('struct point { int x; int y; };');
let p = ffi.ctype('struct point', 10, 20);
p.index('x');      // => cdata (int)
p.index('x').get() // => 10 (number)
// Pointer arithmetic - returns cdata at offset
let ptr = ffi.ctype('int *', arr.ptr());
ptr.index(0);      // => cdata (int) at ptr[0]
ptr.index(2);      // => cdata (int) at ptr[2]
ptr.index(2).get() // => 30 (number)
// Chaining - modify through index()
arr.index(0).set(100);  // Set arr[0] = 100
See
  • get() - Returns converted ucode values
  • ptr() - Get a pointer, not a value

itemsize(fieldnameopt) → {number}

Get the size of an array element or struct field in bytes.

The itemsize() method returns the size in bytes of each element in an array, or the size of a specified struct field.

Parameters:
NameTypeDescription
fieldnamestring(optional)

For struct types, the field name to get the size of.

Returns: number

The size of each array element or the struct field in bytes.

Examples
// Get array element size
let arr = ffi.ctype('int[10]');
print(arr.itemsize());  // => 4 (sizeof(int))
// Get struct field size
ffi.cdef('struct foo { char a; int b; double c; };');
let f = ffi.ctype('struct foo');
print(f.itemsize('b'));  // => 4 (size of int field)

length() → {number}nullable

Get the number of elements in an array cdata.

The length() method returns the number of elements in an array. For non-array types, returns null.

Returns: number

The number of elements in the array, or null if not an array.

Examples
// Get array length
let arr = ffi.ctype('int[10]');
print(arr.length());  // => 10
// Works with initialized arrays
let arr2 = ffi.ctype('char[5]', "hello");
print(arr2.length());  // => 5

ptr() → {CData}

Get a pointer to a C data object.

The ptr() method returns a pointer cdata pointing to the memory of the current cdata. This is useful for passing to C functions that expect pointers.

Returns: CData

A pointer cdata pointing to this cdata's memory.

Examples
// Get pointer to scalar
let x = ffi.ctype('int', 42);
let px = x.ptr();  // int* pointer
// Pass to C function expecting pointer
ffi.cdef('void memset(void *, int, size_t)');
let buf = ffi.ctype('char[10]');
ffi.C.wrap('void memset(void *, int, size_t)')(buf.ptr(), 0, 10);

set(key, value) → {undefined}

Write a value to a C data object.

The set() method writes a value to a cdata. For structs, it can write individual fields by name. For arrays, it can write elements by index.

Parameters:
NameTypeDescription
keystring | number

The field name or array index to write.

value*

The value to write. Will be converted to the appropriate C type.

Throws: Error

Throws an exception if the key is invalid or the value cannot be converted.

Returns: undefined

Returns undefined.

Examples
// Write scalar value
let x = ffi.ctype('int');
x.set(42);
// Write struct field
ffi.cdef('struct point { int x; int y; };');
let p = ffi.ctype('struct point');
p.set('x', 10);
p.set('y', 20);
// Write array element
let arr = ffi.ctype('int[5]');
arr.set(0, 100);
arr.set(4, 200);

size() → {number}

Get the size of a C data object in bytes.

The size() method returns the total size in bytes of the cdata. For arrays, this is the total size including all elements.

Returns: number

The size of the cdata in bytes.

Examples
// Get size of struct
ffi.cdef('struct point { int x; int y; };');
let p = ffi.ctype('struct point');
print(p.size());  // => 8 (on typical systems)
// Get size of array
let arr = ffi.ctype('int[10]');
print(arr.size());  // => 40 (10 * sizeof(int))

slice(startopt, endopt) → {string}

Extract a substring from a char* or char[] cdata.

The slice() method extracts a substring from a character pointer or array. For char* pointers, it reads until the null terminator by default. For char[] arrays, it uses the array length.

Parameters:
NameTypeDescription
startnumber(optional, default: 0)

The starting index (0-based). Negative values count from the end.

endnumber(optional)

The ending index (exclusive). If omitted, uses the end of the string/array.

Throws: Error

Throws an exception if called without arguments on non-char* pointer types.

Returns: string

The extracted substring.

Examples
// Extract from char* pointer
ffi.cdef('char *strdup(const char *)');
let ptr = ffi.C.wrap('char *strdup(const char *)')("hello world");
print(ptr.slice());      // => "hello world"
print(ptr.slice(6));     // => "world"
print(ptr.slice(0, 5));  // => "hello"
// Extract from char[] array
let buf = ffi.ctype('char[10]', "hello");
print(buf.slice());      // => "hello"
print(buf.slice(0, 3));  // => "hel"

string(lenopt) → {string}

Convert a cdata to a ucode string.

The .string() method reads a C string from a char* pointer or char[] array and returns a ucode string. For char* pointers, it reads until the null terminator. For char[] arrays, it reads up to the array length. An optional length parameter can limit the bytes read.

Parameters:
NameTypeDescription
lennumber(optional)

Optional maximum length for reading C strings (reads up to len bytes or until null terminator).

Throws: Error

Throws an exception if the cdata is not a char* or char[] type.

Returns: string

The extracted ucode string.

Examples
// Read from char* pointer
ffi.cdef('char *getenv(char *);');
let ptr = ffi.C.wrap('char *getenv(char *)')("PATH");
let path = ptr.string();
print(path);
// Read from char[] array
let buf = ffi.ctype('char[10]', "hello");
print(buf.string());  // => "hello"
// Read fixed-length string
let buf = ffi.ctype('char[10]', "hello world");
print(buf.string(5));  // => "hello"