CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-cffi

Foreign Function Interface for Python calling C code.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

memory-management.mddocs/

Memory Management

C memory allocation, deallocation, garbage collection, and address operations. CFFI provides automatic memory management with options for custom allocators and manual control.

Capabilities

Memory Allocation

Allocates C data structures with automatic garbage collection and optional initialization.

def new(self, cdecl, init=None):
    """
    Allocate C data according to the specified type.
    
    Parameters:
    - cdecl (str): C type declaration (pointer or array)
    - init: Optional initializer value
    
    Returns:
    CData object with automatic memory management
    """

Usage Examples:

# Allocate single values
p_int = ffi.new("int *")
p_int[0] = 42

# Allocate with initialization
p_initialized = ffi.new("int *", 100)

# Allocate arrays
arr = ffi.new("int[10]")  # Fixed size array
dyn_arr = ffi.new("int[]", 5)  # Dynamic array with 5 elements

# Initialize arrays
data = ffi.new("int[]", [1, 2, 3, 4, 5])

# Allocate structures
ffi.cdef("struct point { int x, y; };")
point = ffi.new("struct point *")
point.x = 10
point.y = 20

# Initialize structures
point_init = ffi.new("struct point *", {"x": 10, "y": 20})

Custom Allocators

Creates custom memory allocators with user-defined allocation and deallocation functions.

def new_allocator(self, alloc=None, free=None, should_clear_after_alloc=True):
    """
    Create custom allocator function.
    
    Parameters:
    - alloc: Custom allocation function (takes size, returns pointer)
    - free: Custom deallocation function (takes pointer)
    - should_clear_after_alloc (bool): Whether to zero-initialize memory
    
    Returns:
    Allocator function compatible with new() interface
    """

Usage Example:

# Create custom allocator
def my_alloc(size):
    print(f"Allocating {size} bytes")
    return ffi.cast("void *", ffi.new("char[]", size))

def my_free(ptr):
    print("Freeing memory")
    # Memory automatically freed by CFFI

allocator = ffi.new_allocator(my_alloc, my_free)

# Use custom allocator
data = allocator("int[100]")

Type Casting

Converts between different C types and Python objects.

def cast(self, cdecl, source):
    """
    Cast source to the specified C type.
    
    Parameters:
    - cdecl (str): Target C type declaration
    - source: Source value (CData, int, pointer, etc.)
    
    Returns:
    CData object of the specified type
    """

Usage Examples:

# Cast integers to pointers
ptr = ffi.cast("void *", 0x12345678)

# Cast between pointer types
int_ptr = ffi.new("int *", 42)
void_ptr = ffi.cast("void *", int_ptr)
back_to_int = ffi.cast("int *", void_ptr)

# Cast arrays to pointers
arr = ffi.new("int[5]", [1, 2, 3, 4, 5])
ptr = ffi.cast("int *", arr)

# Cast to different integer types
value = ffi.cast("unsigned char", 256)  # Results in 0 (overflow)

Address Operations

Gets the address of C data objects and structure fields.

def addressof(self, cdata, *fields_or_indexes):
    """
    Get address of C data or field within structure/array.
    
    Parameters:
    - cdata: C data object
    - *fields_or_indexes: Field names or array indexes for nested access
    
    Returns:
    Pointer to the specified location
    """

Usage Examples:

# Get address of simple data
x = ffi.new("int *", 42)
addr = ffi.addressof(x[0])

# Get address of structure fields
ffi.cdef("struct point { int x, y; };")
point = ffi.new("struct point *")
x_addr = ffi.addressof(point, "x")
y_addr = ffi.addressof(point, "y")

# Get address of array elements
arr = ffi.new("int[10]")
element_addr = ffi.addressof(arr, 5)

# Nested structure access
ffi.cdef("""
    struct inner { int value; };
    struct outer { struct inner data[5]; };
""")
outer = ffi.new("struct outer *")
inner_addr = ffi.addressof(outer, "data", 2, "value")

Garbage Collection Control

Attaches custom destructors to C data objects for cleanup when garbage collected.

def gc(self, cdata, destructor, size=0):
    """
    Attach destructor to C data object.
    
    Parameters:
    - cdata: C data object
    - destructor: Function to call on garbage collection
    - size (int): Estimated size for GC scheduling
    
    Returns:
    New CData object with attached destructor
    """

Usage Example:

def cleanup_func(ptr):
    print("Cleaning up resource")
    # Perform cleanup operations

# Allocate resource with cleanup
resource = ffi.new("void **")
managed_resource = ffi.gc(resource, cleanup_func)

Memory Safety Patterns

Automatic Memory Management

def process_data():
    # Memory automatically freed when function exits
    buffer = ffi.new("char[1024]")
    # Use buffer...
    return ffi.string(buffer)  # Safe: string is copied

Long-lived References

class DataProcessor:
    def __init__(self):
        # Keep reference to prevent garbage collection
        self.buffer = ffi.new("char[4096]")
        self.processed = ffi.gc(self.buffer, self._cleanup)
    
    def _cleanup(self, ptr):
        print("Buffer cleaned up")

Working with C Libraries that Manage Memory

ffi.cdef("""
    void* create_object(int size);
    void destroy_object(void* obj);
""")

lib = ffi.dlopen("mylib.so")

# Create object managed by C library
obj = lib.create_object(1024)

# Attach cleanup function
managed_obj = ffi.gc(obj, lib.destroy_object)

Common Pitfalls and Solutions

Dangling Pointers

# WRONG: pointer becomes invalid
def get_pointer():
    data = ffi.new("int *", 42)
    return ffi.addressof(data[0])  # Danger: data may be freed

# CORRECT: return the data object itself
def get_data():
    data = ffi.new("int *", 42)
    return data  # Safe: data stays alive

Memory Sharing

# Share memory between Python and C
source = bytearray(b"Hello, World!")
c_buffer = ffi.from_buffer("char[]", source)

# Modifications to c_buffer affect source
c_buffer[0] = ord('h')
print(source)  # b"hello, World!"

Array Bounds

# CFFI doesn't check array bounds - be careful!
arr = ffi.new("int[5]")
# arr[10] = 42  # DANGER: out of bounds access

# Safe pattern: check bounds manually
def safe_set(arr, index, value, size):
    if 0 <= index < size:
        arr[index] = value
    else:
        raise IndexError("Array index out of range")

Install with Tessl CLI

npx tessl i tessl/pypi-cffi

docs

callbacks-handles.md

core-ffi.md

data-conversion.md

error-handling.md

index.md

memory-management.md

source-generation.md

type-system.md

tile.json