or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

annotations.mdarrays-runtime.mdc-interop.mdindex.mdmemory-management.mdunsigned-types.md

memory-management.mddocs/

0

# Memory Management

1

2

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.

3

4

## Core Types

5

6

### Zone

7

8

Zone provides scoped memory allocation with automatic cleanup, preventing memory leaks in native code.

9

10

```scala { .api }

11

trait Zone {

12

def alloc(size: CSize): Ptr[Byte]

13

def alloc(size: Int): Ptr[Byte]

14

def alloc(size: UInt): Ptr[Byte]

15

def alloc(size: ULong): Ptr[Byte]

16

def close(): Unit

17

def isOpen: Boolean

18

def isClosed: Boolean

19

}

20

```

21

22

**Zone.alloc methods**:

23

- `alloc(size: CSize)` - Allocate memory of given size in bytes

24

- `alloc(size: Int)` - Allocate memory, converting Int to CSize

25

- `alloc(size: UInt)` - Allocate memory, converting UInt to CSize

26

- `alloc(size: ULong)` - Allocate memory, converting ULong to CSize

27

28

### Zone Companion Object

29

30

```scala { .api }

31

object Zone {

32

def acquire[T](f: Zone => T): T

33

def open(): Zone

34

}

35

```

36

37

**Methods**:

38

- `acquire[T](f: Zone => T): T` - Execute function with fresh zone, automatically cleanup

39

- `open(): Zone` - Create new zone allocator (must call `close()` manually)

40

41

### Ptr[T]

42

43

Type-safe pointer wrapper providing memory access with compile-time type checking.

44

45

```scala { .api }

46

final class Ptr[T] {

47

// Memory access

48

def unary_!(implicit tag: Tag[T]): T

49

def unary_!_=(value: T)(implicit tag: Tag[T]): Unit

50

51

// Pointer arithmetic

52

def +(offset: Int)(implicit tag: Tag[T]): Ptr[T]

53

def +(offset: Long)(implicit tag: Tag[T]): Ptr[T]

54

def +(offset: Size)(implicit tag: Tag[T]): Ptr[T]

55

def +(offset: USize)(implicit tag: Tag[T]): Ptr[T]

56

57

def -(offset: Int)(implicit tag: Tag[T]): Ptr[T]

58

def -(offset: Long)(implicit tag: Tag[T]): Ptr[T]

59

def -(offset: Size)(implicit tag: Tag[T]): Ptr[T]

60

def -(offset: USize)(implicit tag: Tag[T]): Ptr[T]

61

def -(other: Ptr[T])(implicit tag: Tag[T]): CPtrDiff

62

63

// Array-style access

64

def apply(offset: Int)(implicit tag: Tag[T]): T

65

def apply(offset: Long)(implicit tag: Tag[T]): T

66

def apply(offset: Size)(implicit tag: Tag[T]): T

67

def apply(offset: USize)(implicit tag: Tag[T]): T

68

69

def update(offset: Int, value: T)(implicit tag: Tag[T]): Unit

70

def update(offset: Long, value: T)(implicit tag: Tag[T]): Unit

71

def update(offset: Size, value: T)(implicit tag: Tag[T]): Unit

72

def update(offset: USize, value: T)(implicit tag: Tag[T]): Unit

73

74

// Conversions

75

def toInt: Int

76

def toLong: Long

77

}

78

```

79

80

**Memory access**:

81

- `unary_!` - Dereference pointer to read value

82

- `unary_!_=` - Dereference pointer to write value

83

84

**Pointer arithmetic**:

85

- `+` / `-` with numeric offsets - Move pointer by offset * sizeof(T)

86

- `-(other: Ptr[T])` - Calculate pointer difference

87

88

**Array access**:

89

- `apply(offset)` - Read value at pointer + offset

90

- `update(offset, value)` - Write value at pointer + offset

91

92

### Size Types

93

94

Platform-dependent size types that match C `size_t` and `ssize_t` behavior.

95

96

```scala { .api }

97

// Int on 32-bit architectures, Long on 64-bit architectures

98

type Size = /* platform dependent */

99

type CPtrDiff = Size

100

type CSSize = Size

101

```

102

103

## Memory Allocation Functions

104

105

### Generic Allocation

106

107

