xref: /aosp_15_r20/external/okio/docs/recipes.md (revision f9742813c14b702d71392179818a9e591da8620c)
1Recipes
2=======
3
4We've written some recipes that demonstrate how to solve common problems with Okio. Read through
5them to learn about how everything works together. Cut-and-paste these examples freely; that's what
6they're for.
7
8These recipes work on all platforms: Java, Android, Kotlin/Native, and Kotlin/JS. See
9[java.io Recipes](java_io_recipes.md) for samples that integrate Java APIs.
10
11
12Read a text file line-by-line ([Java][ReadFileLineByLine]/[Kotlin][ReadFileLineByLineKt])
13-----------------------------------------------------------------------------------------
14
15Use `FileSystem.source(Path)` to open a source stream to read a file. The returned `Source`
16interface is very small and has limited uses. Instead we wrap the source with a buffer. This has two
17benefits:
18
19 * **It makes the API more powerful.** Instead of the basic methods offered by `Source`,
20   `BufferedSource` has dozens of methods to address most common problems concisely.
21
22 * **It makes your program run faster.** Buffering allows Okio to get more done with fewer I/O
23   operations.
24
25Each `Source` that is opened needs to be closed. The code that opens the stream is responsible for
26making sure it is closed.
27
28=== "Java"
29
30    Here we use Java's `try` blocks to close our sources automatically.
31
32    ```java
33    public void readLines(Path path) throws IOException {
34      try (Source fileSource = FileSystem.SYSTEM.source(path);
35           BufferedSource bufferedFileSource = Okio.buffer(fileSource)) {
36
37        while (true) {
38          String line = bufferedFileSource.readUtf8Line();
39          if (line == null) break;
40
41          if (line.contains("square")) {
42            System.out.println(line);
43          }
44        }
45
46      }
47    }
48    ```
49
50=== "Kotlin"
51
52    This uses `use` to automatically close the streams. This prevents resource leaks, even if an
53    exception is thrown.
54
55    ```kotlin
56    fun readLines(path: Path) {
57      FileSystem.SYSTEM.source(path).use { fileSource ->
58        fileSource.buffer().use { bufferedFileSource ->
59          while (true) {
60            val line = bufferedFileSource.readUtf8Line() ?: break
61            if ("square" in line) {
62              println(line)
63            }
64          }
65        }
66      }
67    }
68    ```
69
70
71The `readUtf8Line()` API reads all of the data until the next line delimiter – either `\n`, `\r\n`,
72or the end of the file. It returns that data as a string, omitting the delimiter at the end. When it
73encounters empty lines the method will return an empty string. If there isn’t any more data to read
74it will return null.
75
76
77=== "Java"
78
79    The above Java program can be written more compactly by inlining the `fileSource` variable and
80    by using a fancy `for` loop instead of a `while`:
81
82    ```java
83    public void readLines(Path path) throws IOException {
84      try (BufferedSource source = Okio.buffer(FileSystem.SYSTEM.source(path))) {
85        for (String line; (line = source.readUtf8Line()) != null; ) {
86          if (line.contains("square")) {
87            System.out.println(line);
88          }
89        }
90      }
91    }
92    ```
93
94=== "Kotlin"
95
96    In Kotlin, we can use `FileSystem.read()` to buffer the source before our block and close the
97    source afterwards. In the body of the block, `this` is a `BufferedSource`.
98
99    ```kotlin
100    @Throws(IOException::class)
101    fun readLines(path: Path) {
102      FileSystem.SYSTEM.read(path) {
103        while (true) {
104          val line = readUtf8Line() ?: break
105          if ("square" in line) {
106            println(line)
107          }
108        }
109      }
110    }
111    ```
112
113The `readUtf8Line()` method is suitable for parsing most files. For certain use-cases you may also
114consider `readUtf8LineStrict()`. It is similar but it requires that each line is terminated by `\n`
115or `\r\n`. If it encounters the end of the file before that it will throw an `EOFException`. The
116strict variant also permits a byte limit to defend against malformed input.
117
118=== "Java"
119
120    ```java
121    public void readLines(Path path) throws IOException {
122      try (BufferedSource source = Okio.buffer(FileSystem.SYSTEM.source(path))) {
123        while (!source.exhausted()) {
124          String line = source.readUtf8LineStrict(1024L);
125          if (line.contains("square")) {
126            System.out.println(line);
127          }
128        }
129      }
130    }
131    ```
132
133=== "Kotlin"
134
135    ```kotlin
136    @Throws(IOException::class)
137    fun readLines(path: Path) {
138      FileSystem.SYSTEM.read(path) {
139        while (!source.exhausted()) {
140          val line = source.readUtf8LineStrict(1024)
141          if ("square" in line) {
142            println(line)
143          }
144        }
145      }
146    }
147    ```
148
149
150Write a text file ([Java][WriteFile]/[Kotlin][WriteFileKt])
151-----------------------------------------------------------
152
153Above we used a `Source` and a `BufferedSource` to read a file. To write, we use a `Sink` and a
154`BufferedSink`. The advantages of buffering are the same: a more capable API and better performance.
155
156```java
157public void writeEnv(Path path) throws IOException {
158  try (Sink fileSink = FileSystem.SYSTEM.sink(path);
159       BufferedSink bufferedSink = Okio.buffer(fileSink)) {
160
161    for (Map.Entry<String, String> entry : System.getenv().entrySet()) {
162      bufferedSink.writeUtf8(entry.getKey());
163      bufferedSink.writeUtf8("=");
164      bufferedSink.writeUtf8(entry.getValue());
165      bufferedSink.writeUtf8("\n");
166    }
167
168  }
169}
170```
171
172There isn’t an API to write a line of input; instead we manually insert our own newline character.
173Most programs should hardcode `"\n"` as the newline character. In rare situations you may use
174`System.lineSeparator()` instead of `"\n"`: it returns `"\r\n"` on Windows and `"\n"` everywhere
175else.
176
177=== "Java"
178
179    We can write the above program more compactly by inlining the `fileSink` variable and by taking
180    advantage of method chaining:
181
182    ```java
183    public void writeEnv(Path path) throws IOException {
184      try (BufferedSink sink = Okio.buffer(FileSystem.SYSTEM.sink(path))) {
185        for (Map.Entry<String, String> entry : System.getenv().entrySet()) {
186          sink.writeUtf8(entry.getKey())
187            .writeUtf8("=")
188            .writeUtf8(entry.getValue())
189            .writeUtf8("\n");
190        }
191      }
192    }
193    ```
194
195=== "Kotlin"
196
197    In Kotlin, we can use `FileSystem.write()` to buffer the sink before our block and close the
198    sink afterwards. In the body of the block, `this` is a `BufferedSink`.
199
200    ```kotlin
201    @Throws(IOException::class)
202    fun writeEnv(path: Path) {
203      FileSystem.SYSTEM.write(path) {
204        for ((key, value) in System.getenv()) {
205          writeUtf8(key)
206          writeUtf8("=")
207          writeUtf8(value)
208          writeUtf8("\n")
209        }
210      }
211    }
212    ```
213
214In the above code we make four calls to `writeUtf8()`. Making four calls is more efficient than the
215code below because the VM doesn’t have to create and garbage collect a temporary string.
216
217```java
218sink.writeUtf8(entry.getKey() + "=" + entry.getValue() + "\n"); // Slower!
219```
220
221
222UTF-8 ([Java][ExploreCharsets]/[Kotlin][ExploreCharsetsKt])
223-----------------------------------------------------------
224
225In the above APIs you can see that Okio really likes UTF-8. Early computer systems suffered many
226incompatible character encodings: ISO-8859-1, ShiftJIS, ASCII, EBCDIC, etc. Writing software to
227support multiple character sets was awful and we didn’t even have emoji! Today we're lucky that the
228world has standardized on UTF-8 everywhere, with some rare uses of other charsets in legacy systems.
229
230If you need another character set, `readString()` and `writeString()` are there for you. These
231 methods require that you specify a character set. Otherwise you may accidentally create data that
232 is only readable by the local computer. Most programs should use the UTF-8 methods only.
233
234When encoding strings you need to be mindful of the different ways that strings are represented and
235encoded. When a glyph has an accent or another adornment it may be represented as a single complex
236 code point (`é`) or as a simple code point (`e`) followed by its modifiers (`´`). When the entire
237 glyph is a single code point that’s called [NFC][nfc]; when it’s multiple it’s [NFD][nfd].
238
239Though we use UTF-8 whenever we read or write strings in I/O, when they are in memory Java Strings
240use an obsolete character encoding called UTF-16. It is a bad encoding because it uses a 16-bit
241`char` for most characters, but some don’t fit. In particular, most emoji use two Java chars. This
242is problematic because `String.length()` returns a surprising result: the number of UTF-16 chars and
243not the natural number of glyphs.
244
245|                       | Café ��                     | Café ��                        |
246| --------------------: | :---------------------------| :------------------------------|
247|                  Form | [NFC][nfc]                  | [NFD][nfd]                     |
248|           Code Points | `c  a  f  é    ␣   ��     ` | `c  a  f  e  ´    ␣   ��     ` |
249|           UTF-8 bytes | `43 61 66 c3a9 20 f09f8da9` | `43 61 66 65 cc81 20 f09f8da9` |
250| String.codePointCount | 6                           | 7                              |
251|         String.length | 7                           | 8                              |
252|             Utf8.size | 10                          | 11                             |
253
254For the most part Okio lets you ignore these problems and focus on your data. But when you need
255them, there are convenient APIs for dealing with low-level UTF-8 strings.
256
257Use `Utf8.size()` to count the number of bytes required to encode a string as UTF-8 without actually
258encoding it. This is handy in length-prefixed encodings like protocol buffers.
259
260Use `BufferedSource.readUtf8CodePoint()` to read a single variable-length code point, and
261`BufferedSink.writeUtf8CodePoint()` to write one.
262
263
264=== "Java"
265
266    ```java
267    public void dumpStringData(String s) throws IOException {
268      System.out.println("                       " + s);
269      System.out.println("        String.length: " + s.length());
270      System.out.println("String.codePointCount: " + s.codePointCount(0, s.length()));
271      System.out.println("            Utf8.size: " + Utf8.size(s));
272      System.out.println("          UTF-8 bytes: " + ByteString.encodeUtf8(s).hex());
273      System.out.println();
274    }
275    ```
276
277=== "Kotlin"
278
279    ```kotlin
280    fun dumpStringData(s: String) {
281      println("                       " + s)
282      println("        String.length: " + s.length)
283      println("String.codePointCount: " + s.codePointCount(0, s.length))
284      println("            Utf8.size: " + s.utf8Size())
285      println("          UTF-8 bytes: " + s.encodeUtf8().hex())
286      println()
287    }
288    ```
289
290Golden Values ([Java][GoldenValue]/[Kotlin][GoldenValueKt])
291-----------------------------------------------------------
292
293Okio likes testing. The library itself is heavily tested, and it has features that are often helpful
294when testing application code. One pattern we’ve found to be quite useful is “golden value” testing.
295The goal of such tests is to confirm that data encoded with earlier versions of a program can safely
296be decoded by the current program.
297
298We’ll illustrate this by encoding a value using Java Serialization. Though we must disclaim that
299Java Serialization is an awful encoding system and most programs should prefer other formats like
300JSON or protobuf! In any case, here’s a method that takes an object, serializes it, and returns the
301result as a `ByteString`:
302
303=== "Java"
304
305    ```Java
306    private ByteString serialize(Object o) throws IOException {
307      Buffer buffer = new Buffer();
308      try (ObjectOutputStream objectOut = new ObjectOutputStream(buffer.outputStream())) {
309        objectOut.writeObject(o);
310      }
311      return buffer.readByteString();
312    }
313    ```
314
315=== "Kotlin"
316
317    ```Kotlin
318    @Throws(IOException::class)
319    private fun serialize(o: Any?): ByteString {
320      val buffer = Buffer()
321      ObjectOutputStream(buffer.outputStream()).use { objectOut ->
322        objectOut.writeObject(o)
323      }
324      return buffer.readByteString()
325    }
326    ```
327
328There’s a lot going on here.
329
3301. We create a buffer as a holding space for our serialized data. It’s a convenient replacement for
331   `ByteArrayOutputStream`.
332
3332. We ask the buffer for its output stream. Writes to a buffer or its output stream always append
334   data to the end of the buffer.
335
3363. We create an `ObjectOutputStream` (the encoding API for Java serialization) and write our object.
337   The try block takes care of closing the stream for us. Note that closing a buffer has no effect.
338
3394. Finally we read a byte string from the buffer. The `readByteString()` method allows us to specify
340   how many bytes to read; here we don’t specify a count in order to read the entire thing. Reads
341   from a buffer always consume data from the front of the buffer.
342
343With our `serialize()` method handy we are ready to compute and print a golden value.
344
345=== "Java"
346
347    ```Java
348    Point point = new Point(8.0, 15.0);
349    ByteString pointBytes = serialize(point);
350    System.out.println(pointBytes.base64());
351    ```
352
353=== "Kotlin"
354
355    ```Kotlin
356    val point = Point(8.0, 15.0)
357    val pointBytes = serialize(point)
358    println(pointBytes.base64())
359    ```
360
361We print the `ByteString` as [base64][base64] because it’s a compact format that’s suitable for
362embedding in a test case. The program prints this:
363
364```
365rO0ABXNyAB5va2lvLnNhbXBsZXMuR29sZGVuVmFsdWUkUG9pbnTdUW8rMji1IwIAAkQAAXhEAAF5eHBAIAAAAAAAAEAuAAAAAAAA
366```
367
368That’s our golden value! We can embed it in our test case using base64 again to convert it back into
369a `ByteString`:
370
371=== "Java"
372
373    ```Java
374    ByteString goldenBytes = ByteString.decodeBase64("rO0ABXNyAB5va2lvLnNhbXBsZ"
375        + "XMuR29sZGVuVmFsdWUkUG9pbnTdUW8rMji1IwIAAkQAAXhEAAF5eHBAIAAAAAAAAEAuA"
376        + "AAAAAAA");
377    ```
378
379=== "Kotlin"
380
381    ```Kotlin
382    val goldenBytes = ("rO0ABXNyACRva2lvLnNhbXBsZXMuS290bGluR29sZGVuVmFsdWUkUG9pbnRF9yaY7cJ9EwIAA" +
383      "kQAAXhEAAF5eHBAIAAAAAAAAEAuAAAAAAAA").decodeBase64()
384    ```
385
386The next step is to deserialize the `ByteString` back into our value class. This method reverses the
387`serialize()` method above: we append a byte string to a buffer then consume it using an
388`ObjectInputStream`:
389
390=== "Java"
391
392    ```Java
393    private Object deserialize(ByteString byteString) throws IOException, ClassNotFoundException {
394      Buffer buffer = new Buffer();
395      buffer.write(byteString);
396      try (ObjectInputStream objectIn = new ObjectInputStream(buffer.inputStream())) {
397        return objectIn.readObject();
398      }
399    }
400    ```
401
402=== "Kotlin"
403
404    ```Kotlin
405    @Throws(IOException::class, ClassNotFoundException::class)
406    private fun deserialize(byteString: ByteString): Any? {
407      val buffer = Buffer()
408      buffer.write(byteString)
409      ObjectInputStream(buffer.inputStream()).use { objectIn ->
410        return objectIn.readObject()
411      }
412    }
413    ```
414
415Now we can test the decoder against the golden value:
416
417=== "Java"
418
419    ```Java
420    ByteString goldenBytes = ByteString.decodeBase64("rO0ABXNyAB5va2lvLnNhbXBsZ"
421        + "XMuR29sZGVuVmFsdWUkUG9pbnTdUW8rMji1IwIAAkQAAXhEAAF5eHBAIAAAAAAAAEAuA"
422        + "AAAAAAA");
423    Point decoded = (Point) deserialize(goldenBytes);
424    assertEquals(new Point(8.0, 15.0), decoded);
425    ```
426
427=== "Kotlin"
428
429    ```Kotlin
430    val goldenBytes = ("rO0ABXNyACRva2lvLnNhbXBsZXMuS290bGluR29sZGVuVmFsdWUkUG9pbnRF9yaY7cJ9EwIAA" +
431      "kQAAXhEAAF5eHBAIAAAAAAAAEAuAAAAAAAA").decodeBase64()!!
432    val decoded = deserialize(goldenBytes) as Point
433    assertEquals(point, decoded)
434    ```
435
436With this test we can change the serialization of the `Point` class without
437breaking compatibility.
438
439
440Write a binary file ([Java][BitmapEncoder]/[Kotlin][BitmapEncoderKt])
441---------------------------------------------------------------------
442
443Encoding a binary file is not unlike encoding a text file. Okio uses the same `BufferedSink` and
444`BufferedSource` bytes for both. This is handy for binary formats that include both byte and
445character data.
446
447Writing binary data is more hazardous than text because if you make a mistake it is often quite
448difficult to diagnose. Avoid such mistakes by being careful around these traps:
449
450 * **The width of each field.** This is the number of bytes used. Okio doesn't include a mechanism
451   to emit partial bytes. If you need that, you’ll need to do your own bit shifting and masking
452   before writing.
453
454 * **The endianness of each field.** All fields that have more than one byte have _endianness_:
455   whether the bytes are ordered most-significant to least (big endian) or least-significant to most
456   (little endian). Okio uses the `Le` suffix for little-endian methods; methods without a suffix
457   are big-endian.
458
459 * **Signed vs. Unsigned.** Java doesn’t have unsigned primitive types (except for `char`!) so
460   coping with this is often something that happens at the application layer. To make this a little
461   easier Okio accepts `int` types for `writeByte()` and `writeShort()`. You can pass an “unsigned”
462   byte like 255 and Okio will do the right thing.
463
464| Method       | Width | Endianness |           Value | Encoded Value             |
465| :----------- | ----: | :--------- | --------------: | :------------------------ |
466| writeByte    |     1 |            |               3 | `03`                      |
467| writeShort   |     2 | big        |               3 | `00 03`                   |
468| writeInt     |     4 | big        |               3 | `00 00 00 03`             |
469| writeLong    |     8 | big        |               3 | `00 00 00 00 00 00 00 03` |
470| writeShortLe |     2 | little     |               3 | `03 00`                   |
471| writeIntLe   |     4 | little     |               3 | `03 00 00 00`             |
472| writeLongLe  |     8 | little     |               3 | `03 00 00 00 00 00 00 00` |
473| writeByte    |     1 |            |  Byte.MAX_VALUE | `7f`                      |
474| writeShort   |     2 | big        | Short.MAX_VALUE | `7f ff`                   |
475| writeInt     |     4 | big        |   Int.MAX_VALUE | `7f ff ff ff`             |
476| writeLong    |     8 | big        |  Long.MAX_VALUE | `7f ff ff ff ff ff ff ff` |
477| writeShortLe |     2 | little     | Short.MAX_VALUE | `ff 7f`                   |
478| writeIntLe   |     4 | little     |   Int.MAX_VALUE | `ff ff ff 7f`             |
479| writeLongLe  |     8 | little     |  Long.MAX_VALUE | `ff ff ff ff ff ff ff 7f` |
480
481This code encodes a bitmap following the [BMP file format][bmp].
482
483=== "Java"
484
485    ```Java
486    void encode(Bitmap bitmap, BufferedSink sink) throws IOException {
487      int height = bitmap.height();
488      int width = bitmap.width();
489
490      int bytesPerPixel = 3;
491      int rowByteCountWithoutPadding = (bytesPerPixel * width);
492      int rowByteCount = ((rowByteCountWithoutPadding + 3) / 4) * 4;
493      int pixelDataSize = rowByteCount * height;
494      int bmpHeaderSize = 14;
495      int dibHeaderSize = 40;
496
497      // BMP Header
498      sink.writeUtf8("BM"); // ID.
499      sink.writeIntLe(bmpHeaderSize + dibHeaderSize + pixelDataSize); // File size.
500      sink.writeShortLe(0); // Unused.
501      sink.writeShortLe(0); // Unused.
502      sink.writeIntLe(bmpHeaderSize + dibHeaderSize); // Offset of pixel data.
503
504      // DIB Header
505      sink.writeIntLe(dibHeaderSize);
506      sink.writeIntLe(width);
507      sink.writeIntLe(height);
508      sink.writeShortLe(1);  // Color plane count.
509      sink.writeShortLe(bytesPerPixel * Byte.SIZE);
510      sink.writeIntLe(0);    // No compression.
511      sink.writeIntLe(16);   // Size of bitmap data including padding.
512      sink.writeIntLe(2835); // Horizontal print resolution in pixels/meter. (72 dpi).
513      sink.writeIntLe(2835); // Vertical print resolution in pixels/meter. (72 dpi).
514      sink.writeIntLe(0);    // Palette color count.
515      sink.writeIntLe(0);    // 0 important colors.
516
517      // Pixel data.
518      for (int y = height - 1; y >= 0; y--) {
519        for (int x = 0; x < width; x++) {
520          sink.writeByte(bitmap.blue(x, y));
521          sink.writeByte(bitmap.green(x, y));
522          sink.writeByte(bitmap.red(x, y));
523        }
524
525        // Padding for 4-byte alignment.
526        for (int p = rowByteCountWithoutPadding; p < rowByteCount; p++) {
527          sink.writeByte(0);
528        }
529      }
530    }
531    ```
532
533=== "Kotlin"
534
535    ```Kotlin
536    @Throws(IOException::class)
537    fun encode(bitmap: Bitmap, sink: BufferedSink) {
538      val height = bitmap.height
539      val width = bitmap.width
540      val bytesPerPixel = 3
541      val rowByteCountWithoutPadding = bytesPerPixel * width
542      val rowByteCount = (rowByteCountWithoutPadding + 3) / 4 * 4
543      val pixelDataSize = rowByteCount * height
544      val bmpHeaderSize = 14
545      val dibHeaderSize = 40
546
547      // BMP Header
548      sink.writeUtf8("BM") // ID.
549      sink.writeIntLe(bmpHeaderSize + dibHeaderSize + pixelDataSize) // File size.
550      sink.writeShortLe(0) // Unused.
551      sink.writeShortLe(0) // Unused.
552      sink.writeIntLe(bmpHeaderSize + dibHeaderSize) // Offset of pixel data.
553
554      // DIB Header
555      sink.writeIntLe(dibHeaderSize)
556      sink.writeIntLe(width)
557      sink.writeIntLe(height)
558      sink.writeShortLe(1) // Color plane count.
559      sink.writeShortLe(bytesPerPixel * Byte.SIZE_BITS)
560      sink.writeIntLe(0) // No compression.
561      sink.writeIntLe(16) // Size of bitmap data including padding.
562      sink.writeIntLe(2835) // Horizontal print resolution in pixels/meter. (72 dpi).
563      sink.writeIntLe(2835) // Vertical print resolution in pixels/meter. (72 dpi).
564      sink.writeIntLe(0) // Palette color count.
565      sink.writeIntLe(0) // 0 important colors.
566
567      // Pixel data.
568      for (y in height - 1 downTo 0) {
569        for (x in 0 until width) {
570          sink.writeByte(bitmap.blue(x, y))
571          sink.writeByte(bitmap.green(x, y))
572          sink.writeByte(bitmap.red(x, y))
573        }
574
575        // Padding for 4-byte alignment.
576        for (p in rowByteCountWithoutPadding until rowByteCount) {
577          sink.writeByte(0)
578        }
579      }
580    }
581    ```
582
583The trickiest part of this program is the format’s required padding. The BMP format expects each row
584to begin on a 4-byte boundary so it is necessary to add zeros to maintain the alignment.
585
586Encoding other binary formats is usually quite similar. Some tips:
587
588 * Write tests with golden values! Confirming that your program emits the expected result can make
589   debugging easier.
590 * Use `Utf8.size()` to compute the number of bytes of an encoded string. This is essential for
591   length-prefixed formats.
592 * Use `Float.floatToIntBits()` and `Double.doubleToLongBits()` to encode floating point values.
593
594
595Communicate on a Socket ([Java][SocksProxyServer]/[Kotlin][SocksProxyServerKt])
596-------------------------------------------------------------------------------
597
598Note that Okio doesn't yet support sockets on Kotlin/Native or Kotlin/JS.
599
600Sending and receiving data over the network is a bit like writing and reading files. We use
601`BufferedSink` to encode output and `BufferedSource` to decode input. Like files, network protocols
602can be text, binary, or a mix of both. But there are also some substantial differences between the
603network and the file system.
604
605With a file you’re either reading or writing but with the network you can do both! Some protocols
606handle this by taking turns: write a request, read a response, repeat. You can implement this kind
607of protocol with a single thread. In other protocols you may read and write simultaneously.
608Typically you’ll want one dedicated thread for reading. For writing you can use either a dedicated
609thread or use `synchronized` so that multiple threads can share a sink. Okio’s streams are not safe
610for concurrent use.
611
612Sinks buffer outbound data to minimize I/O operations. This is efficient but it means you must
613manually call `flush()` to transmit data. Typically message-oriented protocols flush after each
614message. Note that Okio will automatically flush when the buffered data exceeds some threshold. This
615is intended to save memory and you shouldn’t rely on it for interactive protocols.
616
617Okio builds on `java.io.Socket` for connectivity. Create your socket as a server or as a client,
618then use `Okio.source(Socket)` to read and `Okio.sink(Socket)` to write. These APIs also work with
619`SSLSocket`. You should use SSL unless you have a very good reason not to!
620
621Cancel a socket from any thread by calling `Socket.close()`; this will cause its sources and sinks
622to immediately fail with an `IOException`. You can also configure timeouts for all socket
623operations. You don’t need a reference to the socket to adjust timeouts: `Source` and `Sink` expose
624timeouts directly. This API works even if the streams are decorated.
625
626As a complete example of networking with Okio we wrote a [basic SOCKS proxy][SocksProxyServer]
627server. Some highlights:
628
629=== "Java"
630
631    ```Java
632    Socket fromSocket = ...
633    BufferedSource fromSource = Okio.buffer(Okio.source(fromSocket));
634    BufferedSink fromSink = Okio.buffer(Okio.sink(fromSocket));
635    ```
636
637=== "Kotlin"
638
639    ```Kotlin
640    val fromSocket: Socket = ...
641    val fromSource = fromSocket.source().buffer()
642    val fromSink = fromSocket.sink().buffer()
643    ```
644
645Creating sources and sinks for sockets is the same as creating them for files. Once you create a
646`Source` or `Sink` for a socket you must not use its `InputStream` or `OutputStream`, respectively.
647
648=== "Java"
649
650    ```Java
651    Buffer buffer = new Buffer();
652    for (long byteCount; (byteCount = source.read(buffer, 8192L)) != -1; ) {
653      sink.write(buffer, byteCount);
654      sink.flush();
655    }
656    ```
657
658=== "Kotlin"
659
660    ```Kotlin
661    val buffer = Buffer()
662    var byteCount: Long
663    while (source.read(buffer, 8192L).also { byteCount = it } != -1L) {
664      sink.write(buffer, byteCount)
665      sink.flush()
666    }
667    ```
668
669The above loop copies data from the source to the sink, flushing after each read. If we didn’t need
670the flushing we could replace this loop with a single call to `BufferedSink.writeAll(Source)`.
671
672The `8192` argument to `read()` is the maximum number of bytes to read before returning. We could
673have passed any value here, but we like 8 KiB because that’s the largest value Okio can do in a
674single system call. Most of the time application code doesn’t need to deal with such limits!
675
676=== "Java"
677
678    ```Java
679    int addressType = fromSource.readByte() & 0xff;
680    int port = fromSource.readShort() & 0xffff;
681    ```
682
683=== "Kotlin"
684
685    ```Kotlin
686    val addressType = fromSource.readByte().toInt() and 0xff
687    val port = fromSource.readShort().toInt() and 0xffff
688    ```
689
690Okio uses signed types like `byte` and `short`, but often protocols want unsigned values. The
691bitwise `&` operator is Java’s preferred idiom to convert a signed value into an unsigned value.
692Here’s a cheat sheet for bytes, shorts, and ints:
693
694| Type  | Signed Range                  | Unsigned Range   | Signed to Unsigned          |
695| :---- | :---------------------------: | :--------------- | :-------------------------- |
696| byte  | -128..127                     | 0..255           | `int u = s & 0xff;`         |
697| short | -32,768..32,767               | 0..65,535        | `int u = s & 0xffff;`       |
698| int   | -2,147,483,648..2,147,483,647 | 0..4,294,967,295 | `long u = s & 0xffffffffL;` |
699
700Java has no primitive type that can represent unsigned longs.
701
702
703Hashing ([Java][Hashing]/[Kotlin][HashingKt])
704---------------------------------------------
705
706We’re bombarded by hashing in our lives as Java programmers. Early on we're introduced to the
707`hashCode()` method, something we know we need to override otherwise unforeseen bad things happen.
708Later we’re shown `LinkedHashMap` and its friends. These build on that `hashCode()` method to
709organize data for fast retrieval.
710
711Elsewhere we have cryptographic hash functions. These get used all over the place. HTTPS
712certificates, Git commits, BitTorrent integrity checking, and Blockchain blocks all use
713cryptographic hashes. Good use of hashes can improve the performance, privacy, security, and
714simplicity of an application.
715
716Each cryptographic hash function accepts a variable-length stream of input bytes and produces a
717fixed-length byte string value called the “hash”. Hash functions have these important qualities:
718
719 * Deterministic: each input always produces the same output.
720 * Uniform: each output byte string is equally likely. It is very difficult to find or create pairs
721   of different inputs that yield the same output. This is called a “collision”.
722 * Non-reversible: knowing an output doesn't help you to find the input. Note that if you know some
723   possible inputs you can hash them to see if their hashes match.
724 * Well-known: the hash is implemented everywhere and rigorously understood.
725
726Good hash functions are very cheap to compute (dozens of microseconds) and expensive to reverse
727(quintillions of millenia). Steady advances in computing and mathematics have caused once-great hash
728functions to become inexpensive to reverse. When choosing a hash function, beware that not all are
729created equal! Okio supports these well-known cryptographic hash functions:
730
731 * **MD5**: a 128-bit (16 byte) cryptographic hash. It is both insecure and obsolete because it is
732   inexpensive to reverse! This hash is offered because it is popular and convenient for use in
733   legacy systems that are not security-sensitive.
734 * **SHA-1**: a 160-bit (20 byte) cryptographic hash. It was recently demonstrated that it is
735   feasible to create SHA-1 collisions. Consider upgrading from SHA-1 to SHA-256.
736 * **SHA-256**: a 256-bit (32 byte) cryptographic hash. SHA-256 is widely understood and expensive
737   to reverse. This is the hash most systems should use.
738 * **SHA-512**: a 512-bit (64 byte) cryptographic hash. It is expensive to reverse.
739
740Each hash creates a `ByteString` of the specified length. Use `hex()` to get the conventional
741human-readable form. Or leave it as a `ByteString` because that’s a convenient model type!
742
743Okio can produce cryptographic hashes from byte strings:
744
745=== "Java"
746
747    ```Java
748    ByteString byteString = readByteString(Path.get("README.md"));
749    System.out.println("   md5: " + byteString.md5().hex());
750    System.out.println("  sha1: " + byteString.sha1().hex());
751    System.out.println("sha256: " + byteString.sha256().hex());
752    System.out.println("sha512: " + byteString.sha512().hex());
753    ```
754
755=== "Kotlin"
756
757    ```Kotlin
758    val byteString = readByteString("README.md".toPath())
759    println("       md5: " + byteString.md5().hex())
760    println("      sha1: " + byteString.sha1().hex())
761    println("    sha256: " + byteString.sha256().hex())
762    println("    sha512: " + byteString.sha512().hex())
763    ```
764
765From buffers:
766
767=== "Java"
768
769    ```Java
770    Buffer buffer = readBuffer(Path.get("README.md"));
771    System.out.println("   md5: " + buffer.md5().hex());
772    System.out.println("  sha1: " + buffer.sha1().hex());
773    System.out.println("sha256: " + buffer.sha256().hex());
774    System.out.println("sha512: " + buffer.sha512().hex());
775    ```
776
777=== "Kotlin"
778
779    ```Kotlin
780    val buffer = readBuffer("README.md".toPath())
781    println("       md5: " + buffer.md5().hex())
782    println("      sha1: " + buffer.sha1().hex())
783    println("    sha256: " + buffer.sha256().hex())
784    println("    sha512: " + buffer.sha512().hex())
785    ```
786
787While streaming from a source:
788
789=== "Java"
790
791    ```Java
792    try (HashingSink hashingSink = HashingSink.sha256(Okio.blackhole());
793         BufferedSource source = Okio.buffer(FileSystem.SYSTEM.source(path))) {
794      source.readAll(hashingSink);
795      System.out.println("sha256: " + hashingSink.hash().hex());
796    }
797    ```
798
799=== "Kotlin"
800
801    ```Kotlin
802    sha256(blackholeSink()).use { hashingSink ->
803      FileSystem.SYSTEM.source(path).buffer().use { source ->
804        source.readAll(hashingSink)
805        println("    sha256: " + hashingSink.hash.hex())
806      }
807    }
808    ```
809
810While streaming to a sink:
811
812=== "Java"
813
814    ```Java
815    try (HashingSink hashingSink = HashingSink.sha256(Okio.blackhole());
816         BufferedSink sink = Okio.buffer(hashingSink);
817         Source source = FileSystem.SYSTEM.source(path)) {
818      sink.writeAll(source);
819      sink.close(); // Emit anything buffered.
820      System.out.println("sha256: " + hashingSink.hash().hex());
821    }
822    ```
823
824=== "Kotlin"
825
826    ```Kotlin
827    sha256(blackholeSink()).use { hashingSink ->
828      hashingSink.buffer().use { sink ->
829        FileSystem.SYSTEM.source(path).use { source ->
830          sink.writeAll(source)
831          sink.close() // Emit anything buffered.
832          println("    sha256: " + hashingSink.hash.hex())
833        }
834      }
835    }
836    ```
837
838Okio also supports HMAC (Hash Message Authentication Code) which combines a secret and a hash.
839Applications use HMAC for data integrity and authentication.
840
841=== "Java"
842
843    ```Java
844    ByteString secret = ByteString.decodeHex("7065616e7574627574746572");
845    System.out.println("hmacSha256: " + byteString.hmacSha256(secret).hex());
846    ```
847
848=== "Kotlin"
849
850    ```Kotlin
851    val secret = "7065616e7574627574746572".decodeHex()
852    println("hmacSha256: " + byteString.hmacSha256(secret).hex())
853    ```
854
855As with hashing, you can generate an HMAC from a `ByteString`, `Buffer`, `HashingSource`, and
856`HashingSink`. Note that Okio doesn’t implement HMAC for MD5.
857
858On Android and Java, Okio uses Java’s `java.security.MessageDigest` for cryptographic hashes and
859`javax.crypto.Mac` for HMAC. On other platforms Okio uses its own optimized implementation of
860these algorithms.
861
862
863Encryption and Decryption
864-------------------------
865
866On Android and Java it's easy to encrypt streams.
867
868Callers are responsible for the initialization of the encryption or decryption cipher with the
869chosen algorithm, the key, and algorithm-specific additional parameters like the initialization
870vector. The following example shows a typical usage with AES encryption, in which `key` and `iv`
871parameters should both be 16 bytes long.
872
873=== "Java"
874
875    Use `Okio.cipherSink(Sink, Cipher)` or `Okio.cipherSource(Source, Cipher)` to encrypt or decrypt
876    a stream using a block cipher.
877
878    ```java
879    void encryptAes(ByteString bytes, Path path, byte[] key, byte[] iv)
880        throws GeneralSecurityException, IOException {
881      Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
882      cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
883      try (BufferedSink sink = Okio.buffer(
884          Okio.cipherSink(FileSystem.SYSTEM.sink(path), cipher))) {
885        sink.write(bytes);
886      }
887    }
888
889    ByteString decryptAesToByteString(Path path, byte[] key, byte[] iv)
890        throws GeneralSecurityException, IOException {
891      Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
892      cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
893      try (BufferedSource source = Okio.buffer(
894          Okio.cipherSource(FileSystem.SYSTEM.source(path), cipher))) {
895        return source.readByteString();
896      }
897    }
898    ```
899
900=== "Kotlin"
901
902    Encryption and decryption functions are extensions on `Cipher`:
903
904    ```kotlin
905    fun encryptAes(bytes: ByteString, path: Path, key: ByteArray, iv: ByteArray) {
906      val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
907      cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
908      val cipherSink = FileSystem.SYSTEM.sink(path).cipherSink(cipher)
909      cipherSink.buffer().use {
910        it.write(bytes)
911      }
912    }
913
914    fun decryptAesToByteString(path: Path, key: ByteArray, iv: ByteArray): ByteString {
915      val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
916      cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
917      val cipherSource = FileSystem.SYSTEM.source(path).cipherSource(cipher)
918      return cipherSource.buffer().use {
919        it.readByteString()
920      }
921    }
922    ```
923
924
925[base64]: https://tools.ietf.org/html/rfc4648#section-4
926[bmp]: https://en.wikipedia.org/wiki/BMP_file_format
927[nfd]: https://docs.oracle.com/javase/7/docs/api/java/text/Normalizer.Form.html#NFD
928[nfc]: https://docs.oracle.com/javase/7/docs/api/java/text/Normalizer.Form.html#NFC
929[BitmapEncoderKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/BitmapEncoder.kt
930[BitmapEncoder]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/BitmapEncoder.java
931[ExploreCharsetsKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/ExploreCharsets.kt
932[ExploreCharsets]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/ExploreCharsets.java
933[GoldenValueKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/GoldenValue.kt
934[GoldenValue]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/GoldenValue.java
935[HashingKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/Hashing.kt
936[Hashing]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/Hashing.java
937[ReadFileLineByLine]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/ReadFileLineByLine.java
938[ReadFileLineByLineKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/ReadFileLineByLine.kt
939[SocksProxyServerKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/SocksProxyServer.kt
940[SocksProxyServer]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/SocksProxyServer.java
941[WriteFile]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/WriteFile.java
942[WriteFileKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/WriteFile.kt
943