CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-squareup-okio--okio

A modern I/O library for Android, Java, and Kotlin Multiplatform

Pending
Overview
Eval results
Files

core-io.mddocs/

Core I/O Operations

This document covers Okio's essential byte manipulation, buffered I/O, and streaming operations. These are the fundamental building blocks for all data processing in Okio.

ByteString

ByteString represents an immutable sequence of bytes with built-in encoding, hashing, and comparison capabilities.

Creation

// From companion object
val empty = ByteString.EMPTY
val fromBytes = ByteString.of(0x48, 0x65, 0x6c, 0x6c, 0x6f) // "Hello"

// From String extensions
val fromString = "Hello, Okio!".encodeUtf8()

// From ByteArray extensions  
val bytes = byteArrayOf(0x48, 0x65, 0x6c, 0x6c, 0x6f)
val fromByteArray = bytes.toByteString()
val fromByteArrayRange = bytes.toByteString(offset = 1, byteCount = 3)

Core Properties and Methods

expect open class ByteString internal constructor(data: ByteArray) : Comparable<ByteString> {
    // Properties
    val size: Int
    operator fun get(index: Int): Byte
    
    // Text conversion
    fun utf8(): String
    
    // Encoding
    fun base64(): String
    fun base64Url(): String  
    fun hex(): String
    
    // Hashing
    fun md5(): ByteString
    fun sha1(): ByteString
    fun sha256(): ByteString
    fun sha512(): ByteString
    fun hmacSha1(key: ByteString): ByteString
    fun hmacSha256(key: ByteString): ByteString
    fun hmacSha512(key: ByteString): ByteString
    
    // Case conversion
    fun toAsciiLowercase(): ByteString
    fun toAsciiUppercase(): ByteString
    
    // Manipulation
    fun substring(beginIndex: Int = 0, endIndex: Int = size): ByteString
    fun toByteArray(): ByteArray
    fun copyInto(offset: Int = 0, target: ByteArray, targetOffset: Int = 0, byteCount: Int = minOf(size - offset, target.size - targetOffset)): Int
    
    // Comparison and search
    fun rangeEquals(offset: Int, other: ByteString, otherOffset: Int = 0, byteCount: Int): Boolean
    fun startsWith(prefix: ByteString): Boolean
    fun endsWith(suffix: ByteString): Boolean
    fun indexOf(other: ByteString, fromIndex: Int = 0): Int
    fun lastIndexOf(other: ByteString, fromIndex: Int = size): Int
    
    // Comparison
    override fun compareTo(other: ByteString): Int
    override fun equals(other: Any?): Boolean
    override fun hashCode(): Int
    override fun toString(): String
    
    companion object {
        val EMPTY: ByteString
        fun of(vararg data: Byte): ByteString
        fun read(inputStream: InputStream, byteCount: Int): ByteString
    }
}

Extension Functions

// String to ByteString conversion
fun String.encodeUtf8(): ByteString
fun String.decodeBase64(): ByteString?
fun String.decodeHex(): ByteString

// ByteArray to ByteString conversion
fun ByteArray.toByteString(offset: Int = 0, byteCount: Int = size): ByteString

Usage Examples

// Creating and manipulating ByteStrings
val data = "Hello, Okio!".encodeUtf8()
println("Size: ${data.size}") // Size: 12
println("Base64: ${data.base64()}") // Base64: SGVsbG8sIE9raW8h
println("Hex: ${data.hex()}") // Hex: 48656c6c6f2c204f6b696f21

// Hashing
val hash = data.sha256()
println("SHA-256: ${hash.hex()}")

// Substring operations
val hello = data.substring(0, 5) // "Hello"
val okio = data.substring(7, 11)  // "Okio"

// Search operations
val commaIndex = data.indexOf(",".encodeUtf8()) // 5
val startsWithHello = data.startsWith("Hello".encodeUtf8()) // true

Buffer

Buffer is a mutable collection of bytes that implements both BufferedSource and BufferedSink, providing efficient byte array pooling and segment-based storage.

Core Buffer Operations

expect class Buffer() : BufferedSource, BufferedSink {
    // Properties
    var size: Long
    override val buffer: Buffer // Returns self
    
    // Basic operations
    fun copyTo(out: Buffer, offset: Long = 0L, byteCount: Long): Buffer
    fun copyTo(out: Buffer, offset: Long = 0L): Buffer // Overload for remaining bytes
    fun completeSegmentByteCount(): Long
    operator fun get(pos: Long): Byte
    fun clear()
    fun skip(byteCount: Long)
    
    // Hashing (same as ByteString)
    fun md5(): ByteString
    fun sha1(): ByteString  
    fun sha256(): ByteString
    fun sha512(): ByteString
    fun hmacSha1(key: ByteString): ByteString
    fun hmacSha256(key: ByteString): ByteString
    fun hmacSha512(key: ByteString): ByteString
    
    // Buffer management
    fun copy(): Buffer
    fun snapshot(): ByteString
    fun snapshot(byteCount: Int): ByteString
    
    // Advanced operations
    fun readUnsafe(unsafeCursor: UnsafeCursor = UnsafeCursor()): UnsafeCursor
    fun readAndWriteUnsafe(unsafeCursor: UnsafeCursor = UnsafeCursor()): UnsafeCursor
}

