# 17. Native code

> Chapter 17: call C libraries with lib and fun, pass arrays, strings, structs and callbacks without copies, compile numeric methods to C with @[Native], and use raw pointers inside unsafe.

Canonical: https://alice-lang.org/documentation/learn/native/

[Documentation index](https://alice-lang.org/documentation/llms.txt) · [Complete book](https://alice-lang.org/llms-full.txt)

Sometimes the code you need already exists in C: a math library, a codec, an operating system call. Sometimes you want the last bit of speed from a numeric loop. This chapter shows how Alice reaches below .NET, and how it keeps the dangerous parts small and visible.

The examples here call the C library of Linux (`libc.so.6`, `libm.so.6`). On macOS the names are `libSystem.dylib`; on Windows, `msvcrt.dll` or `ucrtbase.dll`. The book's tests run on Linux.

## 17.1 Calling C: `lib` and `fun`

Declare the C functions you need in a `lib` block, and name the shared library with `@[Link]`:

```alice
@[Link("libm.so.6")]
lib LibM
  fun cos(x : Float64) : Float64
  fun pow(x : Float64, y : Float64) : Float64
end

puts LibM.cos(0.0)
puts LibM.pow(2.0, 10.0)
```

```text title="Output"
1.0
1024.0
```

Each `fun` is a C signature written with Alice types. You call it like a module function. For numbers and `Bool`, the call is **blittable**: the values cross as they are, with no conversion code, so the call costs about as much as a call in C.

`@[Fast]` on a `fun` removes the switch between .NET and native mode, which saves about 20 percent per call. Use it only for short functions that never block and never call back into .NET.

## 17.2 Strings, arrays and structs

Other types cross the boundary without copies where possible:

| Alice type            | C sees                                                              |
| --------------------- | ------------------------------------------------------------------- |
| numbers, `Char`       | themselves                                                          |
| `Bool`                | a 1-byte C `bool`                                                   |
| `String` parameter    | a NUL-terminated UTF-8 string, built on the stack for short strings |
| `Array(number)`       | a pointer to the elements, pinned during the call, no copy          |
| a `struct` of numbers | the struct by value, fields in declaration order                    |
| `out x : T`           | a pointer to the caller's variable                                  |

```alice
@[Link("libc.so.6")]
lib LibC
  fun strlen(s : String) : UInt64
  fun getpid : Int32
end

puts LibC.strlen("tea")
puts LibC.getpid > 0
```

```text title="Output"
3
true
```

C functions that fill in a struct take it through `out`, with no pointer code in Alice:

```alice
struct TimeSpec
  property sec : Int64 = 0_i64
  property nsec : Int64 = 0_i64
end

@[Link("libc.so.6")]
lib LibC
  fun clock_gettime(clock : Int32, out ts : TimeSpec) : Int32
end

ts = TimeSpec.new
puts LibC.clock_gettime(0, out ts)
puts ts.sec > 1_000_000_000_i64
```

```text title="Output"
0
true
```

### Callbacks

Some C functions call back into your code, like `qsort`, which asks you how to compare two elements. Declare the parameter as `Callback(A, B, R)` and pass a lambda literal:

```alice
@[Link("libc.so.6")]
lib LibC
  fun qsort(base : Array(Int32), n : UInt64, size : UInt64, cmp : Callback(Pointer(Int32), Pointer(Int32), Int32)) : Void
end

xs = [5, 3, 9, 1]
LibC.qsort(xs, 4_u64, 4_u64, ->(a : Pointer(Int32), b : Pointer(Int32)) { a[0] <=> b[0] })
puts xs
```

```text title="Output"
[1, 3, 5, 9]
```

The lambda becomes a static function that C can call through a plain function pointer: no delegate, no allocation. For that reason it may not capture local variables. C keeps only the function pointer, with nowhere to store them:

```alice fail="AL0001"
@[Link("libc.so.6")]
lib LibC
  fun qsort(base : Array(Int32), n : UInt64, size : UInt64, cmp : Callback(Pointer(Int32), Pointer(Int32), Int32)) : Void
end

xs = [5, 3, 9, 1]
direction = -1
LibC.qsort(xs, 4_u64, 4_u64, ->(a : Pointer(Int32), b : Pointer(Int32)) { (a[0] <=> b[0]) * direction })
```

```text title="Error"
a callback passed to native code cannot capture variables
```

The diagnostic is `AL0215`; this version reports it under the general code `AL0001` with the `AL0215` text. Pass state through the C function's user-data argument when it has one.

## 17.3 Raw pointers: `unsafe`

The callback above used `Pointer(Int32)`: a raw address, as in C. Reading `a[0]` through it is not checked: a wrong pointer reads wrong memory. Alice confines that risk. Outside callback bodies, any pointer operation must be inside `unsafe ... end` or an `unsafe def`:

```alice fail="AL0216"
@[Link("libc.so.6")]
lib LibC
  fun malloc(n : UInt64) : Pointer(UInt8)
end

p = LibC.malloc(4_u64)
```

```text title="Error"
error[AL0216]: pointer operation outside `unsafe`
```

```alice
@[Link("libc.so.6")]
lib LibC
  fun malloc(n : UInt64) : Pointer(UInt8)
  fun free(p : Pointer(UInt8)) : Void
end

unsafe
  p = LibC.malloc(4_u64)
  p[0] = 7_u8
  puts p[0]
  LibC.free(p)
end
```

```text title="Output"
7
```

`unsafe` does not turn off any other check. It marks the lines a reviewer must read with care, and `grep unsafe` finds all of them. Strings, arrays and numbers never need it.

## 17.4 Alice compiled to C: `@[Native]`

For a hot numeric method, `@[Native]` compiles the method body to C with `clang -O3 -march=native`, and calls that from .NET:

```alice
@[Native]
def dot(a : Array(Float64), b : Array(Float64)) : Float64
  s = 0.0
  for i in 0...a.size
    s += a[i] * b[i]
  end
  s
end

puts dot([1.0, 2.0, 3.0], [4.0, 5.0, 6.0])
```

```text title="Output"
32.0
```

You write ordinary Alice. The method is compiled twice, to C and to .NET. Without a C compiler on the machine, or when two array arguments overlap, the .NET version runs. The result is the same either way. The C version keeps Alice's checks: indexing is bounds-checked and integer overflow traps.

Use `@[Native]` for numeric kernels where you measured that clang beats the .NET JIT. It does not help methods that call .NET or allocate, and the call overhead dominates very small methods.

:::caution\[Common mistakes]

- **A library name that only exists on one OS.** `@[Link("m")]` fails on Linux, where the file is `libm.so.6`. Name the platform's file, and test on each platform you ship.
- **Pointers outside `unsafe`.** `error[AL0216]`. Wrap the pointer code, or better, use arrays, `out` and strings, which need no pointers.
- **A capturing callback.** C cannot hold the captured variables. Use the C API's user-data argument.
- **`@[Fast]` on a function that blocks.** It stalls the garbage collector for the length of the call.
- **Freeing memory twice, or not at all.** `malloc`ed memory is not managed. Prefer Alice arrays, which are pinned for the call and need no freeing.
  :::

## Exercises

1. Call `floor` and `sqrt` from `libm.so.6` and print `floor(2.7)` and `sqrt(16.0)`.
2. Sort `[30, 10, 20]` in descending order with `qsort`, without capturing a variable.
3. Why may an `Array(Int32)` be passed to C without `unsafe`, while a `Pointer(Int32)` may not be used without it?

<details>
<summary>Answers</summary>

1. Answer:

   ```alice
   @[Link("libm.so.6")]
   lib LibM
     fun floor(x : Float64) : Float64
     fun sqrt(x : Float64) : Float64
   end

   puts LibM.floor(2.7)
   puts LibM.sqrt(16.0)
   ```

   ```text title="Output"
   2.0
   4.0
   ```

2. Swap the operands of `<=>` inside the comparison:

   ```alice
   @[Link("libc.so.6")]
   lib LibC
     fun qsort(base : Array(Int32), n : UInt64, size : UInt64, cmp : Callback(Pointer(Int32), Pointer(Int32), Int32)) : Void
   end

   xs = [30, 10, 20]
   LibC.qsort(xs, 3_u64, 4_u64, ->(a : Pointer(Int32), b : Pointer(Int32)) { b[0] <=> a[0] })
   puts xs
   ```

   ```text title="Output"
   [30, 20, 10]
   ```

3. Alice knows an array's length and pins it for exactly the duration of the call, so C receives a valid pointer that cannot move or be freed meanwhile. A raw `Pointer(Int32)` carries no length and no lifetime: Alice cannot check what it points to, so you must mark the code as `unsafe`.

</details>

## Summary

- `lib` blocks with `@[Link("library")]` declare C functions as `fun` signatures; blittable calls cost about as much as C calls.
- Strings, arrays, structs of numbers and `out` parameters cross without copies and without pointers.
- `Callback(...)` parameters take non-capturing lambdas, compiled to C function pointers.
- `Pointer(T)` is allowed only in `unsafe` code (`AL0216`).
- `@[Native]` compiles a numeric method to C with clang, with a .NET fallback and the same results.

[Chapter 18](https://alice-lang.org/documentation/learn/packaging.md) packages everything you have built.

## Continue reading

- Previous: [16. The GPU](https://alice-lang.org/documentation/learn/gpu.md)
- Next: [18. Projects and packaging](https://alice-lang.org/documentation/learn/packaging.md)