```scala { .api }

108

def alloc[T](implicit tag: Tag[T]): Ptr[T]

109

def alloc[T](count: Int)(implicit tag: Tag[T]): Ptr[T]

110

def alloc[T](count: UInt)(implicit tag: Tag[T]): Ptr[T]

111

def alloc[T](count: ULong)(implicit tag: Tag[T]): Ptr[T]

112

def alloc[T](count: CSize)(implicit tag: Tag[T]): Ptr[T]

113

```

114

115

**Allocation functions**:

116

- `alloc[T]()` - Allocate single instance of type T

117

- `alloc[T](count)` - Allocate array of count instances of type T

118

119

### Stack Allocation

120

121

```scala { .api }

122

def stackalloc[T](implicit tag: Tag[T]): Ptr[T]

123

def stackalloc[T](count: Int)(implicit tag: Tag[T]): Ptr[T]

124

def stackalloc[T](count: UInt)(implicit tag: Tag[T]): Ptr[T]

125

def stackalloc[T](count: ULong)(implicit tag: Tag[T]): Ptr[T]

126

def stackalloc[T](count: CSize)(implicit tag: Tag[T]): Ptr[T]

127

```

128

129

**Stack allocation functions**:

130

- `stackalloc[T]()` - Allocate single instance on stack

131

- `stackalloc[T](count)` - Allocate array on stack (automatically freed on scope exit)

132

133

## Memory Operations

134

135

### Low-level Memory Functions

136

137

```scala { .api }

138

def memcpy(dest: Ptr[Byte], src: Ptr[Byte], count: CSize): Unit

139

def memmove(dest: Ptr[Byte], src: Ptr[Byte], count: CSize): Unit

140

def memset(ptr: Ptr[Byte], value: Int, count: CSize): Unit

141

def memcmp(ptr1: Ptr[Byte], ptr2: Ptr[Byte], count: CSize): Int

142

```

143

144

**Memory manipulation**:

145

- `memcpy` - Copy memory blocks (undefined behavior for overlapping regions)

146

- `memmove` - Copy memory blocks (safe for overlapping regions)

147

- `memset` - Fill memory with byte value

148

- `memcmp` - Compare memory blocks, returns 0 if equal

149

150

### Type Information

151

152

```scala { .api }

153

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

154

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

155

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

156

```

157

158

**Type queries**:

159

- `sizeOf[T]` - Get size in bytes of type T

160

- `alignmentOf[T]` - Get alignment requirement of type T

161

- `sizeof[T]` - Alias for sizeOf

162

163

## Usage Examples

164

165

### Basic Zone Usage

166

167

```scala

168

import scala.scalanative.unsafe._

169

170

// Automatic cleanup with Zone.acquire

171

Zone.acquire { implicit z =>

172

val ptr = alloc[CInt](10) // allocate array of 10 integers

173

ptr(0) = 42

174

ptr(1) = 84

175

176

val value = ptr(0) // read first element

177

// memory automatically freed when leaving scope

178

}

179

```

180

181

### Manual Zone Management

182

183

```scala

184

import scala.scalanative.unsafe._

185

186

val zone = Zone.open()

187

try {

188

implicit val z: Zone = zone

189

val ptr = alloc[CDouble](5)

190

ptr(0) = 3.14159

191

// ... use pointer

192

} finally {

193

zone.close() // manual cleanup

194

}

195

```

196

197

### Pointer Arithmetic

198

199

```scala

200

import scala.scalanative.unsafe._

201

202

Zone.acquire { implicit z =>

203

val array = alloc[CInt](10)

204

205

// Initialize array

206

var i = 0

207

while (i < 10) {

208

array(i) = i * i

209

i += 1

210

}

211

212

// Pointer arithmetic

213

val second = array + 1 // points to array[1]

214

val value = !second // dereference: reads array[1]

215

216

// Calculate pointer difference

217

val end = array + 10

218

val size = end - array // returns 10

219

}

220

```

221

222

### Stack Allocation

223

224

```scala

225

import scala.scalanative.unsafe._

226

227

def processBuffer(): CInt = {

228

val buffer = stackalloc[CChar](256) // 256 bytes on stack

229

230

// Use buffer for temporary operations

231

!buffer = 'H'.toByte

232

!(buffer + 1) = 'i'.toByte

233

!(buffer + 2) = 0.toByte // null terminator

234

235

// buffer automatically freed on return

236

42

237

}

238

```

239

240

## Type-Safe Memory Operations

241

242

