1a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller/* 2a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * Copyright (C) 2015 Square, Inc. 3a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * 4a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * Licensed under the Apache License, Version 2.0 (the "License"); 5a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * you may not use this file except in compliance with the License. 6a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * You may obtain a copy of the License at 7a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * 8a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * http://www.apache.org/licenses/LICENSE-2.0 9a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * 10a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * Unless required by applicable law or agreed to in writing, software 11a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * distributed under the License is distributed on an "AS IS" BASIS, 12a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * See the License for the specific language governing permissions and 14a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * limitations under the License. 15a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller */ 16a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerpackage okio; 17a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 18a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport org.junit.Test; 19a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 20a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport static okio.TestUtil.assertEquivalent; 21a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport static org.junit.Assert.assertEquals; 22a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport static org.junit.Assert.fail; 23a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 24a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller/** Tests behavior optimized by sharing segments between buffers and byte strings. */ 25a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerpublic final class SegmentSharingTest { 26a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller private static final String us = TestUtil.repeat('u', Segment.SIZE / 2 - 2); 27a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller private static final String vs = TestUtil.repeat('v', Segment.SIZE / 2 - 1); 28a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller private static final String ws = TestUtil.repeat('w', Segment.SIZE / 2); 29a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller private static final String xs = TestUtil.repeat('x', Segment.SIZE / 2 + 1); 30a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller private static final String ys = TestUtil.repeat('y', Segment.SIZE / 2 + 2); 31a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller private static final String zs = TestUtil.repeat('z', Segment.SIZE / 2 + 3); 32a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 33a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Test public void snapshotOfEmptyBuffer() throws Exception { 34a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller ByteString snapshot = new Buffer().snapshot(); 35a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquivalent(snapshot, ByteString.EMPTY); 36a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 37a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 38a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Test public void snapshotsAreEquivalent() throws Exception { 39a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller ByteString byteString = concatenateBuffers(xs, ys, zs).snapshot(); 40a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquivalent(byteString, concatenateBuffers(xs, ys + zs).snapshot()); 41a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquivalent(byteString, concatenateBuffers(xs + ys + zs).snapshot()); 42a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquivalent(byteString, ByteString.encodeUtf8(xs + ys + zs)); 43a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 44a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 45a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Test public void snapshotGetByte() throws Exception { 46a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller ByteString byteString = concatenateBuffers(xs, ys, zs).snapshot(); 47a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals('x', byteString.getByte(0)); 48a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals('x', byteString.getByte(xs.length() - 1)); 49a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals('y', byteString.getByte(xs.length())); 50a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals('y', byteString.getByte(xs.length() + ys.length() - 1)); 51a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals('z', byteString.getByte(xs.length() + ys.length())); 52a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals('z', byteString.getByte(xs.length() + ys.length() + zs.length() - 1)); 53a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller try { 54a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller byteString.getByte(-1); 55a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller fail(); 56a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } catch (IndexOutOfBoundsException expected) { 57a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 58a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller try { 59a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller byteString.getByte(xs.length() + ys.length() + zs.length()); 60a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller fail(); 61a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } catch (IndexOutOfBoundsException expected) { 62a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 63a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 64a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 65a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Test public void snapshotWriteToOutputStream() throws Exception { 66a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller ByteString byteString = concatenateBuffers(xs, ys, zs).snapshot(); 67a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Buffer out = new Buffer(); 68a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller byteString.write(out.outputStream()); 69a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals(xs + ys + zs, out.readUtf8()); 70a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 71a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 72a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller /** 73a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * Snapshots share their backing byte arrays with the source buffers. Those byte arrays must not 74a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * be recycled, otherwise the new writer could corrupt the segment. 75a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller */ 76a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Test public void snapshotSegmentsAreNotRecycled() throws Exception { 77a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Buffer buffer = concatenateBuffers(xs, ys, zs); 78a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller ByteString snapshot = buffer.snapshot(); 79a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals(xs + ys + zs, snapshot.utf8()); 80a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 81a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller // While locking the pool, confirm that clearing the buffer doesn't release its segments. 82a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller synchronized (SegmentPool.class) { 83a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller SegmentPool.next = null; 84a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller SegmentPool.byteCount = 0L; 85a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller buffer.clear(); 86a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals(null, SegmentPool.next); 87a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 88a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 89a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 90a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller /** 91a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * Clones share their backing byte arrays with the source buffers. Those byte arrays must not 92a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * be recycled, otherwise the new writer could corrupt the segment. 93a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller */ 94a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Test public void cloneSegmentsAreNotRecycled() throws Exception { 95a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Buffer buffer = concatenateBuffers(xs, ys, zs); 96a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Buffer clone = buffer.clone(); 97a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 98a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller // While locking the pool, confirm that clearing the buffer doesn't release its segments. 99a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller synchronized (SegmentPool.class) { 100a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller SegmentPool.next = null; 101a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller SegmentPool.byteCount = 0L; 102a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller buffer.clear(); 103a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals(null, SegmentPool.next); 104a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller clone.clear(); 105a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals(null, SegmentPool.next); 106a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 107a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 108a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 109a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Test public void snapshotJavaSerialization() throws Exception { 110a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller ByteString byteString = concatenateBuffers(xs, ys, zs).snapshot(); 111a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquivalent(byteString, TestUtil.reserialize(byteString)); 112a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 113a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 114a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Test public void clonesAreEquivalent() throws Exception { 115a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Buffer bufferA = concatenateBuffers(xs, ys, zs); 116a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Buffer bufferB = bufferA.clone(); 117a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquivalent(bufferA, bufferB); 118a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquivalent(bufferA, concatenateBuffers(xs + ys, zs)); 119a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 120a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 121a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller /** Even though some segments are shared, clones can be mutated independently. */ 122a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Test public void mutateAfterClone() throws Exception { 123a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Buffer bufferA = new Buffer(); 124a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller bufferA.writeUtf8("abc"); 125a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Buffer bufferB = bufferA.clone(); 126a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller bufferA.writeUtf8("def"); 127a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller bufferB.writeUtf8("DEF"); 128a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals("abcdef", bufferA.readUtf8()); 129a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals("abcDEF", bufferB.readUtf8()); 130a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 131a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 132a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Test public void concatenateSegmentsCanCombine() throws Exception { 133a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Buffer bufferA = new Buffer().writeUtf8(ys).writeUtf8(us); 134a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals(ys, bufferA.readUtf8(ys.length())); 135a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Buffer bufferB = new Buffer().writeUtf8(vs).writeUtf8(ws); 136a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Buffer bufferC = bufferA.clone(); 137a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller bufferA.write(bufferB, vs.length()); 138a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller bufferC.writeUtf8(xs); 139a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 140a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals(us + vs, bufferA.readUtf8()); 141a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals(ws, bufferB.readUtf8()); 142a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals(us + xs, bufferC.readUtf8()); 143a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 144a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 145a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Test public void shareAndSplit() throws Exception { 146a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Buffer bufferA = new Buffer().writeUtf8("xxxx"); 147a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller ByteString snapshot = bufferA.snapshot(); // Share the segment. 148a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Buffer bufferB = new Buffer(); 149a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller bufferB.write(bufferA, 2); // Split the shared segment in two. 150a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller bufferB.writeUtf8("yy"); // Append to the first half of the shared segment. 151a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals("xxxx", snapshot.utf8()); 152a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 153a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 154a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Test public void appendSnapshotToEmptyBuffer() throws Exception { 155a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Buffer bufferA = concatenateBuffers(xs, ys); 156a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller ByteString snapshot = bufferA.snapshot(); 157a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Buffer bufferB = new Buffer(); 158a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller bufferB.write(snapshot); 159a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquivalent(bufferB, bufferA); 160a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 161a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 162a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Test public void appendSnapshotToNonEmptyBuffer() throws Exception { 163a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Buffer bufferA = concatenateBuffers(xs, ys); 164a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller ByteString snapshot = bufferA.snapshot(); 165a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Buffer bufferB = new Buffer().writeUtf8(us); 166a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller bufferB.write(snapshot); 167a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquivalent(bufferB, new Buffer().writeUtf8(us + xs + ys)); 168a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 169a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 170a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Test public void copyToSegmentSharing() throws Exception { 171a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Buffer bufferA = concatenateBuffers(ws, xs + "aaaa", ys, "bbbb" + zs); 172a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Buffer bufferB = concatenateBuffers(us); 173a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller bufferA.copyTo(bufferB, ws.length() + xs.length(), 4 + ys.length() + 4); 174a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquivalent(bufferB, new Buffer().writeUtf8(us + "aaaa" + ys + "bbbb")); 175a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 176a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 177a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller /** 178a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * Returns a new buffer containing the contents of {@code segments}, attempting to isolate each 179a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * string to its own segment in the returned buffer. 180a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller */ 181a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller public Buffer concatenateBuffers(String... segments) throws Exception { 182a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Buffer result = new Buffer(); 183a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller for (String s : segments) { 184a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller int offsetInSegment = s.length() < Segment.SIZE ? (Segment.SIZE - s.length()) / 2 : 0; 185a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Buffer buffer = new Buffer(); 186a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller buffer.writeUtf8(TestUtil.repeat('_', offsetInSegment)); 187a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller buffer.writeUtf8(s); 188a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller buffer.skip(offsetInSegment); 189a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller result.write(buffer, buffer.size); 190a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 191a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller return result; 192a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 193a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller} 194