Module c_api

Source
Expand description

§Minimal Unsafe C API for OpenMP Parser

This module provides a direct pointer-based C FFI with minimal unsafe code.

§Design Philosophy: Direct Pointers vs Handles

We use raw C pointers to Rust structs instead of opaque handles for:

  • Idiomatic C API: Standard malloc/free pattern familiar to C programmers
  • Simple memory model: No global registry, no handle bookkeeping
  • Easy integration: Works naturally with C/C++ code
  • Minimal code: 632 lines vs 4000+ lines of handle management

§Safety Analysis: 18 Unsafe Blocks (~60 lines)

All unsafe blocks are:

  • NULL-checked before dereferencing
  • Documented with explicit safety requirements
  • Isolated only at FFI boundary, never in business logic
  • Auditable: ~0.9% of file (60 unsafe lines / 632 total)

§Case-Insensitive Matching: String Allocation Tradeoff

Functions directive_name_to_kind() and convert_clause() allocate a String for case-insensitive matching (Fortran uses uppercase, C uses lowercase).

Why not optimize with eq_ignore_ascii_case()?

  • Constants generator (src/constants_gen.rs) requires match expressions
  • Parser uses syn crate to extract directive/clause mappings from AST
  • Cannot parse if-else chains → must use match normalized_name.as_str()
  • String allocation is necessary for match arm patterns

Is this a performance issue?

  • No: These functions are called once per directive/clause at API boundary
  • Typical usage: Parse a few dozen directives in an entire program
  • String allocation cost is negligible compared to parsing overhead

Future optimization path: Update constants_gen.rs to parse if-else chains, then use eq_ignore_ascii_case() without allocations.

§Learning Rust: Why Unsafe is Needed at FFI Boundary

  1. C strings → Rust strings: CStr::from_ptr() requires unsafe
  2. Memory ownership transfer: Box::into_raw() / Box::from_raw()
  3. Raw pointer dereferencing: C passes pointers, we must dereference

Safe Rust cannot verify C’s guarantees, so we explicitly document them.

§C Caller Responsibilities

C callers MUST:

  • ✅ Check for NULL returns before use
  • ✅ Call _free() functions to prevent memory leaks
  • ✅ Never use pointers after calling _free()
  • ✅ Pass only valid null-terminated strings
  • ✅ Not modify strings returned by this API

Structs§

OmpClause
Opaque clause type (C-compatible)
OmpClauseIterator
Iterator over clauses
OmpDirective
Opaque directive type (C-compatible)
OmpStringList
List of strings (for variable names in clauses)

Constants§

ROUP_LANG_C
C language (default) - uses #pragma omp
ROUP_LANG_FORTRAN_FIXED
Fortran fixed-form - uses !$OMP or C$OMP in columns 1-6
ROUP_LANG_FORTRAN_FREE
Fortran free-form - uses !$OMP sentinel

Functions§

roup_clause_default_data_sharing
Get default data sharing from default clause.
roup_clause_free
Free a clause.
roup_clause_iterator_free
Free clause iterator.
roup_clause_iterator_next
Get next clause from iterator.
roup_clause_kind
Get clause kind.
roup_clause_reduction_operator
Get reduction operator from reduction clause.
roup_clause_schedule_kind
Get schedule kind from schedule clause.
roup_clause_variables
Get variable list from clause (private, shared, reduction, etc.).
roup_directive_clause_count
Get number of clauses in a directive.
roup_directive_clauses_iter
Create an iterator over directive clauses.
roup_directive_free
Free a directive allocated by roup_parse().
roup_directive_kind
Get directive kind.
roup_parse
Parse an OpenMP directive from a C string.
roup_parse_with_language
Parse an OpenMP directive with explicit language specification.
roup_string_list_free
Free string list.
roup_string_list_get
Get string at index from list.
roup_string_list_len
Get length of string list.