UnsafeCursor for High-Performance Access

class Buffer.UnsafeCursor {
    var buffer: Buffer?
    var readWrite: Boolean
    var offset: Long
    var data: ByteArray?
    var start: Int
    var end: Int
    
    fun next(): Int
    fun seek(offset: Long): Int
    fun resizeBuffer(newSize: Long): Long
    fun expandBuffer(minByteCount: Int): Long
}

Usage Examples

// Basic buffer operations
val buffer = Buffer()
buffer.writeUtf8("Hello")
buffer.writeUtf8(", ")
buffer.writeUtf8("Okio!")

println("Buffer size: ${buffer.size}") // 12
println("Content: ${buffer.readUtf8()}") // Hello, Okio!

// Copying data between buffers
val source = Buffer().writeUtf8("Source data")
val destination = Buffer()
source.copyTo(destination)

// Snapshot to immutable ByteString
val buffer2 = Buffer().writeUtf8("Test data")
val snapshot = buffer2.snapshot() // Creates ByteString without consuming buffer

Source and Sink

Source and Sink are the fundamental streaming interfaces in Okio.

Source Interface

interface Source : Closeable {
    /**
     * Removes at least 1, and up to byteCount bytes from this source and appends them to sink.
     * Returns the number of bytes read, or -1 if this source is exhausted.
     */
    fun read(sink: Buffer, byteCount: Long): Long
    
    /**
     * Returns the timeout for this source.
     */
    fun timeout(): Timeout
    
    /**
     * Closes this source and releases the resources held by this source.
     */
    override fun close()
}

Sink Interface

expect interface Sink : Closeable {
    /**
     * Removes exactly byteCount bytes from source and appends them to this sink.
     */
    fun write(source: Buffer, byteCount: Long)
    
    /**
     * Pushes all buffered bytes to their final destination.
     */
    fun flush()
    
    /**
     * Returns the timeout for this sink.
     */
    fun timeout(): Timeout
    
    /**
     * Closes this sink and releases the resources held by this sink.
     */
    override fun close()
}

Utility Functions

// Convert Source/Sink to buffered versions
fun Source.buffer(): BufferedSource
fun Sink.buffer(): BufferedSink

// Utility sinks
fun blackholeSink(): Sink

// Resource management
inline fun <T : Closeable?, R> T.use(block: (T) -> R): R

BufferedSource

BufferedSource provides an efficient interface for reading data with internal buffering.

Reading Primitives

expect sealed interface BufferedSource : Source {
    // Buffer access
    val buffer: Buffer
    
    // State checking
    fun exhausted(): Boolean
    fun require(byteCount: Long) // Throws EOFException if not available
    fun request(byteCount: Long): Boolean // Returns false if not available
    
    // Byte reading
    fun readByte(): Byte
    fun readShort(): Short
    fun readShortLe(): Short
    fun readInt(): Int
    fun readIntLe(): Int
    fun readLong(): Long
    fun readLongLe(): Long
    
    // Number parsing
    fun readDecimalLong(): Long
    fun readHexadecimalUnsignedLong(): Long
    
    // Skipping
    fun skip(byteCount: Long)
    
    // Bulk reading
    fun readByteString(): ByteString
    fun readByteString(byteCount: Long): ByteString
    fun readByteArray(): ByteArray
    fun readByteArray(byteCount: Long): ByteArray
    
    // Array reading
    fun read(sink: ByteArray): Int
    fun read(sink: ByteArray, offset: Int, byteCount: Int): Int
    fun readFully(sink: ByteArray)
    fun readFully(sink: Buffer, byteCount: Long)
    fun readAll(sink: Sink): Long
    
    // String reading
    fun readUtf8(): String
    fun readUtf8(byteCount: Long): String
    fun readUtf8Line(): String?
    fun readUtf8LineStrict(): String
    fun readUtf8LineStrict(limit: Long): String
    fun readUtf8CodePoint(): Int
}

Search and Selection Operations

