1Okio 2==== 3 4Okio is a library that complements `java.io` and `java.nio` to make it much 5easier to access, store, and process your data. It started as a component of 6[OkHttp][1], the capable HTTP client included in Android. It's well-exercised 7and ready to solve new problems. 8 9ByteStrings and Buffers 10----------------------- 11 12Okio is built around two types that pack a lot of capability into a 13straightforward API: 14 15 * [**ByteString**][3] is an immutable sequence of bytes. For character data, `String` 16 is fundamental. `ByteString` is String's long-lost brother, making it easy to 17 treat binary data as a value. This class is ergonomic: it knows how to encode 18 and decode itself as hex, base64, and UTF-8. 19 20 * [**Buffer**][4] is a mutable sequence of bytes. Like `ArrayList`, you don't need 21 to size your buffer in advance. You read and write buffers as a queue: write 22 data to the end and read it from the front. There's no obligation to manage 23 positions, limits, or capacities. 24 25Internally, `ByteString` and `Buffer` do some clever things to save CPU and 26memory. If you encode a UTF-8 string as a `ByteString`, it caches a reference to 27that string so that if you decode it later, there's no work to do. 28 29`Buffer` is implemented as a linked list of segments. When you move data from 30one buffer to another, it _reassigns ownership_ of the segments rather than 31copying the data across. This approach is particularly helpful for multithreaded 32programs: a thread that talks to the network can exchange data with a worker 33thread without any copying or ceremony. 34 35Sources and Sinks 36----------------- 37 38An elegant part of the `java.io` design is how streams can be layered for 39transformations like encryption and compression. Okio includes its own stream 40types called [`Source`][5] and [`Sink`][6] that work like `InputStream` and 41`OutputStream`, but with some key differences: 42 43 * **Timeouts.** The streams provide access to the timeouts of the underlying 44 I/O mechanism. Unlike the `java.io` socket streams, both `read()` and 45 `write()` calls honor timeouts. 46 47 * **Easy to implement.** `Source` declares three methods: `read()`, `close()`, 48 and `timeout()`. There are no hazards like `available()` or single-byte reads 49 that cause correctness and performance surprises. 50 51 * **Easy to use.** Although _implementations_ of `Source` and `Sink` have only 52 three methods to write, _callers_ are given a rich API with the 53 [`BufferedSource`][7] and [`BufferedSink`][8] interfaces. These interfaces give you 54 everything you need in one place. 55 56 * **No artificial distinction between byte streams and char streams.** It's all 57 data. Read and write it as bytes, UTF-8 strings, big-endian 32-bit integers, 58 little-endian shorts; whatever you want. No more `InputStreamReader`! 59 60 * **Easy to test.** The `Buffer` class implements both `BufferedSource` and 61 `BufferedSink` so your test code is simple and clear. 62 63Sources and sinks interoperate with `InputStream` and `OutputStream`. You can 64view any `Source` as an `InputStream`, and you can view any `InputStream` as a 65`Source`. Similarly for `Sink` and `OutputStream`. 66 67 68Presentations 69------------- 70 71[A Few “Ok” Libraries][ok_libraries_talk] ([slides][ok_libraries_slides]): An introduction to Okio 72and three libraries written with it. 73 74[Decoding the Secrets of Binary Data][encoding_talk] ([slides][encoding_slides]): How data encoding 75works and how Okio does it. 76 77[Ok Multiplatform!][ok_multiplatform_talk] ([slides][ok_multiplatform_slides]): How we changed 78Okio’s implementation language from Java to Kotlin. 79 80 81Requirements 82------------ 83 84Okio supports Android 4.0.3+ (API level 15+) and Java 7+. 85 86Okio depends on the [Kotlin standard library][kotlin]. It is a small library with strong 87backward-compatibility. 88 89 90Recipes 91------- 92 93We've written some recipes that demonstrate how to solve common problems with 94Okio. Read through them to learn about how everything works together. 95Cut-and-paste these examples freely; that's what they're for. 96 97### Read a text file line-by-line ([Java][ReadFileLineByLine]/[Kotlin][ReadFileLineByLineKt]) 98 99Use `Okio.source(File)` to open a source stream to read a file. The returned 100`Source` interface is very small and has limited uses. Instead we wrap the 101source with a buffer. This has two benefits: 102 103 * **It makes the API more powerful.** Instead of the basic methods offered by 104 `Source`, `BufferedSource` has dozens of methods to address most common 105 problems concisely. 106 107 * **It makes your program run faster.** Buffering allows Okio to get more done 108 with fewer I/O operations. 109 110Each `Source` that is opened needs to be closed. The code that opens the stream 111is responsible for making sure it is closed. 112 113=== "Java" 114 115 Here we use Java's `try` blocks to close our sources automatically. 116 117 ```java 118 public void readLines(File file) throws IOException { 119 try (Source fileSource = Okio.source(file); 120 BufferedSource bufferedSource = Okio.buffer(fileSource)) { 121 122 while (true) { 123 String line = bufferedSource.readUtf8Line(); 124 if (line == null) break; 125 126 if (line.contains("square")) { 127 System.out.println(line); 128 } 129 } 130 131 } 132 } 133 ``` 134 135=== "Kotlin" 136 137 Note that static `Okio` methods become extension functions (`Okio.source(file)` => 138 `file.source()`), and `use` is used to automatically close the streams: 139 140 ```kotlin 141 @Throws(IOException::class) 142 fun readLines(file: File) { 143 file.source().use { fileSource -> 144 fileSource.buffer().use { bufferedFileSource -> 145 while (true) { 146 val line = bufferedFileSource.readUtf8Line() ?: break 147 if ("square" in line) { 148 println(line) 149 } 150 } 151 } 152 } 153 } 154 ``` 155 156The `readUtf8Line()` API reads all of the data until the next line delimiter – 157either `\n`, `\r\n`, or the end of the file. It returns that data as a string, 158omitting the delimiter at the end. When it encounters empty lines the method 159will return an empty string. If there isn’t any more data to read it will 160return null. 161 162The above program can be written more compactly by inlining the `fileSource` 163variable and by using a fancy `for` loop instead of a `while`: 164 165```java 166public void readLines(File file) throws IOException { 167 try (BufferedSource source = Okio.buffer(Okio.source(file))) { 168 for (String line; (line = source.readUtf8Line()) != null; ) { 169 if (line.contains("square")) { 170 System.out.println(line); 171 } 172 } 173 } 174} 175``` 176 177In Kotlin, we can wrap invocations of `source.readUtf8Line()` into the `generateSequence` builder to 178create a sequence of lines that will end once null is returned. Plus, transforming streams is easy 179thanks to the extension functions: 180 181```kotlin 182@Throws(IOException::class) 183fun readLines(file: File) { 184 file.source().buffer().use { source -> 185 generateSequence { source.readUtf8Line() } 186 .filter { line -> "square" in line } 187 .forEach(::println) 188 } 189} 190``` 191 192The `readUtf8Line()` method is suitable for parsing most files. For certain 193use-cases you may also consider `readUtf8LineStrict()`. It is similar but it 194requires that each line is terminated by `\n` or `\r\n`. If it encounters the 195end of the file before that it will throw an `EOFException`. The strict variant 196also permits a byte limit to defend against malformed input. 197 198```java 199public void readLines(File file) throws IOException { 200 try (BufferedSource source = Okio.buffer(Okio.source(file))) { 201 while (!source.exhausted()) { 202 String line = source.readUtf8LineStrict(1024L); 203 if (line.contains("square")) { 204 System.out.println(line); 205 } 206 } 207 } 208} 209``` 210 211Here's a similar example written in Kotlin: 212 213```kotlin 214@Throws(IOException::class) 215fun readLines(file: File) { 216 file.source().buffer().use { source -> 217 while (!source.exhausted()) { 218 val line = source.readUtf8LineStrict(1024) 219 if ("square" in line) { 220 println(line) 221 } 222 } 223 } 224} 225``` 226 227### Write a text file ([Java][WriteFile]/[Kotlin][WriteFileKt]) 228 229Above we used a `Source` and a `BufferedSource` to read a file. To write, we use 230a `Sink` and a `BufferedSink`. The advantages of buffering are the same: a more 231capable API and better performance. 232 233```java 234public void writeEnv(File file) throws IOException { 235 try (Sink fileSink = Okio.sink(file); 236 BufferedSink bufferedSink = Okio.buffer(fileSink)) { 237 238 for (Map.Entry<String, String> entry : System.getenv().entrySet()) { 239 bufferedSink.writeUtf8(entry.getKey()); 240 bufferedSink.writeUtf8("="); 241 bufferedSink.writeUtf8(entry.getValue()); 242 bufferedSink.writeUtf8("\n"); 243 } 244 245 } 246} 247``` 248 249There isn’t an API to write a line of input; instead we manually insert our own 250newline character. Most programs should hardcode `"\n"` as the newline 251character. In rare situations you may use `System.lineSeparator()` instead of 252`"\n"`: it returns `"\r\n"` on Windows and `"\n"` everywhere else. 253 254We can write the above program more compactly by inlining the `fileSink` 255variable and by taking advantage of method chaining: 256 257=== "Java" 258 259 ```Java 260 public void writeEnv(File file) throws IOException { 261 try (BufferedSink sink = Okio.buffer(Okio.sink(file))) { 262 for (Map.Entry<String, String> entry : System.getenv().entrySet()) { 263 sink.writeUtf8(entry.getKey()) 264 .writeUtf8("=") 265 .writeUtf8(entry.getValue()) 266 .writeUtf8("\n"); 267 } 268 } 269 } 270 ``` 271 272=== "Kotlin" 273 274 ```Kotlin 275 @Throws(IOException::class) 276 fun writeEnv(file: File) { 277 file.sink().buffer().use { sink -> 278 for ((key, value) in System.getenv()) { 279 sink.writeUtf8(key) 280 sink.writeUtf8("=") 281 sink.writeUtf8(value) 282 sink.writeUtf8("\n") 283 } 284 } 285 } 286 ``` 287 288In the above code we make four calls to `writeUtf8()`. Making four calls is 289more efficient than the code below because the VM doesn’t have to create and 290garbage collect a temporary string. 291 292```java 293sink.writeUtf8(entry.getKey() + "=" + entry.getValue() + "\n"); // Slower! 294``` 295 296### UTF-8 ([Java][ExploreCharsets]/[Kotlin][ExploreCharsetsKt]) 297 298In the above APIs you can see that Okio really likes UTF-8. Early computer 299systems suffered many incompatible character encodings: ISO-8859-1, ShiftJIS, 300ASCII, EBCDIC, etc. Writing software to support multiple character sets was 301awful and we didn’t even have emoji! Today we're lucky that the world has 302standardized on UTF-8 everywhere, with some rare uses of other charsets in 303legacy systems. 304 305If you need another character set, `readString()` and `writeString()` are there 306for you. These methods require that you specify a character set. Otherwise you 307may accidentally create data that is only readable by the local computer. Most 308programs should use the UTF-8 methods only. 309 310When encoding strings you need to be mindful of the different ways that strings 311are represented and encoded. When a glyph has an accent or another adornment 312it may be represented as a single complex code point (`é`) or as a simple code 313point (`e`) followed by its modifiers (`´`). When the entire glyph is a single 314code point that’s called [NFC][nfc]; when it’s multiple it’s [NFD][nfd]. 315 316Though we use UTF-8 whenever we read or write strings in I/O, when they are in 317memory Java Strings use an obsolete character encoding called UTF-16. It is a 318bad encoding because it uses a 16-bit `char` for most characters, but some don’t 319fit. In particular, most emoji use two Java chars. This is problematic because 320`String.length()` returns a surprising result: the number of UTF-16 chars and 321not the natural number of glyphs. 322 323| | Café | Café | 324| --------------------: | :---------------------------| :------------------------------| 325| Form | [NFC][nfc] | [NFD][nfd] | 326| Code Points | `c a f é ␣ ` | `c a f e ´ ␣ ` | 327| UTF-8 bytes | `43 61 66 c3a9 20 f09f8da9` | `43 61 66 65 cc81 20 f09f8da9` | 328| String.codePointCount | 6 | 7 | 329| String.length | 7 | 8 | 330| Utf8.size | 10 | 11 | 331 332For the most part Okio lets you ignore these problems and focus on your data. 333But when you need them, there are convenient APIs for dealing with low-level 334UTF-8 strings. 335 336Use `Utf8.size()` to count the number of bytes required to encode a string as 337UTF-8 without actually encoding it. This is handy in length-prefixed encodings 338like protocol buffers. 339 340Use `BufferedSource.readUtf8CodePoint()` to read a single variable-length code 341point, and `BufferedSink.writeUtf8CodePoint()` to write one. 342 343### Golden Values ([Java][GoldenValue]/[Kotlin][GoldenValueKt]) 344 345Okio likes testing. The library itself is heavily tested, and it has features 346that are often helpful when testing application code. One pattern we’ve found to 347be quite useful is “golden value” testing. The goal of such tests is to confirm 348that data encoded with earlier versions of a program can safely be decoded by 349the current program. 350 351We’ll illustrate this by encoding a value using Java Serialization. Though we 352must disclaim that Java Serialization is an awful encoding system and most 353programs should prefer other formats like JSON or protobuf! In any case, here’s 354a method that takes an object, serializes it, and returns the result as a 355`ByteString`: 356 357=== "Java" 358 359 ```Java 360 private ByteString serialize(Object o) throws IOException { 361 Buffer buffer = new Buffer(); 362 try (ObjectOutputStream objectOut = new ObjectOutputStream(buffer.outputStream())) { 363 objectOut.writeObject(o); 364 } 365 return buffer.readByteString(); 366 } 367 ``` 368 369=== "Kotlin" 370 371 ```Kotlin 372 @Throws(IOException::class) 373 private fun serialize(o: Any?): ByteString { 374 val buffer = Buffer() 375 ObjectOutputStream(buffer.outputStream()).use { objectOut -> 376 objectOut.writeObject(o) 377 } 378 return buffer.readByteString() 379 } 380 ``` 381 382There’s a lot going on here. 383 3841. We create a buffer as a holding space for our serialized data. It’s a convenient 385 replacement for `ByteArrayOutputStream`. 386 3872. We ask the buffer for its output stream. Writes to a buffer or its output stream 388 always append data to the end of the buffer. 389 3903. We create an `ObjectOutputStream` (the encoding API for Java serialization) and 391 write our object. The try block takes care of closing the stream for us. Note 392 that closing a buffer has no effect. 393 3944. Finally we read a byte string from the buffer. The `readByteString()` method 395 allows us to specify how many bytes to read; here we don’t specify a count in 396 order to read the entire thing. Reads from a buffer always consume data from 397 the front of the buffer. 398 399With our `serialize()` method handy we are ready to compute and print a golden 400value. 401 402=== "Java" 403 404 ```Java 405 Point point = new Point(8.0, 15.0); 406 ByteString pointBytes = serialize(point); 407 System.out.println(pointBytes.base64()); 408 ``` 409 410=== "Kotlin" 411 412 ```Kotlin 413 val point = Point(8.0, 15.0) 414 val pointBytes = serialize(point) 415 println(pointBytes.base64()) 416 ``` 417 418We print the `ByteString` as [base64][base64] because it’s a compact format 419that’s suitable for embedding in a test case. The program prints this: 420 421``` 422rO0ABXNyAB5va2lvLnNhbXBsZXMuR29sZGVuVmFsdWUkUG9pbnTdUW8rMji1IwIAAkQAAXhEAAF5eHBAIAAAAAAAAEAuAAAAAAAA 423``` 424 425That’s our golden value! We can embed it in our test case using base64 again 426to convert it back into a `ByteString`: 427 428=== "Java" 429 430 ```Java 431 ByteString goldenBytes = ByteString.decodeBase64("rO0ABXNyAB5va2lvLnNhbXBsZ" 432 + "XMuR29sZGVuVmFsdWUkUG9pbnTdUW8rMji1IwIAAkQAAXhEAAF5eHBAIAAAAAAAAEAuA" 433 + "AAAAAAA"); 434 ``` 435 436=== "Kotlin" 437 438 ```Kotlin 439 val goldenBytes = ("rO0ABXNyACRva2lvLnNhbXBsZXMuS290bGluR29sZGVuVmFsdWUkUG9pbnRF9yaY7cJ9EwIAA" + 440 "kQAAXhEAAF5eHBAIAAAAAAAAEAuAAAAAAAA").decodeBase64() 441 ``` 442 443The next step is to deserialize the `ByteString` back into our value class. This 444method reverses the `serialize()` method above: we append a byte string to a 445buffer then consume it using an `ObjectInputStream`: 446 447=== "Java" 448 449 ```Java 450 private Object deserialize(ByteString byteString) throws IOException, ClassNotFoundException { 451 Buffer buffer = new Buffer(); 452 buffer.write(byteString); 453 try (ObjectInputStream objectIn = new ObjectInputStream(buffer.inputStream())) { 454 return objectIn.readObject(); 455 } 456 } 457 ``` 458 459=== "Kotlin" 460 461 ```Kotlin 462 @Throws(IOException::class, ClassNotFoundException::class) 463 private fun deserialize(byteString: ByteString): Any? { 464 val buffer = Buffer() 465 buffer.write(byteString) 466 ObjectInputStream(buffer.inputStream()).use { objectIn -> 467 return objectIn.readObject() 468 } 469 } 470 ``` 471 472Now we can test the decoder against the golden value: 473 474=== "Java" 475 476 ```Java 477 ByteString goldenBytes = ByteString.decodeBase64("rO0ABXNyAB5va2lvLnNhbXBsZ" 478 + "XMuR29sZGVuVmFsdWUkUG9pbnTdUW8rMji1IwIAAkQAAXhEAAF5eHBAIAAAAAAAAEAuA" 479 + "AAAAAAA"); 480 Point decoded = (Point) deserialize(goldenBytes); 481 assertEquals(new Point(8.0, 15.0), decoded); 482 ``` 483 484=== "Kotlin" 485 486 ```Kotlin 487 val goldenBytes = ("rO0ABXNyACRva2lvLnNhbXBsZXMuS290bGluR29sZGVuVmFsdWUkUG9pbnRF9yaY7cJ9EwIAA" + 488 "kQAAXhEAAF5eHBAIAAAAAAAAEAuAAAAAAAA").decodeBase64()!! 489 val decoded = deserialize(goldenBytes) as Point 490 assertEquals(point, decoded) 491 ``` 492 493With this test we can change the serialization of the `Point` class without 494breaking compatibility. 495 496 497### Write a binary file ([Java][BitmapEncoder]/[Kotlin][BitmapEncoderKt]) 498 499Encoding a binary file is not unlike encoding a text file. Okio uses the same 500`BufferedSink` and `BufferedSource` bytes for both. This is handy for binary 501formats that include both byte and character data. 502 503Writing binary data is more hazardous than text because if you make a mistake it 504is often quite difficult to diagnose. Avoid such mistakes by being careful 505around these traps: 506 507 * **The width of each field.** This is the number of bytes used. Okio doesn't 508 include a mechanism to emit partial bytes. If you need that, you’ll need to 509 do your own bit shifting and masking before writing. 510 511 * **The endianness of each field.** All fields that have more than one byte 512 have _endianness_: whether the bytes are ordered most-significant to least 513 (big endian) or least-significant to most (little endian). Okio uses the `Le` 514 suffix for little-endian methods; methods without a suffix are big-endian. 515 516 * **Signed vs. Unsigned.** Java doesn’t have unsigned primitive types (except 517 for `char`!) so coping with this is often something that happens at the 518 application layer. To make this a little easier Okio accepts `int` types for 519 `writeByte()` and `writeShort()`. You can pass an “unsigned” byte like 255 520 and Okio will do the right thing. 521 522| Method | Width | Endianness | Value | Encoded Value | 523| :----------- | ----: | :--------- | --------------: | :------------------------ | 524| writeByte | 1 | | 3 | `03` | 525| writeShort | 2 | big | 3 | `00 03` | 526| writeInt | 4 | big | 3 | `00 00 00 03` | 527| writeLong | 8 | big | 3 | `00 00 00 00 00 00 00 03` | 528| writeShortLe | 2 | little | 3 | `03 00` | 529| writeIntLe | 4 | little | 3 | `03 00 00 00` | 530| writeLongLe | 8 | little | 3 | `03 00 00 00 00 00 00 00` | 531| writeByte | 1 | | Byte.MAX_VALUE | `7f` | 532| writeShort | 2 | big | Short.MAX_VALUE | `7f ff` | 533| writeInt | 4 | big | Int.MAX_VALUE | `7f ff ff ff` | 534| writeLong | 8 | big | Long.MAX_VALUE | `7f ff ff ff ff ff ff ff` | 535| writeShortLe | 2 | little | Short.MAX_VALUE | `ff 7f` | 536| writeIntLe | 4 | little | Int.MAX_VALUE | `ff ff ff 7f` | 537| writeLongLe | 8 | little | Long.MAX_VALUE | `ff ff ff ff ff ff ff 7f` | 538 539This code encodes a bitmap following the [BMP file format][bmp]. 540 541=== "Java" 542 543 ```Java 544 void encode(Bitmap bitmap, BufferedSink sink) throws IOException { 545 int height = bitmap.height(); 546 int width = bitmap.width(); 547 548 int bytesPerPixel = 3; 549 int rowByteCountWithoutPadding = (bytesPerPixel * width); 550 int rowByteCount = ((rowByteCountWithoutPadding + 3) / 4) * 4; 551 int pixelDataSize = rowByteCount * height; 552 int bmpHeaderSize = 14; 553 int dibHeaderSize = 40; 554 555 // BMP Header 556 sink.writeUtf8("BM"); // ID. 557 sink.writeIntLe(bmpHeaderSize + dibHeaderSize + pixelDataSize); // File size. 558 sink.writeShortLe(0); // Unused. 559 sink.writeShortLe(0); // Unused. 560 sink.writeIntLe(bmpHeaderSize + dibHeaderSize); // Offset of pixel data. 561 562 // DIB Header 563 sink.writeIntLe(dibHeaderSize); 564 sink.writeIntLe(width); 565 sink.writeIntLe(height); 566 sink.writeShortLe(1); // Color plane count. 567 sink.writeShortLe(bytesPerPixel * Byte.SIZE); 568 sink.writeIntLe(0); // No compression. 569 sink.writeIntLe(16); // Size of bitmap data including padding. 570 sink.writeIntLe(2835); // Horizontal print resolution in pixels/meter. (72 dpi). 571 sink.writeIntLe(2835); // Vertical print resolution in pixels/meter. (72 dpi). 572 sink.writeIntLe(0); // Palette color count. 573 sink.writeIntLe(0); // 0 important colors. 574 575 // Pixel data. 576 for (int y = height - 1; y >= 0; y--) { 577 for (int x = 0; x < width; x++) { 578 sink.writeByte(bitmap.blue(x, y)); 579 sink.writeByte(bitmap.green(x, y)); 580 sink.writeByte(bitmap.red(x, y)); 581 } 582 583 // Padding for 4-byte alignment. 584 for (int p = rowByteCountWithoutPadding; p < rowByteCount; p++) { 585 sink.writeByte(0); 586 } 587 } 588 } 589 ``` 590 591=== "Kotlin" 592 593 ```Kotlin 594 @Throws(IOException::class) 595 fun encode(bitmap: Bitmap, sink: BufferedSink) { 596 val height = bitmap.height 597 val width = bitmap.width 598 val bytesPerPixel = 3 599 val rowByteCountWithoutPadding = bytesPerPixel * width 600 val rowByteCount = (rowByteCountWithoutPadding + 3) / 4 * 4 601 val pixelDataSize = rowByteCount * height 602 val bmpHeaderSize = 14 603 val dibHeaderSize = 40 604 605 // BMP Header 606 sink.writeUtf8("BM") // ID. 607 sink.writeIntLe(bmpHeaderSize + dibHeaderSize + pixelDataSize) // File size. 608 sink.writeShortLe(0) // Unused. 609 sink.writeShortLe(0) // Unused. 610 sink.writeIntLe(bmpHeaderSize + dibHeaderSize) // Offset of pixel data. 611 612 // DIB Header 613 sink.writeIntLe(dibHeaderSize) 614 sink.writeIntLe(width) 615 sink.writeIntLe(height) 616 sink.writeShortLe(1) // Color plane count. 617 sink.writeShortLe(bytesPerPixel * Byte.SIZE_BITS) 618 sink.writeIntLe(0) // No compression. 619 sink.writeIntLe(16) // Size of bitmap data including padding. 620 sink.writeIntLe(2835) // Horizontal print resolution in pixels/meter. (72 dpi). 621 sink.writeIntLe(2835) // Vertical print resolution in pixels/meter. (72 dpi). 622 sink.writeIntLe(0) // Palette color count. 623 sink.writeIntLe(0) // 0 important colors. 624 625 // Pixel data. 626 for (y in height - 1 downTo 0) { 627 for (x in 0 until width) { 628 sink.writeByte(bitmap.blue(x, y)) 629 sink.writeByte(bitmap.green(x, y)) 630 sink.writeByte(bitmap.red(x, y)) 631 } 632 633 // Padding for 4-byte alignment. 634 for (p in rowByteCountWithoutPadding until rowByteCount) { 635 sink.writeByte(0) 636 } 637 } 638 } 639 ``` 640 641The trickiest part of this program is the format’s required padding. The BMP 642format expects each row to begin on a 4-byte boundary so it is necessary to add 643zeros to maintain the alignment. 644 645Encoding other binary formats is usually quite similar. Some tips: 646 647 * Write tests with golden values! Confirming that your program emits the 648 expected result can make debugging easier. 649 * Use `Utf8.size()` to compute the number of bytes of an encoded string. This 650 is essential for length-prefixed formats. 651 * Use `Float.floatToIntBits()` and `Double.doubleToLongBits()` to encode 652 floating point values. 653 654 655### Communicate on a Socket ([Java][SocksProxyServer]/[Kotlin][SocksProxyServerKt]) 656 657Sending and receiving data over the network is a bit like writing and reading 658files. We use `BufferedSink` to encode output and `BufferedSource` to decode 659input. Like files, network protocols can be text, binary, or a mix of both. But 660there are also some substantial differences between the network and the 661file system. 662 663With a file you’re either reading or writing but with the network you can do 664both! Some protocols handle this by taking turns: write a request, read a 665response, repeat. You can implement this kind of protocol with a single thread. 666In other protocols you may read and write simultaneously. Typically you’ll want 667one dedicated thread for reading. For writing you can use either a dedicated 668thread or use `synchronized` so that multiple threads can share a sink. Okio’s 669streams are not safe for concurrent use. 670 671Sinks buffer outbound data to minimize I/O operations. This is efficient but it 672means you must manually call `flush()` to transmit data. Typically 673message-oriented protocols flush after each message. Note that Okio will 674automatically flush when the buffered data exceeds some threshold. This is 675intended to save memory and you shouldn’t rely on it for interactive protocols. 676 677Okio builds on `java.io.Socket` for connectivity. Create your socket as a server 678or as a client, then use `Okio.source(Socket)` to read and `Okio.sink(Socket)` 679to write. These APIs also work with `SSLSocket`. You should use SSL unless you 680have a very good reason not to! 681 682Cancel a socket from any thread by calling `Socket.close()`; this will cause its 683sources and sinks to immediately fail with an `IOException`. You can also 684configure timeouts for all socket operations. You don’t need a reference to the 685socket to adjust timeouts: `Source` and `Sink` expose timeouts directly. This 686API works even if the streams are decorated. 687 688As a complete example of networking with Okio we wrote a [basic SOCKS 689proxy][SocksProxyServer] server. Some highlights: 690 691=== "Java" 692 693 ```Java 694 Socket fromSocket = ... 695 BufferedSource fromSource = Okio.buffer(Okio.source(fromSocket)); 696 BufferedSink fromSink = Okio.buffer(Okio.sink(fromSocket)); 697 ``` 698 699=== "Kotlin" 700 701 ```Kotlin 702 val fromSocket: Socket = ... 703 val fromSource = fromSocket.source().buffer() 704 val fromSink = fromSocket.sink().buffer() 705 ``` 706 707Creating sources and sinks for sockets is the same as creating them for files. 708Once you create a `Source` or `Sink` for a socket you must not use its 709`InputStream` or `OutputStream`, respectively. 710 711=== "Java" 712 713 ```Java 714 Buffer buffer = new Buffer(); 715 for (long byteCount; (byteCount = source.read(buffer, 8192L)) != -1; ) { 716 sink.write(buffer, byteCount); 717 sink.flush(); 718 } 719 ``` 720 721=== "Kotlin" 722 723 ```Kotlin 724 val buffer = Buffer() 725 var byteCount: Long 726 while (source.read(buffer, 8192L).also { byteCount = it } != -1L) { 727 sink.write(buffer, byteCount) 728 sink.flush() 729 } 730 ``` 731 732The above loop copies data from the source to the sink, flushing after each 733read. If we didn’t need the flushing we could replace this loop with a single 734call to `BufferedSink.writeAll(Source)`. 735 736The `8192` argument to `read()` is the maximum number of bytes to read before 737returning. We could have passed any value here, but we like 8 KiB because that’s 738the largest value Okio can do in a single system call. Most of the time 739application code doesn’t need to deal with such limits! 740 741=== "Java" 742 743 ```Java 744 int addressType = fromSource.readByte() & 0xff; 745 int port = fromSource.readShort() & 0xffff; 746 ``` 747 748=== "Kotlin" 749 750 ```Kotlin 751 val addressType = fromSource.readByte().toInt() and 0xff 752 val port = fromSource.readShort().toInt() and 0xffff 753 ``` 754 755Okio uses signed types like `byte` and `short`, but often protocols want 756unsigned values. The bitwise `&` operator is Java’s preferred idiom to convert 757a signed value into an unsigned value. Here’s a cheat sheet for bytes, shorts, 758and ints: 759 760| Type | Signed Range | Unsigned Range | Signed to Unsigned | 761| :---- | :---------------------------: | :--------------- | :-------------------------- | 762| byte | -128..127 | 0..255 | `int u = s & 0xff;` | 763| short | -32,768..32,767 | 0..65,535 | `int u = s & 0xffff;` | 764| int | -2,147,483,648..2,147,483,647 | 0..4,294,967,295 | `long u = s & 0xffffffffL;` | 765 766Java has no primitive type that can represent unsigned longs. 767 768 769### Hashing ([Java][Hashing]/[Kotlin][HashingKt]) 770 771We’re bombarded by hashing in our lives as Java programmers. Early on we're introduced to the 772`hashCode()` method, something we know we need to override otherwise unforeseen bad things happen. 773Later we’re shown `LinkedHashMap` and its friends. These build on that `hashCode()` method to 774organize data for fast retrieval. 775 776Elsewhere we have cryptographic hash functions. These get used all over the place. HTTPS 777certificates, Git commits, BitTorrent integrity checking, and Blockchain blocks all use 778cryptographic hashes. Good use of hashes can improve the performance, privacy, security, and 779simplicity of an application. 780 781Each cryptographic hash function accepts a variable-length stream of input bytes and produces a 782fixed-length byte string value called the “hash”. Hash functions have these important qualities: 783 784 * Deterministic: each input always produces the same output. 785 * Uniform: each output byte string is equally likely. It is very difficult to find or create pairs 786 of different inputs that yield the same output. This is called a “collision”. 787 * Non-reversible: knowing an output doesn't help you to find the input. Note that if you know some 788 possible inputs you can hash them to see if their hashes match. 789 * Well-known: the hash is implemented everywhere and rigorously understood. 790 791Good hash functions are very cheap to compute (dozens of microseconds) and expensive to reverse 792(quintillions of millenia). Steady advances in computing and mathematics have caused once-great hash 793functions to become inexpensive to reverse. When choosing a hash function, beware that not all are 794created equal! Okio supports these well-known cryptographic hash functions: 795 796 * **MD5**: a 128-bit (16 byte) cryptographic hash. It is both insecure and obsolete because it is 797 inexpensive to reverse! This hash is offered because it is popular and convenient for use in 798 legacy systems that are not security-sensitive. 799 * **SHA-1**: a 160-bit (20 byte) cryptographic hash. It was recently demonstrated that it is 800 feasible to create SHA-1 collisions. Consider upgrading from SHA-1 to SHA-256. 801 * **SHA-256**: a 256-bit (32 byte) cryptographic hash. SHA-256 is widely understood and expensive 802 to reverse. This is the hash most systems should use. 803 * **SHA-512**: a 512-bit (64 byte) cryptographic hash. It is expensive to reverse. 804 805Each hash creates a `ByteString` of the specified length. Use `hex()` to get the conventional 806human-readable form. Or leave it as a `ByteString` because that’s a convenient model type! 807 808Okio can produce cryptographic hashes from byte strings: 809 810=== "Java" 811 812 ```Java 813 ByteString byteString = readByteString(new File("README.md")); 814 System.out.println(" md5: " + byteString.md5().hex()); 815 System.out.println(" sha1: " + byteString.sha1().hex()); 816 System.out.println("sha256: " + byteString.sha256().hex()); 817 System.out.println("sha512: " + byteString.sha512().hex()); 818 ``` 819 820=== "Kotlin" 821 822 ```Kotlin 823 val byteString = readByteString(File("README.md")) 824 println(" md5: " + byteString.md5().hex()) 825 println(" sha1: " + byteString.sha1().hex()) 826 println(" sha256: " + byteString.sha256().hex()) 827 println(" sha512: " + byteString.sha512().hex()) 828 ``` 829 830From buffers: 831 832=== "Java" 833 834 ```Java 835 Buffer buffer = readBuffer(new File("README.md")); 836 System.out.println(" md5: " + buffer.md5().hex()); 837 System.out.println(" sha1: " + buffer.sha1().hex()); 838 System.out.println("sha256: " + buffer.sha256().hex()); 839 System.out.println("sha512: " + buffer.sha512().hex()); 840 ``` 841 842=== "Kotlin" 843 844 ```Kotlin 845 val buffer = readBuffer(File("README.md")) 846 println(" md5: " + buffer.md5().hex()) 847 println(" sha1: " + buffer.sha1().hex()) 848 println(" sha256: " + buffer.sha256().hex()) 849 println(" sha512: " + buffer.sha512().hex()) 850 ``` 851 852While streaming from a source: 853 854=== "Java" 855 856 ```Java 857 try (HashingSink hashingSink = HashingSink.sha256(Okio.blackhole()); 858 BufferedSource source = Okio.buffer(Okio.source(file))) { 859 source.readAll(hashingSink); 860 System.out.println("sha256: " + hashingSink.hash().hex()); 861 } 862 ``` 863 864=== "Kotlin" 865 866 ```Kotlin 867 sha256(blackholeSink()).use { hashingSink -> 868 file.source().buffer().use { source -> 869 source.readAll(hashingSink) 870 println(" sha256: " + hashingSink.hash.hex()) 871 } 872 } 873 ``` 874 875While streaming to a sink: 876 877=== "Java" 878 879 ```Java 880 try (HashingSink hashingSink = HashingSink.sha256(Okio.blackhole()); 881 BufferedSink sink = Okio.buffer(hashingSink); 882 Source source = Okio.source(file)) { 883 sink.writeAll(source); 884 sink.close(); // Emit anything buffered. 885 System.out.println("sha256: " + hashingSink.hash().hex()); 886 } 887 ``` 888 889=== "Kotlin" 890 891 ```Kotlin 892 sha256(blackholeSink()).use { hashingSink -> 893 hashingSink.buffer().use { sink -> 894 file.source().use { source -> 895 sink.writeAll(source) 896 sink.close() // Emit anything buffered. 897 println(" sha256: " + hashingSink.hash.hex()) 898 } 899 } 900 } 901 ``` 902 903Okio also supports HMAC (Hash Message Authentication Code) which combines a secret and a hash. 904Applications use HMAC for data integrity and authentication. 905 906=== "Java" 907 908 ```Java 909 ByteString secret = ByteString.decodeHex("7065616e7574627574746572"); 910 System.out.println("hmacSha256: " + byteString.hmacSha256(secret).hex()); 911 ``` 912 913=== "Kotlin" 914 915 ```Kotlin 916 val secret = "7065616e7574627574746572".decodeHex() 917 println("hmacSha256: " + byteString.hmacSha256(secret).hex()) 918 ``` 919 920As with hashing, you can generate an HMAC from a `ByteString`, `Buffer`, `HashingSource`, and 921`HashingSink`. Note that Okio doesn’t implement HMAC for MD5. Okio uses Java’s 922`java.security.MessageDigest` for cryptographic hashes and `javax.crypto.Mac` for HMAC. 923 924### Encryption and Decryption 925 926Use `Okio.cipherSink(Sink, Cipher)` or `Okio.cipherSource(Source, Cipher)` to encrypt or decrypt a 927stream using a block cipher. 928 929Callers are responsible for the initialization of the encryption or decryption cipher with the 930chosen algorithm, the key, and algorithm-specific additional parameters like the initialization 931vector. The following example shows a typical usage with AES encryption, in which `key` and `iv` 932parameters should both be 16 bytes long. 933 934```java 935void encryptAes(ByteString bytes, File file, byte[] key, byte[] iv) 936 throws GeneralSecurityException, IOException { 937 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 938 cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv)); 939 try (BufferedSink sink = Okio.buffer(Okio.cipherSink(Okio.sink(file), cipher))) { 940 sink.write(bytes); 941 } 942} 943 944ByteString decryptAesToByteString(File file, byte[] key, byte[] iv) 945 throws GeneralSecurityException, IOException { 946 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 947 cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv)); 948 try (BufferedSource source = Okio.buffer(Okio.cipherSource(Okio.source(file), cipher))) { 949 return source.readByteString(); 950 } 951} 952``` 953 954In Kotlin, these encryption and decryption methods are extensions on `Cipher`: 955 956```kotlin 957fun encryptAes(bytes: ByteString, file: File, key: ByteArray, iv: ByteArray) { 958 val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") 959 cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv)) 960 val cipherSink = file.sink().cipherSink(cipher) 961 cipherSink.buffer().use { 962 it.write(bytes) 963 } 964} 965 966fun decryptAesToByteString(file: File, key: ByteArray, iv: ByteArray): ByteString { 967 val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") 968 cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv)) 969 val cipherSource = file.source().cipherSource(cipher) 970 return cipherSource.buffer().use { 971 it.readByteString() 972 } 973} 974``` 975 976File System Examples 977-------------------- 978 979Okio's recently gained a multiplatform file system API. These examples work on JVM, native, and 980Node.js platforms. In the examples below `fileSystem` is an instance of [FileSystem] such as 981`FileSystem.SYSTEM` or `FakeFileSystem`. 982 983Read all of `readme.md` as a string: 984 985``` 986val path = "readme.md".toPath() 987val entireFileString = fileSystem.read(path) { 988 readUtf8() 989} 990``` 991 992Read all of `thumbnail.png` as a [ByteString][3]: 993 994``` 995val path = "thumbnail.png".toPath() 996val entireFileByteString = fileSystem.read(path) { 997 readByteString() 998} 999``` 1000 1001Read all lines of `/etc/hosts` into a `List<String>`: 1002 1003``` 1004val path = "/etc/hosts".toPath() 1005val allLines = fileSystem.read(path) { 1006 generateSequence { readUtf8Line() }.toList() 1007} 1008``` 1009 1010Read the prefix of `index.html` that precedes the first `<html>` substring: 1011 1012``` 1013val path = "index.html".toPath() 1014val untilHtmlTag = fileSystem.read(path) { 1015 val htmlTag = indexOf("<html>".encodeUtf8()) 1016 if (htmlTag != -1L) readUtf8(htmlTag) else null 1017} 1018``` 1019 1020Write `readme.md` as a string: 1021 1022``` 1023val path = "readme.md".toPath() 1024fileSystem.write(path) { 1025 writeUtf8( 1026 """ 1027 |Hello, World 1028 |------------ 1029 | 1030 |This is a sample file. 1031 |""".trimMargin() 1032 ) 1033} 1034``` 1035 1036Write `data.bin` as a [ByteString][3]: 1037 1038``` 1039val path = "data.bin".toPath() 1040fileSystem.write(path) { 1041 val byteString = "68656c6c6f20776f726c640a".decodeHex() 1042 write(byteString) 1043} 1044``` 1045 1046Write `readme.md` from a `List<String>`: 1047 1048``` 1049val path = "readme.md".toPath() 1050val lines = listOf( 1051 "Hello, World", 1052 "------------", 1053 "", 1054 "This is a sample file.", 1055 "" 1056) 1057fileSystem.write(path) { 1058 for (line in lines) { 1059 writeUtf8(line) 1060 writeUtf8("\n") 1061 } 1062} 1063``` 1064 1065Generate `binary.txt` programmatically: 1066 1067``` 1068val path = "binary.txt".toPath() 1069fileSystem.write(path) { 1070 for (i in 1 until 100) { 1071 writeUtf8("$i ${i.toString(2)}") 1072 writeUtf8("\n") 1073 } 1074} 1075``` 1076 1077 1078Releases 1079-------- 1080 1081Our [change log][changelog] has release history. 1082 1083```kotlin 1084implementation("com.squareup.okio:okio:2.10.0") 1085``` 1086 1087<details> 1088 <summary>Snapshot builds are also available</summary> 1089 1090```kotlin 1091repositories { 1092 maven { 1093 url = uri("https://oss.sonatype.org/content/repositories/snapshots/") 1094 } 1095} 1096 1097dependencies { 1098 implementation("com.squareup.okio:okio:2.10.0") 1099} 1100``` 1101 1102</details> 1103 1104 1105R8 / ProGuard 1106-------- 1107 1108If you are using R8 or ProGuard add the options from [this file][proguard]. 1109 1110 1111License 1112-------- 1113 1114 Copyright 2013 Square, Inc. 1115 1116 Licensed under the Apache License, Version 2.0 (the "License"); 1117 you may not use this file except in compliance with the License. 1118 You may obtain a copy of the License at 1119 1120 http://www.apache.org/licenses/LICENSE-2.0 1121 1122 Unless required by applicable law or agreed to in writing, software 1123 distributed under the License is distributed on an "AS IS" BASIS, 1124 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1125 See the License for the specific language governing permissions and 1126 limitations under the License. 1127 1128 [1]: https://github.com/square/okhttp 1129 [3]: https://square.github.io/okio/2.x/okio/okio/-byte-string/index.html 1130 [4]: https://square.github.io/okio/2.x/okio/okio/-buffer/index.html 1131 [5]: https://square.github.io/okio/2.x/okio/okio/-source/index.html 1132 [6]: https://square.github.io/okio/2.x/okio/okio/-sink/index.html 1133 [7]: https://square.github.io/okio/2.x/okio/okio/-buffered-source/index.html 1134 [8]: https://square.github.io/okio/2.x/okio/okio/-buffered-sink/index.html 1135 [changelog]: http://square.github.io/okio/changelog/ 1136 [javadoc]: https://square.github.io/okio/2.x/okio/okio/index.html 1137 [nfd]: https://docs.oracle.com/javase/7/docs/api/java/text/Normalizer.Form.html#NFD 1138 [nfc]: https://docs.oracle.com/javase/7/docs/api/java/text/Normalizer.Form.html#NFC 1139 [base64]: https://tools.ietf.org/html/rfc4648#section-4 1140 [bmp]: https://en.wikipedia.org/wiki/BMP_file_format 1141 [kotlin]: https://kotlinlang.org/ 1142 [ok_libraries_talk]: https://www.youtube.com/watch?v=WvyScM_S88c 1143 [ok_libraries_slides]: https://speakerdeck.com/jakewharton/a-few-ok-libraries-droidcon-mtl-2015 1144 [encoding_talk]: https://www.youtube.com/watch?v=T_p22jMZSrk 1145 [encoding_slides]: https://speakerdeck.com/swankjesse/decoding-the-secrets-of-binary-data-droidcon-nyc-2016 1146 [ok_multiplatform_talk]: https://www.youtube.com/watch?v=Q8B4eDirgk0 1147 [ok_multiplatform_slides]: https://speakerdeck.com/swankjesse/ok-multiplatform 1148 [ReadFileLineByLine]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/ReadFileLineByLine.java 1149 [ReadFileLineByLineKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/ReadFileLineByLine.kt 1150 [WriteFile]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/WriteFile.java 1151 [WriteFileKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/WriteFile.kt 1152 [ExploreCharsets]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/ExploreCharsets.java 1153 [ExploreCharsetsKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/ExploreCharsets.kt 1154 [FileSystem]: https://square.github.io/okio/2.x/okio/okio/-file-system/index.html 1155 [GoldenValue]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/GoldenValue.java 1156 [GoldenValueKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/GoldenValue.kt 1157 [BitmapEncoder]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/BitmapEncoder.java 1158 [BitmapEncoderKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/BitmapEncoder.kt 1159 [SocksProxyServer]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/SocksProxyServer.java 1160 [SocksProxyServerKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/SocksProxyServer.kt 1161 [Hashing]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/Hashing.java 1162 [HashingKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/Hashing.kt 1163 [proguard]: https://github.com/square/okio/blob/master/okio/src/jvmMain/resources/META-INF/proguard/okio.pro 1164