mirror of
https://github.com/square/okhttp.git
synced 2026-01-24 04:02:07 +03:00
Merge pull request #492 from square/jwilson_0127_indexof
More buffer stuff: deadlines, indexOf and read.
This commit is contained in:
@@ -18,12 +18,12 @@ package com.squareup.okhttp.internal.bytes;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* The deadline for a requested operation. If the timeout elapses before the
|
||||
* operation has completed, the operation should be aborted.
|
||||
* The time that a requested operation is due. If the deadline is reached before
|
||||
* the operation has completed, the operation should be aborted.
|
||||
*/
|
||||
public class Timeout {
|
||||
public static final Timeout NONE = new Timeout() {
|
||||
@Override public Timeout start(long timeout, TimeUnit unit) {
|
||||
public class Deadline {
|
||||
public static final Deadline NONE = new Deadline() {
|
||||
@Override public Deadline start(long timeout, TimeUnit unit) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@@ -34,10 +34,10 @@ public class Timeout {
|
||||
|
||||
private long deadlineNanos;
|
||||
|
||||
public Timeout() {
|
||||
public Deadline() {
|
||||
}
|
||||
|
||||
public Timeout start(long timeout, TimeUnit unit) {
|
||||
public Deadline start(long timeout, TimeUnit unit) {
|
||||
deadlineNanos = System.nanoTime() + unit.toNanos(timeout);
|
||||
return this;
|
||||
}
|
||||
@@ -121,7 +121,7 @@ public final class OkBuffer implements Source, Sink {
|
||||
this.byteCount += data.length;
|
||||
}
|
||||
|
||||
@Override public void write(OkBuffer source, long byteCount, Timeout timeout) {
|
||||
@Override public void write(OkBuffer source, long byteCount, Deadline deadline) {
|
||||
// Move bytes from the head of the source buffer to the tail of this buffer
|
||||
// while balancing two conflicting goals: don't waste CPU and don't waste
|
||||
// memory.
|
||||
@@ -160,8 +160,8 @@ public final class OkBuffer implements Source, Sink {
|
||||
// memory use like [100%, 100%, 4%].
|
||||
//
|
||||
// When combining buffers, we will compact adjacent buffers when their
|
||||
// combined level is less than 100%. For example, when we start with [100%,
|
||||
// 40%] and append [30%, 80%], the result is [100%, 70%, 80%].
|
||||
// combined level doesn't exceed 100%. For example, when we start with
|
||||
// [100%, 40%] and append [30%, 80%], the result is [100%, 70%, 80%].
|
||||
//
|
||||
//
|
||||
// Splitting segments
|
||||
@@ -213,19 +213,34 @@ public final class OkBuffer implements Source, Sink {
|
||||
}
|
||||
}
|
||||
|
||||
@Override public long read(OkBuffer sink, long byteCount, Timeout timeout) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
@Override public long read(OkBuffer sink, long byteCount, Deadline deadline) throws IOException {
|
||||
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
|
||||
if (this.byteCount == 0) return -1L;
|
||||
if (byteCount > this.byteCount) byteCount = this.byteCount;
|
||||
sink.write(this, byteCount, deadline);
|
||||
return byteCount;
|
||||
}
|
||||
|
||||
@Override public long indexOf(byte b, Timeout timeout) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
@Override public long indexOf(byte b, Deadline deadline) throws IOException {
|
||||
Segment s = head;
|
||||
if (s == null) return -1L;
|
||||
long offset = 0L;
|
||||
do {
|
||||
byte[] data = s.data;
|
||||
for (int pos = s.pos, limit = s.limit; pos < limit; pos++) {
|
||||
if (data[pos] == b) return offset + pos - s.pos;
|
||||
}
|
||||
offset += s.limit - s.pos;
|
||||
s = s.next;
|
||||
} while (s != head);
|
||||
return -1L;
|
||||
}
|
||||
|
||||
@Override public void flush(Timeout timeout) {
|
||||
@Override public void flush(Deadline deadline) {
|
||||
throw new UnsupportedOperationException("Cannot flush() an OkBuffer");
|
||||
}
|
||||
|
||||
@Override public void close(Timeout timeout) {
|
||||
@Override public void close(Deadline deadline) {
|
||||
throw new UnsupportedOperationException("Cannot close() an OkBuffer");
|
||||
}
|
||||
|
||||
|
||||
@@ -22,15 +22,15 @@ import java.io.IOException;
|
||||
*/
|
||||
public interface Sink {
|
||||
/** Removes {@code byteCount} bytes from {@code source} and appends them to this. */
|
||||
void write(OkBuffer source, long byteCount, Timeout timeout) throws IOException;
|
||||
void write(OkBuffer source, long byteCount, Deadline deadline) throws IOException;
|
||||
|
||||
/** Pushes all buffered bytes to their final destination. */
|
||||
void flush(Timeout timeout) throws IOException;
|
||||
void flush(Deadline deadline) throws IOException;
|
||||
|
||||
/**
|
||||
* Pushes all buffered bytes to their final destination and releases the
|
||||
* resources held by this sink. It is an error to write a closed sink. It is
|
||||
* safe to close a sink more than once.
|
||||
*/
|
||||
void close(Timeout timeout) throws IOException;
|
||||
void close(Deadline deadline) throws IOException;
|
||||
}
|
||||
|
||||
@@ -25,17 +25,17 @@ public interface Source {
|
||||
* Removes {@code byteCount} bytes from this and appends them to {@code sink}.
|
||||
* Returns the number of bytes actually written.
|
||||
*/
|
||||
long read(OkBuffer sink, long byteCount, Timeout timeout) throws IOException;
|
||||
long read(OkBuffer sink, long byteCount, Deadline deadline) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the index of {@code b} in this, or -1 if this source is exhausted
|
||||
* first. This may cause this source to buffer a large number of bytes.
|
||||
*/
|
||||
long indexOf(byte b, Timeout timeout) throws IOException;
|
||||
long indexOf(byte b, Deadline deadline) throws IOException;
|
||||
|
||||
/**
|
||||
* Closes this source and releases the resources held by this source. It is an
|
||||
* error to read a closed source. It is safe to close a source more than once.
|
||||
*/
|
||||
void close(Timeout timeout) throws IOException;
|
||||
void close(Deadline deadline) throws IOException;
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ public final class OkBufferTest {
|
||||
for (String s : contents) {
|
||||
OkBuffer source = new OkBuffer();
|
||||
source.writeUtf8(s);
|
||||
buffer.write(source, source.byteCount(), Timeout.NONE);
|
||||
buffer.write(source, source.byteCount(), Deadline.NONE);
|
||||
expected.append(s);
|
||||
}
|
||||
List<Integer> segmentSizes = buffer.segmentSizes();
|
||||
@@ -132,7 +132,7 @@ public final class OkBufferTest {
|
||||
|
||||
OkBuffer source = new OkBuffer();
|
||||
source.writeUtf8(repeat('a', Segment.SIZE * 2));
|
||||
sink.write(source, writeSize, Timeout.NONE);
|
||||
sink.write(source, writeSize, Deadline.NONE);
|
||||
|
||||
assertEquals(asList(Segment.SIZE - 10, writeSize), sink.segmentSizes());
|
||||
assertEquals(asList(Segment.SIZE - writeSize, Segment.SIZE), source.segmentSizes());
|
||||
@@ -147,7 +147,7 @@ public final class OkBufferTest {
|
||||
|
||||
OkBuffer source = new OkBuffer();
|
||||
source.writeUtf8(repeat('a', Segment.SIZE * 2));
|
||||
sink.write(source, writeSize, Timeout.NONE);
|
||||
sink.write(source, writeSize, Deadline.NONE);
|
||||
|
||||
assertEquals(asList(Segment.SIZE - 10, writeSize), sink.segmentSizes());
|
||||
assertEquals(asList(Segment.SIZE - writeSize, Segment.SIZE), source.segmentSizes());
|
||||
@@ -159,7 +159,7 @@ public final class OkBufferTest {
|
||||
|
||||
OkBuffer source = new OkBuffer();
|
||||
source.writeUtf8(repeat('a', Segment.SIZE * 2));
|
||||
sink.write(source, 20, Timeout.NONE);
|
||||
sink.write(source, 20, Deadline.NONE);
|
||||
|
||||
assertEquals(asList(30), sink.segmentSizes());
|
||||
assertEquals(asList(Segment.SIZE - 20, Segment.SIZE), source.segmentSizes());
|
||||
@@ -174,7 +174,7 @@ public final class OkBufferTest {
|
||||
|
||||
OkBuffer source = new OkBuffer();
|
||||
source.writeUtf8(repeat('a', Segment.SIZE * 2));
|
||||
sink.write(source, 20, Timeout.NONE);
|
||||
sink.write(source, 20, Deadline.NONE);
|
||||
|
||||
assertEquals(asList(30), sink.segmentSizes());
|
||||
assertEquals(asList(Segment.SIZE - 20, Segment.SIZE), source.segmentSizes());
|
||||
@@ -182,6 +182,98 @@ public final class OkBufferTest {
|
||||
assertEquals(Segment.SIZE * 2 - 20, source.byteCount());
|
||||
}
|
||||
|
||||
@Test public void readExhaustedSource() throws Exception {
|
||||
OkBuffer sink = new OkBuffer();
|
||||
sink.writeUtf8(repeat('a', 10));
|
||||
|
||||
OkBuffer source = new OkBuffer();
|
||||
|
||||
assertEquals(-1, source.read(sink, 10, Deadline.NONE));
|
||||
assertEquals(10, sink.byteCount());
|
||||
assertEquals(0, source.byteCount());
|
||||
}
|
||||
|
||||
@Test public void readZeroBytesFromSource() throws Exception {
|
||||
OkBuffer sink = new OkBuffer();
|
||||
sink.writeUtf8(repeat('a', 10));
|
||||
|
||||
OkBuffer source = new OkBuffer();
|
||||
|
||||
// Either 0 or -1 is reasonable here. For consistency with Android's
|
||||
// ByteArrayInputStream we return 0.
|
||||
assertEquals(-1, source.read(sink, 0, Deadline.NONE));
|
||||
assertEquals(10, sink.byteCount());
|
||||
assertEquals(0, source.byteCount());
|
||||
}
|
||||
|
||||
@Test public void moveAllRequestedBytesWithRead() throws Exception {
|
||||
OkBuffer sink = new OkBuffer();
|
||||
sink.writeUtf8(repeat('a', 10));
|
||||
|
||||
OkBuffer source = new OkBuffer();
|
||||
source.writeUtf8(repeat('b', 15));
|
||||
|
||||
assertEquals(10, source.read(sink, 10, Deadline.NONE));
|
||||
assertEquals(20, sink.byteCount());
|
||||
assertEquals(5, source.byteCount());
|
||||
assertEquals(repeat('a', 10) + repeat('b', 10), sink.readUtf8(20));
|
||||
}
|
||||
|
||||
@Test public void moveFewerThanRequestedBytesWithRead() throws Exception {
|
||||
OkBuffer sink = new OkBuffer();
|
||||
sink.writeUtf8(repeat('a', 10));
|
||||
|
||||
OkBuffer source = new OkBuffer();
|
||||
source.writeUtf8(repeat('b', 20));
|
||||
|
||||
assertEquals(20, source.read(sink, 25, Deadline.NONE));
|
||||
assertEquals(30, sink.byteCount());
|
||||
assertEquals(0, source.byteCount());
|
||||
assertEquals(repeat('a', 10) + repeat('b', 20), sink.readUtf8(30));
|
||||
}
|
||||
|
||||
@Test public void indexOf() throws Exception {
|
||||
OkBuffer buffer = new OkBuffer();
|
||||
|
||||
// The segment is empty.
|
||||
assertEquals(-1, buffer.indexOf((byte) 'a', Deadline.NONE));
|
||||
|
||||
// The segment has one value.
|
||||
buffer.writeUtf8("a"); // a
|
||||
assertEquals(0, buffer.indexOf((byte) 'a', Deadline.NONE));
|
||||
assertEquals(-1, buffer.indexOf((byte) 'b', Deadline.NONE));
|
||||
|
||||
// The segment has lots of data.
|
||||
buffer.writeUtf8(repeat('b', Segment.SIZE - 2)); // ab...b
|
||||
assertEquals(0, buffer.indexOf((byte) 'a', Deadline.NONE));
|
||||
assertEquals(1, buffer.indexOf((byte) 'b', Deadline.NONE));
|
||||
assertEquals(-1, buffer.indexOf((byte) 'c', Deadline.NONE));
|
||||
|
||||
// The segment doesn't start at 0, it starts at 2.
|
||||
buffer.readUtf8(2); // b...b
|
||||
assertEquals(-1, buffer.indexOf((byte) 'a', Deadline.NONE));
|
||||
assertEquals(0, buffer.indexOf((byte) 'b', Deadline.NONE));
|
||||
assertEquals(-1, buffer.indexOf((byte) 'c', Deadline.NONE));
|
||||
|
||||
// The segment is full.
|
||||
buffer.writeUtf8("c"); // b...bc
|
||||
assertEquals(-1, buffer.indexOf((byte) 'a', Deadline.NONE));
|
||||
assertEquals(0, buffer.indexOf((byte) 'b', Deadline.NONE));
|
||||
assertEquals(Segment.SIZE - 3, buffer.indexOf((byte) 'c', Deadline.NONE));
|
||||
|
||||
// The segment doesn't start at 2, it starts at 4.
|
||||
buffer.readUtf8(2); // b...bc
|
||||
assertEquals(-1, buffer.indexOf((byte) 'a', Deadline.NONE));
|
||||
assertEquals(0, buffer.indexOf((byte) 'b', Deadline.NONE));
|
||||
assertEquals(Segment.SIZE - 5, buffer.indexOf((byte) 'c', Deadline.NONE));
|
||||
|
||||
// Two segments.
|
||||
buffer.writeUtf8("d"); // b...bcd, d is in the 2nd segment.
|
||||
assertEquals(asList(Segment.SIZE - 4, 1), buffer.segmentSizes());
|
||||
assertEquals(Segment.SIZE - 4, buffer.indexOf((byte) 'd', Deadline.NONE));
|
||||
assertEquals(-1, buffer.indexOf((byte) 'e', Deadline.NONE));
|
||||
}
|
||||
|
||||
private String repeat(char c, int count) {
|
||||
char[] array = new char[count];
|
||||
Arrays.fill(array, c);
|
||||
|
||||
Reference in New Issue
Block a user