apple-cf 0.11.7

Safe Rust bindings for Apple's shared Core* frameworks (CoreFoundation, CoreMedia, CoreVideo, CoreGraphics, IOSurface, Dispatch).
Documentation

apple-cf

Safe Rust bindings for Apple's shared Core* frameworks — the foundation underneath the doom-fish macOS Rust suite. The only Rust dependency is the family's doom-fish-utils helper crate.

Status: Active correctness and FFI-contract maintenance. See COVERAGE.md for what the safe layer covers and what the audit numbers in COVERAGE_AUDIT.md and COVERAGE_AUDIT_V2.md do and don't measure.

What's in the box

Module Framework Feature flag Status
cf CoreFoundation values, collections, property lists, locale/formatter helpers, runtime primitives — ✅
raw Exhaustive low-level CoreFoundation/CoreMedia/CoreVideo/IOSurface/Dispatch bindings — ✅
cg CoreGraphics value types, bitmap drawing, active displays and display modes cg ✅
iosurface IOSurface (zero-copy GPU buffers, multi-planar formats) iosurface ✅
dispatch_queue Dispatch queues, async/apply helpers, groups, semaphores, timer and data-add sources dispatch ✅
cm CMTime, CMTimeRange, CMTimebase, CMSampleBuffer, CMBlockBuffer, CMFormatDescription, CMMetadataFormatDescription cm ✅
cv CVBuffer, CVImageBuffer, CVPixelBuffer, CVPixelBufferPool, CVMetalTextureCache cv ✅
utils Shared FFI helpers (always on) — ✅

Why this crate exists

Every doom-fish crate that wraps a media-adjacent Apple framework needs the same primitives — CFString, CGRect, CVPixelBuffer, IOSurface, dispatch queues, time values. Instead of re-vendoring those inside every crate (and drifting), this crate owns them once.

Safe Rust wrappers
    ├── CoreFoundation / Dispatch / CoreMedia / CoreVideo ergonomic APIs
    ├── exhaustive raw C bindings in `apple_cf::raw`
    ├── direct C FFI for value-only primitives where appropriate
    └── Swift @_cdecl bridge for reference-counted / callback-heavy surfaces

Requirements

  • macOS 12.0+
  • Xcode toolchain (Swift 5.9+); Command Line Tools alone is not enough — xcrun --sdk macosx --show-sdk-version must succeed
  • Apple Silicon or Intel Mac

Installation

[dependencies]
apple-cf = "0.11"

Or enable only the Rust modules you use:

[dependencies]
apple-cf = { version = ">=0.11, <0.12", default-features = false, features = ["cg", "cm", "cv", "dispatch", "iosurface"] }

The cg, iosurface, dispatch, cm and cv features gate the Rust modules only. The Swift bridge is built as a single static library, so every build compiles all of it and links CoreFoundation, CoreGraphics, CoreMedia, CoreVideo, IOSurface, Metal and Foundation whichever features are enabled. The metal feature is a deprecated no-op.

Quick examples

Build CoreFoundation values and collections

use apple_cf::cf::{CFArray, CFString};

let first = CFString::new("first");
let second = CFString::new("second");
let array = CFArray::from_values(&[&first, &second]);
assert_eq!(array.len(), 2);

Read text boundaries

CFString::composed_character_range returns the composed-character range at a valid UTF-16 index. CFString::composed_character_ranges iterates the composed-character ranges. CFStringTokenizer::new accepts a string, a CFStringTokenUnit, and an optional locale. The tokenizer borrows the string and releases its native handle when it drops. Word returns word tokens. WordBoundary also returns separator ranges. All ranges use UTF-16 code units, not UTF-8 bytes. Invalid indexes give None. Every native range has a length and a checked end within the source string. The system supplies Unicode data and language-specific word rules. CFCharacterSet::predefined supplies decimal-digit and non-base character sets.

Read URL components

CFURL::parse accepts UTF-8 input and an optional base URL. A base URL gives an absolute URL. CFURL::component returns encoded component text without percent decoding. An absent component gives None. An empty component gives an empty string.

use apple_cf::cf::{CFURL, CFURLComponent};

let base = CFURL::parse("https://example.com/pages/index.html", None).unwrap();
let page = CFURL::parse("../a%20b.html?x=1#next", Some(&base)).unwrap();
assert_eq!(page.component(CFURLComponent::Path).as_deref(), Some("/a%20b.html"));
assert_eq!(page.component(CFURLComponent::Fragment).as_deref(), Some("next"));

The system parser can reject input on a newer OS. The application must keep its own input and normalization rules.

Create a timer-backed dispatch source

use apple_cf::dispatch_queue::DispatchSource;
use std::thread;
use std::time::Duration;

let source = DispatchSource::timer(Duration::from_millis(5), Duration::from_millis(1));
source.resume();
thread::sleep(Duration::from_millis(20));
source.cancel();
assert!(source.fire_count() > 0);

DispatchDataAddSource::new makes a data-add source on the main queue. Call it on the main thread, then call resume. Send a DispatchDataAddMergeHandle to other threads to merge data. The handler runs on the main queue. Drop or cancel the source to stop it. Merge handles remain valid after cancellation.

Read the active displays

use apple_cf::cg::Display;

