MemoryLayout and Data in Swift3

今回は、普段の開発では触れる機会が少ない `MemoryLayout` とそれに関係した `Data` について書きます。

[API Reference - MemoryLayout](https://developer.apple.com/reference/swift/memorylayout)

## `MemoryLayout` 以前

`Swift3.0` 以前は、同じ役割を `sizeof` や `strideof` 等が担っていました。こちらの方が見覚えのある方は多いと思います。

`MemoryLayout` は以下の Proposal で提案されました。
[Reconfiguring `sizeof` and related functions into a unified `MemoryLayout` struct](https://github.com/apple/swift-evolution/blob/master/proposals/0101-standardizing-sizeof-naming.md)

Motivation には

> 関数を一つのネームスペースにまとめることで検索性を向上させる。

といったことが書かれています。（`LLVM` の呼び出しにも触れられていますが、そこは調べられていません。）

## `MemoryLayout` の概要

`MemoryLayout` は、それぞれの型に対するデータのメモリ上の配置に関する情報を取得することができます。情報は、`size`,  `stride`, `alignment` の3種類があります。それぞれ

| 情報 | 説明 |
|:---|:---|
| `size` | 一つの値が必要とするメモリ上のバイト数 |
| `stride` | 配列中で一つの値が必要とするメモリ上のバイト数 |
| `alignment` | 配列中の値のバイト数はこの値の整数倍である必要がある |

という内容です。`alignment` は特に分かりにくいですね。stackoverflow の以下の投稿にある `Ray Fix` さんの返信が理解の助けになってくれます。
[stackoverflow - Swift: How to use sizeof?](http://stackoverflow.com/a/27640066/6393251)

サンプルコードの一部を引用します。

```swift
MemoryLayout<Foo>.size      // returns 9
MemoryLayout<Foo>.stride    // returns 16 because of alignment requirements
MemoryLayout<Foo>.alignment // returns 8, addresses must be multiples of 8
```

上から、

- `size`
  - `Foo` クラス単体ではメモリ上の `9` バイトを占める。
- `alignment`
  - 要素が並んだ時、一つの要素が占めるメモリは `8` バイトの倍数でなければならない。
- `stride`
  - 配列中で一つの要素が必要とするバイト数。これは `9` バイトではなく `alignment` の整数倍でなければならないので、要素は `16` バイトを必要とする。

という情報と読めます。

## `Data` と値の相互変換

`MemoryLayout` を一部利用して、`Data` と値の変換に関する振る舞いを見てみましょう。

### `Int` と `Data` を相互に変換する

以下の stackoverflow の投稿が参考になりました。
[stackoverflow - round trip Swift number types to/from Data](http://stackoverflow.com/questions/38023838/round-trip-swift-number-types-to-from-data)

__・ `Int` → `Data`__

```swift
var num: Int = 6
let data = Data(buffer: UnsafeBufferPointer(start: &num, count: 1))
// data: <06000000 00000000>
// 上のコメントアウト部は `data` の内容を16進数で表示したものです。
// NSDataにキャストして `print` することで表示することができます
```

__・ `Data` → `Int`__

```swift
let num: Int = data.withUnsafeBytes { $0.pointee }
// 6
```
`withUnsafeBytes` 関数は型推論やジェネリクス、`pointee` など要素が多く、理解が大変そうです。

__・ `[Int32]` -> `Data`__

```swift
var nums: [Int32] = [2, 11, 17]
let data = Data(buffer: UnsafeBufferPointer(start: nums, count: nums.count))
// data: <02000000 0b000000 11000000>
// 32ビットごとに 2, 11, 17 が入っていることが分かります。
```
データが32ビットでまとまっていると見やすいので `Int32` にしています。`nums` の先頭から `count(3)` 個分の `Int32` を変換です。

__・ `Data` -> `[Int32]`__

```swift
let values: [Int32] = data.withUnsafeBytes {
  [Int32](UnsafeBufferPointer(start: $0, count: data.count/MemoryLayout<Int32>.stride))
}
// [2, 11, 17]
```
`count` のところは `データ長/各要素のメモリ長` で要素数を計算しています。

簡単なデータは相互に変換できることが分かりました。`String` は少し方法が違いますが簡単に出来ます。

*Structの場合を見てみましょう。*

### Struct と `Data` の相互変換を試みる

__シンプルな要素を持つStruct__

`Int8` を要素としてもつ Struct `Number` を定義します。

```swift
struct Number {
    let value: Int8
}
```

`Data` に変換して、続けて `Data` から `Numbers` にも変換します。

```swift
var value: Number = Number(value: 10)
let data = Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
// value: 10
// <0a>

let restored_value: Number = data.withUnsafeBytes { $0.pointee }
// restored_value: 10
```

シンプル。データには `10` を表す `<0a>` しか見えません。


__複雑な構造の要素を持つStruct__

`Int` の配列を要素としてもつ Struct `Numbers` を定義します。

```swift
struct Numbers {
    let values: [Int]
}
```

`Data` に変換して、続けて `Data` から `Numbers` にも変換します。

```swift
var value: Numbers = Numbers(values: [1, 2, 3])
let data = Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
// value: [1, 2, 3] 
// data: <40480700 00600000>

let restored_value: Numbers = data.withUnsafeBytes { $0.pointee }
// restored_value: [1, 2, 3]
```

無事できたようです。ですがデータの内容がさっきとだいぶ違いますね。
要素を変えながらデータを観察してみます。

```swift
var value: Numbers = Numbers(values: [1])
let data = Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
// data: <a09d0500 00600000>
```

```swift
var value: Numbers = Numbers(values: [1, 2, 3, 4, 5])
let data = Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
// data: <b0850900 80600000>
```

あれ、要素が増えても `data` のバイナリデータは大きくならない。。


試しに、`Int` の配列から `Numbers` を生成、データへ変換して返却する関数を定義、それを使って同じ処理の結果を見てみます。

```swift
func numbersData(from values: [Int]) -> Data {
    var value: Numbers = Numbers(values: values)
    return Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
}

let data: Data = numbersData(from: [1, 2, 3])
let restored_value: Numbers = data.withUnsafeBytes { $0.pointee } // ここで error: SIGABRT !!!
```

落ちました。

おそらく大きさが可変な要素を持つ Struct は、バイナリとして値自体を保持しているのではなく、ポインタを保持しているようです。関数の中でデータの生成に使った配列データは、関数の外ではスコープ外になり、ポインタが指すアドレスの先にはアクセスできるデータはなくなってしまっているように見えます。

つまり、ここで得られたデータは有効なスコープ内でしか生きられない、__永続化の為には使えない__ということです。

## そして、永続化の為に

ライブラリを作りました。
[https://github.com/naru-jpn/pencil](https://github.com/naru-jpn/pencil)

標準的な `Int`, `String`, `[Int]`, ... 等はもちろん、作成した Struct も簡単に永続化できます。(ちょっと `Data` の変換処理とかイケてない箇所が結構残ってますが)お時間のある時に覗いてみてください。

お粗末様でした。