Skip to content
CryoCryo home
StdlibSystem

ffi

The C ABI boundary. This is the only place in the standard library where raw platform types are allowed — every other module works in length-typed Str and String.

ItemImport
POSIX / glibc bindings, typed enums, constantsimport std::ffi::libc;
OpenSSL bindingsimport std::ffi::openssl;

Strings across the boundary

Cryo strings carry a length and no terminator; C strings carry a terminator and no length. Translation happens at the boundary, and there is no CString wrapper type — String does the job.

Going out, build a NUL-terminated copy and pass its pointer:

import std::ffi::libc;
import std::collections::string;

mut path: String = String::with_null(name);
const fd: i32 = libc::open(path.as_ptr() as string, libc::O_RDONLY, 0);

String::with_null(source) produces an owned copy that keeps a trailing NUL. as_cstr(mut &this) does the same in place on a string you already have: it writes the terminator at index length, reserving a spare byte if needed, without changing the logical length. It is idempotent and allocation-free once that byte exists.

Coming back, restore the length:

const view: Str = Str::from(c_string);     // length via strlen; the NUL is excluded

Check pointers for null before you do it.

libc

A single home for every extern "C" the standard library needs, plus broad coverage of the POSIX and glibc surface for downstream use. Modules call libc::read(...), libc::sin(...) and so on, which keeps the rest of the tree free of inline extern blocks.

Functions mirror the libc and POSIX headers verbatim — same names, same signatures, same errno semantics. Idiomatic wrappers that return Result, work in Slice<u8>, or retry on EINTR live at the call site, not here. io::fd is the canonical example.

Sections are grouped by header, so you can skim for a function the way you would in man 3 or /usr/include.

Enums versus constants

The split is deliberate and worth knowing before you go looking for a name.

Closed sets of mutually exclusive values are type enum Name : i32, so they pass to libc as typed parameters and the compiler catches a wrong-family argument. Variant names are kept verbatim from the C header — ClockId::CLOCK_MONOTONIC, not ClockId::Monotonic — so call sites read like the man pages.

Among them: Whence, ClockId, ItimerWhich, AddressFamily, Protocol, SocketLevel, ShutdownHow, EpollOp, DirentType, MutexType, DetachState, CancelState, CancelType, LockType, FadviseAdvice, MadviseAdvice, BufferingMode, SigprocmaskHow, LocaleCategory, Resource, RusageWho, PriorityWhich, and the termios TcAction / TcQueue / TcFlow.

Bit-flag families stay as plain consts, because they get OR'd together and compared against return values, which a closed enum does not fit: O_*, S_IF*, POLL*, MAP_*, EPOLL*, AI_*, signal numbers, and errno codes.

C structs

Layout-stable POSIX structs are defined here as Cryo types with matching field order, so you can construct them directly: timespec, timeval, pollfd, iovec, in_addr, in6_addr, rlimit, tm, winsize, regmatch_t.

Platform-variant structs are treated as opaque byte buffers that you pack and unpack yourself — sockaddr_in, struct stat, addrinfo, epoll_event, struct termios, and the pthread_* family. The accompanying SIZEOF_* constants give the Linux x86-64 size so a u8[SIZEOF_X] buffer comes out the right length. net::socket::tcp does exactly this for sockaddr_in.

Errno, signal, mode, fcntl, epoll, and ioctl values are the Linux x86-64 / glibc ABI. Other platforms re-derive them from the corresponding headers.

Floating-point classification — isnan, isinf, finite, signbit, fpclassify — is deliberately not declared here. It is implemented in pure Cryo over the IEEE-754 bit pattern instead, since those are macros rather than exported symbols in most libcs.

openssl

Raw extern "C" declarations for the subset of the OpenSSL 3.x API that net::tls drives. Opaque handles — SSL, SSL_CTX, SSL_METHOD — cross as void*, and you own the lifetime: pair every *_new with its *_free.

A binary that pulls in net::tls must add ssl and crypto to link_libs in its cryoconfig. Nothing here is referenced unless net::tls is used, so non-TLS binaries link without OpenSSL at all.

Several "functions" in <openssl/ssl.h> are preprocessor macros over SSL_ctrl rather than exported symbols. The one that matters, SSL_set_tlsext_host_name for SNI, is wrapped here as ssl_set_sni over the real SSL_ctrl symbol. Every extern in this file was checked against nm -D on the shipped libraries.