Nothing
# Rtinycc - TinyCC for R
# Copyright (C) 2025-2026 Sounkou Mahamane Toure
# SPDX-License-Identifier: GPL-3.0-or-later
# FFI Type System - R/C focused type mapping
# Designed for C FFI (not Zig), optimized for R data types
#
# Key insight: R has native vector types that map directly to C arrays:
# - raw() → uint8_t* (byte buffers)
# - integer() → int32_t* (via INTEGER())
# - numeric() → double* (via REAL())
# - logical() → int* (via LOGICAL())
#
# We support both scalar types (with coercion) and array types (zero-copy)
new_rtinycc_ffi_type <- function(name, ..., family = name) {
fields <- list(name = name, family = family, ...)
structure(
fields,
class = c(
paste0("rtinycc_ffi_type_", fields$kind),
"rtinycc_ffi_type"
)
)
}
is_rtinycc_ffi_type <- function(x) {
inherits(x, "rtinycc_ffi_type")
}
ffi_type_family <- function(x) {
if (!is_rtinycc_ffi_type(x)) {
stop("Expected rtinycc_ffi_type object", call. = FALSE)
}
x$family
}
FFI_TYPES <- list(
# Scalar integer types (with range checking)
i8 = list(c_type = "int8_t", r_type = "integer", size = 1L, kind = "scalar"),
i16 = list(
c_type = "int16_t",
r_type = "integer",
size = 2L,
kind = "scalar"
),
i32 = list(
c_type = "int32_t",
r_type = "integer",
size = 4L,
kind = "scalar"
),
i64 = list(
c_type = "int64_t",
r_type = "numeric",
size = 8L,
kind = "scalar"
),
# Scalar unsigned integers
u8 = list(c_type = "uint8_t", r_type = "integer", size = 1L, kind = "scalar"),
u16 = list(
c_type = "uint16_t",
r_type = "integer",
size = 2L,
kind = "scalar"
),
u32 = list(
c_type = "uint32_t",
r_type = "numeric",
size = 4L,
kind = "scalar"
),
u64 = list(
c_type = "uint64_t",
r_type = "numeric",
size = 8L,
kind = "scalar"
),
# Scalar float types
f32 = list(c_type = "float", r_type = "numeric", size = 4L, kind = "scalar"),
f64 = list(c_type = "double", r_type = "numeric", size = 8L, kind = "scalar"),
# Boolean scalar
bool = list(c_type = "bool", r_type = "logical", size = 1L, kind = "scalar"),
# String types
cstring = list(
c_type = "char*",
r_type = "character",
size = NA_integer_,
kind = "scalar"
),
# Pointer (opaque externalptr)
ptr = list(
c_type = "void*",
r_type = "externalptr",
size = NA_integer_,
kind = "scalar"
),
# Array types - R native vector types (zero-copy)
# R raw vector → uint8_t* (byte buffer)
raw = list(
c_type = "uint8_t*",
r_type = "raw",
size = NA_integer_,
kind = "array",
r_accessor = "RAW",
c_element = "uint8_t"
),
# R integer vector → int32_t* (zero-copy via INTEGER())
integer_array = list(
c_type = "int32_t*",
r_type = "integer",
size = NA_integer_,
kind = "array",
r_accessor = "INTEGER",
c_element = "int32_t"
),
# R numeric vector → double* (zero-copy via REAL())
numeric_array = list(
c_type = "double*",
r_type = "numeric",
size = NA_integer_,
kind = "array",
r_accessor = "REAL",
c_element = "double"
),
# R logical vector → int* (zero-copy via LOGICAL())
logical_array = list(
c_type = "int*",
r_type = "logical",
size = NA_integer_,
kind = "array",
r_accessor = "LOGICAL",
c_element = "int"
),
# R character vector -> read-only character-vector elements via STRING_PTR_RO
character_array = list(
c_type = "const SEXP*",
r_type = "character",
size = NA_integer_,
kind = "array",
r_accessor = "STRING_PTR_RO",
c_element = "SEXP (CHARSXP cell)"
),
# R character vector → const char** (allocated with R_alloc in wrapper)
cstring_array = list(
c_type = "const char**",
r_type = "character",
size = NA_integer_,
kind = "array",
r_accessor = NA_character_,
c_element = "const char*"
),
# R-specific: pass R object directly (SEXP)
sexp = list(
c_type = "SEXP",
r_type = "ANY",
size = NA_integer_,
kind = "scalar"
),
# Void (return only)
void = list(c_type = "void", r_type = "NULL", size = 0L, kind = "scalar"),
# Callback type - function pointer passed from R
callback = list(
c_type = "void*",
r_type = "externalptr",
size = NA_integer_,
kind = "scalar"
)
)
# Valid FFI type names
VALID_FFI_TYPES <- names(FFI_TYPES)
# Get array types
ARRAY_TYPES <- names(Filter(function(x) x$kind == "array", FFI_TYPES))
SCALAR_TYPES <- names(Filter(function(x) x$kind == "scalar", FFI_TYPES))
# Validate FFI type
check_ffi_type <- function(type, context = "argument") {
# Check for enum:type pattern
if (startsWith(type, "enum:")) {
# Enum types are always i32 (int)
return(new_rtinycc_ffi_type(
name = type,
family = "enum",
c_type = "int",
r_type = "integer",
size = 4L,
kind = "scalar"
))
}
# Check for callback:type pattern (e.g., callback:double(int,int))
if (startsWith(type, "callback_async:")) {
return(new_rtinycc_ffi_type(
name = type,
family = "callback_async",
c_type = "void*",
r_type = "externalptr",
size = NA_integer_,
kind = "scalar"
))
}
if (startsWith(type, "callback")) {
return(new_rtinycc_ffi_type(
name = type,
family = "callback",
c_type = "void*",
r_type = "externalptr",
size = NA_integer_,
kind = "scalar"
))
}
if (!type %in% VALID_FFI_TYPES) {
stop(
"Invalid FFI type '",
type,
"' for ",
context,
"\nValid types: ",
paste(VALID_FFI_TYPES, collapse = ", "),
call. = FALSE
)
}
do.call(new_rtinycc_ffi_type, c(list(name = type), FFI_TYPES[[type]]))
}
# Check if type is an array type
is_array_type <- function(type) {
type %in% ARRAY_TYPES
}
# Check if type is a scalar type
is_scalar_type <- function(type) {
type %in% SCALAR_TYPES
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.