ByteBufferを使う

# はじめに
ByteBufferの使い方を説明します。

# インスタンスをつくる

## wrap
wrapはByteArrayの**リファレンス**を指定してByteBufferをつくります。
リファレンスを保持するだけでByteArrayを**コピーしません**。
そのため元のByteArrayを変更するとByteBufferに反映します。
position = 0, limit = capacity = ByteArray.size, mark = 未設定 になります。

wrapにoffset / lengthを指定すると
position = offset, limit = offset + length, mark = 未設定 になります。

```kotlin:Test.kt
import java.nio.ByteBuffer

fun ByteBuffer.println() {
    print("$this ")
    repeat(times = this.limit()) {
        print(String.format(format = "%02x ", this[it]))
    }
    println("")
}

fun main() {
    val byteArray = byteArrayOf(1, 2, 3)
    ByteBuffer.wrap(byteArray).also {
        it.println()
        // java.nio.HeapByteBuffer[pos=0 lim=3 cap=3] 01 02 03
        byteArray[0] = 4 // (*1) ByteArrayを更新するとByteBufferに反映する
        it.println()
        // java.nio.HeapByteBuffer[pos=0 lim=3 cap=3] 04 02 03
    }

    ByteBuffer.wrap(byteArray, 1 /* offset */, 1 /* length */).also {
        it.println()
        // java.nio.HeapByteBuffer[pos=1 lim=2 cap=3] 04 02
    }
}
```

## allocate / allocateDirect
capacityを指定してByteBufferをつくります。
position = 0, limit = capacity, mark = 未設定 になります。

allocateDirectについて[1]に次の記載があります。

> It is therefore recommended that direct buffers be allocated primarily for **large, long-lived buffers** that are subject to the underlying system's native I/O operations. In general it is best to allocate direct buffers **only when they yield a measureable gain in program performance**.

サイズが大きくて、生存期間が長く、性能上のメリットがあることが明白な場合に**のみ**使いましょう。

```kotlin:Test.kt
import java.nio.ByteBuffer

fun ByteBuffer.println() {
    print("$this hasArray=${this.hasArray()} ")
    repeat(times = this.capacity()) {
        print(String.format(format = "%02x ", this[it]))
    }
    println("")
}

fun main() {
    ByteBuffer.allocate(3).also {
        it.println()
        // java.nio.HeapByteBuffer[pos=0 lim=3 cap=3] hasArray=true 00 00 00
    }

    ByteBuffer.allocateDirect(3).also {
        it.println()
        // java.nio.DirectByteBuffer[pos=0 lim=3 cap=3] hasArray=false 00 00 00
    }
}
```

# mark / position / limit / capacity
mark / position / limit / capacityについて説明します。
常に以下が成り立ちます。

```
0 <= mark <= position <= limit <= capacity
```

## capacity
ByteBuffer全体のバイト長です。ByteBuffer生成時に決めます。

## limit
読み書きできる上限です。limitを超えて読み書きするとExceptionが発生します。
capacityを超える値を設定できません。

## position
次に読み書きをおこなう現在位置をあらわします。範囲は[0, limit]です。

## mark / reset
markはpositionを記録します。初期値は未設定です。
resetはpositionをmark位置に戻します。markが未設定の時にresetを試みるとExceptionが発生します。

```kotlin:Test.kt
fun main() {
    val byteArray = byteArrayOf(1, 2, 3)
    ByteBuffer.wrap(byteArray).also {
        // it.reset() InvalidMarkExceptionが発生する
        it.mark()
        it.println()
        // java.nio.HeapByteBuffer[pos=0 lim=3 cap=3] 01 02 03
        it.position(1)
        it.println()
        // java.nio.HeapByteBuffer[pos=1 lim=3 cap=3] 01 02 03
        it.reset()
        it.println()
        // java.nio.HeapByteBuffer[pos=0 lim=3 cap=3] 01 02 03
    }
}
```

# remaining
現時点で読み書きできる最大のバイト長です。
remaining = limit - poisitonです。**capacityを基準としていないこと**に注意してください。

# clear
position = 0 , limit = capacity にします。markを未設定にします。

```kotlin:Test.kt
fun main() {
    val byteArray = byteArrayOf(1, 2, 3)
    ByteBuffer.wrap(byteArray).also {
        it.mark()
        it.limit(1)
        it.position(1)
        it.println()
        // java.nio.HeapByteBuffer[pos=1 lim=1 cap=3] 01 
        it.clear()
        // it.reset() InvalidMarkExceptionが発生する
        it.println()
        // java.nio.HeapByteBuffer[pos=0 lim=3 cap=3] 01 02 03 
    }
}
```

# flip
次の処理を実行します。markを未設定にします。

1. limit = position
2. position = 0 

利用ケースを具体例で説明します。
ByteBufferに書き込みをおこなった後、読み込みのための準備に使用します。
同一のByteBufferに対して読み書きするwriterとreaderを考えます。

