4 Memory
dxtr edited this page 2026-10-19 18:44:55 +02:00

Memory

Pointers

A foreign pointer is SBCL's system-area pointer; the type foreign-pointer names it. null-pointer, null-pointer-p, pointerp, pointer-eq, pointer-address, make-pointer and inc-pointer (whose offset may be negative) make and compare them.

A pointer make-pointer makes from an integer address and only reads or writes through allocates nothing:

(ffi:mem-ref (ffi:make-pointer address) :uint32 4)

Reading and writing

(ffi:mem-ref pointer :uint32)              ; at the pointer
(ffi:mem-ref pointer :double 8)            ; 8 bytes past it
(setf (ffi:mem-aref pointer :int16 3) -1)  ; element 3 of an array of int16
(ffi:mem-aptr pointer '(:struct point) 2)  ; a pointer to element 2

mem-ref and mem-aref read scalars, enums, bitfields and booleans, and setf writes them. With a constant type each compiles to one memory access. mem-aptr takes any type, structs included.

The heap

(foreign-alloc type &key count initial-element) allocates with malloc: count elements, filled with initial-element if it is given and otherwise as malloc leaves them. foreign-free frees. Running out of memory signals an ffi-error.

The stack

(ffi:with-foreign-object (rect '(:struct rect))
  (get-client-rect window rect)
  (ffi:foreign-slot-value rect '(:struct rect) 'right))

(ffi:with-foreign-objects ((width :int) (height :int))
  ...)

(ffi:with-foreign-pointer (buffer 256 size)
  ...)

with-foreign-object gives count elements of a type, and with-foreign-pointer a number of bytes, for the extent of the body, aligned to 8 bytes and not cleared. They are on the stack when the size is a constant; a size known only at run time comes from malloc and is freed when the body returns.

Lisp vectors

(let ((pixels (make-array (* 4 width height) :element-type '(unsigned-byte 8))))
  (ffi:with-pointer-to-vector-data (pointer pixels)
    (read-pixels pointer))
  pixels)

with-pointer-to-vector-data lends C the elements of a simple vector of a specialized numeric element type, without copying. The vector stays in place for the extent of the body.

Allocation

What code that runs every frame uses allocates nothing, and the tests measure it: mem-ref, mem-aref, foreign-slot-value and their setf forms with constant types, with-foreign-object of a constant size, on the stack, and of a size given at run time, from malloc, calls through foreign-funcall and foreign-funcall-pointer of numbers and pointers, and reading through a pointer make-pointer made from an address.

On SBCL, a pointer to stack or malloc'd memory passed to a function that is not inlined is boxed, 16 bytes. A binding's per-frame functions should therefore be declared inline, as cl-vk's commands, cl-icu's functions and cl-harfbuzz's raw bindings are.