### Tag System

243

244

The Tag system provides type-safe access to memory with automatic size and alignment information:

245

246

```scala { .api }

247

sealed abstract class Tag[T] {

248

def size: Int

249

def alignment: Int

250

def load(ptr: Ptr[T]): T

251

def store(ptr: Ptr[T], value: T): Unit

252

}

253

254

object Tag {

255

// Platform pointer size

256

def SizeOfPtr: Int

257

258

// Tag implementations for pointer types

259

final case class Ptr[T](of: Tag[T]) extends Tag[unsafe.Ptr[T]]

260

261

// Built-in tags for primitive types

262

case object Size extends Tag[unsafe.Size]

263

case object Boolean extends Tag[Boolean]

264

case object Byte extends Tag[Byte]

265

case object UByte extends Tag[UByte]

266

case object Short extends Tag[Short]

267

case object UShort extends Tag[UShort]

268

case object Int extends Tag[Int]

269

case object UInt extends Tag[UInt]

270

case object Long extends Tag[Long]

271

case object ULong extends Tag[ULong]

272

case object Float extends Tag[Float]

273

case object Double extends Tag[Double]

274

case object Char extends Tag[Char]

275

276

// Tags for structured types

277

case class CArray[T, N <: Nat](of: Tag[T], n: Tag[N]) extends Tag[CArray[T, N]]

278

case class CStruct1[T1](tag1: Tag[T1]) extends Tag[CStruct1[T1]]

279

case class CStruct2[T1, T2](tag1: Tag[T1], tag2: Tag[T2]) extends Tag[CStruct2[T1, T2]]

280

// ... continues for CStruct3 through CStruct22

281

}

282

```

283

284

**Tag system methods**:

285

- `size` - Size in bytes of the type

286

- `alignment` - Memory alignment requirement

287

- `load(ptr)` - Type-safe memory read

288

- `store(ptr, value)` - Type-safe memory write

289

290

### Runtime Memory Intrinsics

291

292

Low-level memory operations for performance-critical code:

293

294

```scala { .api }

295

object Intrinsics {

296

// Stack allocation (automatic cleanup)

297

def stackalloc[T](): RawPtr

298

def stackalloc[T](size: RawSize): RawPtr

299

300

// Raw memory load operations

301

def loadBoolean(rawptr: RawPtr): Boolean

302

def loadChar(rawptr: RawPtr): Char

303

def loadByte(rawptr: RawPtr): Byte

304

def loadShort(rawptr: RawPtr): Short

305

def loadInt(rawptr: RawPtr): Int

306

def loadLong(rawptr: RawPtr): Long

307

def loadFloat(rawptr: RawPtr): Float

308

def loadDouble(rawptr: RawPtr): Double

309

def loadRawPtr(rawptr: RawPtr): RawPtr

310

def loadRawSize(rawptr: RawPtr): RawSize

311

def loadObject(rawptr: RawPtr): Object

312

313

// Raw memory store operations

314

def storeBoolean(rawptr: RawPtr, value: Boolean): Unit

315

def storeChar(rawptr: RawPtr, value: Char): Unit

316

def storeByte(rawptr: RawPtr, value: Byte): Unit

317

def storeShort(rawptr: RawPtr, value: Short): Unit

318

def storeInt(rawptr: RawPtr, value: Int): Unit

319

def storeLong(rawptr: RawPtr, value: Long): Unit

320

def storeFloat(rawptr: RawPtr, value: Float): Unit

321

def storeDouble(rawptr: RawPtr, value: Double): Unit

322

def storeRawPtr(rawptr: RawPtr, value: RawPtr): Unit

323

def storeRawSize(rawptr: RawPtr, value: RawSize): Unit

324

def storeObject(rawptr: RawPtr, value: Object): Unit

325

326

// Pointer arithmetic

327

def elemRawPtr(rawptr: RawPtr, offset: RawSize): RawPtr

328

def elemRawPtr(rawptr: RawPtr, offset: Int): RawPtr

329

330

// Type casting intrinsics

331

def castRawPtrToObject(rawptr: RawPtr): Object

332

def castObjectToRawPtr(obj: Object): RawPtr

333

def castIntToFloat(int: Int): Float

334

def castFloatToInt(float: Float): Int

335

def castLongToDouble(long: Long): Double

336

def castDoubleToLong(double: Double): Long

337

def castRawPtrToLong(rawptr: RawPtr): Long

338

def castRawPtrToInt(rawptr: RawPtr): Int

339

def castIntToRawPtr(int: Int): RawPtr

340

def castLongToRawPtr(long: Long): RawPtr

341

def castIntToRawSize(int: Int): RawSize

342

def castIntToRawSizeUnsigned(int: Int): RawSize

343

def castLongToRawSize(long: Long): RawSize

344

def castRawSizeToInt(size: RawSize): Int

345

def castRawSizeToLong(size: RawSize): Long

346

347

// Unsigned arithmetic

348

def divUInt(l: Int, r: Int): Int

349

def remUInt(l: Int, r: Int): Int

350

def divULong(l: Long, r: Long): Long

351

def remULong(l: Long, r: Long): Long

352

353

// Unsigned type conversions

354

def byteToUInt(b: Byte): Int

355

def byteToULong(b: Byte): Long

356

def shortToUInt(v: Short): Int

357

def shortToULong(v: Short): Long

358

def intToULong(v: Int): Long

359

def uintToFloat(v: Int): Float

360

def ulongToFloat(v: Long): Float

361

def uintToDouble(v: Int): Double

362

def ulongToDouble(v: Long): Double

363

}

364

```

