0
# Utilities and Extensions
1
2
This document covers Okio's helper functions, timeout management, advanced buffer operations, selection utilities, and platform-specific extensions.
3
4
## Timeout Management
5
6
Timeout provides configurable timeout policies for I/O operations, allowing you to set deadlines and prevent operations from blocking indefinitely.
7
8
### Timeout Class
9
10
```kotlin { .api }
11
expect open class Timeout {
12
// Timeout configuration
13
fun timeout(timeout: Long, unit: TimeUnit): Timeout
14
fun deadlineNanoTime(deadlineNanoTime: Long): Timeout
15
fun clearTimeout(): Timeout
16
fun clearDeadline(): Timeout
17
18
// Timeout checking
19
fun hasDeadline(): Boolean
20
fun deadlineNanoTime(): Long
21
fun timeoutNanos(): Long
22
23
// Timeout enforcement
24
fun throwIfReached()
25
fun waitUntilNotified(monitor: Any)
26
27
companion object {
28
val NONE: Timeout // Timeout that never times out
29
}
30
}
31
```
32
33
### Usage Examples
34
35
```kotlin
36
// Basic timeout configuration
37
val timeout = Timeout()
38
.timeout(5, TimeUnit.SECONDS)
39
.deadlineNanoTime(System.nanoTime() + TimeUnit.MINUTES.toNanos(1))
40
41
// Check timeout status
42
if (timeout.hasDeadline()) {
43
val remainingTime = timeout.deadlineNanoTime() - System.nanoTime()
44
println("Time remaining: ${remainingTime / 1_000_000}ms")
45
}
46
47
// Using timeout with I/O operations
48
val fs = FileSystem.SYSTEM
49
val path = "/tmp/large-file.txt".toPath()
50
51
try {
52
val source = fs.source(path)
53
source.timeout().timeout(30, TimeUnit.SECONDS)
54
55
source.buffer().use { bufferedSource ->
56
while (!bufferedSource.exhausted()) {
57
// This will throw if timeout is exceeded
58
val data = bufferedSource.readByteString(8192)
59
processData(data)
60
}
61
}
62
} catch (e: IOException) {
63
if (e.message?.contains("timeout") == true) {
64
println("Operation timed out")
65
} else {
66
println("I/O error: ${e.message}")
67
}
68
}
69
```
70
71
### Custom Timeout Implementations
72
73
```kotlin
74
// Monitor long-running operation with timeout
75
fun monitoredOperation(operation: () -> Unit, timeoutSeconds: Long) {
76
val timeout = Timeout().timeout(timeoutSeconds, TimeUnit.SECONDS)
77
val startTime = System.nanoTime()
78
79
try {
80
operation()
81
val duration = (System.nanoTime() - startTime) / 1_000_000
82
println("Operation completed in ${duration}ms")
83
} catch (e: Exception) {
84
timeout.throwIfReached()
85
throw e
86
}
87
}
88
```
89
90
## Selection and Matching
91
92
Okio provides efficient selection mechanisms for matching byte sequences from streams.
93
94
### Options Class
95
96
```kotlin { .api }
97
class Options private constructor() : List<ByteString> {
98
override val size: Int
99
override fun get(index: Int): ByteString
100
override fun iterator(): Iterator<ByteString>
101
102
companion object {
103
fun of(vararg byteStrings: ByteString): Options
104
}
105
}
106
```
107
108
### TypedOptions Class
109
110
```kotlin { .api }
111
class TypedOptions<T : Any> private constructor() : List<T> {
112
override val size: Int
113
override fun get(index: Int): T
114
115
companion object {
116
inline fun <T : Any> of(
117
values: Iterable<T>,
118
encode: (T) -> ByteString
119
): TypedOptions<T>
120
}
121
}
122
```
123
124
### BufferedSource Selection Methods
125
126
```kotlin { .api }
127
expect sealed interface BufferedSource {
128
// Selection operations
129
fun select(options: Options): Int
130
fun <T : Any> select(options: TypedOptions<T>): T?
131
}
132
```
133
134
### Selection Usage Examples
135
136
```kotlin
137
// HTTP method parsing
138
val httpMethods = Options.of(
139
"GET".encodeUtf8(),
140
"POST".encodeUtf8(),
141
"PUT".encodeUtf8(),
142
"DELETE".encodeUtf8(),
143
"PATCH".encodeUtf8()
144
)
145
146
fun parseHttpMethod(request: BufferedSource): String? {
147
return when (request.select(httpMethods)) {
148
0 -> "GET"
149
1 -> "POST"
150
2 -> "PUT"
151
3 -> "DELETE"
152
4 -> "PATCH"
153
else -> null // No match found
154
}
155
}
156
157
// Usage
158
val requestBuffer = Buffer().writeUtf8("POST /api/users HTTP/1.1\r\n")
159
val method = parseHttpMethod(requestBuffer)
160
println("HTTP Method: $method")
161
162
// Typed options for enum-like behavior
163
enum class ResponseCode(val code: String) {
164
OK("200 OK"),
165
NOT_FOUND("404 Not Found"),
166
SERVER_ERROR("500 Internal Server Error")
167
}
168
169
val responseOptions = TypedOptions.of(
170
ResponseCode.values().asIterable()
171
) { code -> code.code.encodeUtf8() }
172
173
fun parseResponseCode(response: BufferedSource): ResponseCode? {
174
return response.select(responseOptions)
175
}
176
177
// Usage
178
val responseBuffer = Buffer().writeUtf8("404 Not Found\r\n")
179
val responseCode = parseResponseCode(responseBuffer)
180
println("Response Code: $responseCode")
181
```
182
183
### Protocol Parsing Example
184
185
```kotlin
186
// Parse different message types in a protocol
187
sealed class Message
188
object PingMessage : Message()
189
data class DataMessage(val payload: String) : Message()
190
data class ErrorMessage(val error: String) : Message()
191
192
val messageTypes = Options.of(
193
"PING".encodeUtf8(),
194
"DATA".encodeUtf8(),
195
"ERROR".encodeUtf8()
196
)
197
198
fun parseMessage(source: BufferedSource): Message? {
199
return when (source.select(messageTypes)) {
200
0 -> {
201
source.readUtf8Line() // Consume rest of line
202
PingMessage
203
}
204
1 -> {
205
val length = source.readUtf8Line()?.toIntOrNull() ?: return null
206
val payload = source.readUtf8(length.toLong())
207
DataMessage(payload)
208
}
209
2 -> {
210
val error = source.readUtf8Line() ?: return null
211
ErrorMessage(error)
212
}
213
else -> null
214
}
215
}
216
217
// Test protocol parsing
218
val protocolBuffer = Buffer()
219
.writeUtf8("DATA\n")
220
.writeUtf8("12\n")
221
.writeUtf8("Hello, World")
222
223
val message = parseMessage(protocolBuffer)
224
println("Parsed message: $message")
225
```
226
227
## Advanced Buffer Operations
228
229
### UnsafeCursor for High-Performance Access
230
231
The UnsafeCursor provides low-level access to Buffer's internal segments for high-performance operations.
232
233
```kotlin { .api }
234
class Buffer.UnsafeCursor {
235
var buffer: Buffer?
236
var readWrite: Boolean
237
var offset: Long
238
var data: ByteArray?
239
var start: Int
240
var end: Int
241
242
fun next(): Int
243
fun seek(offset: Long): Int
244
fun resizeBuffer(newSize: Long): Long
245
fun expandBuffer(minByteCount: Int): Long
246
}
247
248
expect class Buffer {
249
fun readUnsafe(unsafeCursor: UnsafeCursor = UnsafeCursor()): UnsafeCursor
250
fun readAndWriteUnsafe(unsafeCursor: UnsafeCursor = UnsafeCursor()): UnsafeCursor
251
}
252
```
253
254
### UnsafeCursor Usage Examples
255
256
```kotlin
257
// High-performance byte array search
258
fun findBytePattern(buffer: Buffer, pattern: ByteArray): Long {
259
var position = -1L
260
261
buffer.readUnsafe().use { cursor ->
262
cursor.seek(0L)
263
264
while (cursor.next() != -1) {
265
val segment = cursor.data ?: continue
266
267
// Search within current segment
268
for (i in cursor.start until cursor.end - pattern.size + 1) {
269
var match = true
270
for (j in pattern.indices) {
271
if (segment[i + j] != pattern[j]) {
272
match = false
273
break
274
}
275
}
276
if (match) {
277
position = cursor.offset + (i - cursor.start)
278
return position
279
}
280
}
281
}
282
}
283
284
return position
285
}
286
287
// Efficient byte manipulation
288
fun xorBuffer(buffer: Buffer, key: ByteArray) {
289
buffer.readAndWriteUnsafe().use { cursor ->
290
cursor.seek(0L)
291
var keyIndex = 0
292
293
while (cursor.next() != -1) {
294
val segment = cursor.data ?: continue
295
296
for (i in cursor.start until cursor.end) {
297
segment[i] = (segment[i].toInt() xor key[keyIndex % key.size].toInt()).toByte()
298
keyIndex++
299
}
300
}
301
}
302
}
303
304
// Usage
305
val buffer = Buffer().writeUtf8("Hello, Okio UnsafeCursor!")
306
val pattern = "Okio".encodeUtf8().toByteArray()
307
val position = findBytePattern(buffer, pattern)
308
println("Pattern found at position: $position")
309
310
// XOR encryption/decryption
311
val key = "SECRET".toByteArray()
312
val originalText = buffer.copy().readUtf8()
313
xorBuffer(buffer, key)
314
val encrypted = buffer.copy().readUtf8()
315
xorBuffer(buffer, key) // XOR again to decrypt
316
val decrypted = buffer.readUtf8()
317
318
println("Original: $originalText")
319
println("Encrypted: ${encrypted.encodeUtf8().hex()}")
320
println("Decrypted: $decrypted")
321
```
322
323
## Utility Functions
324
325
### Resource Management
326
327
```kotlin { .api }
328
// Automatic resource cleanup
329
inline fun <T : Closeable?, R> T.use(block: (T) -> R): R
330
```
331
332
### Core I/O Utilities
333
334
```kotlin { .api }
335
// Source and Sink utilities
336
fun Source.buffer(): BufferedSource
337
fun Sink.buffer(): BufferedSink
338
fun blackholeSink(): Sink
339
```
340
341
### Usage Examples
342
343
```kotlin
344
// Safe resource handling
345
fun processFileWithCleanup(path: Path): String {
346
return FileSystem.SYSTEM.source(path).use { source ->
347
source.buffer().use { buffered ->
348
buffered.readUtf8()
349
}
350
}
351
}
352
353
// Blackhole sink for testing or discarding data
354
fun measureCompressionRatio(data: String): Double {
355
val originalSize = data.length
356
val compressedSize = Buffer()
357
358
blackholeSink().gzip().use { compressor ->
359
compressor.writeUtf8(data)
360
// Data is compressed but discarded
361
}
362
363
// In real scenario, you'd measure compressed size
364
return 0.5 // Placeholder
365
}
366
367
// Chaining buffer operations
368
fun transformData(input: String): String {
369
return Buffer()
370
.writeUtf8(input)
371
.let { buffer ->
372
// Transform to uppercase
373
val upperData = buffer.readUtf8().uppercase()
374
Buffer().writeUtf8(upperData)
375
}
376
.let { buffer ->
377
// Add prefix and suffix
378
buffer.writeUtf8(" [PROCESSED]")
379
"[DATA] ${buffer.readUtf8()}"
380
}
381
}
382
383
val result = transformData("hello world")
384
println(result) // [DATA] HELLO WORLD [PROCESSED]
385
```
386
387
## Platform-Specific Extensions
388
389
### Apple Platform Extensions (appleMain)
390
391
Okio provides specific extensions for Apple platforms to integrate with Foundation framework.
392
393
```kotlin { .api }
394
// Foundation NSData integration
395
fun NSData.toByteString(): ByteString
396
```
397
398
### Usage Examples
399
400
```kotlin
401
// Converting Foundation NSData to Okio ByteString (iOS/macOS)
402
import platform.Foundation.NSData
403
import platform.Foundation.NSString
404
import platform.Foundation.dataUsingEncoding
405
import platform.Foundation.NSUTF8StringEncoding
406
407
fun foundationIntegration() {
408
// Create NSData from NSString
409
val nsString = NSString.create(string = "Hello from Foundation!")
410
val nsData = nsString.dataUsingEncoding(NSUTF8StringEncoding) ?: return
411
412
// Convert to Okio ByteString
413
val byteString = nsData.toByteString()
414
415
println("NSData converted to ByteString: ${byteString.utf8()}")
416
println("ByteString hex: ${byteString.hex()}")
417
418
// Use with Okio operations
419
val hash = byteString.sha256()
420
println("SHA-256: ${hash.hex()}")
421
}
422
```
423
424
### Native Platform Features
425
426
On native platforms (including iOS), Okio provides optimized implementations:
427
428
- **Native zlib integration**: High-performance compression using system libraries
429
- **Memory-mapped file I/O**: Efficient file access for large files
430
- **POSIX-compliant file operations**: Full Unix file system support
431
432
## Threading and Concurrency
433
434
### Thread Safety Considerations
435
436
```kotlin { .api }
437
// Platform-specific lock implementation
438
expect class Lock() {
439
inline fun <T> withLock(action: () -> T): T
440
}
441
```
442
443
### Safe Concurrent Operations
444
445
```kotlin
446
// Thread-safe buffer operations
447
class ThreadSafeBuffer {
448
private val buffer = Buffer()
449
private val lock = Lock()
450
451
fun write(data: ByteString) {
452
lock.withLock {
453
buffer.write(data)
454
}
455
}
456
457
fun read(): ByteString? {
458
return lock.withLock {
459
if (buffer.size > 0) {
460
buffer.readByteString()
461
} else {
462
null
463
}
464
}
465
}
466
467
fun size(): Long {
468
return lock.withLock {
469
buffer.size
470
}
471
}
472
}
473
474
// Usage in multi-threaded environment
475
val safeBuffer = ThreadSafeBuffer()
476
477
// Thread 1: Writing data
478
Thread {
479
repeat(100) { i ->
480
safeBuffer.write("Message $i\n".encodeUtf8())
481
Thread.sleep(10)
482
}
483
}.start()
484
485
// Thread 2: Reading data
486
Thread {
487
repeat(50) {
488
val data = safeBuffer.read()
489
if (data != null) {
490
println("Read: ${data.utf8().trim()}")
491
}
492
Thread.sleep(20)
493
}
494
}.start()
495
```
496
497
## Error Handling Utilities
498
499
### Common Exception Types
500
501
```kotlin { .api }
502
// Platform-specific exception implementations
503
expect open class IOException : Exception
504
expect class EOFException : IOException
505
expect class FileNotFoundException : IOException
506
expect class ProtocolException : IOException
507
expect class ArrayIndexOutOfBoundsException : Exception
508
```
509
510
### Robust Error Handling Patterns
511
512
```kotlin
513
// Retry mechanism for I/O operations
514
fun <T> retryOperation(
515
maxAttempts: Int = 3,
516
delayMs: Long = 1000,
517
operation: () -> T
518
): T? {
519
repeat(maxAttempts) { attempt ->
520
try {
521
return operation()
522
} catch (e: IOException) {
523
println("Attempt ${attempt + 1} failed: ${e.message}")
524
if (attempt < maxAttempts - 1) {
525
Thread.sleep(delayMs)
526
}
527
}
528
}
529
return null
530
}
531
532
// Safe file operations with fallback
533
fun safeReadFile(path: Path, fallbackContent: String = ""): String {
534
return try {
535
FileSystem.SYSTEM.read(path) { readUtf8() }
536
} catch (e: FileNotFoundException) {
537
println("File not found, using fallback: $path")
538
fallbackContent
539
} catch (e: IOException) {
540
println("I/O error reading file: ${e.message}")
541
fallbackContent
542
}
543
}
544
545
// Graceful resource cleanup
546
fun <T : Closeable> safeClose(resource: T?) {
547
try {
548
resource?.close()
549
} catch (e: Exception) {
550
println("Error closing resource: ${e.message}")
551
}
552
}
553
554
// Usage
555
val result = retryOperation(maxAttempts = 3) {
556
FileSystem.SYSTEM.read("/tmp/unreliable-file.txt".toPath()) {
557
readUtf8()
558
}
559
}
560
561
if (result != null) {
562
println("Successfully read file: ${result.take(50)}...")
563
} else {
564
println("Failed to read file after retries")
565
}
566
```
567
568
## Performance Optimization Utilities
569
570
### Memory Pool Management
571
572
```kotlin
573
// Efficient buffer reuse
574
class BufferPool(private val maxSize: Int = 10) {
575
private val pool = mutableListOf<Buffer>()
576
private val lock = Lock()
577
578
fun acquire(): Buffer {
579
return lock.withLock {
580
if (pool.isNotEmpty()) {
581
pool.removeAt(pool.size - 1)
582
} else {
583
Buffer()
584
}
585
}
586
}
587
588
fun release(buffer: Buffer) {
589
lock.withLock {
590
if (pool.size < maxSize) {
591
buffer.clear()
592
pool.add(buffer)
593
}
594
}
595
}
596
597
inline fun <T> withBuffer(action: (Buffer) -> T): T {
598
val buffer = acquire()
599
try {
600
return action(buffer)
601
} finally {
602
release(buffer)
603
}
604
}
605
}
606
607
// Usage
608
val bufferPool = BufferPool()
609
610
// Efficient data processing without allocations
611
fun processData(data: List<String>): List<String> {
612
return data.map { input ->
613
bufferPool.withBuffer { buffer ->
614
buffer.writeUtf8(input.uppercase())
615
buffer.writeUtf8(" [PROCESSED]")
616
buffer.readUtf8()
617
}
618
}
619
}
620
```
621
622
### Batch Operations
623
624
```kotlin
625
// Efficient batch file processing
626
fun processBatchFiles(filePaths: List<Path>, processor: (String) -> String) {
627
val fs = FileSystem.SYSTEM
628
629
filePaths.chunked(10).forEach { batch ->
630
batch.forEach { path ->
631
try {
632
val content = fs.read(path) { readUtf8() }
633
val processed = processor(content)
634
635
val outputPath = path.parent!! / "${path.name}.processed"
636
fs.write(outputPath) { writeUtf8(processed) }
637
638
} catch (e: Exception) {
639
println("Error processing $path: ${e.message}")
640
}
641
}
642
643
// Small delay between batches to prevent resource exhaustion
644
Thread.sleep(100)
645
}
646
}
647
```
648
649
## Debugging and Inspection Utilities
650
651
### Buffer Content Inspection
652
653
```kotlin
654
// Debug buffer contents
655
fun debugBuffer(buffer: Buffer, name: String = "Buffer") {
656
println("=== $name Debug Info ===")
657
println("Size: ${buffer.size} bytes")
658
659
if (buffer.size > 0) {
660
val snapshot = buffer.snapshot()
661
println("Hex: ${snapshot.hex()}")
662
663
// Try to display as text if printable
664
val text = try {
665
snapshot.utf8()
666
} catch (e: Exception) {
667
"[Non-UTF8 data]"
668
}
669
670
println("Text: ${text.take(100)}${if (text.length > 100) "..." else ""}")
671
}
672
println("========================")
673
}
674
675
// Trace I/O operations
676
class TracingSource(private val delegate: Source, private val name: String) : Source by delegate {
677
override fun read(sink: Buffer, byteCount: Long): Long {
678
val bytesRead = delegate.read(sink, byteCount)
679
println("$name: Read $bytesRead bytes (requested $byteCount)")
680
return bytesRead
681
}
682
}
683
684
class TracingSink(private val delegate: Sink, private val name: String) : Sink by delegate {
685
override fun write(source: Buffer, byteCount: Long) {
686
println("$name: Writing $byteCount bytes")
687
delegate.write(source, byteCount)
688
}
689
}
690
691
// Usage
692
val buffer = Buffer().writeUtf8("Hello, debugging!")
693
debugBuffer(buffer, "Test Buffer")
694
695
val tracingSource = TracingSource(buffer, "Debug Source")
696
val output = Buffer()
697
tracingSource.read(output, 5) // Will print tracing info
698
```