Native interoperability library for Scala Native providing unsafe operations, C-style data types, and memory management primitives for direct interaction with native code
—
The memory management system in Scala Native nativelib provides zone-based allocation with automatic cleanup, type-safe pointers, and low-level memory operations for efficient native code integration.
Zone provides scoped memory allocation with automatic cleanup, preventing memory leaks in native code.
trait Zone {
def alloc(size: CSize): Ptr[Byte]
def alloc(size: Int): Ptr[Byte]
def alloc(size: UInt): Ptr[Byte]
def alloc(size: ULong): Ptr[Byte]
def close(): Unit
def isOpen: Boolean
def isClosed: Boolean
}Zone.alloc methods:
alloc(size: CSize) - Allocate memory of given size in bytesalloc(size: Int) - Allocate memory, converting Int to CSizealloc(size: UInt) - Allocate memory, converting UInt to CSizealloc(size: ULong) - Allocate memory, converting ULong to CSizeobject Zone {
def acquire[T](f: Zone => T): T
def open(): Zone
}Methods:
acquire[T](f: Zone => T): T - Execute function with fresh zone, automatically cleanupopen(): Zone - Create new zone allocator (must call close() manually)Type-safe pointer wrapper providing memory access with compile-time type checking.
final class Ptr[T] {
// Memory access
def unary_!(implicit tag: Tag[T]): T
def unary_!_=(value: T)(implicit tag: Tag[T]): Unit
// Pointer arithmetic
def +(offset: Int)(implicit tag: Tag[T]): Ptr[T]
def +(offset: Long)(implicit tag: Tag[T]): Ptr[T]
def +(offset: Size)(implicit tag: Tag[T]): Ptr[T]
def +(offset: USize)(implicit tag: Tag[T]): Ptr[T]
def -(offset: Int)(implicit tag: Tag[T]): Ptr[T]
def -(offset: Long)(implicit tag: Tag[T]): Ptr[T]
def -(offset: Size)(implicit tag: Tag[T]): Ptr[T]
def -(offset: USize)(implicit tag: Tag[T]): Ptr[T]
def -(other: Ptr[T])(implicit tag: Tag[T]): CPtrDiff
// Array-style access
def apply(offset: Int)(implicit tag: Tag[T]): T
def apply(offset: Long)(implicit tag: Tag[T]): T
def apply(offset: Size)(implicit tag: Tag[T]): T
def apply(offset: USize)(implicit tag: Tag[T]): T
def update(offset: Int, value: T)(implicit tag: Tag[T]): Unit
def update(offset: Long, value: T)(implicit tag: Tag[T]): Unit
def update(offset: Size, value: T)(implicit tag: Tag[T]): Unit
def update(offset: USize, value: T)(implicit tag: Tag[T]): Unit
// Conversions
def toInt: Int
def toLong: Long
}Memory access:
unary_! - Dereference pointer to read valueunary_!_= - Dereference pointer to write valuePointer arithmetic:
+ / - with numeric offsets - Move pointer by offset * sizeof(T)-(other: Ptr[T]) - Calculate pointer differenceArray access:
apply(offset) - Read value at pointer + offsetupdate(offset, value) - Write value at pointer + offsetPlatform-dependent size types that match C size_t and ssize_t behavior.
// Int on 32-bit architectures, Long on 64-bit architectures
type Size = /* platform dependent */
type CPtrDiff = Size
type CSSize = Sizedef alloc[T](implicit tag: Tag[T]): Ptr[T]
def alloc[T](count: Int)(implicit tag: Tag[T]): Ptr[T]
def alloc[T](count: UInt)(implicit tag: Tag[T]): Ptr[T]
def alloc[T](count: ULong)(implicit tag: Tag[T]): Ptr[T]
def alloc[T](count: CSize)(implicit tag: Tag[T]): Ptr[T]Allocation functions:
alloc[T]() - Allocate single instance of type Talloc[T](count) - Allocate array of count instances of type Tdef stackalloc[T](implicit tag: Tag[T]): Ptr[T]
def stackalloc[T](count: Int)(implicit tag: Tag[T]): Ptr[T]
def stackalloc[T](count: UInt)(implicit tag: Tag[T]): Ptr[T]
def stackalloc[T](count: ULong)(implicit tag: Tag[T]): Ptr[T]
def stackalloc[T](count: CSize)(implicit tag: Tag[T]): Ptr[T]Stack allocation functions:
stackalloc[T]() - Allocate single instance on stackstackalloc[T](count) - Allocate array on stack (automatically freed on scope exit)def memcpy(dest: Ptr[Byte], src: Ptr[Byte], count: CSize): Unit
def memmove(dest: Ptr[Byte], src: Ptr[Byte], count: CSize): Unit
def memset(ptr: Ptr[Byte], value: Int, count: CSize): Unit
def memcmp(ptr1: Ptr[Byte], ptr2: Ptr[Byte], count: CSize): IntMemory manipulation:
memcpy - Copy memory blocks (undefined behavior for overlapping regions)memmove - Copy memory blocks (safe for overlapping regions)memset - Fill memory with byte valuememcmp - Compare memory blocks, returns 0 if equaldef sizeOf[T](implicit tag: Tag[T]): CSize
def alignmentOf[T](implicit tag: Tag[T]): CSize
def sizeof[T](implicit tag: Tag[T]): CSizeType queries:
sizeOf[T] - Get size in bytes of type TalignmentOf[T] - Get alignment requirement of type Tsizeof[T] - Alias for sizeOfimport scala.scalanative.unsafe._
// Automatic cleanup with Zone.acquire
Zone.acquire { implicit z =>
val ptr = alloc[CInt](10) // allocate array of 10 integers
ptr(0) = 42
ptr(1) = 84
val value = ptr(0) // read first element
// memory automatically freed when leaving scope
}import scala.scalanative.unsafe._
val zone = Zone.open()
try {
implicit val z: Zone = zone
val ptr = alloc[CDouble](5)
ptr(0) = 3.14159
// ... use pointer
} finally {
zone.close() // manual cleanup
}import scala.scalanative.unsafe._
Zone.acquire { implicit z =>
val array = alloc[CInt](10)
// Initialize array
var i = 0
while (i < 10) {
array(i) = i * i
i += 1
}
// Pointer arithmetic
val second = array + 1 // points to array[1]
val value = !second // dereference: reads array[1]
// Calculate pointer difference
val end = array + 10
val size = end - array // returns 10
}import scala.scalanative.unsafe._
def processBuffer(): CInt = {
val buffer = stackalloc[CChar](256) // 256 bytes on stack
// Use buffer for temporary operations
!buffer = 'H'.toByte
!(buffer + 1) = 'i'.toByte
!(buffer + 2) = 0.toByte // null terminator
// buffer automatically freed on return
42
}The Tag system provides type-safe access to memory with automatic size and alignment information:
sealed abstract class Tag[T] {
def size: Int
def alignment: Int
def load(ptr: Ptr[T]): T
def store(ptr: Ptr[T], value: T): Unit
}
object Tag {
// Platform pointer size
def SizeOfPtr: Int
// Tag implementations for pointer types
final case class Ptr[T](of: Tag[T]) extends Tag[unsafe.Ptr[T]]
// Built-in tags for primitive types
case object Size extends Tag[unsafe.Size]
case object Boolean extends Tag[Boolean]
case object Byte extends Tag[Byte]
case object UByte extends Tag[UByte]
case object Short extends Tag[Short]
case object UShort extends Tag[UShort]
case object Int extends Tag[Int]
case object UInt extends Tag[UInt]
case object Long extends Tag[Long]
case object ULong extends Tag[ULong]
case object Float extends Tag[Float]
case object Double extends Tag[Double]
case object Char extends Tag[Char]
// Tags for structured types
case class CArray[T, N <: Nat](of: Tag[T], n: Tag[N]) extends Tag[CArray[T, N]]
case class CStruct1[T1](tag1: Tag[T1]) extends Tag[CStruct1[T1]]
case class CStruct2[T1, T2](tag1: Tag[T1], tag2: Tag[T2]) extends Tag[CStruct2[T1, T2]]
// ... continues for CStruct3 through CStruct22
}Tag system methods:
size - Size in bytes of the typealignment - Memory alignment requirementload(ptr) - Type-safe memory readstore(ptr, value) - Type-safe memory writeLow-level memory operations for performance-critical code:
object Intrinsics {
// Stack allocation (automatic cleanup)
def stackalloc[T](): RawPtr
def stackalloc[T](size: RawSize): RawPtr
// Raw memory load operations
def loadBoolean(rawptr: RawPtr): Boolean
def loadChar(rawptr: RawPtr): Char
def loadByte(rawptr: RawPtr): Byte
def loadShort(rawptr: RawPtr): Short
def loadInt(rawptr: RawPtr): Int
def loadLong(rawptr: RawPtr): Long
def loadFloat(rawptr: RawPtr): Float
def loadDouble(rawptr: RawPtr): Double
def loadRawPtr(rawptr: RawPtr): RawPtr
def loadRawSize(rawptr: RawPtr): RawSize
def loadObject(rawptr: RawPtr): Object
// Raw memory store operations
def storeBoolean(rawptr: RawPtr, value: Boolean): Unit
def storeChar(rawptr: RawPtr, value: Char): Unit
def storeByte(rawptr: RawPtr, value: Byte): Unit
def storeShort(rawptr: RawPtr, value: Short): Unit
def storeInt(rawptr: RawPtr, value: Int): Unit
def storeLong(rawptr: RawPtr, value: Long): Unit
def storeFloat(rawptr: RawPtr, value: Float): Unit
def storeDouble(rawptr: RawPtr, value: Double): Unit
def storeRawPtr(rawptr: RawPtr, value: RawPtr): Unit
def storeRawSize(rawptr: RawPtr, value: RawSize): Unit
def storeObject(rawptr: RawPtr, value: Object): Unit
// Pointer arithmetic
def elemRawPtr(rawptr: RawPtr, offset: RawSize): RawPtr
def elemRawPtr(rawptr: RawPtr, offset: Int): RawPtr
// Type casting intrinsics
def castRawPtrToObject(rawptr: RawPtr): Object
def castObjectToRawPtr(obj: Object): RawPtr
def castIntToFloat(int: Int): Float
def castFloatToInt(float: Float): Int
def castLongToDouble(long: Long): Double
def castDoubleToLong(double: Double): Long
def castRawPtrToLong(rawptr: RawPtr): Long
def castRawPtrToInt(rawptr: RawPtr): Int
def castIntToRawPtr(int: Int): RawPtr
def castLongToRawPtr(long: Long): RawPtr
def castIntToRawSize(int: Int): RawSize
def castIntToRawSizeUnsigned(int: Int): RawSize
def castLongToRawSize(long: Long): RawSize
def castRawSizeToInt(size: RawSize): Int
def castRawSizeToLong(size: RawSize): Long
// Unsigned arithmetic
def divUInt(l: Int, r: Int): Int
def remUInt(l: Int, r: Int): Int
def divULong(l: Long, r: Long): Long
def remULong(l: Long, r: Long): Long
// Unsigned type conversions
def byteToUInt(b: Byte): Int
def byteToULong(b: Byte): Long
def shortToUInt(v: Short): Int
def shortToULong(v: Short): Long
def intToULong(v: Int): Long
def uintToFloat(v: Int): Float
def ulongToFloat(v: Long): Float
def uintToDouble(v: Int): Double
def ulongToDouble(v: Long): Double
}import scala.scalanative.unsafe._
import scala.scalanative.runtime._
Zone.acquire { implicit z =>
// Type-safe generic allocation
def allocTyped[T](implicit tag: Tag[T]): Ptr[T] = {
val ptr = alloc[Byte](tag.size)
ptr.asInstanceOf[Ptr[T]]
}
// Get size information at runtime
val intSize = Tag.Int.size // Size of Int (4 bytes)
val alignment = Tag.Long.alignment // Alignment of Long (8 bytes)
// Type-safe memory operations
val ptr = allocTyped[Int]
Tag.Int.store(ptr, 42)
val value = Tag.Int.load(ptr) // value = 42
}import scala.scalanative.runtime.Intrinsics._
import scala.scalanative.unsafe._
// Stack allocation with automatic cleanup
def processBuffer(): Int = {
val buffer = stackalloc[Byte](1024.toULong)
// Direct memory operations (no bounds checking)
storeInt(buffer, 0x12345678)
storeByte(elemRawPtr(buffer, 4), 0xFF.toByte)
// Read back
val intValue = loadInt(buffer) // 0x12345678
val byteValue = loadByte(elemRawPtr(buffer, 4)) // 0xFF
intValue
// buffer automatically freed on function exit
}import scala.scalanative.runtime.Intrinsics._
import scala.scalanative.unsafe._
Zone.acquire { implicit z =>
val array = alloc[Double](1000)
val basePtr = toRawPtr(array)
// Initialize using raw operations for maximum performance
var i = 0
while (i < 1000) {
val offset = castIntToRawSize(i * 8) // sizeof(Double) = 8
val elemPtr = elemRawPtr(basePtr, offset)
storeDouble(elemPtr, i.toDouble * 1.5)
i += 1
}
// Process data using vectorized operations (hypothetical)
// Raw access allows for SIMD optimization
}Install with Tessl CLI
npx tessl i tessl/maven-org-scala-native--nativelib-native0-5-3