365

366

### Advanced Memory Usage

367

368

#### Using the Tag System

369

370

```scala

371

import scala.scalanative.unsafe._

372

import scala.scalanative.runtime._

373

374

Zone.acquire { implicit z =>

375

// Type-safe generic allocation

376

def allocTyped[T](implicit tag: Tag[T]): Ptr[T] = {

377

val ptr = alloc[Byte](tag.size)

378

ptr.asInstanceOf[Ptr[T]]

379

}

380

381

// Get size information at runtime

382

val intSize = Tag.Int.size // Size of Int (4 bytes)

383

val alignment = Tag.Long.alignment // Alignment of Long (8 bytes)

384

385

// Type-safe memory operations

386

val ptr = allocTyped[Int]

387

Tag.Int.store(ptr, 42)

388

val value = Tag.Int.load(ptr) // value = 42

389

}

390

```

391

392

#### Raw Memory Operations

393

394

```scala

395

import scala.scalanative.runtime.Intrinsics._

396

import scala.scalanative.unsafe._

397

398

// Stack allocation with automatic cleanup

399

def processBuffer(): Int = {

400

val buffer = stackalloc[Byte](1024.toULong)

401

402

// Direct memory operations (no bounds checking)

403

storeInt(buffer, 0x12345678)

404

storeByte(elemRawPtr(buffer, 4), 0xFF.toByte)

405

406

// Read back

407

val intValue = loadInt(buffer) // 0x12345678

408

val byteValue = loadByte(elemRawPtr(buffer, 4)) // 0xFF

409

410

intValue

411

// buffer automatically freed on function exit

412

}

413

```

414

415

#### Performance-Critical Memory Access

416

417

```scala

418

import scala.scalanative.runtime.Intrinsics._

419

import scala.scalanative.unsafe._

420

421

Zone.acquire { implicit z =>

422

val array = alloc[Double](1000)

423

val basePtr = toRawPtr(array)

424

425

// Initialize using raw operations for maximum performance

426

var i = 0

427

while (i < 1000) {

428

val offset = castIntToRawSize(i * 8) // sizeof(Double) = 8

429

val elemPtr = elemRawPtr(basePtr, offset)

430

storeDouble(elemPtr, i.toDouble * 1.5)

431

i += 1

432

}

433

434

// Process data using vectorized operations (hypothetical)

435

// Raw access allows for SIMD optimization

436

}

437

```

438

439

## Best Practices

440

441

1. **Always use Zone.acquire** when possible for automatic memory management

442

2. **Check for null pointers** when interfacing with C libraries

443

3. **Be careful with pointer arithmetic** - ensure bounds checking when needed

444

4. **Use type tags consistently** for generic operations

445

5. **Prefer stack allocation** for small, temporary buffers

446

6. **Don't mix different allocation methods** (zone vs malloc) for the same memory

447

7. **Use raw intrinsics sparingly** - only for performance-critical sections

448

8. **Validate pointer arithmetic** - raw operations bypass safety checks

449

9. **Consider alignment** - use Tag system for proper alignment information

450

10. **Profile memory-intensive code** - intrinsics can provide significant speedups