オープンソースプログラミング言語zigまとめ


WebAssemblyのモジュール記述のためのプログラミング言語を調べていると、比較的新しいオープンソースのプログラミング言語zigがWebAssemblyモジュールを書くのに適している感じだったので、ソースコードを書いて動かしながら**調べたことについて、コードに出ている順で並べ**ています。

この文章中で用いているソースコードはmacOSやlinux上でビルド可能な状態で以下のURLにおいてあります:

- https://gist.github.com/bellbind/3c52ea6e506656701c9b7ff00a8599fa

Qiitaは現時点でzigコードのシンタックスハイライトに未対応であり視認性がよくないですが、githubではzigコードでもシンタックスハイライトされます。

## 0. プログラミング言語zigについて

オープンソースのプログラミング言語zigは、以下のURLで公開されています。

- https://ziglang.org/
- https://github.com/ziglang/zig

zigはLLVMで実装しているプログラミング言語ですが、C++やGoやRustと違い、C言語のように**ヒープメモリの利用がオプション**であり、`new`のようなものが言語構文に組み込まれていません。

zig言語の特徴は:

- 各OS環境の主要パッケージシステムでインストールできる
- 開発ツールが**`zig`コマンド1つに統合**されている
- スクリプト言語のように、**ソースファイル(.zigファイル)を直接実行**できる(`zig run xxx.zig`)
- **ユニットテスト用の構文**が組み込まれており、テストコードを実装と同一のソースファイル中に記述でき、`zig`コマンドでテストを実行できる(`zig test xxx.zig`)
- 実行ファイルなどを生成する、ビルド記述`build.zig`もzig言語で記述し、`zig`コマンドでビルド実行できる**ビルドシステム**を備える(`zig build`)
- **シンタックスフォーマッタ**を組み込んでいる(`zig fmt xxx.zig`)
- ビット数指定の数値型、式としての`if`や`switch`やループ構文、ブロック脱出時実行の`defer`文、配列のスライス、コルーチン、コンパイル時実行式、等の**モダンな言語仕様**
- **C言語とABIレベルの相互運用ができる**言語モデル
- Cライブラリのヘッダファイル(.h)にある関数や`struct`/`union`/`enum`を、zigの関数や`struct`/`union`/`enum`として直接使用するための**Cプログラムを組み込む機構(`@cImport`)を言語として組み込んでいる**
- C言語より厳しい型制約、そして、型推論を備える
- [musl-libc](https://www.musl-libc.org/)実装を含み、libcコードを埋め込んだ**スタンドアロンのバイナリを生成できる**
- WebAssemblyのwasmファイルやUEFI用バイナリ、各OS用のスタティック/ダイナミックライブラリを含む**多彩なビルドターゲット**を持つ

などです。

ただし、構文や標準ライブラリの**仕様がまだ安定していない**点は、注意です。
現在のバージョン0.4.0と、その次のバージョンでは、文字列の配列型の構文のレベルで非互換が起きるような段階です。
そして以下、この記事では、**バージョン0.4.0の仕様**に基づいてすべて記述しています。

- https://ziglang.org/documentation/0.4.0/

最初に、`zig zen`コマンドを実行すると、zig言語のポリシーを垣間見ることができます:

```shell-executable
$ zig zen

 * Communicate intent precisely.
 * Edge cases matter.
 * Favor reading code over writing code.
 * Only one obvious way to do things.
 * Runtime crashes are better than bugs.
 * Compile errors are better than runtime crashes.
 * Incremental improvements.
 * Avoid local maximums.
 * Reduce the amount one must remember.
 * Minimize energy spent on coding style.
 * Together we serve end users.

$
```

zig言語では、twitterなどのハッシュタグでは[ziglang](https://twitter.com/search?q=%23ziglang)が使われています。

## `1. zig`コマンドのインストール

zig言語は、メジャーなパッケージマネージャのhomebrew, snap, nixos, scoop, chocolatey, packmanで、`zig`(scoopでは`ziglang`)パッケージを提供しています。これらのパッケージマネージャから`zig`コマンドをインストールしするのが手軽です。

- https://github.com/ziglang/zig/wiki/Install-Zig-from-a-Package-Manager

### emacsのzig-mode

MELPAに、zig言語オフィシャルの`zig-mode`が登録されています。

- https://melpa.org/#/zig-mode
- https://github.com/ziglang/zig-mode

vim, vscode, sublime用のモードもあるようです:

- https://github.com/ziglang/zig.vim
- https://github.com/ziglang/vscode-zig
- https://github.com/ziglang/sublime-zig-language


## 2. `zig`コマンドの基本

開発ツールは、すべて`zig`コマンドのサブコマンドとして提供されています。

zigファイル実行系コマンド:

- `zig run xxx.zig`:  ソースファイル`xxx.zig`の`main`関数を(バイナリ生成せずに)実行します
- `zig test xxx.zig`:  ソースファイル`xxx.zig`に記述された`test`構文内のコードを順に実行し、テスト結果のサマリを表示します
- `zig build`: ビルド記述`build.zig`に記述されたビルド処理を実行します

zigファイルコンパイル系コマンド:

- `zig build-exe xxx.zig`: zigファイル(`xxx.zig`)から、**各種ターゲットのバイナリ(`xxx`、`xxx.exe`等)**を生成します
- `zig build-obj xxx.zig`: zigファイル(`xxx.zig`)から、**Cヘッダファイル(`xxx.h`)とCオブジェクトファイル(`xxx.o`, `xxx.obj`等)**を生成します
- `zig build-lib xxx.zig`: zigファイル(`xxx.zig`)から、Cヘッダファイル(`xxx.h`)とCスタティックライブラリファイル(`libxxx.a`, `xxx.lib`等)を生成します
- `zig build-lib -dynamic xxx.zig`: zigファイルから、Cヘッダファイル(`xxx.h`)とDLLファイル(`libxxx.so`、`libxxx.dylib`、`xxx.dll`と`xxx.pdb`等)などを生成します

ユーティリティ系コマンド:

- `zig init-exe`: このディレクトリ以下に、実行ファイル用のプロジェクトテンプレート(`build.zig`と`src/main.zig`)を生成します
- `zig init-lib`: このディレクトリ以下に、スタティックライブラリ用のプロジェクトテンプレート(`build.zig`と`src/main.zig`)を生成します
- `zig fmt xxx.zig`: `xxx.zig`に、シンタックスフォーマッタをかけて**上書き**します
- `zig translate-c xxx.c`: Cヘッダファイルや簡単なC実装ファイルを、zigコード化して標準出力します


情報表示系コマンド: 

- `zig --help`: サブコマンドの一覧およびコマンド引数の一覧ヘルプを表示します 
- `zig builtin`: zigコマンドのランタイム環境情報のモジュール`@import("builtin")`を、zigソースコード形式で表示します(ビルドターゲット等の情報があり、`build.zig`内のコードで主に使う)
- `zig targets`: `zig build-exe`等で扱える**ビルドターゲットの一覧**を表示します
- `zig version`: zigコマンドの仕様バージョン(0.4.0など)を表示します
- `zig libc`: zigコマンド実行環境のlibcのディレクトリパス情報を表示します
- `zig zen`: zig言語のデザインポリシーを表示します

### コマンド例 `zig build-exe --release-fast -target wasm32-freestanding --name fft.wasm fft.zig`

このコマンドラインは、zigライブラリコードからWebAssemblyでの`.wasm`モジュールファイルを生成するものです。
コマンドラインオプションは以下の意味となります。

- `--release-fast`: 速度重視のビルドを指定しています
    - `--relase-safe`: 速度重視だがトラップ処理は埋め込んだままのビルドを指定します
    - `--release-small`: (トラップなしで)サイズ重視のビルドを指定します
- `-target wasm32-freestanding`は、WebAssemblyの32bit wasm仕様で、`import`する依存なし(freestanding)のターゲットを指定しています
    - `-target x84_64-windows`: Windowsの64bitコンソールコマンド(exe)をターゲットに指定します
- `--name fft.wasm`: アウトプットファイル名を`fft.wasm`に設定します
    - デフォルトのアウトプットファイル名は、メインのzigファイル名をベースにした、ターゲットOSでの実行ファイル名(この場合、`fft`や`fft.exe`)になります

## 3. zigプログラム: FFT実装

言語のもつ各種機能に触れられる程度には複雑なコードの例として、高速フーリエ変換FFTのループ実装を採用し、これをzigで記述します。

- 参照: [「高速フーリエ変換FFTを理解する」](https://qiita.com/bellbind/items/ba7aa07f6c915d400000)

`main`関数のないライブラリとして`fft.zig`を記述し、この中で`test`構文でテストを記述し、`zig test fft.zig`でテスト実行で動作確認します。

`fft.zig`のコード全体にはテストコードを含めて200行弱になります。ここではコードを7分割し、そのコード片を上から順に切り出し、そのコード片ごとに含むzig言語の機能や特徴を説明していきます。

---

### 3.1. 標準ライブラリ読み込み

以下のコード片は、標準ライブラリから円周率値、sin/cos関数の読み込みをするコードです:

```zig:fft.zig(1)
const pi = @import("std").math.pi;
const sin = @import("std").math.sin;
const cos = @import("std").math.cos;
```

#### 標準ライブラリとビルトイン関数

zigでは、`@import("std")`によって**標準ライブラリ** の機能を利用できます。
`@import`のような`@`で始まる識別子はzigでは**ビルトイン関数** と呼び、型のキャストもこのビルトイン関数による機能として提供されます。

#### 変数のモデル

zigでは、(C言語と同じく)**変数は連続メモリ領域**のモデルです。

このため**`const`変数は、メモリ領域の内容全体が変更不可能**となり、構造体の場合での各メンバーや配列の場合での各要素も変更不可能となります。
また、zigでは、関数の引数も`const`変数扱いです。内容が変更可能な変数は`var`で宣言します。

#### `const`型

zigには`const`をつけた型があります。`const`型は、(ポインタ利用で)その**参照経由の変更操作は不許可**、という意味であり、`const`変数とは別の作用になります。

たとえば、`var a: f64 = 0.0`の変数は値を変更でき、`var ptr: *const f64 = &a`で参照できますが、この参照からの変更`ptr.* = 1.0`はコンパイルエラーとなります(`ptr.*`はメンバー全部の意味でポインタの先のメモリ領域を指す。Cでの`*ptr`のこと)。
逆に、`var a: f64 = 0.0`の変数への、`const`変数でのポインタ`const ptr: *f64 = &a`に対しては、`ptr.* = 1.0`が可能です。

また、`const a: f64 = 0.0`の`const`変数へは、`const`型のポインタ`var ptr: *const f64 = &a`でなければコンパイルエラーとなります。

#### 型への名前付けの`const`変数

`const`変数は、`struct`/`union`/`enum`/`error`などの型(`type`型変数)への別名付けにも使われます。
zigの型チェックは、型名での一致ではなく、**構造の一致**で判断します。このため、別名の型の変数と元の名前の型の変数との間で代入可能です。

のコードでは標準ライブラリの関数や変数を`const`で変数名を割り当てて使用していますが、`@import("std")`は式であり、コード内のどこでも使えます。

また、zig-0.4.0では、`sin`や`cos`は標準ライブラリ提供の関数となっていますが、次のバージョンではビルトインに移籍し`@sin`、`@cos`として使用することになるようです。

---

### 3.2. 複素数`struct`の定義

以下のコード片は、FFTで扱う複素数構造体と、そのコンストラクタやメソッドの宣言です:

``` zig:fft.zig(2)
pub export const CN = extern struct {
    pub re: f64,
    pub im: f64,
    pub fn rect(re: f64, im: f64) CN {
        return CN{.re = re, .im = im,};
    }
    pub fn expi(t: f64) CN {
        return CN{.re = cos(t), .im = sin(t),};
    }
    pub fn add(self: CN, other: CN) CN {
        return CN{.re = self.re + other.re, .im = self.im + other.im,};
    }
    pub fn sub(self: CN, other: CN) CN {
        return CN{.re = self.re - other.re, .im = self.im - other.im,};
    }
    pub fn mul(self: CN, other: CN) CN {
        return CN{
            .re = self.re * other.re - self.im * other.im,
            .im = self.re * other.im + self.im * other.re,
        };
    }
    pub fn rdiv(self: CN, re: f64) CN {
        return CN{.re = self.re / re, .im = self.im / re,};
    }
};
```

#### `pub`属性

`pub`属性は、(標準ライブラリの`pi`や`sin`のように)他のzigファイルから**`@import`されたときに利用させる**変数や関数につける属性です。
`pub`がなければ`@import`されても、変数や関数は直接利用できません。


#### `export`属性

`extport`属性は、**Cヘッダ(.h)から利用させる**関数や変数につける属性です。**オブジェクトファイルやライブラリファイルから利用させる対象**へつける属性です。

関数に`export`をつけた場合、Cヘッダファイルにその関数に対応するプロトタイプ宣言が埋め込まれます。
`struct`のような型への変数に`export`をつける場合、Cヘッダファイルにその型情報が埋め込まれます。

Cでの`extern`と同様に、**ファイルをまたいでも名前が被らない**必要があります。

#### `struct`のバリエーション

zigの`struct`型は、メモリレイアウトと`export`での扱いの違いで、3タイプが存在します。

- `struct`: メンバーの順序を含む**メモリレイアウトを規定しない**`struct`。`export`時のCヘッダでは`struct`ポインタでの宣言として埋め込まれる
- `packed struct`:  **宣言順にメンバーを配置**するメモリレイアウトの`struct`。`export`時のCヘッダでは`struct`ポインタ宣言として埋め込まれる
- `extern struct`: 宣言順にメンバーを配置するメモリレイアウトの`struct`。`export`時のCヘッダでは**`struct`のメンバー定義宣言**として埋め込まれる

#### `struct`のメンバー関数

zigでは、`struct`内部に、関数`fn`を宣言できます。この関数は`型名.関数名(...)`のかたちで呼び出します。`add`や`sub`のように、第一引数が定義する`struct`である場合は、**メソッド呼び出し**スタイルでも記述できます(`CN.add(a, b)`を、`a.add(b)`としても記述ができる)。

zig-0.4.0では、`struct`内の関数でも、`export`時のCヘッダでは、構造体名のプレフィックスなどなにもない、関数名そのままの関数名でのプロトタイプ宣言となります(`CN.rect()`を`export`すると、`CN_rect()`のようになるのではなく、ただの`rect()`になる)。

#### 数値型

zigの数値型は、**ビット数を明示**します。C言語インタフェース用のターゲット依存の数値型もあります:

- 非数値型: `type`, `noreturn`, `void`, `bool`
- 符号付き整数型: `i8`, `i16`, `i32`, `i64`, `i128`
- 符号なし整数型: `u8`, `u16`, `u32`, `u64`, `u128`
- 浮動小数型: `f16`, `f32`, `f64`, `f128`
- ターゲット依存数値型: `isize`, `usize`, `c_short`, `c_ushort`, `c_int`, `c_uint`, `c_long`, `c_ulong`, `c_longlong`, `c_ulonglong`, `c_longdouble` 

また、**シフト演算(`>>`、`<<`)の右辺は、`u3`、`u4`、`u5`、`u6`, `u7`**といった左辺のビット数-1を上限値とする整数型を使います(変数は、これらの型へダウンキャストします)。

zigでは`true`と`false`を受ける**`bool`型は数値型ではありません**が、ビルトインを使って0/1の数値型にキャスト可能です。

- `type`は(`struct`等のカスタムタイプも含む)型の型
- `void`は値なしの型で、(`break`で値を渡さない)ブロック式の型でもある
- `noreturn`は、途中でプロセス終了したり、無限ループしたりなど、正常に終わらない関数のための戻り値の型

#### `struct`イニシャライザ

構造体のイニシャライザ(リテラル)は`構造体名 { .メンバー名 = 値, ...}`です。C99の構造体メンバーのイニシャライザ(`(型) {.メンバー名=値,...}`)に似た構文です。

#### 関数の引数や戻り値は値のコピー

関数の引数や戻り値は**値のコピー**のモデルです。`struct`や配列だろうとコピー扱いです。
引数や戻り値の型としてポインタ型を使うことは可能です。

どんな大きさの型だろうとスタック上でコピーするため、配列型を下手に扱うと、OSのスタックリミットにかかる可能性があります。

----

### 3.3. 構造体`CN`のテストコード

以下のコード片は、テストコード構文内で、宣言した`CN`のメンバー関数を利用しているコードです。

```zig:fft.zig(3)
test "CN" {
    const assert = @import("std").debug.assert;
    const eps = @import("std").math.f64_epsilon;
    const abs = @import("std").math.fabs;

    const a = CN.rect(1.0, 0.0);
    const b = CN.expi(pi / 2.0);
    assert(a.re == 1.0 and a.im == 0.0);
    assert(abs(b.re) < eps and abs(b.im - 1.0) < eps);
    
    const apb = a.add(b);
    const asb = a.sub(b);
    assert(abs(apb.re - 1.0) < eps and abs(apb.im - 1.0) < eps);
    assert(abs(asb.re - 1.0) < eps and abs(asb.im + 1.0) < eps);
    
    const bmb = b.mul(b);
    assert(abs(bmb.re + 1.0) < eps and abs(bmb.im) < eps);
    
    const apb2 = apb.rdiv(2.0);
    assert(abs(apb2.re - 0.5) < eps and abs(apb2.im - 0.5) < eps);
}
```

#### `test`部

`test "名前" {...}`は、**`zig test`コマンド時に実行されるコード**を記述する部分です。

zig-0.4.0現在、`test`内で直接的には関数を定義できません。
しかし、`test`内でも`struct`内での関数宣言は可能で、メソッド呼び出しによって関数を使うことは可能です。

#### 構造体のメソッド呼び出し

`struct`内の`fn`の関数は、`構造体名.関数名()`で呼び出せ、とくに第一引数がその`struct`の場合のみ、`変数名.関数名()`でも呼び出せる、という構文になっています。

このコードでの`a.add(b)`は、`CN.add(a, b)`でも呼び出し可能となります。

---

### 3.4. `CN`配列のテストコード

以下のコード片は、`CN`の配列での利用のテストコードです。

```zig:fft.zig(4)
test "CN array" {
    const assert = @import("std").debug.assert;
    const eps = @import("std").math.f64_epsilon;
    const abs = @import("std").math.fabs;

    var cns: [2]CN = undefined;
    cns[0] = CN.rect(1.0, 0.0);
    cns[1] = CN.rect(0.0, 1.0);
    cns[0] = cns[0].add(cns[1]);
    assert(abs(cns[0].re - 1.0) < eps and abs(cns[0].im - 1.0) < eps);
}
```

#### zigの配列

zigの配列型は`[要素数]要素型`で、**要素数が明示された**連続領域を意味します。
多次元配列型は`[外側要素数][内側要素数]要素型`です。メモリレイアウトは、内側要素数個が、外側要素数個並びます。

要素型が`struct`ならメンバーが順に並んで、それが要素数個ある、メモリレイアウトになります。
配列の要素への代入は値の上書きになります。

`var`変数による配列の未初期化宣言は`var 変数名: [要素数]要素型 = undefined`です。
要素数は必須です。

配列型のイニシャライザ(リテラル)は、`[要素数]要素型{初期値0, 初期値1, ....}`です。
イニシャライザでの総素数は省略できます。
同一値でのイニシャライザは、`[]要素型{初期値0, 初期値1, ...} ** 繰り返し数`も可能です。この場合、初期値数✕繰り返し数の要素数の配列型になります。

`var`変数による配列の初期値あり宣言は配列型のイニシャライザをつかって、`var 変数名 = [要素数]要素型 {初期値0, 初期値1, ...}`となります。
`const`変数では初期値あり宣言`const 変数名 = [要素数]要素型 {初期値0, 初期値1, ...}`を使うのが普通です(例: `const v = [3]u8{1,2,3}`)。

注意点は、関数内で配列変数を宣言すると**スタックメモリに領域が割り当てられる**ことです。
このため、配列を使うとOSのスタックリミット(8MBなど)に到達しやすいので注意が必要です。
(標準ライブラリでヒープ等を使う方法は後述します。)


---

### 3.5. FFT用インデックスビット反転

以下のコード片は、FFTでの配列インデックスのビット反転の関数`revbit`の定義とそのテストコードです。

```zig:fft.zig(5)
fn revbit(k: u32, n0: u32) u32 {
    return @bitreverse(u32, n0) >> @truncate(u5, 32 - k);
}

test "revbit" {
    const assert = @import("std").debug.assert;
    const n: u32 = 8;
    const k: u32 = @ctz(n);
    assert(revbit(k, 0) == 0b000);
    assert(revbit(k, 1) == 0b100);
    assert(revbit(k, 2) == 0b010);
    assert(revbit(k, 3) == 0b110);
    assert(revbit(k, 4) == 0b001);
    assert(revbit(k, 5) == 0b101);
    assert(revbit(k, 6) == 0b011);
    assert(revbit(k, 7) == 0b111);
}
```

#### ビット演算のビルトイン

zigでは、**CPU命令で提供される(CPU intrinsics)ビット演算は、ビルトイン**で用意されています。

- `@bitreverse(数値型, 数値)`: ビット上下反転 (注: 次期バージョンでは`@bitReverse`に名前が変わる)
- `@ctz(数値)`: 最下位ビットから0が続く数(count trailing zeros)。２のべき乗値なら、`log2(数値)`と同じ値。

他には、`@clz`(最上位ビットから0が続く数)、`@popCount`(1の数)、`@bswap`(バイト単位で上下反転)、など。

#### キャストの明示

zigでは同一数値型種で**ビット数が増える場合のみ、暗黙的に拡大**できます。
それ以外は、変換の仕方に応じたそれぞれの**キャスト用ビルトイン関数を選んで用いる**必要があります。

- `@truncate(変換型, 値)`: 変換型のビット数までの下位ビットだけを切りとるキャスト

シフト演算ではシフトする変数の型のビット数にフィットした数値型に切り詰める必要があります。
32は2の5乗なので、`u32`のシフト演算の右辺値では、型を`u5`に切り詰めることが必須となります。

キャストビルトイン関数は大量にありますが、以下は代表的なものです

- `@bitCast(変換型、 値)`: ビットレイアウトを保存するキャスト。ビットフィールドとして`f64`を`u64`に変換する場合など。
- `@ptrCast(変換ポインタ型, ポインタ値)`: ポインタの型変換
- `@intCast(変換整数型, 整数値)`: ビット数の違う整数値の型変換
- `@floatCast(変換浮動小数型, 浮動小数値)`: ビット数の違う浮動小数値の型変換

- `@floatToInt(変換整数型, 浮動小数値)`: 整数への数値変換
- `@intToFloat(変換不動小数型, 整数値)`: 浮動小数への数値変換
- `@ptrToInt(変換整数型, ポインタ値)`: ポインタアドレスの整数への変換
- `@intToPtr(変換ポインタ型, 整数値)`: ポインタアドレス値からポインタへの変換
- `@boolToInt(真偽値)`: 真偽値の整数(`u1`)への変換


---

### 3.6. FFT本体

以下のコード片は、ループ版FFTの実装コードです。


```zig:fft.zig(6)
fn fftc(t0: f64, n: u32, c: [*]CN, r: [*]CN) void {
    {
        const k: u32 = @ctz(n);
        var i: u32 = 0;
        while (i < n) : (i += 1) {
            r[i] = c[@inlineCall(revbit, k, i)];
        }
    }
    var t = t0;
    var nh: u32 = 1;
    while (nh < n) : (nh <<= 1) {
        t /= 2.0;
        const nh2 = nh << 1;
        var s: u32 = 0;
        while (s < n) : (s += nh2) {
            var i: u32 = 0;
            while (i < nh) : (i += 1) {
                const li = s + i;
                const ri = li + nh;
                const re = @inlineCall(CN.mul, r[ri], @inlineCall(CN.expi, t * @intToFloat(f64, i)));
                const l = r[li];
                r[li] = @inlineCall(CN.add, l, re);
                r[ri] = @inlineCall(CN.sub, l, re);
            }
        }
    }
}
pub export fn fft(n: u32, f: [*]CN, F: [*]CN) void {
    fftc(-2.0 * pi, n, f, F);
}
pub export fn ifft(n: u32, F: [*]CN, f: [*]CN) void {
    fftc(2.0 * pi, n, F, f);
    const nf64 = @intToFloat(f64, n);
    var i: u32 = 0;
    while (i < n) : (i += 1) {
        f[i] = f[i].rdiv(nf64);
    }
}
```

#### ポインタ型

zigの変数はメモリ領域で、その**先頭アドレスを指すポインタ型**があります。
値型へのポインタ型は、`*u8`など、型の頭に`*`をつけた型になります。

zigでは、ポインタ型の変数と、その元になった型の変数は、**同じ記述でアクセスできる**ようになっています。

zigでは、配列も連続メモリ領域を表す値型です。C言語と違い、**配列型と、配列へのポインタ型は明確に区別**されます。

zig-0.4.0では、配列ポインタ型は、`[*]要素型`で表現します。
配列ポインタ型は、配列上の途中の要素も指すことができ、Cと同様にオフセットインデックス数を足し引きできます。

配列のポインタ型変数も、配列型変数と同じ記述で配列の要素へアクセスできます。

配列ポインタに関係するものとしては他に以下の型があります。

- `*[要素数]要素型`: 配列全体へのポインタ。コンパイル時に要素数(`.len`)が確定している
- `[]要素型`: 実行時に要素数(`.len`)が得られる**スライス**

zigのスライスは、配列領域上の連続領域を指すzig組み込みのデータ構造で、先頭アドレスの配列ポインタ(`.ptr`)と要素数(`.len`)を持ちます。


まとめると、

-  `const a: [12]u8 = []u8 {1} ** 12;`: 配列変数。要素数はコンパイル時に確定
-  `const b: *const [12]u8 = &a;`: 配列全体へのポインタ変数。要素数はコンパイル時に確定
-  `const c: []const u8 = &a;`: スライス型。要素数は実行時に確定
-  `const d: [*]const u8 = &a;`: 配列へのポインタ変数。要素数を持たない

上３つの変数は、要素数を`a.len`、`b.len`、`c.len`で取り出せるけど、`d`にはその手段がありません。

以下は、その他スライスや配列ポインタへ代入するための構文です。

-  `const c0: []const u8 = a[0..];`
-  `const c1: []const u8 = b;`
-  `const c2: []const u8 = c[1..];`
-  `const c3: []const u8 = d[0..12];`
-  `const d1: [*]const u8 = b;`
-  `const d2: [*]const u8 = c.ptr;`
-  `const d3: [*]const u8 = d + 1;`

配列のポインタからスライスを作る(`c3`)には、**終点**のインデックスの指定が必須です。

#### zigの変数は隠蔽不可能なブロックスコープ

zigの変数はブロックスコープですが、多くの言語と違い、ブロックの外で定義済みの変数と**同名の変数をブロック内部で定義することができません**。

関数`fftc`の最初の、ビット反転したインデックスへコピーする部分では、変数`i`を使っています。
この部分を囲う`{``}`を消すと、下のループ内でも`i`を使っているため、コンパイルエラーとなります。

#### zigのループ式とブロック式

zigには、`for`と`while`のループがあります。

- `for`ループ: 配列等へのイテレータループ
- `while`ループ: 条件ループ

`while`ループには、以下のバリエーションがあります。

- `while (条件式) {...}`  : C言語での`while (条件式) {...}`相当
- `while (条件式) : (後処理式) {...}`: C言語での`for (; 条件式; 後処理式) {...}` 相当
- `while (実行式) |変数| {...}` : C言語での`while ((変数 = 実行式)) {...}`相当

zigでは、`while`での**ループカウンタの変数はループ外で宣言する**必要があります。

zigでは、`for`も`while`もループアウト後に処理される`else`部で式をつけることが可能です。

ループは、式として結果値を持てます。ループ式では`else`部が必須で、値付き`break`にわたす値が式の値になります。

- ループ式の最小例1: `const v = while (cond) {break 1;} else 2;`
- ループ式の最小例2: `const v = while (cond) {break 1;} else ret: {break :ret 2;};`
- (zigの`if式`: `const v = if (cond) 1 else 2;`)

この例はどちらも、`cond`が`true`なら`v`は`1`、`false`なら`v`は`2`になります。

最小例2での`else`部はブロックスタイルの構文ではなく、zigの式構文の一つである**ブロック式**です。
**ブロック式の値は、ラベル付き`break`で渡す**必要があり、このためにブロック式になにかラベルを付ける必要があります。


#### 関数のインライン呼び出し

zigの`fn`にも`inline`をつけることができ、この場合すべての呼び出し箇所が強制的にインライン化されます。
しかし、`inline`な`fn`は利用制限が付きます。たとえば、`fn`を変数や引数に渡して使うことができなくなります。
また、C言語と違い、zigでは、一つでも`fn`にインライン化できない状況があると`inline`がコンパイルエラーになります。

zigでは、定義ですべてインライン化を指定するのではなく、普通の関数を**呼び出し側で強制インライン化**を指定することが可能です。それがビルトイン`@inlineCall`です。


---

### 3.7. FFTテストコード

以下のコード片は、ループ版FFTの実装のテストコードです。

```zig:fft.zig(7)`
test "fft/ifft" {
    const warn = @import("std").debug.warn;
    const assert = @import("std").debug.assert;
    const abs = @import("std").math.fabs;
    const eps = 1e-15;

    const util = struct {
        fn warnCNs(n: u32, cns: [*]CN) void {
            var i: u32 = 0;
            while (i < n) : (i += 1) {
                warn("{} + {}i\n", cns[i].re, cns[i].im);
            }
        }
    };

    const n: u32 = 16;
    const v = [n]i32{ 1, 3, 4, 2, 5, 6, 2, 4, 0, 1, 3, 4, 5, 62, 2, 3 };
    var f: [n]CN = undefined;
    {
        var i: u32 = 0;
        while (i < n) : (i += 1) {
            f[i] = CN.rect(@intToFloat(f64, v[i]), 0.0);
        }
    }
    warn("\n[f]\n");
    util.warnCNs(n, &f);

    var F: [n]CN = undefined;
    var r: [n]CN = undefined;
    fft(n, &f, &F);
    ifft(n, &F, &r);

    warn("\n[F]\n");
    util.warnCNs(n, &F);
    warn("\n[r]\n");
    util.warnCNs(n, &r);

    {
        var i: u32 = 0;
        while (i < n) : (i += 1) {
            assert(abs(r[i].re - @intToFloat(f64, v[i])) < eps);
        }
    }
}
```

#### `test`内での関数定義は、`struct`内で行える

zig-0.4.0では、`test`部のトップレベルで`fn`は記述できませんが、**`struct`内では関数が記述可能**で、スタティックメソッド的に呼び出すことは可能でした。

#### ポインタ型への参照

zigで、値型からポインタを得る構文は`&変数`です。
C言語と違い配列変数は配列ポインタとして暗黙的には扱えず、**明示的に`&`をつける**必要があります。

ポインタ型から要素を参照する方法は、値型と同じです(`a[i]`や`a.m`)。

ポインタ固有の式として、**値全体のデリファレンス**があり、変数の後に`.*`を付与します(`v = a.*`や`a.* = v`)。これはC言語でのポインタデリファレンス`*ptr`相当の意味です。

#### `warn`によるコンソール出力

標準ライブラリの`warn`を使うと、フォーマット文字列を使って、コンソールに文字を(`stderr`へ)出力させることが可能です。
`zig test`実行でも出力されます。

---
### 3.8. `zig test`実行結果

この`fft.zig`に対して、`zig test fft.zig`を実行すると、以下の結果になります:

```shell-executable
$ zig test fft.zig
Test 1/4 CN...OK
Test 2/4 CN array...OK
Test 3/4 revbit...OK
Test 4/4 fft/ifft...
[f]
1.0e+00 + 0.0e+00i
3.0e+00 + 0.0e+00i
4.0e+00 + 0.0e+00i
2.0e+00 + 0.0e+00i
5.0e+00 + 0.0e+00i
6.0e+00 + 0.0e+00i
2.0e+00 + 0.0e+00i
4.0e+00 + 0.0e+00i
0.0e+00 + 0.0e+00i
1.0e+00 + 0.0e+00i
3.0e+00 + 0.0e+00i
4.0e+00 + 0.0e+00i
5.0e+00 + 0.0e+00i
6.2e+01 + 0.0e+00i
2.0e+00 + 0.0e+00i
3.0e+00 + 0.0e+00i

[F]
1.07e+02 + 0.0e+00i
2.329589166141268e+01 + 5.1729855807372815e+01i
-5.35477272147525e+01 + 4.2961940777125584e+01i
-4.921391810443094e+01 + -2.567438445589562e+01i
3.612708057484687e-15 + -5.9e+01i
4.9799704542057846e+01 + -2.4260170893522517e+01i
3.554772721475249e+01 + 4.89619407771256e+01i
-1.9881678099039597e+01 + 5.314406936974591e+01i
-6.3e+01 + 0.0e+00i
-1.9881678099039586e+01 + -5.314406936974591e+01i
3.55477272147525e+01 + -4.8961940777125584e+01i
4.9799704542057846e+01 + 2.4260170893522528e+01i
-3.612708057484687e-15 + 5.9e+01i
-4.921391810443094e+01 + 2.567438445589561e+01i
-5.354772721475249e+01 + -4.29619407771256e+01i
2.329589166141269e+01 + -5.1729855807372815e+01i

[r]
1.0e+00 + 0.0e+00i
3.0e+00 + -3.497202527569243e-15i
3.999999999999999e+00 + -1.0143540619928351e-16i
1.9999999999999996e+00 + 8.465450562766819e-16i
5.0e+00 + 0.0e+00i
6.0e+00 + 0.0e+00i
2.0e+00 + 4.592425496802568e-17i
4.0e+00 + -7.077671781985373e-16i
0.0e+00 + 0.0e+00i
1.0e+00 + -5.551115123125783e-17i
3.000000000000001e+00 + 9.586896263232146e-18i
4.0e+00 + 5.134781488891349e-16i
5.0e+00 + 0.0e+00i
6.2e+01 + 3.552713678800501e-15i
2.0e+00 + 4.592425496802568e-17i
2.9999999999999996e+00 + -6.522560269672795e-16i
OK
All tests passed.
$
```


---

##  4. `fft.zig`を使った実行ファイルのzigコード

注: zigでは、コマンドやライブラリの**起動処理は標準ライブラリ内で実装**されています。
ここの記述はzig-0.4.0での標準ライブラリ実装をもとに行っています。この辺の仕様は、将来的に大きく変わる可能性があるでしょう。

まずコマンド実行時にzig標準ライブラリから呼び出される`main`関数の使い方だけを説明し、その後で`@import("fft.zig")`して、前述のFFT実装を呼び出すzigプログラムを説明します。

---

### 4.1. zigの`main`関数オーバービュー

以下は、コマンドライン実行でのエントリーポイント`main`関数で、環境変数、コマンド引数、exitコードを扱うだけのコード例です:

```zig:example-main.zig
pub fn main() noreturn {
    // environment variable
    const pathOptional: ?[]const u8 = @import("std").os.getEnvPosix("SHELL");
    const path: []const u8 = pathOptional orelse "";
    @import("std").debug.warn("SHELL: {}\n", path);

    // command args
    var count: u8 = 0;
    var args = @import("std").os.args();
    const total = while (args.nextPosix()) |arg| {
        @import("std").debug.warn("{}: {}\n", count, arg);
        count += 1;
    } else count;

    // exit with code: `echo $?`
    @import("std").os.exit(total);
}
```

#### `main`関数の型とexitコード

**`pub`な引数なし`main`関数**がユーザーコードのエントリーポイントです。

`main`の戻り値の型は、`noreturn`(最後名で実行されずに終了する)のほかに、`u8`(return値がexit codeになる)、`void`(exit codeは0)、`!void`(エラーか`void`)のどれかです。

このコードの場合では、中で使っているexitする標準ライブラリの`os.exit()`が`noreturn`な関数であるため、`main`を`noreturn`にしています。
`os.exit()`を使う場合、exitコードを引数(`u8`型)に渡します。

戻り値が`!void`の場合、エラーならexitコードは`1`になります。

#### エラーセット型と、エラーユニオン型

zigの**`error`構文で定義するエラーセット型**は、`struct`や`enum`のようなカスタムタイプの一つで、`enum`と似た列挙型となっています。例: `const MyError = error {Fail, Dead,};`

`enum`と違う部分は、エラーセット型では、以下のように、その要素であるエラーを集合的に扱う点です。

- 全カスタムエラーセット型を受ける**`anyerror`型**の存在
- ２つのエラーセット型の**和集合のエラーセット型**が作れる(`const ErrorsC = ErrosA || ErrorsB;`)
- エラーセット型内のエラー名は、エラーセット型が違っても**同名のものは同値扱い**される(例えば、`MyError.Dead`は`const e: MyError = error.Dead;`でもイニシャライズできる)
- エラーユニオン型(`!`つき型)と、エラーユニオン専用構文(`catch`演算子、`try`演算子)が存在する

戻り値型での`!u8`や変数型での`MyError!u8`のような、`!`がついた型は、zigの**エラーユニオン型**というものです。
エラーユニオン型は、**値の型と`error`型の双方を受けられる**変数や戻り値の型です。
このエラーユニオン型を戻り値の型にすることで、zigでは、**エラーも`return`させる**値になります。
戻り値型の`!u8`は左側のエラーセット型が関数実装コードから推論されるもので、実際には、`MyError!u8`等のエラーユニオン型になります。

エラーユニオン型から値の型の式にするには、zigでは`catch`二項演算子と`try`単項演算子が使えます。

- `catch`二項演算子: エラー時のフォールバック値を渡すことができます。例: `const v: u8 = errorOrInt() catch |err| 10;`
- `try`単項演算子: エラーのときはそのエラーを`return`する、という式です。例: `const v: u8 = try errorOrInt();`

`catch`演算子のキャプチャ部`|err|`は、エラー値を使わない場合は省略できます。
`try`演算子を中で使う関数の戻り値は、エラーユニオン型である必要があります。

また、`if`式や`switch`式の条件式にすることでも、エラーユニオン型からエラー時を`else`として振り分けることが可能です。

- `if`例: `const v: u8 = if (errorOrInt()) |n| n else |err| 0;`
- `switch`例: `const v: u8 = switch (errorOrInt()) { n => n, else => 0, };`

#### 環境変数とオプショナル型

環境変数は標準ライブラリの`os.getEnvPosix()`で取り出せます。この関数の戻り値は、`?[]const u8`です。

この`?`が頭についた型は、zigの**オプショナル型**です。オプショナル型は、zigの**`null`値**か値、という型です。(このため、ポインタ型はオプショナル型でなければ`null`にできないことになります)

エラーユニオン型と同様、`if`式や`switch`式で、`null`のときは`else`でフォールバック値を与えることで、値の型にすることができます。
また、エラーユニオンでの`catch`二項演算子と同じ位置づけで、オプショナル型用に`orelse`二項演算子が備わっています。

- 例: `const v: u8 = nullOrInt() orelse 0;`

`const v: u8 = nullOrInt().?`と`.?`をつけることでも、(`error`型での`try`単項演算子のように)強制的に値型にできます。
しかし`try`と違い、エラーや`null`が`return`されるわけではなく、zig-0.4.0ではトラップ不可能な強制終了となるようです。

#### コマンド引数

標準ライブラリの`os.args()`でコマンドライン引数を取り出せます。
これはイテレータであり、`nextPosix()`メソッドを使って、`?[]const u8`型のオプショナル型文字列として次々と取り出すことができます。

イテレータから`null`になるまで逐次取り出しをするには、**条件値をキャプチャする`while`ループ**を使います。`null`になれば`else`部に入ります。

バイナリにした場合コマンド引数の**0番目はコマンド名**になり、引数はその次になります。

`zig run`での場合、`zig run example-main.zig -- a b c`で`--`の後にコマンド引数を渡せます。zig-0.4.0では、この場合では、引数は0番目から始まります。

---

### 4.2. `fft.zig`を`@import`して使うmainのzigコード

以下は、テストで用いたデータでFFT/IFFTの結果を表示させ、続いて100万(2の20乗)要素のFFTとIFFTを実行させたベンチマーク時間を表示させるプログラムです。

```zig:fft-bench-zig.zig
const heap = @import("std").heap;
const warn = @import("std").debug.warn;
const sin = @import("std").math.sin;
const milliTimestamp = @import("std").os.time.milliTimestamp;

const CN = @import("./fft.zig").CN;
const fft = @import("./fft.zig").fft;
const ifft = @import("./fft.zig").ifft;

fn warnCNs(n: u32, cns: [*]CN) void {
    var i: u32 = 0;
    while (i < n) : (i += 1) {
        warn("{} + {}i\n", cns[i].re, cns[i].im);
    }
}

pub fn main() !void {
    { //example
        const n: u32 = 16;
        const v = [n]i32{ 1, 3, 4, 2, 5, 6, 2, 4, 0, 1, 3, 4, 5, 62, 2, 3 };
        var f: [n]CN = undefined;
        {
            var i: u32 = 0;
            while (i < n) : (i += 1) {
                f[i] = CN.rect(@intToFloat(f64, v[i]), 0.0);
            }
        }
        warn("\n[f]\n");
        warnCNs(n, &f);

        var F: [n]CN = undefined;
        var r: [n]CN = undefined;
        fft(n, &f, &F);
        ifft(n, &F, &r);

        warn("\n[F]\n");
        warnCNs(n, &F);
        warn("\n[r]\n");
        warnCNs(n, &r);
    }

    { //benchmark
        //NOTE: allocators in std will be changed on 0.5.0
        var direct_allocator = heap.DirectAllocator.init();
        var arena = heap.ArenaAllocator.init(&direct_allocator.allocator);
        const allocator = &arena.allocator;
        defer direct_allocator.deinit();
        defer arena.deinit();

        const n: u32 = 1024 * 1024;
        var f = try allocator.create([n]CN);
        {
            var i: u32 = 0;
            while (i < n) : (i += 1) {
                const if64 = @intToFloat(f64, i);
                f[i] = CN.rect(sin(if64) * if64, 0.0);
            }
        }

        var F = try allocator.create([n]CN);
        var r = try allocator.create([n]CN);

        const start = milliTimestamp();
        fft(n, f, F);
        ifft(n, F, r);
        const stop = milliTimestamp();
        warn("fft-ifft: {}ms\n", stop  -start);
    }
}
```

#### zigファイルからの`@import`

自分で作ったzigソースファイルも、標準ライブラリと同様に**`@import`して`pub`属性の変数や関数を利用**できます。

実際、標準ライブラリも、zigソースファイルとして存在します。
インストールディレクトリの`lib/zig/std/`以下に展開されていて、実装コードを確認できます。

#### 巨大メモリの利用

zig言語では、基本、変数はスタックメモリ(スタティックメモリ)の領域を使います。
大きい配列を使う場合は、**実行時にOSのスタックリミットを超えてしまう**ので、プログラムではヒープなどのスタック外のメモリ領域を使用する必要があります。

標準ライブラリでは、**ヒープメモリを扱うメモリアロケータ**が用意されています。以下は、zig-0.4.0でのアロケータ実装です:

- `heap.DirectAllocator`: ヒープメモリを確保するアロケータ(`init`, `deinit`, `alloc`, `shrink`, `realloc`)
- `heap.ArenaAllocator`: 他のアロケータを使ってメモリプールを構成し、これまで確保したメモリを`deinit`で一括開放するアロケータ
- `mem.Allocator`: 型指定でメモリを割り当てるインタフェース(`create`, `destroy`)

zig-0.4.0では、`heap`のアロケータは、`mem.Allocator`インタフェースをメンバー`.allocator`として持っています(このコードでは`arena.allocator`)。

注意点は、`mem.Allocator`の`create`の引数で渡すのは値型ですが、戻り値はその値型へのポインタ型(のエラーユニオン型)になっているところです。

- `allocator.create([1048576]CN)`の戻り値の型は、`!*[1048576]CN`
- `var f = try allocator.create([1048576]CN);`の`a`の型は`*[1048576]CN`
- `f: *[1048576]CN`は、`[*]CN`の変数(コードでは`fft()`の第二引数)へ明示キャストせずに代入できる

#### `defer`文

**ブロック脱出時に実行されるコード**を、前もって記述するのが`defer`文です。
同一ブロックに複数の`defer`がある場合は、**後ろ側から順に実行**されます。
`defer`を使って近い場所に書くことで、初期化処理と、その初期化に対応する終了処理を、ペアとして記述することが可能になります。

似たようなものに`errdefer`があります。**エラーが`return`されるときのブロック脱出時**に実行される`defer`です。
エラー`return`でのブロック脱出時には、`errdefer`と`defer`の混ざった並びでの後ろから順に実行されます。

---

### 4.3. 実行ファイルのビルド

この`fft-bench-zig.zig`から、以下のコマンドラインで、実行ファイル`fft-bench-zig`が生成できます。

```shell-executable
$ zig build-exe --release-fast fft-bench-zig.zig
```

ビルドしなくても、`zig run fft-bench-zig.zig`でコードを実行させることも可能です。

---

## 5. `fft.zig`をWebAseemblyモジュールへコンパイルしてJavaScriptで利用する

Rustなど普通のプログラミング言語で書いたコードからWebAssemblyモジュールを生成する場合、そのwasmモジュールを使うためには、そのプログラミング言語のランタイムライブラリ実装、とりわけヒープメモリ関係の機能に配慮する必要があります。そのwasmモジュールのimportsやexportsで、その言語でのメモリ操作等のためのメソッドを持ち、その使い方に合わせて実装、利用する必要があります。

しかしzigプログラムからwasmモジュールを作ると、**言語ランタイム関係のメソッドが一切外部に出ないいwasmモジュール**を生成します。この一番大きな違いは、zigがヒープメモリを基本的には使用しないことに由来します(C言語も基本はそなってますが、ヒープ管理を持つ`stdlib.h`を一切使用しないで書くことはより困難)。

先に実装した`fft.zig`では、``fft`と`ifft`関数を`export`しており、そのままwasmファイルへコンパイルすることが可能となっています。

以下のコマンドラインで`fft.wasm`を生成できます。

 ```shell-executable
 $ zig build-exe --release-fast -target wasm32-freestanding --name fft.wasm fft.zig
 ```

### 5.1. node.jsで`fft.wasm`を使用するJavaScriptコード

以下のJavaScriptコードは、このzigコードから生成したwasmファイルをロードして使うプログラム`fft-bench-nodejs.js`です。
先の`fft-bench-zig.zig`と同様の、例のデータでのFFT/IFFT変換表示し、そのあとで100万要素のデータをFFT/IFFTした実行時間を出すベンチマークとなっています。

```js:fft-bench-nodejs.js
const fs = require("fs").promises;

(async function () {
  const buf = await fs.readFile("./fft.wasm");
  const {instance} = await WebAssembly.instantiate(buf, {});
  const {__wasm_call_ctors, memory, fft, ifft} = instance.exports;
  __wasm_call_ctors();
  memory.grow(1);
  
  {// example
    const N = 16, fofs = 0, Fofs = N * 2 * 8, rofs = N * 4 * 8;
    const f = new Float64Array(memory.buffer, fofs, N * 2);
    const F = new Float64Array(memory.buffer, Fofs, N * 2);
    const r = new Float64Array(memory.buffer, rofs, N * 2);

    const fr0 = [1,3,4,2, 5,6,2,4, 0,1,3,4, 5,62,2,3];
    fr0.forEach((v, i) => {
      [f[i * 2], f[i * 2 + 1]] = [v, 0.0];
    });

    fft(N, fofs, Fofs);
    ifft(N, Fofs, rofs);

    console.log(`[fft]`);
    for (let i = 0; i < N; i++) {
      console.log([F[i * 2], F[i * 2 + 1]]);
    }

    console.log(`[ifft]`);
    for (let i = 0; i < N; i++) {
      console.log([r[i * 2], r[i * 2 + 1]]);
    }
  }
  
  { // benchmark
    const N = 1024 * 1024;
    const fr0 = [...Array(N).keys()].map(i => Math.sin(i) * i);
    const f0 = fr0.map(n => [n, 0]);

    const BN = N * 2 * 8 * 3, fofs = 0, Fofs = N * 2 * 8, rofs = N * 4 * 8;
    while (memory.buffer.byteLength < BN) memory.grow(1);
    const f = new Float64Array(memory.buffer, fofs, N * 2);
    const F = new Float64Array(memory.buffer, Fofs, N * 2);
    const r = new Float64Array(memory.buffer, rofs, N * 2);
    fr0.forEach((v, i) => {
      [f[i * 2], f[i * 2 + 1]] = [v, 0.0];
    });

    console.time(`fft-ifft`);
    fft(N, fofs, Fofs);
    ifft(N, Fofs, rofs);
    console.timeEnd(`fft-ifft`);
  }
})().catch(console.error);
```

生成した`fft.wasm`とこの`fft-bench-nodejs.js`を同一ディレクトリに置いて、`node fft-bench-node.js`で実行できます。

#### zigで生成したwasmモジュールの初期化

zigランタイムには**外部から`import`させなくてはいけない機能は存在しない**ので、`await WebAssembly.instantiate(buf, {})`で作ります。

`exports`には、コードで`export`した関数以外に、`__wasm_call_ctors`と`memmory`が入っています。

`__wasm_call_ctors`は関数で、もしzigコードに初期化処理が含まれていれば、この**`__wasm_call_ctors()`を呼び出すことで初期化処理を実行**します。

`memory`は、WebAssemblyの`Memory`オブジェクトで、初期値では空です。
コード内でメモリを使うなら、`memory.grow()`メソッドを呼び出し、メモリ領域を確保する必要があります。
引数は拡大するメモリページ数で、1ページ64KBです。

#### WebAssemblyでのメモリ

`exports`にある`memory.buffer`は、Typed Arrayの`ArrayBuffer`オブジェクトです。
JavaScript側からは、この`memory.buffer`を`Float64Array`などでラップし、値を入れたり、参照したりします。

**ポインタはアドレス値**であり、WebAssemblyのメモリのアドレス値は`ArrayBuffer`としての**`memory.buffer`のバイトオフセット値**を使います。
zigには数値型のバイト数ごとにアラインメントがあるので注意です。
`f64`なら、アドレス値は8の倍数である必要があり、`Float64Array`コンストラクタの第二引数はこの8の倍数値を使うようにします。

#### 参考: `imports`を受け付けるwasmを作る場合

zigは言語ランタイムを外部モジュールで与える必要のないwasmを作るのであって、自分で用意した外部のモジュールを`imports`させてwasm内で使うことが不可能なわけではありません。

外部で実装した関数を`imports`に与えて使いたい場合、`extern fn sin(x: f64) f64;`のような**`extern`関数プロトタイプを宣言し、コード内で使う**ことで可能です。
zig-0.4.0ではwasmファイル内での外部モジュール名は`"env"`固定で、`WebAssembly.instantiate(buf, {env: {sin: Math.sin}})`として与えることで利用できます。

#### WebAssembly用コードの`export`制限

WebAssemblyとしてJavaScript側へ`export`できる関数にはより多くの制限がかかります。
たとえば、zig-0.4.0では、`CN.rect()`のような**戻り値が`struct`型の関数**はこの制限にかかります。
この制限にかかる`export`が1つでもあるとwasmに変換できなくなるので、zigコードで`export`をつけるのは注意が必要です。

対策としては、`pub`のみする実装コードのzigファイルと、実装のzigファイルを`@import`してターゲットごとに適切な`export`をつけるだけのzigファイルとに分ける、という手段が考えられます。

## 6. `fft.zig`をオブジェクトファイルにしてC言語から利用する

zigコードは、`zig build-obj`コマンドでC言語から利用できるオブジェクトファイルに変換できます。オブジェクトファイルに対応したCヘッダファイルも同時に生成されます。

ここでは、`fft.zig`を使うC言語プログラムを作って、コンパイルして実行ファイルを生成します。

### 6.1 オブジェクトファイルおよびCヘッダファイル生成

以下のコマンドラインで、`fft.zig`から、オブジェクトファイル`fft.o`とヘッダファイル`fft.h`を生成します:

```shell-executable
$ zig build-obj --release-fast fft.zig
```

生成されたヘッダファイル`fft.h`は、以下の内容になっています。

```c:fft.h
#ifndef FFT_H
#define FFT_H

#include <stdint.h>

#ifdef __cplusplus
#define FFT_EXTERN_C extern "C"
#else
#define FFT_EXTERN_C
#endif

#if defined(_WIN32)
#define FFT_EXPORT FFT_EXTERN_C __declspec(dllimport)
#else
#define FFT_EXPORT FFT_EXTERN_C __attribute__((visibility ("default")))
#endif

struct CN {
    double re;
    double im;
};

FFT_EXPORT void fft(uint32_t n, struct CN * f, struct CN * F);
FFT_EXPORT void ifft(uint32_t n, struct CN * F, struct CN * f);

#endif
```

このヘッダファイル`fft.h`に、zigコードで`export`した関数と`struct`が入っていることが確認できます。

---

### 6.2 FFT実装を呼び出すC言語プログラム

以下は、テストで使ったものと同じデータ例でFFT/IFFTを実行して表示し、続いて1000万要素のデータをFFT/IFFT実行させる`main`関数のC11コード`fft-bench-c.c`です。

```c:fft-bench-c.c
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include "./fft.h"

int main() {
  {// example
    const int n = 16;
    const int v[16] = {1,3,4,2, 5,6,2,4, 0,1,3,4, 5,62,2,3};
    struct CN f[n], F[n], r[n];
    for (size_t i = 0; i < n; i++) f[i] = (struct CN) {.re = v[i], .im = 0.0};

    puts("[f]");
    for (size_t i = 0; i < n; i++) printf("%f + %fi\n", f[i].re, f[i].im);

    fft(n, f, F);
    ifft(n, F, r);

    puts("[F]");
    for (size_t i = 0; i < n; i++) printf("%f + %fi\n", F[i].re, F[i].im);
    puts("[r]");
    for (size_t i = 0; i < n; i++) printf("%f + %fi\n", r[i].re, r[i].im);
  }
  
  {// benchmark
    const int n = 1024 * 1024;
    struct CN * f = calloc(n, sizeof (struct CN));
    for (size_t i = 0; i < n; i++) {
      f[i] = (struct CN) {.re = sin(i) * i, .im = 0.0};
    }

    struct CN * F = calloc(n, sizeof (struct CN));
    struct CN * r = calloc(n, sizeof (struct CN));
    fft(n, f, F);
    ifft(n, F, r);
    free(f);
    free(F);
    free(r);
  }
  return 0;
}
```

zigで実装されているからと言って構造体や関数を使う場合、**C側で特別なことを事を行う必要はありません**。

### 6.3. ビルド

実行ファイル`fft-bench-c`を生成するコマンドラインは以下のとおりです(Cコンパイラ実装はclangかgccを想定しています):

```shell-executable
$ cc -Wall -Wextra -std=c11 -pedantic -O3 -o fft-bench-c fft-bench-c.c fft.o
```

(このオプションは、コードを拡張なしのC11標準そのもので解釈させることを指定するものです)


## 7.  C言語で記述したオブジェクトファイルをzigプログラムから読み込む

`fft-bench-c.c`のときとは逆に、C言語で書かれたオブジェクトファイルを、そのCヘッダファイルをつかってzigプログラムで取り込むことも可能です。

ここでは、C11コード`fft-c.c`でFFTを実装し、その実装をzigから使うためのCヘッダファイル`fft-c.h`を記述します。
そして、`fft-bench-zig.zig`と同内容の事を行う`fft-bench-cimport.zig`で、`fft-c.h`を読み込んで実装します。

### 7.1.  C11によるFFT実装プログラム

以下は、C11仕様での複素数`double complex`を使用したFFT実装`fft-c.c`です。

```c:fft-c.c
#include <complex.h> // complex, I, cexp
#include <math.h> // M_PI
#include <stdbool.h> // bool
#include <stddef.h> // size_t
#include <stdint.h> // uint64_t

typedef double complex CN;

static inline uint32_t reverse_bit(uint32_t s0) {
  uint32_t s1 = ((s0 & 0xaaaaaaaa) >> 1) | ((s0 & 0x55555555) << 1);
  uint32_t s2 = ((s1 & 0xcccccccc) >> 2) | ((s1 & 0x33333333) << 2);
  uint32_t s3 = ((s2 & 0xf0f0f0f0) >> 4) | ((s2 & 0x0f0f0f0f) << 4);
  uint32_t s4 = ((s3 & 0xff00ff00) >> 8) | ((s3 & 0x00ff00ff) << 8);
  return ((s4 & 0xffff0000) >> 16) | ((s4 & 0x0000ffff) << 16);
}

static inline uint32_t rev_bit(uint32_t k, uint32_t n) {
  return reverse_bit(n) >> (8 * sizeof (uint32_t) - k);
}

static inline uint32_t trailing_zeros(uint32_t n) {
  uint32_t k = 0, s = n;
  if (!(s & 0x0000ffff)) {k += 16; s >>= 16;}
  if (!(s & 0x000000ff)) {k += 8; s >>= 8;}
  if (!(s & 0x0000000f)) {k += 4; s >>= 4;}
  if (!(s & 0x00000003)) {k += 2; s >>= 2;}
  return (s & 1) ? k : k + 1;
}

static void fftc(double t, uint32_t N, const CN c[N], CN ret[N]) {
  uint32_t k = trailing_zeros(N);
  for (uint32_t i = 0; i < N; i++) ret[i] = c[rev_bit(k, i)];
  for (uint32_t Nh = 1; Nh < N; Nh <<= 1) {
    t *= 0.5;
    for (uint32_t s = 0; s < N; s += Nh << 1) {
      for (uint32_t i = 0; i < Nh; i++) { //NOTE: s-outside/i-inside is faster
        uint32_t li = s + i;
        uint32_t ri = li + Nh;
        CN re = ret[ri] * cexp(t * i * I);
        CN l = ret[li];
        ret[li] = l + re;
        ret[ri] = l - re;
      }
    }
  }
}

extern CN rect(double re, double im) {
  return re + im * I;
}
extern void fft(uint32_t N, const CN f[N], CN F[N]) {
  fftc(-2.0 * M_PI, N, f, F);
}
extern void ifft(uint32_t N, const CN F[N], CN f[N]) {
  fftc(2.0 * M_PI, N, F, f);
  for (size_t i = 0; i < N; i++) f[i] /= N;
}
```

アルゴリズムは`fft.zig`と一緒です。

また`double complex`は実数部と虚数部の`double`値が連続して配置される値なので、**`fft.zig`の`CN`と同じメモリレイアウト**になります。
そして、zig上で`double complex`への操作をしなくてすむよう、`double`値から複素数値を作るコンストラクタ関数`rect`も用意します。

### 7.2. zigで使うためのCヘッダファイル

Cヘッダファイル`fft-c.h`は、zigの`build-obj`が書き出すヘッダファイルのやり方に合わせた内容にしてあります。

```c:fft-c.h
#ifndef FFT_C_H
#define FFT_C_H
#include <stdint.h>

#ifdef __cplusplus
#define FFT_C_EXTERN_C extern "C"
#else
#define FFT_C_EXTERN_C
#endif

#if defined(_WIN32)
#define FFT_C_EXPORT FFT_C_EXTERN_C __declspec(dllimport)
#else
#define FFT_C_EXPORT FFT_C_EXTERN_C __attribute__((visibility ("default")))
#endif

struct CN {
    double re;
    double im;
};

FFT_C_EXPORT struct CN rect(double re, double im);
FFT_C_EXPORT void fft(uint32_t n, struct CN f[], struct CN F[]);
FFT_C_EXPORT void ifft(uint32_t n, struct CN F[], struct CN f[]);

#endif
```

zigには複素数型に対応する型がないので、zigプログラムで読み込ませるCヘッダファイルでは`double`２つを持つ`struct`で代用します。


### 7.3. Cヘッダファイルを取り込むzigプログラム

先の`fft-bench-zig.zig`と同様の、例のデータでのFFT/IFFT変換表示し、そのあとで100万要素のデータをFFT/IFFTした実行時間を出すベンチマークとなっています。

```zig:fft-bench-cimport.zig
const heap = @import("std").heap;
const warn = @import("std").debug.warn;
const sin = @import("std").math.sin;
const milliTimestamp = @import("std").os.time.milliTimestamp;

const libfft = @cImport({
    @cInclude("fft-c.h");
});
const CN = libfft.CN;
const rect = libfft.rect;
const fft = libfft.fft;
const ifft = libfft.ifft;

fn warnCNs(n: u32, cns: [*]CN) void {
    var i: u32 = 0;
    while (i < n) : (i += 1) {
        warn("{} + {}i\n", cns[i].re, cns[i].im);
    }
}

pub fn main() !void {
    { //example
        const n: u32 = 16;
        const v = [n]i32{ 1, 3, 4, 2, 5, 6, 2, 4, 0, 1, 3, 4, 5, 62, 2, 3 };
        var f: [n]CN = undefined;
        {
            var i: u32 = 0;
            while (i < n) : (i += 1) {
                f[i] = rect(@intToFloat(f64, v[i]), 0.0);
            }
        }
        warn("\n[f]\n");
        warnCNs(n, &f);

        var F: [n]CN = undefined;
        var r: [n]CN = undefined;
        fft(n, @ptrCast([*c]CN, &f), @ptrCast([*c]CN, &F));
        ifft(n, @ptrCast([*c]CN, &F), @ptrCast([*c]CN, &r));

        warn("\n[F]\n");
        warnCNs(n, &F);
        warn("\n[r]\n");
        warnCNs(n, &r);
    }
    
    { //benchmark
        //NOTE: allocators in std will be changed on 0.5.0
        var direct_allocator = heap.DirectAllocator.init();
        var arena = heap.ArenaAllocator.init(&direct_allocator.allocator);
        const allocator = &arena.allocator;
        defer direct_allocator.deinit();
        defer arena.deinit();

        const n: u32 = 1024 * 1024;
        var f: [*]CN = try allocator.create([n]CN);
        {
            var i: u32 = 0;
            while (i < n) : (i += 1) {
                const if64 = @intToFloat(f64, i);
                f[i] = rect(sin(if64) * if64, 0.0);
            }
        }

        var F: [*]CN = try allocator.create([n]CN);
        var r: [*]CN = try allocator.create([n]CN);

        const start = milliTimestamp();
        fft(n, f, F);
        ifft(n, F, r);
        const stop = milliTimestamp();
        warn("fft-ifft: {}ms\n", stop - start);
    }
}
```

#### `@cImport`ビルトイン

**`@cImport`ビルトイン**を使うことで、Cヘッダからzigの`struct`や関数として取り込むことができます。
`@cImport`の中で、**`@cInclude`ビルトインによって、解釈させるヘッダファイル名を指定**します。

#### Cポインタ型

zigのポインタ型は値型のポインタ型と配列へのポインタ型が別れていますが、Cのポインタ型ではその区別がありません。
このため`@cImport`した関数の引数や戻り値でポインタ型を使っている場合、zig上ではどちらの型にも対応可能な`[*c]値型`で記述する**Cポインタ型**として解釈されます。

ここでは、Cヘッダからの関数`fft`は、`fft(n: i32, f: [*c]CN, F: [*c]CN)`という型になります。

このCポインタ型`[*c]CN`は、`*CN`と`[*]CN`からは、キャストなしで割り当てられます(コードの後半)。
しかしzig-0.4.0では、`[*]CN`へキャストなしで割り当てられる`&f: *[16]CN`からは、`@ptrCast`による明示キャストが必要になります(コードの前半)。

- `*[16]CN` =キャスト不要=> `[*]CN` =キャスト不要=> `[*c]CN`
- `*[16]CN` =要`@ptrCast`=> `[*c]CN` 

#### 参考: `@cImport`せずにオブジェクトファイルを使う方法`extern fn`

`@cImport`で何をやっているかは、`zig translate-c fft-c.h`コマンドを実行して、そのアウトプットのzigコードをみるとわかります。ただし、`#include`した標準ライブラリの内容も全部入るので大量に出てきます。

必要な部分だけ取り出すと、

```zig:fft-c.h.zig
pub const CN = extern struct {
    re: f64,
    im: f64,
};
pub extern "./fft-c.o" fn rect(re; f64, im: f64) CN;
pub extern "./fft-c.o" fn fft(n: u32, f: [*c]CN, F: [*c]CN) void;
pub extern "./fft-c.o" fn ifft(n: u32, F: [*c]CN, f: [*c]CN) void;
```

と同じ内容になります。この内容を埋め込めば、`fft-c.h`やインクルードパスを加える`zig build`オプションの`-isystem .`は不要です。
また、`extern`の直後にオブジェクトファイルパス(`./`は必須)を埋め込むと`--object fft-c.o`も不要になります。
(`./`がないとライブラリとして解決しようとします(`"fft-c"`は`-lfft-c`として扱われる)。)

zigでは、`extern`な関数プロトタイプがあると、**ライブラリやオブジェクトファイルにある同名の関数を使用**する、という宣言になります。


### 7.4. ビルド

以下のコマンドラインでビルドできます。

```shell-executable
$ cc -Wall -Wextra -std=c11 -pedantic -O3 -c fft-c.c
$ zig build-exe --release-fast --object fft-c.o -isystem . fft-bench-cimport.zig
```

オプション`--object fft-c.o`でオブジェクトファイルを指定し、`-isystem .`で `@cInclude` でヘッダファイルを読み込むディレクトリを指定しています。

この場合でも、`zig run --object fft.o -isystem . fft-bench-cimport.zig`で実行ファイルを作らずに実行させることができます。

## 8. `build.zig`によるビルド記述

zigでは`zig build`コマンドで、`make`などの外部ビルドシステムを使わずに、クロスプラットフォームビルドできるようにする方向を目指しているようです。

`zig build`実行でのビルド内容を記述する`build.zig`には、**`pub`な`build`関数**を記述します。

この中で、ビルドで実行する内容を**ステップのグラフ**として定義します。
各種コンパイルやコマンド実行1つが、それぞれ1つのステップとなります。
各ステップには、事前にこなす必要のあるステップの依存関係を指定できます。

また、ビルドターゲットにあたる、**名前付きステップ**があり、そこに各コンパイルやコマンド実行のステップを順に加えていけます。
ビルドシステムとして**デフォルトのステップ**があり、ステップ名指定なしの`zig build`で実行される内容を記述するためのものです。(名前付きステップのうち、いくつかはデフォルトステップに加えることになるでしょう。)

ただし、zig-0.4.0現在では、`build.zig`で記述可能なことが、完全にはそろっていないようです。たとえば、Cファイルのコンパイルやファイルの削除は、実行環境にあるコマンドを引数を指定して直接実行させる必要があります。
(0.4.0のドキュメントには`addCExecutable`というステップが記載されていますが、実際にはzig-0.4.0には存在しません)

```zig:build.zig
const builtin = @import("builtin");
const Builder = @import("std").build.Builder;

pub fn build(b: *Builder) void {
    { //[case: WebAssembly wasm] build fft.wasm
        const wasmStep = b.step("fft.wasm", "build fft.wasm");
        const wasm = b.addExecutable("fft.wasm", "fft.zig");
        wasm.setTarget(builtin.Arch.wasm32, builtin.Os.freestanding, builtin.Abi.musl);
        wasm.setBuildMode(builtin.Mode.ReleaseFast);
        wasm.setOutputDir(".");
        wasmStep.dependOn(&wasm.step);
        b.default_step.dependOn(wasmStep);
    }
    { //[case: zig code only] build fft-bench-zig command
        const exeStep = b.step("fft-bench-zig", "build fft-bench-zig command");
        const zigExe = b.addExecutable("fft-bench-zig", "fft-bench-zig.zig");
        zigExe.setBuildMode(builtin.Mode.ReleaseFast);
        zigExe.setOutputDir(".");
        exeStep.dependOn(&zigExe.step);
        b.default_step.dependOn(exeStep);
    }
    { //[case: c exe with zig lib] build fft-bench-c command
        // build fft.h and fft.o
        const objStep = b.step("fft.o", "build fft.h and fft.o");
        const fftObj = b.addObject("fft", "fft.zig");
        fftObj.setBuildMode(builtin.Mode.ReleaseFast);
        fftObj.setOutputDir(".");
        objStep.dependOn(&fftObj.step);

        // build fft-bench-c command (cc: expected clang or gcc)
        const cExeStep = b.step("fft-bench-c", "build fft-bench-c command");
        cExeStep.dependOn(objStep);
        const cExe = b.addSystemCommand([][]const u8{
            "cc", "-Wall", "-Wextra", "-O3", "-o", "fft-bench-c", "fft-bench-c.c", "fft.o",
        });
        cExeStep.dependOn(&cExe.step);
        b.default_step.dependOn(cExeStep);
    }
    { //[case: zig exe with c lib] build fft-bench-cimport command
        const exeStep = b.step("fft-bench-cimport", "build fft-bench-cimport command");

        const cLibStep = b.step("fft-c", "build fft-c.o");
        const cLibObj = b.addSystemCommand([][]const u8{
            "cc", "-Wall", "-Wextra", "-std=c11", "-pedantic", "-O3", "-c", "fft-c.c",
        });
        cLibStep.dependOn(&cLibObj.step);
        exeStep.dependOn(cLibStep);

        const mainObj = b.addExecutable("fft-bench-cimport", "fft-bench-cimport.zig");
        mainObj.addIncludeDir("."); //NOTE: same as `-isystem .`
        mainObj.setBuildMode(builtin.Mode.ReleaseFast);
        mainObj.addObjectFile("fft-c.o");
        mainObj.setOutputDir(".");
        exeStep.dependOn(&mainObj.step);

        b.default_step.dependOn(exeStep);
    }
    
    { // clean and dist-clean
        const cleanStep = b.step("clean", "clean-up intermediate iles");
        const rm1 = b.addSystemCommand([][]const u8{
            "rm", "-f", "fft.wasm.o", "fft-bench-zig.o", "fft-bench-c.o", "fft-c.o", "fft-bench-cimport.o",
        });
        cleanStep.dependOn(&rm1.step);
        const rmDir = b.addRemoveDirTree("zig-cache");
        cleanStep.dependOn(&rmDir.step);

        const distCleanStep = b.step("dist-clean", "clean-up build generated files");
        distCleanStep.dependOn(cleanStep);
        const rm2 = b.addSystemCommand([][]const u8{
            "rm", "-f", "fft.wasm", "fft-bench-zig", "fft-bench-c", "fft.o", "fft.h", "fft-bench-cimport",
        });
        distCleanStep.dependOn(&rm2.step);
    }
}
```

この中では、6つの名前付きステップを作っています。ブロックごとに上から:

- `fft.wasm`: wasmファイル生成のためのステップ
- `fft-bench-zig`: zigファイルのみから実行ファイル生成するためのステップ
- `fft-bench-c`: zigファイルをオブジェクトファイルにし、それを使うcファイルをメインとする実行ファイルを作成するためのステップ
- `fft-bench-cimport`: cファイルをオブジェクトファイルにし、それを使うzigファイルをメインとする実行ファイルを作成するためのステップ
- `clean`と`dist-clean`: ビルドで生成したファイルの削除を行うステップ

`clean`と`dist-clean`以外は、デフォルトステップに追加してあります。

どの名前付きステップが使えるかは、`zig build --help`で一覧が表示されます。

今後のバージョンアップで大きく変わる部分だろうとは思いますが、ビルドする４つそれぞれのケースでビルド記述としてどのように構成すればよいかの参考になるかもしれません。

## 9. ベンチマーク結果

最後に生成した実行ファイルのベンチマークの結果を載せておきます。後半の100万要素のFFT/IFFTにかかった時間です。

| command                  | time (ms)  | runtime                                       |
|--------------------------|------------|-----------------------------------------------|
| `node fft-bench-nodejs.js` | 945.458ms | node-12.6.0                                   |
| `./fft-bench-zig`          | 856ms      | zig-0.4.0                                     |
| `./fft-bench-cimport`        | 802ms      | Apple LLVM version 10.0.1 (clang-1001.0.46.4) |

また、wasmでの参考まで、[「2019年のWebAssembly事情」](https://qiita.com/bellbind/items/2619f8b71c3a69cc28be)で行った同内容でのFFT/IFFTベンチマークの、同一環境での実行結果も並べておきます。

- ソース: https://gist.github.com/bellbind/36b7e71b3203e868c10405a61cdd2d09

| command                                               | time (ms)  | runtime     |
|--------------------------|------------|-----------------------------------------------|
| `node call-js-fft.js`                                    | 1074.170ms | node-12.6.0 |
| `node --experimental-modules call-fft-dynamic.js` (fast)| 1117.932ms | node-12.6.0 |
| `node --experimental-modules call-fft-dynamic.js` (slow)| 2177.661ms | node-12.6.0 |

zigによる数学関数埋め込みで`sin`や`cos`を`import`して呼び出す必要がないのは、v8のmath-intrinsics最適化を加えても、実行速度の面で大きいのかもしれません(zigベースのwasmでは、sinやcosの関数自体インライン化されていた)。