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.io.EOFException; 19import java.io.IOException; 20import java.io.InputStream; 21 22import static okio.Util.checkOffsetAndCount; 23 24final class RealBufferedSource implements BufferedSource { 25 public final OkBuffer buffer; 26 public final Source source; 27 private boolean closed; 28 29 public RealBufferedSource(Source source, OkBuffer buffer) { 30 if (source == null) throw new IllegalArgumentException("source == null"); 31 this.buffer = buffer; 32 this.source = source; 33 } 34 35 public RealBufferedSource(Source source) { 36 this(source, new OkBuffer()); 37 } 38 39 @Override public OkBuffer buffer() { 40 return buffer; 41 } 42 43 @Override public long read(OkBuffer sink, long byteCount) throws IOException { 44 if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); 45 if (closed) throw new IllegalStateException("closed"); 46 47 if (buffer.size == 0) { 48 long read = source.read(buffer, Segment.SIZE); 49 if (read == -1) return -1; 50 } 51 52 long toRead = Math.min(byteCount, buffer.size); 53 return buffer.read(sink, toRead); 54 } 55 56 @Override public boolean exhausted() throws IOException { 57 if (closed) throw new IllegalStateException("closed"); 58 return buffer.exhausted() && source.read(buffer, Segment.SIZE) == -1; 59 } 60 61 @Override public void require(long byteCount) throws IOException { 62 if (closed) throw new IllegalStateException("closed"); 63 while (buffer.size < byteCount) { 64 if (source.read(buffer, Segment.SIZE) == -1) throw new EOFException(); 65 } 66 } 67 68 @Override public byte readByte() throws IOException { 69 require(1); 70 return buffer.readByte(); 71 } 72 73 @Override public ByteString readByteString(long byteCount) throws IOException { 74 require(byteCount); 75 return buffer.readByteString(byteCount); 76 } 77 78 @Override public String readUtf8(long byteCount) throws IOException { 79 require(byteCount); 80 return buffer.readUtf8(byteCount); 81 } 82 83 @Override public String readUtf8Line() throws IOException { 84 long newline = indexOf((byte) '\n'); 85 86 if (newline == -1) { 87 return buffer.size != 0 ? readUtf8(buffer.size) : null; 88 } 89 90 return buffer.readUtf8Line(newline); 91 } 92 93 @Override public String readUtf8LineStrict() throws IOException { 94 long newline = indexOf((byte) '\n'); 95 if (newline == -1L) throw new EOFException(); 96 return buffer.readUtf8Line(newline); 97 } 98 99 @Override public short readShort() throws IOException { 100 require(2); 101 return buffer.readShort(); 102 } 103 104 @Override public short readShortLe() throws IOException { 105 require(2); 106 return buffer.readShortLe(); 107 } 108 109 @Override public int readInt() throws IOException { 110 require(4); 111 return buffer.readInt(); 112 } 113 114 @Override public int readIntLe() throws IOException { 115 require(4); 116 return buffer.readIntLe(); 117 } 118 119 @Override public long readLong() throws IOException { 120 require(8); 121 return buffer.readLong(); 122 } 123 124 @Override public long readLongLe() throws IOException { 125 require(8); 126 return buffer.readLongLe(); 127 } 128 129 @Override public void skip(long byteCount) throws IOException { 130 if (closed) throw new IllegalStateException("closed"); 131 while (byteCount > 0) { 132 if (buffer.size == 0 && source.read(buffer, Segment.SIZE) == -1) { 133 throw new EOFException(); 134 } 135 long toSkip = Math.min(byteCount, buffer.size()); 136 buffer.skip(toSkip); 137 byteCount -= toSkip; 138 } 139 } 140 141 @Override public long indexOf(byte b) throws IOException { 142 if (closed) throw new IllegalStateException("closed"); 143 long start = 0; 144 long index; 145 while ((index = buffer.indexOf(b, start)) == -1) { 146 start = buffer.size; 147 if (source.read(buffer, Segment.SIZE) == -1) return -1L; 148 } 149 return index; 150 } 151 152 @Override public InputStream inputStream() { 153 return new InputStream() { 154 @Override public int read() throws IOException { 155 if (closed) throw new IOException("closed"); 156 if (buffer.size == 0) { 157 long count = source.read(buffer, Segment.SIZE); 158 if (count == -1) return -1; 159 } 160 return buffer.readByte() & 0xff; 161 } 162 163 @Override public int read(byte[] data, int offset, int byteCount) throws IOException { 164 if (closed) throw new IOException("closed"); 165 checkOffsetAndCount(data.length, offset, byteCount); 166 167 if (buffer.size == 0) { 168 long count = source.read(buffer, Segment.SIZE); 169 if (count == -1) return -1; 170 } 171 172 return buffer.read(data, offset, byteCount); 173 } 174 175 @Override public int available() throws IOException { 176 if (closed) throw new IOException("closed"); 177 return (int) Math.min(buffer.size, Integer.MAX_VALUE); 178 } 179 180 @Override public void close() throws IOException { 181 RealBufferedSource.this.close(); 182 } 183 184 @Override public String toString() { 185 return RealBufferedSource.this + ".inputStream()"; 186 } 187 }; 188 } 189 190 @Override public Source deadline(Deadline deadline) { 191 source.deadline(deadline); 192 return this; 193 } 194 195 @Override public void close() throws IOException { 196 if (closed) return; 197 closed = true; 198 source.close(); 199 buffer.clear(); 200 } 201 202 @Override public String toString() { 203 return "buffer(" + source + ")"; 204 } 205} 206