███████╗██╗ ██████╗ ██╗███╗ ██╗ █████╗ ██████╗ ██╗ ██╗
██╔════╝██║ ██╔══██╗██║████╗ ██║██╔══██╗██╔══██╗╚██╗ ██╔╝
█████╗ ██║ █████╗ ██████╔╝██║██╔██╗ ██║███████║██████╔╝ ╚████╔╝
██╔══╝ ██║ ╚════╝ ██╔══██╗██║██║╚██╗██║██╔══██║██╔══██╗ ╚██╔╝
██║ ███████╗ ██████╔╝██║██║ ╚████║██║ ██║██║ ██║ ██║
╚═╝ ╚══════╝ ╚═════╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝
FLBinary is a lightweight utility library designed to simplify binary reading and writing on the JVM.
It was created to avoid repetitive boilerplate when manually working with byte streams in Kotlin or Java.
If you're comfortable manipulating bytes directly, this library provides an intuitive and minimalistic approach to handle binary data more effectively.
Why do you created this? Is possible perform this job using existing Kotlin APIs...
Well... I was simply doing binary reading in low-level like the implementations in a lot of projects. All the projects was very simple and really didn't the needs of using more the making the bitwise operations by hand. For this reason, I decided to abstract all of this in a reusable code, in this case, this repository.
I am also using this project to study real case tool development. This project is being used in a tool called FLMidi, which is a reader for MIDI files. For now, my binary helper is being very useful, at least for that propose.
- Minimal and focused implementation
- Explicit byte-level control
- Kotlin-first API, but fully usable from Java
- No unnecessary dependencies
- No extra overheads: directly uses the bytes
FLBinary is available via JitPack.
Add the following to your build.gradle.kts file:
repositories {
maven("https://jitpack.io")
}
dependencies {
implementation("com.github.LucasAlfare:FLBinary:v1.9")
}If you're using Version Catalogs, add the following to your libs.versions.toml:
[versions]
flbinary = "v1.9"
[libraries]
flbinary = { module = "com.github.LucasAlfare:FLBinary", version.ref = "flbinary" }Then, reference it in your build script as a dependency:
dependencies {
implementation(libs.flbinary)
}Check this JitPack page for setup instructions tailored to your build tool.
The Reader class is designed to facilitate reading signed integers and strings from a UByteArray in a structured and safe way. It abstracts away the complexities of dealing with signed and unsigned conversions, allowing you to work directly with binary data.
-
Initialize the Reader with a
UByteArray
Create an instance ofReaderby passing aUByteArraycontaining your binary data.val bytes: UByteArray = ubyteArrayOf(0x01u, 0x02u, 0x03u, 0x04u) val reader = Reader(bytes)
-
Read Values
Use the availablereadXBytes()methods to read signed integers from the binary data:read1Byte()– Reads 1 byte and returns anIntread2Bytes()– Reads 2 bytes and returns anIntread3Bytes()– Reads 3 bytes and returns anIntread4Bytes()– Reads 4 bytes and returns aLong
Optionally, pass a custom position to read from a specific index without affecting the current position.
val oneByte = reader.read1Byte() val twoBytes = reader.read2Bytes() val threeBytes = reader.read3Bytes() val fourBytes = reader.read4Bytes()
-
Read Boolean Values
UsereadBoolean()to interpret a single byte as a Boolean (1=true,0=false).val flag = reader.readBoolean()
-
Read Strings
UsereadString(length)to read a sequence of bytes and convert them into a string of the specified length.val text = reader.readString(5)
-
Manage Position
The internal position advances automatically after each read. If you need to skip bytes manually, useadvancePosition(length).
reader.advancePosition(3) // skips 3 bytes- Read bits
We can read from 0 to 64 bits at once to a single variable using the functionreadBits(numBits). This function basically gets all the nextnumBitsbits and pack then into the result number, which is aLonginteger type.
This function also advances theposition: if to readnumBitsis necessaryn bytes, then those bytes are consumed and the position is updated accordingly.
val data = ubyteArrayOf(0b11111111u, 0b11100000u, 0b11011011u, ...)
val reader = Reader(data)
val bits = reader.readBits(11)
println(bits.toString(2)) // -> "11111111111"Imagine you have a file in a arbitrary format that says in its specification that is composed like following:
2 bytesrepresenting a integer;1 byterepresenting a boolean flag;4 bytesrepresenting other integer;5 bytesrepresenting the text "Hello".
The you can do this to read that information from the file:
val fileBytes = ubyteArrayOf(0x00u, 0x02u, 0x01u, 0x00u, 0x00u, 0x00u, 0x2Au, 0x48u, 0x65u, 0x6Cu, 0x6Cu, 0x6Fu)
val reader = Reader(fileBytes)
val two = reader.read2Bytes() // 0x0002
val boolean = reader.readBoolean() // true (0x01)
val number = reader.read4Bytes() // 42 (0x0000002A)
val string = reader.readString(5) // "Hello"Note: Always ensure that the reads do not exceed the bounds of the data array, as each method includes basic validation and will throw if attempting to read past the end of the array.
Is very needed to abstract the reader to use any other bytes source, such as streams and buffers.
This project is licensed under the MIT License. See the LICENSE file for details.
Developed and maintained by Francisco Lucas.