1
16 package okio;
17
18 import java.io.EOFException;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.nio.ByteBuffer;
22 import java.nio.charset.Charset;
23 import javax.annotation.Nullable;
24
25 import static okio.Util.checkOffsetAndCount;
26
27 final class RealBufferedSource implements BufferedSource {
28 public final Buffer buffer = new Buffer();
29 public final Source source;
30 boolean closed;
31
32 RealBufferedSource(Source source) {
33 if (source == null) throw new NullPointerException("source == null");
34 this.source = source;
35 }
36
37 @Override public Buffer buffer() {
38 return buffer;
39 }
40
41 @Override public Buffer getBuffer() {
42 return buffer;
43 }
44
45 @Override public long read(Buffer sink, long byteCount) throws IOException {
46 if (sink == null) throw new IllegalArgumentException("sink == null");
47 if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
48 if (closed) throw new IllegalStateException("closed");
49
50 if (buffer.size == 0) {
51 long read = source.read(buffer, Segment.SIZE);
52 if (read == -1) return -1;
53 }
54
55 long toRead = Math.min(byteCount, buffer.size);
56 return buffer.read(sink, toRead);
57 }
58
59 @Override public boolean exhausted() throws IOException {
60 if (closed) throw new IllegalStateException("closed");
61 return buffer.exhausted() && source.read(buffer, Segment.SIZE) == -1;
62 }
63
64 @Override public void require(long byteCount) throws IOException {
65 if (!request(byteCount)) throw new EOFException();
66 }
67
68 @Override public boolean request(long byteCount) throws IOException {
69 if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
70 if (closed) throw new IllegalStateException("closed");
71 while (buffer.size < byteCount) {
72 if (source.read(buffer, Segment.SIZE) == -1) return false;
73 }
74 return true;
75 }
76
77 @Override public byte readByte() throws IOException {
78 require(1);
79 return buffer.readByte();
80 }
81
82 @Override public ByteString readByteString() throws IOException {
83 buffer.writeAll(source);
84 return buffer.readByteString();
85 }
86
87 @Override public ByteString readByteString(long byteCount) throws IOException {
88 require(byteCount);
89 return buffer.readByteString(byteCount);
90 }
91
92 @Override public int select(Options options) throws IOException {
93 if (closed) throw new IllegalStateException("closed");
94
95 while (true) {
96 int index = buffer.selectPrefix(options, true);
97 if (index == -1) return -1;
98 if (index == -2) {
99
100 if (source.read(buffer, Segment.SIZE) == -1L) return -1;
101 } else {
102
103 int selectedSize = options.byteStrings[index].size();
104 buffer.skip(selectedSize);
105 return index;
106 }
107 }
108 }
109
110 @Override public byte[] readByteArray() throws IOException {
111 buffer.writeAll(source);
112 return buffer.readByteArray();
113 }
114
115 @Override public byte[] readByteArray(long byteCount) throws IOException {
116 require(byteCount);
117 return buffer.readByteArray(byteCount);
118 }
119
120 @Override public int read(byte[] sink) throws IOException {
121 return read(sink, 0, sink.length);
122 }
123
124 @Override public void readFully(byte[] sink) throws IOException {
125 try {
126 require(sink.length);
127 } catch (EOFException e) {
128
129 int offset = 0;
130 while (buffer.size > 0) {
131 int read = buffer.read(sink, offset, (int) buffer.size);
132 if (read == -1) throw new AssertionError();
133 offset += read;
134 }
135 throw e;
136 }
137 buffer.readFully(sink);
138 }
139
140 @Override public int read(byte[] sink, int offset, int byteCount) throws IOException {
141 checkOffsetAndCount(sink.length, offset, byteCount);
142
143 if (buffer.size == 0) {
144 long read = source.read(buffer, Segment.SIZE);
145 if (read == -1) return -1;
146 }
147
148 int toRead = (int) Math.min(byteCount, buffer.size);
149 return buffer.read(sink, offset, toRead);
150 }
151
152 @Override public int read(ByteBuffer sink) throws IOException {
153 if (buffer.size == 0) {
154 long read = source.read(buffer, Segment.SIZE);
155 if (read == -1) return -1;
156 }
157
158 return buffer.read(sink);
159 }
160
161 @Override public void readFully(Buffer sink, long byteCount) throws IOException {
162 try {
163 require(byteCount);
164 } catch (EOFException e) {
165
166 sink.writeAll(buffer);
167 throw e;
168 }
169 buffer.readFully(sink, byteCount);
170 }
171
172 @Override public long readAll(Sink sink) throws IOException {
173 if (sink == null) throw new IllegalArgumentException("sink == null");
174
175 long totalBytesWritten = 0;
176 while (source.read(buffer, Segment.SIZE) != -1) {
177 long emitByteCount = buffer.completeSegmentByteCount();
178 if (emitByteCount > 0) {
179 totalBytesWritten += emitByteCount;
180 sink.write(buffer, emitByteCount);
181 }
182 }
183 if (buffer.size() > 0) {
184 totalBytesWritten += buffer.size();
185 sink.write(buffer, buffer.size());
186 }
187 return totalBytesWritten;
188 }
189
190 @Override public String readUtf8() throws IOException {
191 buffer.writeAll(source);
192 return buffer.readUtf8();
193 }
194
195 @Override public String readUtf8(long byteCount) throws IOException {
196 require(byteCount);
197 return buffer.readUtf8(byteCount);
198 }
199
200 @Override public String readString(Charset charset) throws IOException {
201 if (charset == null) throw new IllegalArgumentException("charset == null");
202
203 buffer.writeAll(source);
204 return buffer.readString(charset);
205 }
206
207 @Override public String readString(long byteCount, Charset charset) throws IOException {
208 require(byteCount);
209 if (charset == null) throw new IllegalArgumentException("charset == null");
210 return buffer.readString(byteCount, charset);
211 }
212
213 @Override public @Nullable String readUtf8Line() throws IOException {
214 long newline = indexOf((byte) '\n');
215
216 if (newline == -1) {
217 return buffer.size != 0 ? readUtf8(buffer.size) : null;
218 }
219
220 return buffer.readUtf8Line(newline);
221 }
222
223 @Override public String readUtf8LineStrict() throws IOException {
224 return readUtf8LineStrict(Long.MAX_VALUE);
225 }
226
227 @Override public String readUtf8LineStrict(long limit) throws IOException {
228 if (limit < 0) throw new IllegalArgumentException("limit < 0: " + limit);
229 long scanLength = limit == Long.MAX_VALUE ? Long.MAX_VALUE : limit + 1;
230 long newline = indexOf((byte) '\n', 0, scanLength);
231 if (newline != -1) return buffer.readUtf8Line(newline);
232 if (scanLength < Long.MAX_VALUE
233 && request(scanLength) && buffer.getByte(scanLength - 1) == '\r'
234 && request(scanLength + 1) && buffer.getByte(scanLength) == '\n') {
235 return buffer.readUtf8Line(scanLength);
236 }
237 Buffer data = new Buffer();
238 buffer.copyTo(data, 0, Math.min(32, buffer.size()));
239 throw new EOFException("\\n not found: limit=" + Math.min(buffer.size(), limit)
240 + " content=" + data.readByteString().hex() + '…');
241 }
242
243 @Override public int readUtf8CodePoint() throws IOException {
244 require(1);
245
246 byte b0 = buffer.getByte(0);
247 if ((b0 & 0xe0) == 0xc0) {
248 require(2);
249 } else if ((b0 & 0xf0) == 0xe0) {
250 require(3);
251 } else if ((b0 & 0xf8) == 0xf0) {
252 require(4);
253 }
254
255 return buffer.readUtf8CodePoint();
256 }
257
258 @Override public short readShort() throws IOException {
259 require(2);
260 return buffer.readShort();
261 }
262
263 @Override public short readShortLe() throws IOException {
264 require(2);
265 return buffer.readShortLe();
266 }
267
268 @Override public int readInt() throws IOException {
269 require(4);
270 return buffer.readInt();
271 }
272
273 @Override public int readIntLe() throws IOException {
274 require(4);
275 return buffer.readIntLe();
276 }
277
278 @Override public long readLong() throws IOException {
279 require(8);
280 return buffer.readLong();
281 }
282
283 @Override public long readLongLe() throws IOException {
284 require(8);
285 return buffer.readLongLe();
286 }
287
288 @Override public long readDecimalLong() throws IOException {
289 require(1);
290
291 for (int pos = 0; request(pos + 1); pos++) {
292 byte b = buffer.getByte(pos);
293 if ((b < '0' || b > '9') && (pos != 0 || b != '-')) {
294
295 if (pos == 0) {
296 throw new NumberFormatException(String.format(
297 "Expected leading [0-9] or '-' character but was %#x", b));
298 }
299 break;
300 }
301 }
302
303 return buffer.readDecimalLong();
304 }
305
306 @Override public long readHexadecimalUnsignedLong() throws IOException {
307 require(1);
308
309 for (int pos = 0; request(pos + 1); pos++) {
310 byte b = buffer.getByte(pos);
311 if ((b < '0' || b > '9') && (b < 'a' || b > 'f') && (b < 'A' || b > 'F')) {
312
313 if (pos == 0) {
314 throw new NumberFormatException(String.format(
315 "Expected leading [0-9a-fA-F] character but was %#x", b));
316 }
317 break;
318 }
319 }
320
321 return buffer.readHexadecimalUnsignedLong();
322 }
323
324 @Override public void skip(long byteCount) throws IOException {
325 if (closed) throw new IllegalStateException("closed");
326 while (byteCount > 0) {
327 if (buffer.size == 0 && source.read(buffer, Segment.SIZE) == -1) {
328 throw new EOFException();
329 }
330 long toSkip = Math.min(byteCount, buffer.size());
331 buffer.skip(toSkip);
332 byteCount -= toSkip;
333 }
334 }
335
336 @Override public long indexOf(byte b) throws IOException {
337 return indexOf(b, 0, Long.MAX_VALUE);
338 }
339
340 @Override public long indexOf(byte b, long fromIndex) throws IOException {
341 return indexOf(b, fromIndex, Long.MAX_VALUE);
342 }
343
344 @Override public long indexOf(byte b, long fromIndex, long toIndex) throws IOException {
345 if (closed) throw new IllegalStateException("closed");
346 if (fromIndex < 0 || toIndex < fromIndex) {
347 throw new IllegalArgumentException(
348 String.format("fromIndex=%s toIndex=%s", fromIndex, toIndex));
349 }
350
351 while (fromIndex < toIndex) {
352 long result = buffer.indexOf(b, fromIndex, toIndex);
353 if (result != -1L) return result;
354
355
356
357 long lastBufferSize = buffer.size;
358 if (lastBufferSize >= toIndex || source.read(buffer, Segment.SIZE) == -1) return -1L;
359
360
361 fromIndex = Math.max(fromIndex, lastBufferSize);
362 }
363 return -1L;
364 }
365
366 @Override public long indexOf(ByteString bytes) throws IOException {
367 return indexOf(bytes, 0);
368 }
369
370 @Override public long indexOf(ByteString bytes, long fromIndex) throws IOException {
371 if (closed) throw new IllegalStateException("closed");
372
373 while (true) {
374 long result = buffer.indexOf(bytes, fromIndex);
375 if (result != -1) return result;
376
377 long lastBufferSize = buffer.size;
378 if (source.read(buffer, Segment.SIZE) == -1) return -1L;
379
380
381 fromIndex = Math.max(fromIndex, lastBufferSize - bytes.size() + 1);
382 }
383 }
384
385 @Override public long indexOfElement(ByteString targetBytes) throws IOException {
386 return indexOfElement(targetBytes, 0);
387 }
388
389 @Override public long indexOfElement(ByteString targetBytes, long fromIndex) throws IOException {
390 if (closed) throw new IllegalStateException("closed");
391
392 while (true) {
393 long result = buffer.indexOfElement(targetBytes, fromIndex);
394 if (result != -1) return result;
395
396 long lastBufferSize = buffer.size;
397 if (source.read(buffer, Segment.SIZE) == -1) return -1L;
398
399
400 fromIndex = Math.max(fromIndex, lastBufferSize);
401 }
402 }
403
404 @Override public boolean rangeEquals(long offset, ByteString bytes) throws IOException {
405 return rangeEquals(offset, bytes, 0, bytes.size());
406 }
407
408 @Override
409 public boolean rangeEquals(long offset, ByteString bytes, int bytesOffset, int byteCount)
410 throws IOException {
411 if (closed) throw new IllegalStateException("closed");
412
413 if (offset < 0
414 || bytesOffset < 0
415 || byteCount < 0
416 || bytes.size() - bytesOffset < byteCount) {
417 return false;
418 }
419 for (int i = 0; i < byteCount; i++) {
420 long bufferOffset = offset + i;
421 if (!request(bufferOffset + 1)) return false;
422 if (buffer.getByte(bufferOffset) != bytes.getByte(bytesOffset + i)) return false;
423 }
424 return true;
425 }
426
427 @Override public BufferedSource peek() {
428 return Okio.buffer(new PeekSource(this));
429 }
430
431 @Override public InputStream inputStream() {
432 return new InputStream() {
433 @Override public int read() throws IOException {
434 if (closed) throw new IOException("closed");
435 if (buffer.size == 0) {
436 long count = source.read(buffer, Segment.SIZE);
437 if (count == -1) return -1;
438 }
439 return buffer.readByte() & 0xff;
440 }
441
442 @Override public int read(byte[] data, int offset, int byteCount) throws IOException {
443 if (closed) throw new IOException("closed");
444 checkOffsetAndCount(data.length, offset, byteCount);
445
446 if (buffer.size == 0) {
447 long count = source.read(buffer, Segment.SIZE);
448 if (count == -1) return -1;
449 }
450
451 return buffer.read(data, offset, byteCount);
452 }
453
454 @Override public int available() throws IOException {
455 if (closed) throw new IOException("closed");
456 return (int) Math.min(buffer.size, Integer.MAX_VALUE);
457 }
458
459 @Override public void close() throws IOException {
460 RealBufferedSource.this.close();
461 }
462
463 @Override public String toString() {
464 return RealBufferedSource.this + ".inputStream()";
465 }
466 };
467 }
468
469 @Override public boolean isOpen() {
470 return !closed;
471 }
472
473 @Override public void close() throws IOException {
474 if (closed) return;
475 closed = true;
476 source.close();
477 buffer.clear();
478 }
479
480 @Override public Timeout timeout() {
481 return source.timeout();
482 }
483
484 @Override public String toString() {
485 return "buffer(" + source + ")";
486 }
487 }
488