はじめに
ByteBufferの使い方を説明します。
インスタンスをつくる
wrap
wrapはByteArrayのリファレンスを指定してByteBufferをつくります。
リファレンスを保持するだけでByteArrayをコピーしません。
そのため元のByteArrayを変更するとByteBufferに反映します。
position = 0, limit = capacity = ByteArray.size, mark = 未設定 になります。
wrapにoffset / lengthを指定すると
position = offset, limit = offset + length, mark = 未設定 になります。
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.
サイズが大きくて、生存期間が長く、性能上のメリットがあることが明白な場合にのみ使いましょう。
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が発生します。
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を未設定にします。
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を未設定にします。
- limit = position
- 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します。
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)。
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します。
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をつくります。
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)で読み取り専用を徹底することができます。
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
}
}