0
# Native Arrays and Runtime
1
2
Scala Native provides high-performance native array types optimized for native code generation, along with runtime utilities for garbage collection, memory intrinsics, and system integration.
3
4
## Native Array Types
5
6
### Base Array Class
7
8
```scala { .api }
9
sealed abstract class Array[T] {
10
def length: Int
11
def stride: Int
12
def at(i: Int): Ptr[T]
13
def atUnsafe(i: Int): Ptr[T]
14
def apply(i: Int): T
15
def update(i: Int, value: T): Unit
16
def clone(): Array[T]
17
}
18
```
19
20
**Array methods**:
21
- `length` - Number of elements in the array
22
- `stride` - Size in bytes of each element
23
- `at(i)` - Get pointer to element at index i (bounds checked)
24
- `atUnsafe(i)` - Get pointer to element at index i (no bounds checking)
25
- `apply(i)` - Read element at index i
26
- `update(i, value)` - Write element at index i
27
- `clone()` - Create a copy of the array
28
29
### Typed Array Classes
30
31
```scala { .api }
32
final class BooleanArray extends Array[Boolean]
33
final class CharArray extends Array[Char]
34
final class ByteArray extends Array[Byte]
35
final class ShortArray extends Array[Short]
36
final class IntArray extends Array[Int]
37
final class LongArray extends Array[Long]
38
final class FloatArray extends Array[Float]
39
final class DoubleArray extends Array[Double]
40
final class ObjectArray[T] extends Array[T]
41
```
42
43
### Array Companion Objects
44
45
Each array type provides allocation methods:
46
47
```scala { .api }
48
object BooleanArray {
49
def alloc(size: Int): BooleanArray
50
def snapshot(values: scala.Array[Boolean]): BooleanArray
51
}
52
53
object CharArray {
54
def alloc(size: Int): CharArray
55
def snapshot(values: scala.Array[Char]): CharArray
56
}
57
58
object ByteArray {
59
def alloc(size: Int): ByteArray
60
def snapshot(values: scala.Array[Byte]): ByteArray
61
}
62
63
object ShortArray {
64
def alloc(size: Int): ShortArray
65
def snapshot(values: scala.Array[Short]): ShortArray
66
}
67
68
object IntArray {
69
def alloc(size: Int): IntArray
70
def snapshot(values: scala.Array[Int]): IntArray
71
}
72
73
object LongArray {
74
def alloc(size: Int): LongArray
75
def snapshot(values: scala.Array[Long]): LongArray
76
}
77
78
object FloatArray {
79
def alloc(size: Int): FloatArray
80
def snapshot(values: scala.Array[Float]): FloatArray
81
}
82
83
object DoubleArray {
84
def alloc(size: Int): DoubleArray
85
def snapshot(values: scala.Array[Double]): DoubleArray
86
}
87
88
object ObjectArray {
89
def alloc[T](size: Int): ObjectArray[T]
90
def snapshot[T](values: scala.Array[T]): ObjectArray[T]
91
}
92
```
93
94
**Allocation methods**:
95
- `alloc(size)` - Allocate new array of given size
96
- `snapshot(values)` - Create native array from Scala array
97
98
### BlobArray
99
100
Special array type for conservative garbage collection:
101
102
```scala { .api }
103
final class BlobArray extends Array[Byte] {
104
def scannableLimit: Int
105
def withScannableLimit(limit: Int): BlobArray
106
}
107
108
object BlobArray {
109
def alloc(size: Int): BlobArray
110
def snapshot(values: scala.Array[Byte]): BlobArray
111
}
112
```
113
114
**BlobArray methods**:
115
- `scannableLimit` - Get limit for GC scanning (conservative collection)
116
- `withScannableLimit(limit)` - Set scanning limit for this array
117
118
## Runtime Services
119
120
### Garbage Collector Interface
121
122
Complete interface to the Boehm conservative garbage collector:
123
124
```scala { .api }
125
object GC {
126
// Heap statistics
127
def getInitHeapSize(): CSize
128
def getMaxHeapSize(): CSize
129
def getUsedHeapSize(): CSize
130
131
// Collection statistics
132
def getStatsCollectionTotal(): CSize
133
def getStatsCollectionDurationTotal(): CSize
134
135
// Manual collection
136
def collect(): Unit
137
138
// Thread registration and management
139
type ThreadStartRoutine = CFuncPtr1[ThreadRoutineArg, CVoidPtr]
140
type ThreadRoutineArg = CVoidPtr
141
142
// POSIX thread creation proxy (registers thread with GC)
143
def pthread_create(
144
thread: Ptr[CUnsignedLongInt],
145
attr: Ptr[CUnsignedLongLong],
146
startroutine: ThreadStartRoutine,
147
args: ThreadRoutineArg
148
): CInt
149
150
// Windows thread creation proxy (registers thread with GC)
151
def CreateThread(
152
threadAttributes: Ptr[CStruct3[CUnsignedInt, CVoidPtr, Boolean]],
153
stackSize: CSize,
154
startRoutine: ThreadStartRoutine,
155
routineArg: ThreadRoutineArg,
156
creationFlags: CUnsignedInt,
157
threadId: Ptr[CUnsignedInt]
158
): CVoidPtr
159
160
// Thread state management (cooperative GC)
161
object MutatorThreadState {
162
def Managed: CInt // Thread executing Scala Native code
163
def Unmanaged: CInt // Thread executing foreign/blocking code
164
}
165
166
// GC cooperation and synchronization
167
def setMutatorThreadState(newState: CInt): Unit
168
def `yield`(): Unit
169
def yieldPointTrap: RawPtr
170
171
// Root set management for external memory
172
def addRoots(addressLow: CVoidPtr, addressHigh: CVoidPtr): Unit
173
def removeRoots(addressLow: CVoidPtr, addressHigh: CVoidPtr): Unit
174
175
// Weak references callback support
176
type WeakReferencesCollectedCallback = CFuncPtr0[Unit]
177
def setWeakReferencesCollectedCallback(callback: WeakReferencesCollectedCallback): Unit
178
}
179
```
180
181
**GC heap statistics**:
182
- `getInitHeapSize()` - Initial heap size in bytes
183
- `getMaxHeapSize()` - Maximum heap size in bytes
184
- `getUsedHeapSize()` - Currently used heap size in bytes
185
186
**GC collection statistics**:
187
- `getStatsCollectionTotal()` - Total number of GC collections
188
- `getStatsCollectionDurationTotal()` - Total time spent in GC (nanoseconds)
189
190
**GC control**:
191
- `collect()` - Force garbage collection
192
- `setWeakReferencesCollectedCallback` - Set callback for weak reference cleanup
193
194
**Thread registration**:
195
- `pthread_create` - POSIX thread creation with GC registration
196
- `CreateThread` - Windows thread creation with GC registration
197
- Both proxies ensure threads are properly registered with garbage collector
198
199
**Thread cooperation**:
200
- `MutatorThreadState.Managed` - Thread executing managed Scala Native code
201
- `MutatorThreadState.Unmanaged` - Thread executing foreign/blocking operations
202
- `setMutatorThreadState` - Notify GC of thread state changes
203
- `yield` - Cooperative yield point for stop-the-world collection
204
- `yieldPointTrap` - Low-overhead trap-based yield point mechanism
205
206
**Root set management**:
207
- `addRoots` - Register external memory range for GC scanning
208
- `removeRoots` - Unregister external memory range from GC scanning
209
- Used for malloc'd memory containing GC pointers
210
211
### Runtime Information
212
213
Application and system information available at runtime:
214
215
```scala { .api }
216
// Package object scala.scalanative.runtime
217
def filename: String
218
def startTime: Long
219
def uptime: Long
220
221
// Pointer and size conversions
222
def fromRawPtr[T](rawptr: RawPtr): Ptr[T]
223
def toRawPtr[T](ptr: Ptr[T]): RawPtr
224
def fromRawSize(rawSize: RawSize): Size
225
def fromRawUSize(rawSize: RawSize): USize
226
def toRawSize(size: Size): RawSize
227
def toRawSize(size: USize): RawSize
228
229
// Monitor and synchronization support
230
def getMonitor(obj: Object): Monitor
231
def enterMonitor(obj: Object): Unit
232
def exitMonitor(obj: Object): Unit
233
234
// Stack overflow protection
235
object StackOverflowGuards {
236
def size: Int
237
def setup(isMainThread: Boolean): Unit
238
def reset(): Unit
239
def close(): Unit
240
}
241
242
// Runtime exception handling
243
def throwDivisionByZero(): Nothing
244
def throwClassCast(from: RawPtr, to: RawPtr): Nothing
245
def throwNullPointer(): Nothing
246
def throwUndefined(): Nothing
247
def throwOutOfBounds(i: Int, length: Int): Nothing
248
def throwNoSuchMethod(sig: String): Nothing
249
```
250
251
**Application information**:
252
- `filename` - Executable filename
253
- `startTime` - Application start time (milliseconds since epoch)
254
- `uptime` - Application uptime (milliseconds)
255
256
**Memory conversions**:
257
- `fromRawPtr` / `toRawPtr` - Convert between raw and typed pointers
258
- `fromRawSize` / `toRawSize` - Convert between raw and sized types
259
- `fromRawUSize` - Convert raw size to unsigned size
260
261
**Thread synchronization**:
262
- `getMonitor` - Get monitor object for synchronization
263
- `enterMonitor` / `exitMonitor` - Monitor entry/exit operations
264
- Available only in multithreading mode
265
266
**Stack overflow protection**:
267
- `StackOverflowGuards.size` - Size of stack guard region
268
- `StackOverflowGuards.setup` - Initialize stack overflow detection
269
- `StackOverflowGuards.reset` - Reset after stack overflow recovery
270
- `StackOverflowGuards.close` - Cleanup stack guards
271
272
**Runtime exception helpers**:
273
- `throwDivisionByZero` - Division by zero exception
274
- `throwClassCast` - Class cast exception with type information
275
- `throwNullPointer` - Null pointer exception
276
- `throwUndefined` - Undefined behavior error
277
- `throwOutOfBounds` - Array bounds exception
278
- `throwNoSuchMethod` - Reflection method not found
279
280
### Memory Intrinsics
281
282
Low-level memory operations (from `Intrinsics` object):
283
284
```scala { .api }
285
object Intrinsics {
286
// Stack allocation
287
def stackalloc[T](implicit tag: Tag[T]): Ptr[T]
288
def stackalloc[T](count: Int)(implicit tag: Tag[T]): Ptr[T]
289
def stackalloc[T](count: UInt)(implicit tag: Tag[T]): Ptr[T]
290
def stackalloc[T](count: ULong)(implicit tag: Tag[T]): Ptr[T]
291
def stackalloc[T](count: CSize)(implicit tag: Tag[T]): Ptr[T]
292
293
// Memory load operations
294
def loadBoolean(ptr: Ptr[Byte]): Boolean
295
def loadChar(ptr: Ptr[Byte]): Char
296
def loadByte(ptr: Ptr[Byte]): Byte
297
def loadShort(ptr: Ptr[Byte]): Short
298
def loadInt(ptr: Ptr[Byte]): Int
299
def loadLong(ptr: Ptr[Byte]): Long
300
def loadFloat(ptr: Ptr[Byte]): Float
301
def loadDouble(ptr: Ptr[Byte]): Double
302
def loadObject(ptr: Ptr[Byte]): Object
303
def loadRawPtr(ptr: Ptr[Byte]): RawPtr
304
def loadRawSize(ptr: Ptr[Byte]): RawSize
305
306
// Memory store operations
307
def storeBoolean(ptr: Ptr[Byte], value: Boolean): Unit
308
def storeChar(ptr: Ptr[Byte], value: Char): Unit
309
def storeByte(ptr: Ptr[Byte], value: Byte): Unit
310
def storeShort(ptr: Ptr[Byte], value: Short): Unit
311
def storeInt(ptr: Ptr[Byte], value: Int): Unit
312
def storeLong(ptr: Ptr[Byte], value: Long): Unit
313
def storeFloat(ptr: Ptr[Byte], value: Float): Unit
314
def storeDouble(ptr: Ptr[Byte], value: Double): Unit
315
def storeObject(ptr: Ptr[Byte], value: Object): Unit
316
def storeRawPtr(ptr: Ptr[Byte], value: RawPtr): Unit
317
def storeRawSize(ptr: Ptr[Byte], value: RawSize): Unit
318
319
// Unsigned arithmetic
320
def divUInt(l: UInt, r: UInt): UInt
321
def remUInt(l: UInt, r: UInt): UInt
322
def divULong(l: ULong, r: ULong): ULong
323
def remULong(l: ULong, r: ULong): ULong
324
325
// Type conversions
326
def byteToUInt(value: Byte): Int
327
def byteToULong(value: Byte): Long
328
def shortToUInt(value: Short): Int
329
def shortToULong(value: Short): Long
330
def intToULong(value: Int): Long
331
332
// Pointer arithmetic
333
def elemRawPtr(ptr: RawPtr, offset: RawSize): RawPtr
334
335
// Type casting
336
def castIntToFloat(value: Int): Float
337
def castFloatToInt(value: Float): Int
338
def castLongToDouble(value: Long): Double
339
def castDoubleToLong(value: Double): Long
340
def castRawPtrToObject(value: RawPtr): Object
341
def castObjectToRawPtr(value: Object): RawPtr
342
def castRawPtrToLong(value: RawPtr): Long
343
def castRawPtrToInt(value: RawPtr): Int
344
def castIntToRawPtr(value: Int): RawPtr
345
def castLongToRawPtr(value: Long): RawPtr
346
def castIntToRawSize(value: Int): RawSize
347
def castIntToRawSizeUnsigned(value: Int): RawSize
348
def castLongToRawSize(value: Long): RawSize
349
}
350
```
351
352
### Boxing Utilities
353
354
```scala { .api }
355
object Boxes {
356
// Unsigned type boxing
357
def boxToUByte(value: Byte): UByte
358
def boxToUShort(value: Short): UShort
359
def boxToUInt(value: Int): UInt
360
def boxToULong(value: Long): ULong
361
def boxToUSize(value: RawSize): USize
362
363
def unboxToUByte(value: UByte): Byte
364
def unboxToUShort(value: UShort): Short
365
def unboxToUInt(value: UInt): Int
366
def unboxToULong(value: ULong): Long
367
def unboxToUSize(value: USize): RawSize
368
369
// Pointer boxing
370
def boxToPtr[T](value: RawPtr): Ptr[T]
371
def unboxToPtr[T](value: Ptr[T]): RawPtr
372
373
// Size boxing
374
def boxToSize(value: RawSize): Size
375
def unboxToSize(value: Size): RawSize
376
}
377
```
378
379
## Usage Examples
380
381
### Native Array Usage
382
383
```scala
384
import scala.scalanative.runtime._
385
386
// Allocate arrays
387
val intArray = IntArray.alloc(100)
388
val doubleArray = DoubleArray.alloc(50)
389
390
// Initialize with values
391
for (i <- 0 until intArray.length) {
392
intArray(i) = i * i
393
}
394
395
// Access elements
396
val value = intArray(25) // Get element at index 25
397
intArray(30) = 900 // Set element at index 30
398
399
// Get pointers to elements
400
val ptr = intArray.at(10) // Pointer to element 10
401
val unsafePtr = intArray.atUnsafe(10) // No bounds checking
402
403
// Clone array
404
val copy = intArray.clone()
405
```
406
407
### Creating Arrays from Scala Arrays
408
409
```scala
410
import scala.scalanative.runtime._
411
412
// Convert Scala array to native array
413
val scalaArray = Array(1, 2, 3, 4, 5)
414
val nativeArray = IntArray.snapshot(scalaArray)
415
416
// Work with native array
417
nativeArray(0) = 10
418
val firstElement = nativeArray(0) // 10
419
420
// String array example
421
val strings = Array("hello", "world", "native")
422
val nativeStrings = ObjectArray.snapshot(strings)
423
val greeting = nativeStrings(0) // "hello"
424
```
425
426
### GC Integration
427
428
```scala
429
import scala.scalanative.runtime._
430
431
// Check heap status
432
val usedHeap = GC.getUsedHeapSize()
433
val maxHeap = GC.getMaxHeapSize()
434
435
println(s"Heap usage: ${usedHeap}/${maxHeap} bytes")
436
437
// Force garbage collection
438
GC.collect()
439
440
// Check collection statistics
441
val totalCollections = GC.getStatsCollectionTotal()
442
val totalDuration = GC.getStatsCollectionDurationTotal()
443
println(s"GC: ${totalCollections} collections, ${totalDuration} ns total")
444
```
445
446
### BlobArray for Conservative GC
447
448
```scala
449
import scala.scalanative.runtime._
450
451
// Allocate blob array (conservative GC scanning)
452
val blob = BlobArray.alloc(1024)
453
454
// Set scanning limit (only first 512 bytes scanned by GC)
455
val limitedBlob = blob.withScannableLimit(512)
456
457
// Use as regular byte array
458
blob(0) = 0x42.toByte
459
blob(1) = 0x24.toByte
460
461
val scannableLimit = limitedBlob.scannableLimit // 512
462
```
463
464
### Low-level Memory Operations
465
466
```scala
467
import scala.scalanative.runtime.Intrinsics._
468
import scala.scalanative.unsafe._
469
470
// Stack allocation (automatic cleanup)
471
def processData(): Unit = {
472
val buffer = stackalloc[CInt](256)
473
474
// Initialize buffer
475
var i = 0
476
while (i < 256) {
477
storeInt(buffer.asInstanceOf[Ptr[Byte]] + (i * 4), i)
478
i += 1
479
}
480
481
// Read values back
482
val value = loadInt(buffer.asInstanceOf[Ptr[Byte]] + (100 * 4)) // Get 100th element
483
484
// buffer automatically freed on function exit
485
}
486
487
// Pointer arithmetic
488
Zone.acquire { implicit z =>
489
val array = alloc[CInt](10)
490
val basePtr = toRawPtr(array)
491
492
// Move to 5th element (5 * sizeof(CInt))
493
val fifthPtr = elemRawPtr(basePtr, castIntToRawSize(5 * 4))
494
val typedPtr = fromRawPtr[CInt](fifthPtr)
495
496
!typedPtr = 42 // Set 5th element
497
}
498
```
499
500
### Runtime Information
501
502
```scala
503
import scala.scalanative.runtime._
504
505
// Get application info
506
val executableName = filename
507
val applicationStartTime = startTime
508
val runningTime = uptime
509
510
println(s"Running $executableName for ${runningTime}ms")
511
512
// Convert between pointer types
513
Zone.acquire { implicit z =>
514
val ptr = alloc[CInt]
515
val rawPtr = toRawPtr(ptr)
516
val backToTyped = fromRawPtr[CInt](rawPtr)
517
518
// Size conversions
519
val size = Size.valueOf(1024)
520
val rawSize = toRawSize(size)
521
val backToSize = fromRawSize(rawSize)
522
}
523
```
524
525
## Performance Considerations
526
527
1. **Use native arrays** for performance-critical code - they avoid boxing overhead
528
2. **Prefer atUnsafe** when bounds are already checked - eliminates redundant checks
529
3. **Stack allocation** is faster than heap allocation for temporary buffers
530
4. **BlobArray scanning limits** can improve GC performance for large binary data
531
5. **Manual GC control** can help with predictable latency requirements
532
6. **Clone sparingly** - creates full copies, can be expensive for large arrays
533
534
## Best Practices
535
536
1. **Choose appropriate array types** - use primitive arrays when possible
537
2. **Handle GC pressure** - monitor heap usage in allocation-heavy code
538
3. **Use snapshot carefully** - creates copies, consider sharing when possible
539
4. **Stack allocate temporary data** - automatic cleanup and better performance
540
5. **Set blob limits appropriately** - balance GC scanning cost vs precision
541
6. **Profile GC behavior** - use statistics to tune collection strategy