1 /* 2 * Copyright (C) 2018 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 17 package okio 18 19 /** 20 * A [Source] which peeks into an upstream [BufferedSource] and allows reading and expanding of the 21 * buffered data without consuming it. Does this by requesting additional data from the upstream 22 * source if needed and copying out of the internal buffer of the upstream source if possible. 23 * 24 * This source also maintains a snapshot of the starting location of the upstream buffer which it 25 * validates against on every read. If the upstream buffer is read from, this source will become 26 * invalid and throw [IllegalStateException] on any future reads. 27 */ 28 internal class PeekSource( 29 private val upstream: BufferedSource, 30 ) : Source { 31 private val buffer = upstream.buffer 32 private var expectedSegment = buffer.head 33 private var expectedPos = buffer.head?.pos ?: -1 34 35 private var closed = false 36 private var pos = 0L 37 readnull38 override fun read(sink: Buffer, byteCount: Long): Long { 39 require(byteCount >= 0L) { "byteCount < 0: $byteCount" } 40 check(!closed) { "closed" } 41 // Source becomes invalid if there is an expected Segment and it and the expected position 42 // do not match the current head and head position of the upstream buffer 43 check( 44 expectedSegment == null || 45 expectedSegment === buffer.head && expectedPos == buffer.head!!.pos, 46 ) { 47 "Peek source is invalid because upstream source was used" 48 } 49 if (byteCount == 0L) return 0L 50 if (!upstream.request(pos + 1)) return -1L 51 52 if (expectedSegment == null && buffer.head != null) { 53 // Only once the buffer actually holds data should an expected Segment and position be 54 // recorded. This allows reads from the peek source to repeatedly return -1 and for data to be 55 // added later. Unit tests depend on this behavior. 56 expectedSegment = buffer.head 57 expectedPos = buffer.head!!.pos 58 } 59 60 val toCopy = minOf(byteCount, buffer.size - pos) 61 buffer.copyTo(sink, pos, toCopy) 62 pos += toCopy 63 return toCopy 64 } 65 timeoutnull66 override fun timeout(): Timeout { 67 return upstream.timeout() 68 } 69 closenull70 override fun close() { 71 closed = true 72 } 73 } 74