OkBufferTest.java revision 22eb01de23556e7ef34d97140277bc7a8f4740ce
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 */
16package okio;
17
18import java.util.Arrays;
19import java.util.List;
20import java.util.Random;
21import org.junit.Test;
22
23import static java.util.Arrays.asList;
24import static org.junit.Assert.assertEquals;
25import static org.junit.Assert.assertFalse;
26import static org.junit.Assert.assertTrue;
27import static org.junit.Assert.fail;
28
29public 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