expect sealed interface BufferedSource : Source {
    // Selection
    fun select(options: Options): Int
    fun <T : Any> select(options: TypedOptions<T>): T?
    
    // Search for bytes
    fun indexOf(b: Byte): Long
    fun indexOf(b: Byte, fromIndex: Long): Long
    fun indexOf(b: Byte, fromIndex: Long, toIndex: Long): Long
    
    // Search for byte strings
    fun indexOf(bytes: ByteString): Long
    fun indexOf(bytes: ByteString, fromIndex: Long): Long
    fun indexOf(bytes: ByteString, fromIndex: Long, toIndex: Long): Long
    
    // Search for any of multiple bytes
    fun indexOfElement(targetBytes: ByteString): Long
    fun indexOfElement(targetBytes: ByteString, fromIndex: Long): Long
    
    // Range comparison
    fun rangeEquals(offset: Long, bytes: ByteString): Boolean
    fun rangeEquals(offset: Long, bytes: ByteString, bytesOffset: Int, byteCount: Int): Boolean
    
    // Peek source (non-consuming reads)
    fun peek(): BufferedSource
}

Usage Examples

// Reading different data types
val source = Buffer()
    .writeByte(42)
    .writeInt(1234)
    .writeUtf8("Hello")
    
val byte = source.readByte() // 42
val int = source.readInt() // 1234
val text = source.readUtf8() // "Hello"

// Line-based reading
val lines = Buffer().writeUtf8("Line 1\nLine 2\nLine 3\n")
while (!lines.exhausted()) {
    val line = lines.readUtf8Line()
    println("Read: $line")
}

// Selection-based reading
val options = Options.of("GET".encodeUtf8(), "POST".encodeUtf8(), "PUT".encodeUtf8())
val request = Buffer().writeUtf8("POST /api/users")
val method = request.select(options) // Returns 1 (index of "POST")

BufferedSink

BufferedSink provides an efficient interface for writing data with internal buffering.

Writing Operations

expect sealed interface BufferedSink : Sink {
    // Buffer access
    val buffer: Buffer
    
    // ByteString and ByteArray writing
    fun write(byteString: ByteString): BufferedSink
    fun write(byteString: ByteString, offset: Int, byteCount: Int): BufferedSink
    fun write(source: ByteArray): BufferedSink
    fun write(source: ByteArray, offset: Int, byteCount: Int): BufferedSink
    
    // Source writing
    fun writeAll(source: Source): Long
    fun write(source: Source, byteCount: Long): BufferedSink
    
    // String writing
    fun writeUtf8(string: String): BufferedSink
    fun writeUtf8(string: String, beginIndex: Int, endIndex: Int): BufferedSink
    fun writeUtf8CodePoint(codePoint: Int): BufferedSink
    
    // Primitive writing
    fun writeByte(b: Int): BufferedSink
    fun writeShort(s: Int): BufferedSink
    fun writeShortLe(s: Int): BufferedSink
    fun writeInt(i: Int): BufferedSink
    fun writeIntLe(i: Int): BufferedSink
    fun writeLong(v: Long): BufferedSink
    fun writeLongLe(v: Long): BufferedSink
    
    // Number formatting
    fun writeDecimalLong(v: Long): BufferedSink
    fun writeHexadecimalUnsignedLong(v: Long): BufferedSink
    
    // Buffer management
    override fun flush()
    fun emit(): BufferedSink
    fun emitCompleteSegments(): BufferedSink
}

Usage Examples

// Basic writing operations
val sink = Buffer()
sink.writeUtf8("Hello, ")
    .writeUtf8("Okio!")
    .writeByte(10) // newline
    .writeInt(42)
    .flush()

// Writing different data types
val data = Buffer()
data.writeByte(0xFF)
    .writeShort(1234)
    .writeInt(0x12345678)
    .writeLong(System.currentTimeMillis())
    .writeUtf8("Timestamp")

// Chaining operations
val output = Buffer()
    .writeUtf8("HTTP/1.1 200 OK\r\n")
    .writeUtf8("Content-Type: application/json\r\n")
    .writeUtf8("Content-Length: 13\r\n")
    .writeUtf8("\r\n")
    .writeUtf8("{\"ok\": true}")

Advanced Buffer Features

Segment Pool Optimization

Okio uses internal segment pools to minimize memory allocations and garbage collection:

// Efficient data movement - segments are moved, not copied
val source = Buffer().writeUtf8("Large data chunk")
val destination = Buffer()
source.copyTo(destination) // Segments are transferred, not duplicated

Memory-Efficient Operations

// Reading large files without loading everything into memory
val largeFile: Source = // ... source for large file
val processedData = Buffer()

largeFile.use { source ->
    val buffered = source.buffer()
    while (!buffered.exhausted()) {
        val chunk = buffered.readByteString(8192) // Read 8KB chunks
        val processed = processData(chunk)
        processedData.write(processed)
    }
}

Error Handling

Common exceptions when working with core I/O operations:

expect open class IOException : Exception
expect class EOFException : IOException
expect class ArrayIndexOutOfBoundsException : Exception
  • EOFException: Thrown when trying to read beyond available data
  • ArrayIndexOutOfBoundsException: Thrown for invalid array access
  • IOException: General I/O operation failures

Install with Tessl CLI

npx tessl i tessl/maven-com-squareup-okio--okio

docs

compression.md

core-io.md

filesystem.md

hashing.md

index.md

utilities.md

tile.json