for display in Display::active() {
    println!("display {}: {:?}", display.id(), display.bounds());
    if let Some(mode) = display.mode() {
        println!("{} × {} pixels at {} Hz", mode.pixel_width(), mode.pixel_height(), mode.refresh_rate());
    }
}

Display::main gives the main display. Display::uuid returns its UUID if one exists. DisplayMode gives the logical size, pixel size and refresh rate of the current mode.

Allocate an IOSurface-backed CVPixelBuffer

use apple_cf::cv::CVPixelBuffer;
use apple_cf::iosurface::IOSurface;

let surface = IOSurface::create(16, 16, 0x4247_5241, 4).expect("surface");
let pixel_buffer = CVPixelBuffer::create_with_io_surface(&surface).expect("pixel buffer");
assert!(pixel_buffer.is_backed_by_io_surface());
assert_eq!(pixel_buffer.width(), 16);

CVPixelBuffer::create passes no attributes, so the buffer it returns is not guaranteed to be IOSurface-backed.

Architecture

This crate uses the same Swift-bridge pattern as the rest of the doom-fish crates:

  • swift-bridge/Sources/<Framework>Bridge/ exposes thin @_cdecl entry points
  • src/ffi/*.rs declares the matching extern "C" bindings
  • src/<framework>/ provides the safe Rust API on top

The only runtime Rust dependency is doom-fish-utils.

Ownership and mapped-memory contracts

CGContext::new_srgb_rgba8 creates an sRGB bitmap with premultiplied RGBA bytes and exact row sizes. The unsafe copy_bytes method copies pixels into owned storage. The caller must ensure that all bytes have values and that no native alias changes them during the copy. The sRGB constructor initializes all bytes. Quadratic and cubic paths support typed caps, joins, miter limits, fill rules, clipping, and affine transforms. These operations support macOS 12. Use a separate context for each concurrent operation.

Raw from_raw constructors adopt one caller-owned +1 retain and are therefore unsafe. Use from_raw_borrowed when importing a live +0 pointer that the wrapper must retain. AsCFType is an unsafe trait because implementations promise a valid Core Foundation object pointer.

CVPixelBuffer and IOSurface lock guards balance native synchronization and mapping only; they do not establish Rust exclusivity across retained, native, GPU, or cross-process aliases. Raw pointers remain available, while slice, row, plane, and zero-copy cursor views require unsafe with an explicit no-alias/no-mutation guarantee. CGContext clones likewise share one mutable native context, so its byte-slice views are unsafe even though drawing methods remain safe.

CMBlockBuffer::as_slice and CMBlockBuffer::cursor_ref are unsafe for the same reason: the bytes belong to Core Media, can be shared with other sample buffers, and must be initialized and left unmodified while the slice lives. CMBlockBuffer::cursor and copy_data_bytes copy instead. CMSampleBuffer::audio_buffer_list hands out read-only views that keep the backing block buffer alive.

Constructors that can fail on ordinary input return Option (CFURL::from_string, CFURL::from_file_system_path, CFTimeZone::new, CFCalendar::new, CFMessagePort::create_echo_local, DispatchSemaphore::new), and CFString::new accepts interior NUL bytes. CFURL::from_string follows the running system's CFURLCreateWithString, whose rules changed in macOS 27: it rejects an authority with an unbalanced or misplaced [ or ] or a second @ (http://[bad, http://bad], http://user@@host/), which earlier releases accept unchanged. CFFileDescriptor takes an OwnedFd, which Core Foundation closes, or a BorrowedFd, which it never closes.

CVPixelBufferPool::create(..., max_buffers) enforces the cap through Core Video's per-allocation threshold. Clones share that immutable policy across threads. Per-call auxiliary attributes are honored, flush flags map directly to the native API, and try_create_pixel_buffer distinguishes threshold exhaustion from other errors.

Examples and tests

This release ships 15 numbered examples plus dedicated smoke tests for:

  • CoreFoundation primitives, collections, property lists, resources, runtime helpers
  • Dispatch serial, concurrent, main and global queues, dispatch_async, dispatch_async_and_wait, dispatch_apply, dispatch_after, groups, semaphores, and timer sources
  • CMTimeRange, CMTimebase, CMMetadataFormatDescription, and low-level CMTag/CMSync coverage through apple_cf::raw
  • CVBuffer, CVImageBuffer, CVPixelBuffer, CVMetalTextureCache, and the remaining CVMetal entry points through apple_cf::raw
  • Exhaustive low-level constants / inline helpers surfaced by apple_cf::raw

CVDisplayLink remains exempt in the audit because Apple deprecated the family on macOS 15.

Not wrapped yet

These are reachable only through the unsafe declarations in apple_cf::raw:

  • CMSampleBuffer creation, timing-info arrays, CMSampleBufferCopyPCMDataIntoAudioBufferList and the other audio-buffer-list constructors
  • Run-loop sources and observers, CFRunLoopTimer/CFTimer with a user callback, and socket, file-descriptor and stream callbacks
  • Dispatch sources with user event handlers, dispatch work items and dispatch I/O
  • IOSurface lookup by ID, Mach port or XPC object, and Metal texture creation from CVMetalTextureCache
  • CFTreeRef: cf::CFTree is a Swift-side tree of retained values, not a Core Foundation tree

License

Licensed under either of Apache-2.0 or MIT at your option.