```
1. writerはbufferにデータを書き込みます。limitまで書き込むとは限りません。
2. writerはbuffer.flipします。書き込んだところまでがlimitになります。
3. writerはreaderが読み込み完了するまで待ちます。readerに通知します。
4. readerはbufferからlimitまでデータを読み込みます。
5. readerはbuffer.clearします。writerに通知します。
6. 1.から5.を繰り返します。
```

# rewind
flipに似ていますがlimitを変更しない点が違いです。
position=0 にします。markを未設定にします。

# get
ByteBufferからデータを読みだします。
positionから1Byte読みだす **get()** と位置を指定して読みだす **get(index)** があります。
範囲[0, limit]を超えて読みだしを試みるとExceptionが発生します。

getXxx (Xxx: Char, Int, Long, Float, Double)があります。振る舞いはget系と同じです。
put/putXxxについては書き込みであることを除いてgetと考え方が類似であるため説明を省略します。

## get()
1Byte読みだします。positionを+1します。

```kotlin:Test.kt
fun main() {
    val byteArray = byteArrayOf(1, 2, 3)
    ByteBuffer.wrap(byteArray).also {
        it.limit(1)
        it.println()
        // java.nio.HeapByteBuffer[pos=0 lim=1 cap=3] 01 
        it.get()
        it.println()
        // java.nio.HeapByteBuffer[pos=1 lim=1 cap=3] 01 
        // it.get() BufferUnderflowExceptionが発生します
    }
}
```

## get(index)
1Byte読みだします。positionを更新しません。
配列のように[index]と表記できます(*1)。

```kotlin:Test.kt
fun main() {
    val byteArray = byteArrayOf(1, 2, 3)
    ByteBuffer.wrap(byteArray).also {
        it.limit(1)
        it.println()
        // java.nio.HeapByteBuffer[pos=0 lim=1 cap=3] 01
        it.get(0)
        it[0]  // get(0) と同じ (*1)
        it.println()
        // java.nio.HeapByteBuffer[pos=0 lim=1 cap=3] 01
        // it[1] BufferUnderflowExceptionが発生します
    }
}
```

## get(byteArray: ByteArray)
positionからbyteArray.sizeを読みだします。
positionを+byteArray.sizeします。

```kotlin:Test.kt
fun main() {
    val byteArray = byteArrayOf(1, 2, 3)
    ByteBuffer.wrap(byteArray).also {
        ByteArray(3).also { byteArray ->
            it.println()
            // java.nio.HeapByteBuffer[pos=0 lim=3 cap=3] 01 02 03
            it.get(byteArray)
            it.println()
            // java.nio.HeapByteBuffer[pos=3 lim=3 cap=3] 01 02 03
            // it.get(byteArray) BufferUnderflowExceptionが発生します
        }
    }
}
```

# slice / duplicate
元のByteBufferの**ByteArrayを共有する**新しいByteBufferをつくります。
新しいByteBufferと元のByteBufferのposition / limit / capacity / markは独立しています。

**slice**は元のByteBufferのpositionからlimitの間を切り出したByteBufferをつくります。
position = 0, limit = capacity = (original limit - original position), mark = 未設定 になります。

**duplicate**は元のByteBufferと同じposition / limit / capacity / markのByteBufferをつくります。

```kotlin:Test.kt
fun main() {
    val byteArray = byteArrayOf(1, 2, 3, 4)
    ByteBuffer.wrap(byteArray).also {
        it.println()
        // java.nio.HeapByteBuffer[pos=0 lim=4 cap=4] 01 02 03 04
        it.get()
        it.limit(2)
        it.println()
        // java.nio.HeapByteBuffer[pos=1 lim=2 cap=4] 01 02
        it.slice().also { it.println() }
        // java.nio.HeapByteBuffer[pos=0 lim=1 cap=1] 02
        it.duplicate().also { it.println() }
        // java.nio.HeapByteBuffer[pos=1 lim=2 cap=4] 01 02
    }
}
```

# asReadOnlyBuffer
読み取り専用のByteBufferをつくります。
putなどの書き込み操作をするとExceptionが発生します。
ByteArrayをcopyしてwrapすること(*1)で読み取り専用を徹底することができます。

```kotlin:Test.kt
fun main() {
    val byteArray = byteArrayOf(1, 2, 3)
    ByteBuffer.wrap(byteArray.copyOf()).asReadOnlyBuffer().also { // (*1)
        it.println()
        // java.nio.HeapByteBufferR[pos=0 lim=3 cap=3] 01 02 03
        println("isReadOnly=${it.isReadOnly}")
        // isReadOnly=true
        byteArray[0] = 4 // copyしているのでByteBufferに影響しない
        it.println()
        // java.nio.HeapByteBufferR[pos=0 lim=3 cap=3] 01 02 03
        it.get()
        it.println()
        // it.put(1) ReadOnlyBufferException
    }
}
```

# References
- [1] [ByteBuffer(Java Platform SE8)](https://docs.oracle.com/javase/8/docs/api/index.html?java/nio/ByteBuffer.html)
- [2] [Buffer(Java Platform SE8)](https://docs.oracle.com/javase/8/docs/api/index.html?java/nio/Buffer.html)