| /* |
| * Copyright (C) 2015 Square, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package okio |
| |
| import okio.ByteString.Companion.encodeUtf8 |
| import okio.TestUtil.assertEquivalent |
| import okio.TestUtil.bufferWithSegments |
| import okio.TestUtil.takeAllPoolSegments |
| import org.junit.Test |
| import kotlin.test.assertEquals |
| import kotlin.test.assertFailsWith |
| import kotlin.test.assertTrue |
| |
| /** Tests behavior optimized by sharing segments between buffers and byte strings. */ |
| class SegmentSharingTest { |
| @Test fun snapshotOfEmptyBuffer() { |
| val snapshot = Buffer().snapshot() |
| assertEquivalent(snapshot, ByteString.EMPTY) |
| } |
| |
| @Test fun snapshotsAreEquivalent() { |
| val byteString = bufferWithSegments(xs, ys, zs).snapshot() |
| assertEquivalent(byteString, bufferWithSegments(xs, ys + zs).snapshot()) |
| assertEquivalent(byteString, bufferWithSegments(xs + ys + zs).snapshot()) |
| assertEquivalent(byteString, (xs + ys + zs).encodeUtf8()) |
| } |
| |
| @Test fun snapshotGetByte() { |
| val byteString = bufferWithSegments(xs, ys, zs).snapshot() |
| assertEquals('x', byteString[0].toChar()) |
| assertEquals('x', byteString[xs.length - 1].toChar()) |
| assertEquals('y', byteString[xs.length].toChar()) |
| assertEquals('y', byteString[xs.length + ys.length - 1].toChar()) |
| assertEquals('z', byteString[xs.length + ys.length].toChar()) |
| assertEquals('z', byteString[xs.length + ys.length + zs.length - 1].toChar()) |
| assertFailsWith<IndexOutOfBoundsException> { |
| byteString[-1] |
| } |
| |
| assertFailsWith<IndexOutOfBoundsException> { |
| byteString[xs.length + ys.length + zs.length] |
| } |
| } |
| |
| @Test fun snapshotWriteToOutputStream() { |
| val byteString = bufferWithSegments(xs, ys, zs).snapshot() |
| val out = Buffer() |
| byteString.write(out.outputStream()) |
| assertEquals(xs + ys + zs, out.readUtf8()) |
| } |
| |
| /** |
| * Snapshots share their backing byte arrays with the source buffers. Those byte arrays must not |
| * be recycled, otherwise the new writer could corrupt the segment. |
| */ |
| @Test fun snapshotSegmentsAreNotRecycled() { |
| val buffer = bufferWithSegments(xs, ys, zs) |
| val snapshot = buffer.snapshot() |
| assertEquals(xs + ys + zs, snapshot.utf8()) |
| |
| // Confirm that clearing the buffer doesn't release its segments. |
| val bufferHead = buffer.head |
| takeAllPoolSegments() // Make room for new segments. |
| buffer.clear() |
| assertTrue(bufferHead !in takeAllPoolSegments()) |
| } |
| |
| /** |
| * Clones share their backing byte arrays with the source buffers. Those byte arrays must not |
| * be recycled, otherwise the new writer could corrupt the segment. |
| */ |
| @Test fun cloneSegmentsAreNotRecycled() { |
| val buffer = bufferWithSegments(xs, ys, zs) |
| val clone = buffer.clone() |
| |
| // While locking the pool, confirm that clearing the buffer doesn't release its segments. |
| val bufferHead = buffer.head!! |
| takeAllPoolSegments() // Make room for new segments. |
| buffer.clear() |
| assertTrue(bufferHead !in takeAllPoolSegments()) |
| |
| val cloneHead = clone.head!! |
| takeAllPoolSegments() // Make room for new segments. |
| clone.clear() |
| assertTrue(cloneHead !in takeAllPoolSegments()) |
| } |
| |
| @Test fun snapshotJavaSerialization() { |
| val byteString = bufferWithSegments(xs, ys, zs).snapshot() |
| assertEquivalent(byteString, TestUtil.reserialize(byteString)) |
| } |
| |
| @Test fun clonesAreEquivalent() { |
| val bufferA = bufferWithSegments(xs, ys, zs) |
| val bufferB = bufferA.clone() |
| assertEquivalent(bufferA, bufferB) |
| assertEquivalent(bufferA, bufferWithSegments(xs + ys, zs)) |
| } |
| |
| /** Even though some segments are shared, clones can be mutated independently. */ |
| @Test fun mutateAfterClone() { |
| val bufferA = Buffer() |
| bufferA.writeUtf8("abc") |
| val bufferB = bufferA.clone() |
| bufferA.writeUtf8("def") |
| bufferB.writeUtf8("DEF") |
| assertEquals("abcdef", bufferA.readUtf8()) |
| assertEquals("abcDEF", bufferB.readUtf8()) |
| } |
| |
| @Test fun concatenateSegmentsCanCombine() { |
| val bufferA = Buffer().writeUtf8(ys).writeUtf8(us) |
| assertEquals(ys, bufferA.readUtf8(ys.length.toLong())) |
| val bufferB = Buffer().writeUtf8(vs).writeUtf8(ws) |
| val bufferC = bufferA.clone() |
| bufferA.write(bufferB, vs.length.toLong()) |
| bufferC.writeUtf8(xs) |
| |
| assertEquals(us + vs, bufferA.readUtf8()) |
| assertEquals(ws, bufferB.readUtf8()) |
| assertEquals(us + xs, bufferC.readUtf8()) |
| } |
| |
| @Test fun shareAndSplit() { |
| val bufferA = Buffer().writeUtf8("xxxx") |
| val snapshot = bufferA.snapshot() // Share the segment. |
| val bufferB = Buffer() |
| bufferB.write(bufferA, 2) // Split the shared segment in two. |
| bufferB.writeUtf8("yy") // Append to the first half of the shared segment. |
| assertEquals("xxxx", snapshot.utf8()) |
| } |
| |
| @Test fun appendSnapshotToEmptyBuffer() { |
| val bufferA = bufferWithSegments(xs, ys) |
| val snapshot = bufferA.snapshot() |
| val bufferB = Buffer() |
| bufferB.write(snapshot) |
| assertEquivalent(bufferB, bufferA) |
| } |
| |
| @Test fun appendSnapshotToNonEmptyBuffer() { |
| val bufferA = bufferWithSegments(xs, ys) |
| val snapshot = bufferA.snapshot() |
| val bufferB = Buffer().writeUtf8(us) |
| bufferB.write(snapshot) |
| assertEquivalent(bufferB, Buffer().writeUtf8(us + xs + ys)) |
| } |
| |
| @Test fun copyToSegmentSharing() { |
| val bufferA = bufferWithSegments(ws, xs + "aaaa", ys, "bbbb$zs") |
| val bufferB = bufferWithSegments(us) |
| bufferA.copyTo(bufferB, (ws.length + xs.length).toLong(), (4 + ys.length + 4).toLong()) |
| assertEquivalent(bufferB, Buffer().writeUtf8(us + "aaaa" + ys + "bbbb")) |
| } |
| } |
| |
| private val us = "u".repeat(Segment.SIZE / 2 - 2) |
| private val vs = "v".repeat(Segment.SIZE / 2 - 1) |
| private val ws = "w".repeat(Segment.SIZE / 2) |
| private val xs = "x".repeat(Segment.SIZE / 2 + 1) |
| private val ys = "y".repeat(Segment.SIZE / 2 + 2) |
| private val zs = "z".repeat(Segment.SIZE / 2 + 3) |