CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-scala-native--nativelib-native0-5-3

Native interoperability library for Scala Native providing unsafe operations, C-style data types, and memory management primitives for direct interaction with native code

Pending
Overview
Eval results
Files

memory-management.mddocs/

Memory Management

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.

Core Types

Zone

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 bytes
  • alloc(size: Int) - Allocate memory, converting Int to CSize
  • alloc(size: UInt) - Allocate memory, converting UInt to CSize
  • alloc(size: ULong) - Allocate memory, converting ULong to CSize

Zone Companion Object

object Zone {
  def acquire[T](f: Zone => T): T
  def open(): Zone
}

Methods:

  • acquire[T](f: Zone => T): T - Execute function with fresh zone, automatically cleanup
  • open(): Zone - Create new zone allocator (must call close() manually)

Ptr[T]

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 value
  • unary_!_= - Dereference pointer to write value

Pointer arithmetic:

  • + / - with numeric offsets - Move pointer by offset * sizeof(T)
  • -(other: Ptr[T]) - Calculate pointer difference

Array access:

  • apply(offset) - Read value at pointer + offset
  • update(offset, value) - Write value at pointer + offset

Size Types

Platform-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 = Size

Memory Allocation Functions

Generic Allocation

def 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 T
  • alloc[T](count) - Allocate array of count instances of type T

Stack Allocation

def 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 stack
  • stackalloc[T](count) - Allocate array on stack (automatically freed on scope exit)

Memory Operations

Low-level Memory Functions

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): Int

Memory manipulation:

  • memcpy - Copy memory blocks (undefined behavior for overlapping regions)
  • memmove - Copy memory blocks (safe for overlapping regions)
  • memset - Fill memory with byte value
  • memcmp - Compare memory blocks, returns 0 if equal

Type Information

def sizeOf[T](implicit tag: Tag[T]): CSize
def alignmentOf[T](implicit tag: Tag[T]): CSize
def sizeof[T](implicit tag: Tag[T]): CSize

Type queries:

  • sizeOf[T] - Get size in bytes of type T
  • alignmentOf[T] - Get alignment requirement of type T
  • sizeof[T] - Alias for sizeOf

Usage Examples

Basic Zone Usage

import 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
}

Manual Zone Management

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
}

Pointer Arithmetic

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
}

Stack Allocation

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
}

Type-Safe Memory Operations

Tag System

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 type
  • alignment - Memory alignment requirement
  • load(ptr) - Type-safe memory read
  • store(ptr, value) - Type-safe memory write

Runtime Memory Intrinsics

Low-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
}

Advanced Memory Usage

Using the Tag System

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
}

Raw Memory Operations

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
}

Performance-Critical Memory Access

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
}

Best Practices

  1. Always use Zone.acquire when possible for automatic memory management
  2. Check for null pointers when interfacing with C libraries
  3. Be careful with pointer arithmetic - ensure bounds checking when needed
  4. Use type tags consistently for generic operations
  5. Prefer stack allocation for small, temporary buffers
  6. Don't mix different allocation methods (zone vs malloc) for the same memory
  7. Use raw intrinsics sparingly - only for performance-critical sections
  8. Validate pointer arithmetic - raw operations bypass safety checks
  9. Consider alignment - use Tag system for proper alignment information
  10. Profile memory-intensive code - intrinsics can provide significant speedups

Install with Tessl CLI

npx tessl i tessl/maven-org-scala-native--nativelib-native0-5-3

docs

annotations.md

arrays-runtime.md

c-interop.md

index.md

memory-management.md

unsigned-types.md

tile.json