Home | History | Annotate | Download | only in okio
      1 /*
      2  * Copyright (C) 2014 Square, Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package okio;
     17 
     18 import java.util.Arrays;
     19 import java.util.List;
     20 import java.util.Random;
     21 import org.junit.Test;
     22 
     23 import static java.util.Arrays.asList;
     24 import static org.junit.Assert.assertEquals;
     25 import static org.junit.Assert.assertFalse;
     26 import static org.junit.Assert.assertTrue;
     27 import static org.junit.Assert.fail;
     28 
     29 public final class OkBufferTest {
     30   @Test public void readAndWriteUtf8() throws Exception {
     31     OkBuffer buffer = new OkBuffer();
     32     buffer.writeUtf8("ab");
     33     assertEquals(2, buffer.size());
     34     buffer.writeUtf8("cdef");
     35     assertEquals(6, buffer.size());
     36     assertEquals("abcd", buffer.readUtf8(4));
     37     assertEquals(2, buffer.size());
     38     assertEquals("ef", buffer.readUtf8(2));
     39     assertEquals(0, buffer.size());
     40     try {
     41       buffer.readUtf8(1);
     42       fail();
     43     } catch (ArrayIndexOutOfBoundsException expected) {
     44     }
     45   }
     46 
     47   @Test public void completeSegmentByteCountOnEmptyBuffer() throws Exception {
     48     OkBuffer buffer = new OkBuffer();
     49     assertEquals(0, buffer.completeSegmentByteCount());
     50   }
     51 
     52   @Test public void completeSegmentByteCountOnBufferWithFullSegments() throws Exception {
     53     OkBuffer buffer = new OkBuffer();
     54     buffer.writeUtf8(repeat('a', Segment.SIZE * 4));
     55     assertEquals(Segment.SIZE * 4, buffer.completeSegmentByteCount());
     56   }
     57 
     58   @Test public void completeSegmentByteCountOnBufferWithIncompleteTailSegment() throws Exception {
     59     OkBuffer buffer = new OkBuffer();
     60     buffer.writeUtf8(repeat('a', Segment.SIZE * 4 - 10));
     61     assertEquals(Segment.SIZE * 3, buffer.completeSegmentByteCount());
     62   }
     63 
     64   @Test public void readUtf8SpansSegments() throws Exception {
     65     OkBuffer buffer = new OkBuffer();
     66     buffer.writeUtf8(repeat('a', Segment.SIZE * 2));
     67     buffer.readUtf8(Segment.SIZE - 1);
     68     assertEquals("aa", buffer.readUtf8(2));
     69   }
     70 
     71   @Test public void readUtf8EntireBuffer() throws Exception {
     72     OkBuffer buffer = new OkBuffer();
     73     buffer.writeUtf8(repeat('a', Segment.SIZE));
     74     assertEquals(repeat('a', Segment.SIZE), buffer.readUtf8(Segment.SIZE));
     75   }
     76 
     77   @Test public void toStringOnEmptyBuffer() throws Exception {
     78     OkBuffer buffer = new OkBuffer();
     79     assertEquals("OkBuffer[size=0]", buffer.toString());
     80   }
     81 
     82   @Test public void toStringOnSmallBufferIncludesContents() throws Exception {
     83     OkBuffer buffer = new OkBuffer();
     84     buffer.write(ByteString.decodeHex("a1b2c3d4e5f61a2b3c4d5e6f10203040"));
     85     assertEquals("OkBuffer[size=16 data=a1b2c3d4e5f61a2b3c4d5e6f10203040]", buffer.toString());
     86   }
     87 
     88   @Test public void toStringOnLargeBufferIncludesMd5() throws Exception {
     89     OkBuffer buffer = new OkBuffer();
     90     buffer.write(ByteString.encodeUtf8("12345678901234567"));
     91     assertEquals("OkBuffer[size=17 md5=2c9728a2138b2f25e9f89f99bdccf8db]", buffer.toString());
     92   }
     93 
     94   @Test public void toStringOnMultipleSegmentBuffer() throws Exception {
     95     OkBuffer buffer = new OkBuffer();
     96     buffer.writeUtf8(repeat('a', 6144));
     97     assertEquals("OkBuffer[size=6144 md5=d890021f28522533c1cc1b9b1f83ce73]", buffer.toString());
     98   }
     99 
    100   @Test public void multipleSegmentBuffers() throws Exception {
    101     OkBuffer buffer = new OkBuffer();
    102     buffer.writeUtf8(repeat('a',  1000));
    103     buffer.writeUtf8(repeat('b', 2500));
    104     buffer.writeUtf8(repeat('c', 5000));
    105     buffer.writeUtf8(repeat('d', 10000));
    106     buffer.writeUtf8(repeat('e', 25000));
    107     buffer.writeUtf8(repeat('f', 50000));
    108 
    109     assertEquals(repeat('a', 999), buffer.readUtf8(999)); // a...a
    110     assertEquals("a" + repeat('b', 2500) + "c", buffer.readUtf8(2502)); // ab...bc
    111     assertEquals(repeat('c', 4998), buffer.readUtf8(4998)); // c...c
    112     assertEquals("c" + repeat('d', 10000) + "e", buffer.readUtf8(10002)); // cd...de
    113     assertEquals(repeat('e', 24998), buffer.readUtf8(24998)); // e...e
    114     assertEquals("e" + repeat('f', 50000), buffer.readUtf8(50001)); // ef...f
    115     assertEquals(0, buffer.size());
    116   }
    117 
    118   @Test public void fillAndDrainPool() throws Exception {
    119     OkBuffer buffer = new OkBuffer();
    120 
    121     // Take 2 * MAX_SIZE segments. This will drain the pool, even if other tests filled it.
    122     buffer.write(new byte[(int) SegmentPool.MAX_SIZE]);
    123     buffer.write(new byte[(int) SegmentPool.MAX_SIZE]);
    124     assertEquals(0, SegmentPool.INSTANCE.byteCount);
    125 
    126     // Recycle MAX_SIZE segments. They're all in the pool.
    127     buffer.readByteString(SegmentPool.MAX_SIZE);
    128     assertEquals(SegmentPool.MAX_SIZE, SegmentPool.INSTANCE.byteCount);
    129 
    130     // Recycle MAX_SIZE more segments. The pool is full so they get garbage collected.
    131     buffer.readByteString(SegmentPool.MAX_SIZE);
    132     assertEquals(SegmentPool.MAX_SIZE, SegmentPool.INSTANCE.byteCount);
    133 
    134     // Take MAX_SIZE segments to drain the pool.
    135     buffer.write(new byte[(int) SegmentPool.MAX_SIZE]);
    136     assertEquals(0, SegmentPool.INSTANCE.byteCount);
    137 
    138     // Take MAX_SIZE more segments. The pool is drained so these will need to be allocated.
    139     buffer.write(new byte[(int) SegmentPool.MAX_SIZE]);
    140     assertEquals(0, SegmentPool.INSTANCE.byteCount);
    141   }
    142 
    143   @Test public void moveBytesBetweenBuffersShareSegment() throws Exception {
    144     int size = (Segment.SIZE / 2) - 1;
    145     List<Integer> segmentSizes = moveBytesBetweenBuffers(repeat('a', size), repeat('b', size));
    146     assertEquals(asList(size * 2), segmentSizes);
    147   }
    148 
    149   @Test public void moveBytesBetweenBuffersReassignSegment() throws Exception {
    150     int size = (Segment.SIZE / 2) + 1;
    151     List<Integer> segmentSizes = moveBytesBetweenBuffers(repeat('a', size), repeat('b', size));
    152     assertEquals(asList(size, size), segmentSizes);
    153   }
    154 
    155   @Test public void moveBytesBetweenBuffersMultipleSegments() throws Exception {
    156     int size = 3 * Segment.SIZE + 1;
    157     List<Integer> segmentSizes = moveBytesBetweenBuffers(repeat('a', size), repeat('b', size));
    158     assertEquals(asList(Segment.SIZE, Segment.SIZE, Segment.SIZE, 1,
    159         Segment.SIZE, Segment.SIZE, Segment.SIZE, 1), segmentSizes);
    160   }
    161 
    162   private List<Integer> moveBytesBetweenBuffers(String... contents) {
    163     StringBuilder expected = new StringBuilder();
    164     OkBuffer buffer = new OkBuffer();
    165     for (String s : contents) {
    166       OkBuffer source = new OkBuffer();
    167       source.writeUtf8(s);
    168       buffer.write(source, source.size());
    169       expected.append(s);
    170     }
    171     List<Integer> segmentSizes = buffer.segmentSizes();
    172     assertEquals(expected.toString(), buffer.readUtf8(expected.length()));
    173     return segmentSizes;
    174   }
    175 
    176   /** The big part of source's first segment is being moved. */
    177   @Test public void writeSplitSourceBufferLeft() throws Exception {
    178     int writeSize = Segment.SIZE / 2 + 1;
    179 
    180     OkBuffer sink = new OkBuffer();
    181     sink.writeUtf8(repeat('b', Segment.SIZE - 10));
    182 
    183     OkBuffer source = new OkBuffer();
    184     source.writeUtf8(repeat('a', Segment.SIZE * 2));
    185     sink.write(source, writeSize);
    186 
    187     assertEquals(asList(Segment.SIZE - 10, writeSize), sink.segmentSizes());
    188     assertEquals(asList(Segment.SIZE - writeSize, Segment.SIZE), source.segmentSizes());
    189   }
    190 
    191   /** The big part of source's first segment is staying put. */
    192   @Test public void writeSplitSourceBufferRight() throws Exception {
    193     int writeSize = Segment.SIZE / 2 - 1;
    194 
    195     OkBuffer sink = new OkBuffer();
    196     sink.writeUtf8(repeat('b', Segment.SIZE - 10));
    197 
    198     OkBuffer source = new OkBuffer();
    199     source.writeUtf8(repeat('a', Segment.SIZE * 2));
    200     sink.write(source, writeSize);
    201 
    202     assertEquals(asList(Segment.SIZE - 10, writeSize), sink.segmentSizes());
    203     assertEquals(asList(Segment.SIZE - writeSize, Segment.SIZE), source.segmentSizes());
    204   }
    205 
    206   @Test public void writePrefixDoesntSplit() throws Exception {
    207     OkBuffer sink = new OkBuffer();
    208     sink.writeUtf8(repeat('b', 10));
    209 
    210     OkBuffer source = new OkBuffer();
    211     source.writeUtf8(repeat('a', Segment.SIZE * 2));
    212     sink.write(source, 20);
    213 
    214     assertEquals(asList(30), sink.segmentSizes());
    215     assertEquals(asList(Segment.SIZE - 20, Segment.SIZE), source.segmentSizes());
    216     assertEquals(30, sink.size());
    217     assertEquals(Segment.SIZE * 2 - 20, source.size());
    218   }
    219 
    220   @Test public void writePrefixDoesntSplitButRequiresCompact() throws Exception {
    221     OkBuffer sink = new OkBuffer();
    222     sink.writeUtf8(repeat('b', Segment.SIZE - 10)); // limit = size - 10
    223     sink.readUtf8(Segment.SIZE - 20); // pos = size = 20
    224 
    225     OkBuffer source = new OkBuffer();
    226     source.writeUtf8(repeat('a', Segment.SIZE * 2));
    227     sink.write(source, 20);
    228 
    229     assertEquals(asList(30), sink.segmentSizes());
    230     assertEquals(asList(Segment.SIZE - 20, Segment.SIZE), source.segmentSizes());
    231     assertEquals(30, sink.size());
    232     assertEquals(Segment.SIZE * 2 - 20, source.size());
    233   }
    234 
    235   @Test public void readExhaustedSource() throws Exception {
    236     OkBuffer sink = new OkBuffer();
    237     sink.writeUtf8(repeat('a', 10));
    238 
    239     OkBuffer source = new OkBuffer();
    240 
    241     assertEquals(-1, source.read(sink, 10));
    242     assertEquals(10, sink.size());
    243     assertEquals(0, source.size());
    244   }
    245 
    246   @Test public void readZeroBytesFromSource() throws Exception {
    247     OkBuffer sink = new OkBuffer();
    248     sink.writeUtf8(repeat('a', 10));
    249 
    250     OkBuffer source = new OkBuffer();
    251 
    252     // Either 0 or -1 is reasonable here. For consistency with Android's
    253     // ByteArrayInputStream we return 0.
    254     assertEquals(-1, source.read(sink, 0));
    255     assertEquals(10, sink.size());
    256     assertEquals(0, source.size());
    257   }
    258 
    259   @Test public void moveAllRequestedBytesWithRead() throws Exception {
    260     OkBuffer sink = new OkBuffer();
    261     sink.writeUtf8(repeat('a', 10));
    262 
    263     OkBuffer source = new OkBuffer();
    264     source.writeUtf8(repeat('b', 15));
    265 
    266     assertEquals(10, source.read(sink, 10));
    267     assertEquals(20, sink.size());
    268     assertEquals(5, source.size());
    269     assertEquals(repeat('a', 10) + repeat('b', 10), sink.readUtf8(20));
    270   }
    271 
    272   @Test public void moveFewerThanRequestedBytesWithRead() throws Exception {
    273     OkBuffer sink = new OkBuffer();
    274     sink.writeUtf8(repeat('a', 10));
    275 
    276     OkBuffer source = new OkBuffer();
    277     source.writeUtf8(repeat('b', 20));
    278 
    279     assertEquals(20, source.read(sink, 25));
    280     assertEquals(30, sink.size());
    281     assertEquals(0, source.size());
    282     assertEquals(repeat('a', 10) + repeat('b', 20), sink.readUtf8(30));
    283   }
    284 
    285   @Test public void indexOf() throws Exception {
    286     OkBuffer buffer = new OkBuffer();
    287 
    288     // The segment is empty.
    289     assertEquals(-1, buffer.indexOf((byte) 'a'));
    290 
    291     // The segment has one value.
    292     buffer.writeUtf8("a"); // a
    293     assertEquals(0, buffer.indexOf((byte) 'a'));
    294     assertEquals(-1, buffer.indexOf((byte) 'b'));
    295 
    296     // The segment has lots of data.
    297     buffer.writeUtf8(repeat('b', Segment.SIZE - 2)); // ab...b
    298     assertEquals(0, buffer.indexOf((byte) 'a'));
    299     assertEquals(1, buffer.indexOf((byte) 'b'));
    300     assertEquals(-1, buffer.indexOf((byte) 'c'));
    301 
    302     // The segment doesn't start at 0, it starts at 2.
    303     buffer.readUtf8(2); // b...b
    304     assertEquals(-1, buffer.indexOf((byte) 'a'));
    305     assertEquals(0, buffer.indexOf((byte) 'b'));
    306     assertEquals(-1, buffer.indexOf((byte) 'c'));
    307 
    308     // The segment is full.
    309     buffer.writeUtf8("c"); // b...bc
    310     assertEquals(-1, buffer.indexOf((byte) 'a'));
    311     assertEquals(0, buffer.indexOf((byte) 'b'));
    312     assertEquals(Segment.SIZE - 3, buffer.indexOf((byte) 'c'));
    313 
    314     // The segment doesn't start at 2, it starts at 4.
    315     buffer.readUtf8(2); // b...bc
    316     assertEquals(-1, buffer.indexOf((byte) 'a'));
    317     assertEquals(0, buffer.indexOf((byte) 'b'));
    318     assertEquals(Segment.SIZE - 5, buffer.indexOf((byte) 'c'));
    319 
    320     // Two segments.
    321     buffer.writeUtf8("d"); // b...bcd, d is in the 2nd segment.
    322     assertEquals(asList(Segment.SIZE - 4, 1), buffer.segmentSizes());
    323     assertEquals(Segment.SIZE - 4, buffer.indexOf((byte) 'd'));
    324     assertEquals(-1, buffer.indexOf((byte) 'e'));
    325   }
    326 
    327   @Test public void indexOfWithOffset() throws Exception {
    328     OkBuffer buffer = new OkBuffer();
    329     int halfSegment = Segment.SIZE / 2;
    330     buffer.writeUtf8(repeat('a', halfSegment));
    331     buffer.writeUtf8(repeat('b', halfSegment));
    332     buffer.writeUtf8(repeat('c', halfSegment));
    333     buffer.writeUtf8(repeat('d', halfSegment));
    334     assertEquals(0, buffer.indexOf((byte) 'a', 0));
    335     assertEquals(halfSegment - 1, buffer.indexOf((byte) 'a', halfSegment - 1));
    336     assertEquals(halfSegment, buffer.indexOf((byte) 'b', halfSegment - 1));
    337     assertEquals(halfSegment * 2, buffer.indexOf((byte) 'c', halfSegment - 1));
    338     assertEquals(halfSegment * 3, buffer.indexOf((byte) 'd', halfSegment - 1));
    339     assertEquals(halfSegment * 3, buffer.indexOf((byte) 'd', halfSegment * 2));
    340     assertEquals(halfSegment * 3, buffer.indexOf((byte) 'd', halfSegment * 3));
    341     assertEquals(halfSegment * 4 - 1, buffer.indexOf((byte) 'd', halfSegment * 4 - 1));
    342   }
    343 
    344   @Test public void writeBytes() throws Exception {
    345     OkBuffer data = new OkBuffer();
    346     data.writeByte(0xab);
    347     data.writeByte(0xcd);
    348     assertEquals("OkBuffer[size=2 data=abcd]", data.toString());
    349   }
    350 
    351   @Test public void writeLastByteInSegment() throws Exception {
    352     OkBuffer data = new OkBuffer();
    353     data.writeUtf8(repeat('a', Segment.SIZE - 1));
    354     data.writeByte(0x20);
    355     data.writeByte(0x21);
    356     assertEquals(asList(Segment.SIZE, 1), data.segmentSizes());
    357     assertEquals(repeat('a', Segment.SIZE - 1), data.readUtf8(Segment.SIZE - 1));
    358     assertEquals("OkBuffer[size=2 data=2021]", data.toString());
    359   }
    360 
    361   @Test public void writeShort() throws Exception {
    362     OkBuffer data = new OkBuffer();
    363     data.writeShort(0xabcd);
    364     data.writeShort(0x4321);
    365     assertEquals("OkBuffer[size=4 data=abcd4321]", data.toString());
    366   }
    367 
    368   @Test public void writeShortLe() throws Exception {
    369     OkBuffer data = new OkBuffer();
    370     data.writeShortLe(0xabcd);
    371     data.writeShortLe(0x4321);
    372     assertEquals("OkBuffer[size=4 data=cdab2143]", data.toString());
    373   }
    374 
    375   @Test public void writeInt() throws Exception {
    376     OkBuffer data = new OkBuffer();
    377     data.writeInt(0xabcdef01);
    378     data.writeInt(0x87654321);
    379     assertEquals("OkBuffer[size=8 data=abcdef0187654321]", data.toString());
    380   }
    381 
    382   @Test public void writeLastIntegerInSegment() throws Exception {
    383     OkBuffer data = new OkBuffer();
    384     data.writeUtf8(repeat('a', Segment.SIZE - 4));
    385     data.writeInt(0xabcdef01);
    386     data.writeInt(0x87654321);
    387     assertEquals(asList(Segment.SIZE, 4), data.segmentSizes());
    388     assertEquals(repeat('a', Segment.SIZE - 4), data.readUtf8(Segment.SIZE - 4));
    389     assertEquals("OkBuffer[size=8 data=abcdef0187654321]", data.toString());
    390   }
    391 
    392   @Test public void writeIntegerDoesntQuiteFitInSegment() throws Exception {
    393     OkBuffer data = new OkBuffer();
    394     data.writeUtf8(repeat('a', Segment.SIZE - 3));
    395     data.writeInt(0xabcdef01);
    396     data.writeInt(0x87654321);
    397     assertEquals(asList(Segment.SIZE - 3, 8), data.segmentSizes());
    398     assertEquals(repeat('a', Segment.SIZE - 3), data.readUtf8(Segment.SIZE - 3));
    399     assertEquals("OkBuffer[size=8 data=abcdef0187654321]", data.toString());
    400   }
    401 
    402   @Test public void writeIntLe() throws Exception {
    403     OkBuffer data = new OkBuffer();
    404     data.writeIntLe(0xabcdef01);
    405     data.writeIntLe(0x87654321);
    406     assertEquals("OkBuffer[size=8 data=01efcdab21436587]", data.toString());
    407   }
    408 
    409   @Test public void writeLong() throws Exception {
    410     OkBuffer data = new OkBuffer();
    411     data.writeLong(0xabcdef0187654321L);
    412     data.writeLong(0xcafebabeb0b15c00L);
    413     assertEquals("OkBuffer[size=16 data=abcdef0187654321cafebabeb0b15c00]", data.toString());
    414   }
    415 
    416   @Test public void writeLongLe() throws Exception {
    417     OkBuffer data = new OkBuffer();
    418     data.writeLongLe(0xabcdef0187654321L);
    419     data.writeLongLe(0xcafebabeb0b15c00L);
    420     assertEquals("OkBuffer[size=16 data=2143658701efcdab005cb1b0bebafeca]", data.toString());
    421   }
    422 
    423   @Test public void readByte() throws Exception {
    424     OkBuffer data = new OkBuffer();
    425     data.write(new byte[] { (byte) 0xab, (byte) 0xcd });
    426     assertEquals(0xab, data.readByte() & 0xff);
    427     assertEquals(0xcd, data.readByte() & 0xff);
    428     assertEquals(0, data.size());
    429   }
    430 
    431   @Test public void readShort() throws Exception {
    432     OkBuffer data = new OkBuffer();
    433     data.write(new byte[] {
    434         (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x01
    435     });
    436     assertEquals((short) 0xabcd, data.readShort());
    437     assertEquals((short) 0xef01, data.readShort());
    438     assertEquals(0, data.size());
    439   }
    440 
    441   @Test public void readShortLe() throws Exception {
    442     OkBuffer data = new OkBuffer();
    443     data.write(new byte[] {
    444         (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x10
    445     });
    446     assertEquals((short) 0xcdab, data.readShortLe());
    447     assertEquals((short) 0x10ef, data.readShortLe());
    448     assertEquals(0, data.size());
    449   }
    450 
    451   @Test public void readShortSplitAcrossMultipleSegments() throws Exception {
    452     OkBuffer data = new OkBuffer();
    453     data.writeUtf8(repeat('a', Segment.SIZE - 1));
    454     data.write(new byte[] { (byte) 0xab, (byte) 0xcd });
    455     data.readUtf8(Segment.SIZE - 1);
    456     assertEquals((short) 0xabcd, data.readShort());
    457     assertEquals(0, data.size());
    458   }
    459 
    460   @Test public void readInt() throws Exception {
    461     OkBuffer data = new OkBuffer();
    462     data.write(new byte[] {
    463         (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x01,
    464         (byte) 0x87, (byte) 0x65, (byte) 0x43, (byte) 0x21
    465     });
    466     assertEquals(0xabcdef01, data.readInt());
    467     assertEquals(0x87654321, data.readInt());
    468     assertEquals(0, data.size());
    469   }
    470 
    471   @Test public void readIntLe() throws Exception {
    472     OkBuffer data = new OkBuffer();
    473     data.write(new byte[] {
    474         (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x10,
    475         (byte) 0x87, (byte) 0x65, (byte) 0x43, (byte) 0x21
    476     });
    477     assertEquals(0x10efcdab, data.readIntLe());
    478     assertEquals(0x21436587, data.readIntLe());
    479     assertEquals(0, data.size());
    480   }
    481 
    482   @Test public void readIntSplitAcrossMultipleSegments() throws Exception {
    483     OkBuffer data = new OkBuffer();
    484     data.writeUtf8(repeat('a', Segment.SIZE - 3));
    485     data.write(new byte[] {
    486         (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x01
    487     });
    488     data.readUtf8(Segment.SIZE - 3);
    489     assertEquals(0xabcdef01, data.readInt());
    490     assertEquals(0, data.size());
    491   }
    492 
    493   @Test public void readLong() throws Exception {
    494     OkBuffer data = new OkBuffer();
    495     data.write(new byte[] {
    496         (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x10,
    497         (byte) 0x87, (byte) 0x65, (byte) 0x43, (byte) 0x21,
    498         (byte) 0x36, (byte) 0x47, (byte) 0x58, (byte) 0x69,
    499         (byte) 0x12, (byte) 0x23, (byte) 0x34, (byte) 0x45
    500     });
    501     assertEquals(0xabcdef1087654321L, data.readLong());
    502     assertEquals(0x3647586912233445L, data.readLong());
    503     assertEquals(0, data.size());
    504   }
    505 
    506   @Test public void readLongLe() throws Exception {
    507     OkBuffer data = new OkBuffer();
    508     data.write(new byte[] {
    509         (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x10,
    510         (byte) 0x87, (byte) 0x65, (byte) 0x43, (byte) 0x21,
    511         (byte) 0x36, (byte) 0x47, (byte) 0x58, (byte) 0x69,
    512         (byte) 0x12, (byte) 0x23, (byte) 0x34, (byte) 0x45
    513     });
    514     assertEquals(0x2143658710efcdabL, data.readLongLe());
    515     assertEquals(0x4534231269584736L, data.readLongLe());
    516     assertEquals(0, data.size());
    517   }
    518 
    519   @Test public void readLongSplitAcrossMultipleSegments() throws Exception {
    520     OkBuffer data = new OkBuffer();
    521     data.writeUtf8(repeat('a', Segment.SIZE - 7));
    522     data.write(new byte[] {
    523         (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x01,
    524         (byte) 0x87, (byte) 0x65, (byte) 0x43, (byte) 0x21,
    525     });
    526     data.readUtf8(Segment.SIZE - 7);
    527     assertEquals(0xabcdef0187654321L, data.readLong());
    528     assertEquals(0, data.size());
    529   }
    530 
    531   @Test public void byteAt() throws Exception {
    532     OkBuffer buffer = new OkBuffer();
    533     buffer.writeUtf8("a");
    534     buffer.writeUtf8(repeat('b', Segment.SIZE));
    535     buffer.writeUtf8("c");
    536     assertEquals('a', buffer.getByte(0));
    537     assertEquals('a', buffer.getByte(0)); // getByte doesn't mutate!
    538     assertEquals('c', buffer.getByte(buffer.size - 1));
    539     assertEquals('b', buffer.getByte(buffer.size - 2));
    540     assertEquals('b', buffer.getByte(buffer.size - 3));
    541   }
    542 
    543   @Test public void getByteOfEmptyBuffer() throws Exception {
    544     OkBuffer buffer = new OkBuffer();
    545     try {
    546       buffer.getByte(0);
    547       fail();
    548     } catch (IndexOutOfBoundsException expected) {
    549     }
    550   }
    551 
    552   @Test public void skip() throws Exception {
    553     OkBuffer buffer = new OkBuffer();
    554     buffer.writeUtf8("a");
    555     buffer.writeUtf8(repeat('b', Segment.SIZE));
    556     buffer.writeUtf8("c");
    557     buffer.skip(1);
    558     assertEquals('b', buffer.readByte() & 0xff);
    559     buffer.skip(Segment.SIZE - 2);
    560     assertEquals('b', buffer.readByte() & 0xff);
    561     buffer.skip(1);
    562     assertEquals(0, buffer.size());
    563   }
    564 
    565   @Test public void testWritePrefixToEmptyBuffer() {
    566     OkBuffer sink = new OkBuffer();
    567     OkBuffer source = new OkBuffer();
    568     source.writeUtf8("abcd");
    569     sink.write(source, 2);
    570     assertEquals("ab", sink.readUtf8(2));
    571   }
    572 
    573   @Test public void cloneDoesNotObserveWritesToOriginal() throws Exception {
    574     OkBuffer original = new OkBuffer();
    575     OkBuffer clone = original.clone();
    576     original.writeUtf8("abc");
    577     assertEquals(0, clone.size());
    578   }
    579 
    580   @Test public void cloneDoesNotObserveReadsFromOriginal() throws Exception {
    581     OkBuffer original = new OkBuffer();
    582     original.writeUtf8("abc");
    583     OkBuffer clone = original.clone();
    584     assertEquals("abc", original.readUtf8(3));
    585     assertEquals(3, clone.size());
    586     assertEquals("ab", clone.readUtf8(2));
    587   }
    588 
    589   @Test public void originalDoesNotObserveWritesToClone() throws Exception {
    590     OkBuffer original = new OkBuffer();
    591     OkBuffer clone = original.clone();
    592     clone.writeUtf8("abc");
    593     assertEquals(0, original.size());
    594   }
    595 
    596   @Test public void originalDoesNotObserveReadsFromClone() throws Exception {
    597     OkBuffer original = new OkBuffer();
    598     original.writeUtf8("abc");
    599     OkBuffer clone = original.clone();
    600     assertEquals("abc", clone.readUtf8(3));
    601     assertEquals(3, original.size());
    602     assertEquals("ab", original.readUtf8(2));
    603   }
    604 
    605   @Test public void cloneMultipleSegments() throws Exception {
    606     OkBuffer original = new OkBuffer();
    607     original.writeUtf8(repeat('a', Segment.SIZE * 3));
    608     OkBuffer clone = original.clone();
    609     original.writeUtf8(repeat('b', Segment.SIZE * 3));
    610     clone.writeUtf8(repeat('c', Segment.SIZE * 3));
    611 
    612     assertEquals(repeat('a', Segment.SIZE * 3) + repeat('b', Segment.SIZE * 3),
    613         original.readUtf8(Segment.SIZE * 6));
    614     assertEquals(repeat('a', Segment.SIZE * 3) + repeat('c', Segment.SIZE * 3),
    615         clone.readUtf8(Segment.SIZE * 6));
    616   }
    617 
    618   @Test public void testEqualsAndHashCodeEmpty() throws Exception {
    619     OkBuffer a = new OkBuffer();
    620     OkBuffer b = new OkBuffer();
    621     assertTrue(a.equals(b));
    622     assertTrue(a.hashCode() == b.hashCode());
    623   }
    624 
    625   @Test public void testEqualsAndHashCode() throws Exception {
    626     OkBuffer a = new OkBuffer().writeUtf8("dog");
    627     OkBuffer b = new OkBuffer().writeUtf8("hotdog");
    628     assertFalse(a.equals(b));
    629     assertFalse(a.hashCode() == b.hashCode());
    630 
    631     b.readUtf8(3); // Leaves b containing 'dog'.
    632     assertTrue(a.equals(b));
    633     assertTrue(a.hashCode() == b.hashCode());
    634   }
    635 
    636   @Test public void testEqualsAndHashCodeSpanningSegments() throws Exception {
    637     byte[] data = new byte[1024 * 1024];
    638     Random dice = new Random(0);
    639     dice.nextBytes(data);
    640 
    641     OkBuffer a = bufferWithRandomSegmentLayout(dice, data);
    642     OkBuffer b = bufferWithRandomSegmentLayout(dice, data);
    643     assertTrue(a.equals(b));
    644     assertTrue(a.hashCode() == b.hashCode());
    645 
    646     data[data.length / 2]++; // Change a single byte.
    647     OkBuffer c = bufferWithRandomSegmentLayout(dice, data);
    648     assertFalse(a.equals(c));
    649     assertFalse(a.hashCode() == c.hashCode());
    650   }
    651 
    652   /**
    653    * Returns a new buffer containing the data in {@code data}, and a segment
    654    * layout determined by {@code dice}.
    655    */
    656   private OkBuffer bufferWithRandomSegmentLayout(Random dice, byte[] data) {
    657     OkBuffer result = new OkBuffer();
    658 
    659     // Writing to result directly will yield packed segments. Instead, write to
    660     // other buffers, then write those buffers to result.
    661     for (int pos = 0, byteCount; pos < data.length; pos += byteCount) {
    662       byteCount = (Segment.SIZE / 2) + dice.nextInt(Segment.SIZE / 2);
    663       if (byteCount > data.length - pos) byteCount = data.length - pos;
    664       int offset = dice.nextInt(Segment.SIZE - byteCount);
    665 
    666       OkBuffer segment = new OkBuffer();
    667       segment.write(new byte[offset]);
    668       segment.write(data, pos, byteCount);
    669       segment.skip(offset);
    670 
    671       result.write(segment, byteCount);
    672     }
    673 
    674     return result;
    675   }
    676 
    677   private String repeat(char c, int count) {
    678     char[] array = new char[count];
    679     Arrays.fill(array, c);
    680     return new String(array);
    681   }
    682 }
    683