Add BufferedFileChannel

Similar to the equivalent class in the client.

Signed-off-by: Graham <gpe@openrs2.dev>
pull/132/head
Graham 4 years ago
parent 1e1711820d
commit 9f1b2dbc29
  1. 265
      cache/src/main/java/dev/openrs2/cache/BufferedFileChannel.kt
  2. 35
      cache/src/main/java/dev/openrs2/cache/DiskStore.kt
  3. 559
      cache/src/test/java/dev/openrs2/cache/BufferedFileChannelTest.kt

@ -1,53 +1,300 @@
package dev.openrs2.cache
import io.netty.buffer.ByteBuf
import io.netty.buffer.ByteBufAllocator
import java.io.Closeable
import java.io.EOFException
import java.io.Flushable
import java.nio.channels.FileChannel
import kotlin.math.max
import kotlin.math.min
// TODO(gpe): actually implement buffering
class BufferedFileChannel(
private val channel: FileChannel
private val channel: FileChannel,
readBufferSize: Int,
writeBufferSize: Int,
alloc: ByteBufAllocator = ByteBufAllocator.DEFAULT
) : Flushable, Closeable {
private var size = channel.size()
private val readBuffer = alloc.buffer(readBufferSize, readBufferSize)
private var readPos = -1L
private val writeBuffer = alloc.buffer(writeBufferSize, writeBufferSize)
private var writePos = -1L
fun read(pos: Long, dest: ByteBuf, len: Int) {
require(pos >= 0)
require(len <= dest.writableBytes())
val originalDestIndex = dest.writerIndex()
var off = pos
var remaining = len
while (remaining > 0) {
val n = dest.writeBytes(channel, off, remaining)
/*
* Service the whole read from the write buffer, if we can. This code
* isn't necessary, but it is more optimal than following the whole
* sequence of reads below.
*/
val writeLen = writeBuffer.readableBytes()
if (writePos != -1L && off >= writePos && off + remaining <= writePos + writeLen) {
val copyOff = (off - writePos).toInt()
dest.writeBytes(writeBuffer, copyOff, remaining)
return
}
// Service the first part of the read from the read buffer.
if (readPos != -1L && off >= readPos && off < readPos + readBuffer.readableBytes()) {
val copyOff = (off - readPos).toInt()
val copyLen = min(readBuffer.readableBytes() - copyOff, remaining)
dest.writeBytes(readBuffer, copyOff, copyLen)
off += copyLen
remaining -= copyLen
}
if (remaining > readBuffer.capacity()) {
/*
* If the remaining part of the read is larger than the read
* buffer, read directly from the file into the destination buffer.
*/
while (remaining > 0) {
val n = dest.writeBytes(channel, off, remaining)
if (n == -1) {
break
}
off += n
remaining -= n
}
} else if (remaining > 0) {
/*
* Otherwise clear and repopulate the entire read buffer from the
* current position, then copy into the destination buffer.
*/
fill(off)
val copyLen = min(readBuffer.readableBytes(), remaining)
dest.writeBytes(readBuffer, 0, copyLen)
off += copyLen
remaining -= copyLen
}
if (writePos != -1L) {
/*
* If an unflushed write extended the length of the file, fill in
* the gap between the current position and the write position with
* zeroes to reflect what the filesystem would do.
*/
if (off < writePos && remaining > 0) {
val zeroLen = min((writePos - off).toInt(), remaining)
dest.writeZero(zeroLen)
off += zeroLen
remaining -= zeroLen
}
/*
* If a subset of the write buffer overlaps with a subset of the
* destination buffer, overwrite that subset of the destination
* buffer with the write buffer as the write buffer must take
* precedence over the read buffer.
*/
val start = if (writePos >= pos && writePos < pos + len) {
writePos
} else if (pos >= writePos && pos < writePos + writeLen) {
pos
} else {
-1L
}
val end = if (writePos + writeLen > pos && writePos + writeLen <= pos + len) {
writePos + writeLen
} else if (pos + len > writePos && pos + len <= writePos + writeLen) {
pos + len
} else {
-1L
}
if (start != -1L && end != -1L && start < end) {
val destIndex = originalDestIndex + (start - pos).toInt()
val copyOff = (start - writePos).toInt()
val copyLen = (end - start).toInt()
dest.setBytes(destIndex, writeBuffer, copyOff, copyLen)
/*
* If we filled in any remaining bytes in the destination
* buffer from the write buffer then adjust the indexes to take
* that into account.
*/
if (end > off) {
val n = (end - off).toInt()
dest.writerIndex(dest.writerIndex() + n)
off += n
remaining -= n
}
}
}
if (remaining > 0) {
throw EOFException()
}
}
private fun fill(pos: Long) {
require(pos >= 0)
readBuffer.clear()
readPos = pos
var off = pos
while (readBuffer.isWritable) {
val n = readBuffer.writeBytes(channel, off, readBuffer.writableBytes())
if (n == -1) {
throw EOFException()
break
}
off += n
remaining -= n
}
}
fun write(pos: Long, src: ByteBuf, len: Int) {
require(pos >= 0)
require(len <= src.readableBytes())
size = max(size, pos + len.toLong())
var off = pos
var remaining = len
/*
* If the start of the write doesn't overlap with the write buffer,
* flush the existing write buffer.
*/
if (writePos != -1L && (off < writePos || off > writePos + writeBuffer.readableBytes())) {
flush()
}
/*
* If the start of the write does overlap with the write buffer
* (implicit due to the if condition and flush() call above) and the
* end of the write runs beyond the end of the write buffer, overwrite
* the relevant part of the write buffer with the start of the source
* buffer and then flush the whole write buffer.
*/
if (writePos != -1L && off + remaining > writePos + writeBuffer.capacity()) {
val copyOff = (off - writePos).toInt()
val copyLen = writeBuffer.capacity() - copyOff
src.readBytes(writeBuffer, copyOff, copyLen)
off += copyLen
remaining -= copyLen
writeBuffer.writerIndex(writeBuffer.capacity())
flush()
}
if (remaining > writeBuffer.capacity()) {
/*
* If the remaining part of the write is longer than the write
* buffer, write directly to the underlying file.
*/
val originalSrcIndex = src.readerIndex()
writeFully(off, src, remaining)
/*
* If the write overlaps with the read buffer, update the relevant
* portion of the read buffer. (As we bypassed the write buffer, we
* can't rely on the write buffer taking precedence over the read
* buffer.)
*/
val readLen = readBuffer.readableBytes()
val start = if (off >= readPos && off < readPos + readLen) {
off
} else if (readPos >= off && readPos < off + remaining) {
readPos
} else {
-1L
}
val end = if (off + remaining > readPos && off + remaining <= readPos + readLen) {
off + remaining
} else if (readPos + readLen > off && readPos + readLen <= off + remaining) {
readPos + readLen
} else {
-1L
}
if (start != -1L && end != -1L && start < end) {
val srcIndex = originalSrcIndex + (start - off).toInt()
val copyOff = (start - readPos).toInt()
val copyLen = (end - start).toInt()
src.getBytes(srcIndex, readBuffer, copyOff, copyLen)
}
} else if (remaining > 0) {
// Otherwise write to the write buffer.
if (writePos == -1L) {
writePos = off
}
val copyOff = (off - writePos).toInt()
src.readBytes(writeBuffer, copyOff, remaining)
off += remaining
// Increase write buffer length if necessary.
val newWriteLen = (off - writePos).toInt()
if (newWriteLen > writeBuffer.readableBytes()) {
writeBuffer.writerIndex(newWriteLen)
}
}
}
private fun writeFully(pos: Long, src: ByteBuf, len: Int) {
require(pos >= 0)
require(len <= src.readableBytes())
var off = pos
var remaining = len
while (remaining > 0) {
val n = src.readBytes(channel, off, remaining)
val n = src.readBytes(channel, off, len)
off += n
remaining -= n
}
}
fun size(): Long {
return channel.size()
return size
}
override fun flush() {
// empty
if (writePos != -1L) {
writeFully(writePos, writeBuffer, writeBuffer.readableBytes())
writeBuffer.clear()
writePos = -1L
}
}
override fun close() {
flush()
channel.close()
readBuffer.release()
writeBuffer.release()
}
}

@ -109,7 +109,12 @@ class DiskStore private constructor(
return index
}
val newIndex = BufferedFileChannel(FileChannel.open(indexPath(root, archive), CREATE, READ, WRITE))
val newIndex = BufferedFileChannel(
FileChannel.open(indexPath(root, archive), CREATE, READ, WRITE),
INDEX_BUFFER_SIZE,
INDEX_BUFFER_SIZE,
alloc
)
indexes[archive] = newIndex
return newIndex
}
@ -444,6 +449,8 @@ class DiskStore private constructor(
private const val MAX_BLOCK = (1 shl 24) - 1
private val TEMP_BUFFER_SIZE = max(INDEX_ENTRY_SIZE, max(BLOCK_HEADER_SIZE, EXTENDED_BLOCK_HEADER_SIZE))
private const val INDEX_BUFFER_SIZE = INDEX_ENTRY_SIZE * 1000
private const val DATA_BUFFER_SIZE = BLOCK_SIZE * 10
private fun dataPath(root: Path): Path {
return root.resolve("main_file_cache.dat2")
@ -454,22 +461,42 @@ class DiskStore private constructor(
}
fun open(root: Path, alloc: ByteBufAllocator = ByteBufAllocator.DEFAULT): Store {
val data = BufferedFileChannel(FileChannel.open(dataPath(root), READ, WRITE))
val data = BufferedFileChannel(
FileChannel.open(dataPath(root), READ, WRITE),
DATA_BUFFER_SIZE,
DATA_BUFFER_SIZE,
alloc
)
val archives = Array(Store.MAX_ARCHIVE + 1) { archive ->
val path = indexPath(root, archive)
if (Files.exists(path)) {
BufferedFileChannel(FileChannel.open(path, READ, WRITE))
BufferedFileChannel(
FileChannel.open(path, READ, WRITE),
INDEX_BUFFER_SIZE,
INDEX_BUFFER_SIZE,
alloc
)
} else {
null
}
}
return DiskStore(root, data, archives, alloc)
}
fun create(root: Path, alloc: ByteBufAllocator = ByteBufAllocator.DEFAULT): Store {
Files.createDirectories(root)
val data = BufferedFileChannel(FileChannel.open(dataPath(root), CREATE, READ, WRITE))
val data = BufferedFileChannel(
FileChannel.open(dataPath(root), CREATE, READ, WRITE),
DATA_BUFFER_SIZE,
DATA_BUFFER_SIZE,
alloc
)
val archives = Array<BufferedFileChannel?>(Store.MAX_ARCHIVE + 1) { null }
return DiskStore(root, data, archives, alloc)
}
}

@ -0,0 +1,559 @@
package dev.openrs2.cache
import com.google.common.jimfs.Configuration
import com.google.common.jimfs.Jimfs
import dev.openrs2.buffer.use
import io.netty.buffer.Unpooled
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import java.io.EOFException
import java.nio.channels.FileChannel
import java.nio.file.Files
import java.nio.file.StandardOpenOption.CREATE
import java.nio.file.StandardOpenOption.READ
import java.nio.file.StandardOpenOption.WRITE
import kotlin.test.assertEquals
object BufferedFileChannelTest {
@Test
fun testEmpty() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
BufferedFileChannel(FileChannel.open(path, CREATE, READ, WRITE), 8, 8).use { channel ->
assertEquals(0, channel.size())
}
}
}
@Test
fun testBufferedWrite() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
BufferedFileChannel(FileChannel.open(path, CREATE, READ, WRITE), 8, 8).use { channel ->
Unpooled.wrappedBuffer("OpenRS2".toByteArray()).use { buf ->
channel.write(0, buf, buf.readableBytes())
}
assertEquals(7, channel.size())
}
Unpooled.wrappedBuffer(Files.readAllBytes(path)).use { actual ->
Unpooled.wrappedBuffer("OpenRS2".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
}
}
@Test
fun testBufferedWriteOverlapStart() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
BufferedFileChannel(FileChannel.open(path, CREATE, READ, WRITE), 8, 8).use { channel ->
Unpooled.wrappedBuffer("OpenRS2".toByteArray()).use { buf ->
channel.write(1, buf.slice(), buf.readableBytes())
channel.write(0, buf.slice(), buf.readableBytes())
}
}
Unpooled.wrappedBuffer(Files.readAllBytes(path)).use { actual ->
Unpooled.wrappedBuffer("OpenRS22".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
}
}
@Test
fun testBufferedWriteOverlapEnd() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
BufferedFileChannel(FileChannel.open(path, CREATE, READ, WRITE), 8, 8).use { channel ->
Unpooled.wrappedBuffer("OpenRS2".toByteArray()).use { buf ->
channel.write(0, buf.slice(), buf.readableBytes())
channel.write(1, buf.slice(), buf.readableBytes())
}
}
Unpooled.wrappedBuffer(Files.readAllBytes(path)).use { actual ->
Unpooled.wrappedBuffer("OOpenRS2".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
}
}
@Test
fun testBufferedWriteAdjacent() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
BufferedFileChannel(FileChannel.open(path, CREATE, READ, WRITE), 8, 8).use { channel ->
Unpooled.wrappedBuffer("OpenRS2".toByteArray()).use { buf ->
channel.write(0, buf.slice(), buf.readableBytes())
channel.write(7, buf.slice(), buf.readableBytes())
}
}
Unpooled.wrappedBuffer(Files.readAllBytes(path)).use { actual ->
Unpooled.wrappedBuffer("OpenRS2OpenRS2".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
}
}
@Test
fun testBufferedWriteNoOverlap() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
BufferedFileChannel(FileChannel.open(path, CREATE, READ, WRITE), 8, 8).use { channel ->
Unpooled.wrappedBuffer("OpenRS2".toByteArray()).use { buf ->
channel.write(0, buf.slice(), buf.readableBytes())
channel.write(8, buf.slice(), buf.readableBytes())
}
}
Unpooled.wrappedBuffer(Files.readAllBytes(path)).use { actual ->
Unpooled.wrappedBuffer("OpenRS2\u0000OpenRS2".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
}
}
@Test
fun testUnbufferedWrite() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
BufferedFileChannel(FileChannel.open(path, CREATE, READ, WRITE), 8, 8).use { channel ->
Unpooled.wrappedBuffer("Hello, world!".toByteArray()).use { buf ->
channel.write(0, buf, buf.readableBytes())
}
assertEquals(13, channel.size())
}
Unpooled.wrappedBuffer(Files.readAllBytes(path)).use { actual ->
Unpooled.wrappedBuffer("Hello, world!".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
}
}
@Test
fun testBufferedRead() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
Files.write(path, "OpenRS2OpenRS2".toByteArray())
BufferedFileChannel(FileChannel.open(path, READ), 8, 8).use { channel ->
Unpooled.buffer(7, 7).use { actual ->
Unpooled.wrappedBuffer("OpenRS2".toByteArray()).use { expected ->
channel.read(0, actual, actual.writableBytes())
assertEquals(expected, actual)
actual.clear()
channel.read(7, actual, actual.writableBytes())
assertEquals(expected, actual)
}
}
}
}
}
@Test
fun testUnbufferedRead() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
Files.write(path, "OpenRS2OpenRS2".toByteArray())
BufferedFileChannel(FileChannel.open(path, READ), 8, 8).use { channel ->
Unpooled.buffer(14, 14).use { actual ->
Unpooled.wrappedBuffer("OpenRS2OpenRS2".toByteArray()).use { expected ->
channel.read(0, actual, actual.writableBytes())
assertEquals(expected, actual)
}
}
}
}
}
@Test
fun testBufferedEof() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
BufferedFileChannel(FileChannel.open(path, CREATE, READ, WRITE), 8, 8).use { channel ->
Unpooled.buffer(1, 1).use { buf ->
assertThrows<EOFException> {
channel.read(0, buf, buf.writableBytes())
}
}
}
}
}
@Test
fun testUnbufferedEof() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
Files.write(path, "OpenRS2OpenRS2".toByteArray())
BufferedFileChannel(FileChannel.open(path, READ), 8, 8).use { channel ->
Unpooled.buffer(15, 15).use { buf ->
assertThrows<EOFException> {
channel.read(0, buf, buf.writableBytes())
}
}
}
}
}
@Test
fun testZeroExtension() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
BufferedFileChannel(FileChannel.open(path, CREATE, READ, WRITE), 8, 8).use { channel ->
Unpooled.wrappedBuffer(byteArrayOf(1)).use { buf ->
channel.write(7, buf, buf.readableBytes())
}
Unpooled.buffer(8, 8).use { actual ->
channel.read(0, actual, actual.writableBytes())
Unpooled.wrappedBuffer(byteArrayOf(0, 0, 0, 0, 0, 0, 0, 1)).use { expected ->
assertEquals(expected, actual)
}
}
}
}
}
@Test
fun testBufferedWriteThenRead() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
BufferedFileChannel(FileChannel.open(path, CREATE, READ, WRITE), 8, 8).use { channel ->
Unpooled.wrappedBuffer("OpenRS2".toByteArray()).use { buf ->
channel.write(0, buf, buf.readableBytes())
}
Unpooled.buffer(7, 7).use { actual ->
channel.read(0, actual, actual.writableBytes())
Unpooled.wrappedBuffer("OpenRS2".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
}
}
}
@Test
fun testBufferedWriteThenReadSubsetStart() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
BufferedFileChannel(FileChannel.open(path, CREATE, READ, WRITE), 8, 8).use { channel ->
Unpooled.wrappedBuffer("OpenRS2".toByteArray()).use { buf ->
channel.write(0, buf, buf.readableBytes())
}
Unpooled.buffer(6, 6).use { actual ->
channel.read(0, actual, actual.writableBytes())
Unpooled.wrappedBuffer("OpenRS".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
}
}
}
@Test
fun testBufferedWriteThenReadSubsetEnd() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
BufferedFileChannel(FileChannel.open(path, CREATE, READ, WRITE), 8, 8).use { channel ->
Unpooled.wrappedBuffer("OpenRS2".toByteArray()).use { buf ->
channel.write(0, buf, buf.readableBytes())
}
Unpooled.buffer(6, 6).use { actual ->
channel.read(1, actual, actual.writableBytes())
Unpooled.wrappedBuffer("penRS2".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
}
}
}
@Test
fun testBufferedWriteThenReadSuperset() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
Files.write(path, "OpenRS2OpenRS2".toByteArray())
BufferedFileChannel(FileChannel.open(path, CREATE, READ, WRITE), 8, 8).use { channel ->
Unpooled.wrappedBuffer("Hello".toByteArray()).use { buf ->
channel.write(4, buf, buf.readableBytes())
}
Unpooled.buffer(14, 14).use { actual ->
channel.read(0, actual, actual.writableBytes())
Unpooled.wrappedBuffer("OpenHelloenRS2".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
}
}
}
@Test
fun testBufferedWriteThenReadSupersetStart() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
Files.write(path, "OpenRS2OpenRS2".toByteArray())
BufferedFileChannel(FileChannel.open(path, CREATE, READ, WRITE), 8, 8).use { channel ->
Unpooled.wrappedBuffer("Hello".toByteArray()).use { buf ->
channel.write(4, buf, buf.readableBytes())
}
Unpooled.buffer(7, 7).use { actual ->
channel.read(0, actual, actual.writableBytes())
Unpooled.wrappedBuffer("OpenHel".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
}
}
}
@Test
fun testBufferedWriteThenReadSupersetEnd() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
Files.write(path, "OpenRS2OpenRS2".toByteArray())
BufferedFileChannel(FileChannel.open(path, CREATE, READ, WRITE), 8, 8).use { channel ->
Unpooled.wrappedBuffer("Hello".toByteArray()).use { buf ->
channel.write(4, buf, buf.readableBytes())
}
Unpooled.buffer(7, 7).use { actual ->
channel.read(7, actual, actual.writableBytes())
Unpooled.wrappedBuffer("loenRS2".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
}
}
}
@Test
fun testBufferedWriteThenReadNoOverlap() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
Files.write(path, "OpenRS2OpenRS2".toByteArray())
BufferedFileChannel(FileChannel.open(path, READ, WRITE), 4, 8).use { channel ->
Unpooled.wrappedBuffer("Hello".toByteArray()).use { buf ->
channel.write(4, buf, buf.readableBytes())
}
Unpooled.buffer(4, 4).use { actual ->
channel.read(0, actual, actual.writableBytes())
Unpooled.wrappedBuffer("Open".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
Unpooled.buffer(5, 5).use { actual ->
channel.read(9, actual, actual.writableBytes())
Unpooled.wrappedBuffer("enRS2".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
}
}
}
@Test
fun testBufferedReadThenWrite() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
Files.write(path, "OpenRS2OpenRS2".toByteArray())
BufferedFileChannel(FileChannel.open(path, READ, WRITE), 4, 0).use { channel ->
Unpooled.buffer(4, 4).use { actual ->
channel.read(4, actual, actual.writableBytes())
Unpooled.wrappedBuffer("RS2O".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
Unpooled.wrappedBuffer("ABCD".toByteArray()).use { buf ->
channel.write(4, buf.slice(), buf.readableBytes())
}
Unpooled.buffer(4, 4).use { actual ->
channel.read(4, actual, actual.writableBytes())
Unpooled.wrappedBuffer("ABCD".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
}
}
}
@Test
fun testBufferedReadThenWriteSubsetStart() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
Files.write(path, "OpenRS2OpenRS2".toByteArray())
BufferedFileChannel(FileChannel.open(path, READ, WRITE), 4, 0).use { channel ->
Unpooled.buffer(4, 4).use { actual ->
channel.read(4, actual, actual.writableBytes())
Unpooled.wrappedBuffer("RS2O".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
Unpooled.wrappedBuffer("ABC".toByteArray()).use { buf ->
channel.write(4, buf.slice(), buf.readableBytes())
}
Unpooled.buffer(4, 4).use { actual ->
channel.read(4, actual, actual.writableBytes())
Unpooled.wrappedBuffer("ABCO".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
}
}
}
@Test
fun testBufferedReadThenWriteSubsetEnd() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
Files.write(path, "OpenRS2OpenRS2".toByteArray())
BufferedFileChannel(FileChannel.open(path, READ, WRITE), 4, 0).use { channel ->
Unpooled.buffer(4, 4).use { actual ->
channel.read(4, actual, actual.writableBytes())
Unpooled.wrappedBuffer("RS2O".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
Unpooled.wrappedBuffer("BCD".toByteArray()).use { buf ->
channel.write(5, buf.slice(), buf.readableBytes())
}
Unpooled.buffer(4, 4).use { actual ->
channel.read(4, actual, actual.writableBytes())
Unpooled.wrappedBuffer("RBCD".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
}
}
}
@Test
fun testBufferedReadThenWriteSupersetStart() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
Files.write(path, "OpenRS2OpenRS2".toByteArray())
BufferedFileChannel(FileChannel.open(path, READ, WRITE), 4, 0).use { channel ->
Unpooled.buffer(4, 4).use { actual ->
channel.read(4, actual, actual.writableBytes())
Unpooled.wrappedBuffer("RS2O".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
Unpooled.wrappedBuffer("ZABCD".toByteArray()).use { buf ->
channel.write(3, buf.slice(), buf.readableBytes())
}
Unpooled.buffer(4, 4).use { actual ->
channel.read(4, actual, actual.writableBytes())
Unpooled.wrappedBuffer("ABCD".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
}
}
}
@Test
fun testBufferedReadThenWriteSupersetEnd() {
Jimfs.newFileSystem(Configuration.unix()).use { fs ->
val path = fs.getPath("/test.dat")
Files.write(path, "OpenRS2OpenRS2".toByteArray())
BufferedFileChannel(FileChannel.open(path, READ, WRITE), 4, 0).use { channel ->
Unpooled.buffer(4, 4).use { actual ->
channel.read(4, actual, actual.writableBytes())
Unpooled.wrappedBuffer("RS2O".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
Unpooled.wrappedBuffer("ABCDZ".toByteArray()).use { buf ->
channel.write(4, buf.slice(), buf.readableBytes())
}
Unpooled.buffer(4, 4).use { actual ->
channel.read(4, actual, actual.writableBytes())
Unpooled.wrappedBuffer("ABCD".toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
}
}
}
}
Loading…
Cancel
Save