Skip to content

Commit

Permalink
use windowed reads for windows due to jdk bug in windows with memory …
Browse files Browse the repository at this point in the history
…mapped io
  • Loading branch information
froks committed Oct 28, 2024
1 parent 0d03716 commit 0240fe6
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 9 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ plugins {
}

group = "io.github.froks"
version = "0.2.2"
version = "0.3.0"

repositories {
mavenLocal()
Expand Down
21 changes: 17 additions & 4 deletions src/main/kotlin/dltcore/DltParser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ package dltcore

import library.BinaryInputStream
import library.ByteOrder
import library.LargeFileByteBufferInputStream
import library.LargeFileWindowedByteBufferInputStream
import library.LargeFileMemoryMappedByteBufferInputStream
import library.ParseException
import java.nio.file.Path
import kotlin.io.path.fileSize
Expand Down Expand Up @@ -43,8 +44,13 @@ private class DltMessageIterator(
DltStorageVersion.V2 -> throw UnsupportedOperationException("not supported yet")
}

override fun hasNext(): Boolean =
buffer.hasRemaining()
override fun hasNext(): Boolean {
val hasNext = buffer.hasRemaining()
if (!hasNext) {
buffer.close()
}
return hasNext
}

override fun next(): DltReadStatus {
buffer.order(ByteOrder.BIG_ENDIAN)
Expand Down Expand Up @@ -88,10 +94,17 @@ private class DltMessageIterator(
public class DltMessageParser private constructor() {

public companion object {
private fun isWindows() =
System.getProperty("os.name", "unknown").contains("windows", true)

public fun parseBuffer(buffer: BinaryInputStream, totalSize: Long?): Sequence<DltReadStatus> =
DltMessageIterator(buffer, totalSize).asSequence()

public fun parseFile(path: Path): Sequence<DltReadStatus> {
val bis = LargeFileByteBufferInputStream(path)
val bis = if (isWindows()) // Windows memory mapped io keeps the file locked
LargeFileWindowedByteBufferInputStream(path)
else
LargeFileMemoryMappedByteBufferInputStream(path)
return parseBuffer(bis, path.fileSize())
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/library/BinaryInputStream.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package library

import java.nio.ByteBuffer

public interface BinaryInputStream {
public interface BinaryInputStream : AutoCloseable {
public fun order(order: ByteOrder)
public fun hasRemaining(): Boolean
public fun position(): Long
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/library/BinaryOutputStream.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package library

public interface BinaryOutputStream {
public interface BinaryOutputStream : AutoCloseable {
public fun order(order: ByteOrder)

public fun writeByte(value: Byte)
Expand Down
3 changes: 3 additions & 0 deletions src/main/kotlin/library/ByteBufferBinaryInputStream.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,7 @@ public class ByteBufferBinaryInputStream(private val buffer: ByteBuffer) : Binar
buffer.get(data)
return data
}

override fun close() {
}
}
3 changes: 3 additions & 0 deletions src/main/kotlin/library/ByteBufferBinaryOutputStream.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,7 @@ public class ByteBufferBinaryOutputStream(private val buffer: ByteBuffer) : Bina

override fun position(): Long =
position

override fun close() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import kotlin.math.min

private const val OVERLAP = 10_000_000

internal class LargeFileByteBufferInputStream(path: Path) : BinaryInputStream {
private lateinit var currentInputStream: BinaryInputStream
public class LargeFileMemoryMappedByteBufferInputStream(path: Path) : BinaryInputStream {
private lateinit var currentInputStream: ByteBufferBinaryInputStream

private val fileSize = path.fileSize()
private var fileChannel: FileChannel = FileChannel.open(path, StandardOpenOption.READ)
Expand Down Expand Up @@ -37,6 +37,7 @@ internal class LargeFileByteBufferInputStream(path: Path) : BinaryInputStream {
absolutePosition,
min(fileSize - absolutePosition, Integer.MAX_VALUE.toLong())
)
currentInputStream.close()
currentInputStream = ByteBufferBinaryInputStream(buffer)
}
return currentInputStream
Expand Down Expand Up @@ -71,4 +72,9 @@ internal class LargeFileByteBufferInputStream(path: Path) : BinaryInputStream {

override fun readArray(len: Int): ByteArray =
buffer.readArray(len)

override fun close() {
buffer.close()
fileChannel.close()
}
}
77 changes: 77 additions & 0 deletions src/main/kotlin/library/LargeFileWindowedByteBufferInputStream.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package library

import java.nio.ByteBuffer
import java.nio.channels.FileChannel
import java.nio.file.Path
import java.nio.file.StandardOpenOption
import kotlin.io.path.fileSize
import kotlin.math.min

private const val BUFFER_SIZE = 100_000_000
private const val OVERLAP = 10_000_000

public class LargeFileWindowedByteBufferInputStream(path: Path) : BinaryInputStream {
private lateinit var currentInputStream: ByteBufferBinaryInputStream
private val fileSize = path.fileSize()
private var fileChannel: FileChannel = FileChannel.open(path, StandardOpenOption.READ)
private var absolutePosition = -1L
private var bufferIndex = 0

private val buffer: BinaryInputStream
get() {
if (absolutePosition == -1L) {
absolutePosition = 0
val buffer = ByteBuffer.allocate(BUFFER_SIZE)
fileChannel.read(buffer, absolutePosition)
buffer.position(0)
currentInputStream = ByteBufferBinaryInputStream(buffer)
bufferIndex = 0
return currentInputStream
}
val relativePosition = currentInputStream.position()
if (relativePosition >= (BUFFER_SIZE - OVERLAP)) {
absolutePosition += relativePosition
val buffer = ByteBuffer.allocate(min(BUFFER_SIZE.toLong(), fileSize - absolutePosition).toInt())
fileChannel.read(buffer, absolutePosition)
buffer.position(0)
currentInputStream.close()
currentInputStream = ByteBufferBinaryInputStream(buffer)
}
return currentInputStream
}

override fun order(order: ByteOrder) {
buffer.order(order)
}

override fun hasRemaining(): Boolean {
val remaining = buffer.hasRemaining()
if (!remaining) {
fileChannel.close()
}
return remaining
}

override fun position(): Long =
absolutePosition + currentInputStream.position()

override fun readByte(): Byte =
buffer.readByte()

override fun readShort(): Short =
buffer.readShort()

override fun readInt(): Int =
buffer.readInt()

override fun readLong(): Long =
buffer.readLong()

override fun readArray(len: Int): ByteArray =
buffer.readArray(len)

override fun close() {
buffer.close()
fileChannel.close()
}
}

0 comments on commit 0240fe6

Please sign in to comment.