1
0
mirror of https://github.com/square/okhttp.git synced 2025-11-29 06:23:09 +03:00

Merge pull request #684 from square/jwilson_0413_switch_to_okio

Switch to Okio.
This commit is contained in:
Jesse Wilson
2014-04-16 00:08:07 -04:00
54 changed files with 181 additions and 5031 deletions

View File

@@ -63,9 +63,9 @@ import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import okio.Buffer;
import okio.BufferedSink; import okio.BufferedSink;
import okio.ByteString; import okio.ByteString;
import okio.OkBuffer;
import okio.Okio; import okio.Okio;
import static com.squareup.okhttp.mockwebserver.SocketPolicy.DISCONNECT_AT_START; import static com.squareup.okhttp.mockwebserver.SocketPolicy.DISCONNECT_AT_START;
@@ -695,7 +695,7 @@ public final class MockWebServer {
} }
spdyHeaders.add(new Header(headerParts[0], headerParts[1])); spdyHeaders.add(new Header(headerParts[0], headerParts[1]));
} }
OkBuffer body = new OkBuffer(); Buffer body = new Buffer();
if (response.getBody() != null) { if (response.getBody() != null) {
body.write(response.getBody()); body.write(response.getBody());
} }

View File

@@ -17,7 +17,7 @@ package com.squareup.okhttp.curl;
import com.squareup.okhttp.Request; import com.squareup.okhttp.Request;
import java.io.IOException; import java.io.IOException;
import okio.OkBuffer; import okio.Buffer;
import org.junit.Test; import org.junit.Test;
import static com.squareup.okhttp.curl.Main.fromArgs; import static com.squareup.okhttp.curl.Main.fromArgs;
@@ -85,7 +85,7 @@ public class MainTest {
private static String bodyAsString(Request.Body body) { private static String bodyAsString(Request.Body body) {
try { try {
OkBuffer buffer = new OkBuffer(); Buffer buffer = new Buffer();
body.writeTo(buffer); body.writeTo(buffer);
return new String(buffer.readByteString(buffer.size()).toByteArray(), return new String(buffer.readByteString(buffer.size()).toByteArray(),
body.contentType().charset()); body.contentType().charset());

View File

@@ -16,7 +16,6 @@
<dependency> <dependency>
<groupId>com.squareup.okio</groupId> <groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId> <artifactId>okio</artifactId>
<version>${project.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.squareup.okhttp</groupId> <groupId>com.squareup.okhttp</groupId>

View File

@@ -19,7 +19,7 @@ import com.squareup.okhttp.internal.Util;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import okio.OkBuffer; import okio.Buffer;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -74,7 +74,7 @@ public final class RequestTest {
} }
private String bodyToHex(Request.Body body) throws IOException { private String bodyToHex(Request.Body body) throws IOException {
OkBuffer buffer = new OkBuffer(); Buffer buffer = new Buffer();
body.writeTo(buffer); body.writeTo(buffer);
return buffer.readByteString(buffer.size()).hex(); return buffer.readByteString(buffer.size()).hex();
} }

View File

@@ -18,8 +18,8 @@ package com.squareup.okhttp.internal.spdy;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import okio.Buffer;
import okio.ByteString; import okio.ByteString;
import okio.OkBuffer;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -30,9 +30,9 @@ import static org.junit.Assert.assertTrue;
public class HpackDraft05Test { public class HpackDraft05Test {
private final OkBuffer bytesIn = new OkBuffer(); private final Buffer bytesIn = new Buffer();
private HpackDraft05.Reader hpackReader; private HpackDraft05.Reader hpackReader;
private OkBuffer bytesOut = new OkBuffer(); private Buffer bytesOut = new Buffer();
private HpackDraft05.Writer hpackWriter; private HpackDraft05.Writer hpackWriter;
@Before public void reset() { @Before public void reset() {
@@ -66,7 +66,7 @@ public class HpackDraft05Test {
* Ensure the larger header content is not lost. * Ensure the larger header content is not lost.
*/ */
@Test public void tooLargeToHPackIsStillEmitted() throws IOException { @Test public void tooLargeToHPackIsStillEmitted() throws IOException {
OkBuffer out = new OkBuffer(); Buffer out = new Buffer();
out.writeByte(0x00); // Literal indexed out.writeByte(0x00); // Literal indexed
out.writeByte(0x0a); // Literal name (len = 10) out.writeByte(0x0a); // Literal name (len = 10)
@@ -87,7 +87,7 @@ public class HpackDraft05Test {
/** Oldest entries are evicted to support newer ones. */ /** Oldest entries are evicted to support newer ones. */
@Test public void testEviction() throws IOException { @Test public void testEviction() throws IOException {
OkBuffer out = new OkBuffer(); Buffer out = new Buffer();
out.writeByte(0x00); // Literal indexed out.writeByte(0x00); // Literal indexed
out.writeByte(0x0a); // Literal name (len = 10) out.writeByte(0x0a); // Literal name (len = 10)
@@ -138,7 +138,7 @@ public class HpackDraft05Test {
/** Header table backing array is initially 8 long, let's ensure it grows. */ /** Header table backing array is initially 8 long, let's ensure it grows. */
@Test public void dynamicallyGrowsBeyond64Entries() throws IOException { @Test public void dynamicallyGrowsBeyond64Entries() throws IOException {
OkBuffer out = new OkBuffer(); Buffer out = new Buffer();
for (int i = 0; i < 256; i++) { for (int i = 0; i < 256; i++) {
out.writeByte(0x00); // Literal indexed out.writeByte(0x00); // Literal indexed
@@ -160,7 +160,7 @@ public class HpackDraft05Test {
} }
@Test public void huffmanDecodingSupported() throws IOException { @Test public void huffmanDecodingSupported() throws IOException {
OkBuffer out = new OkBuffer(); Buffer out = new Buffer();
out.writeByte(0x04); // == Literal indexed == out.writeByte(0x04); // == Literal indexed ==
// Indexed name (idx = 4) -> :path // Indexed name (idx = 4) -> :path
@@ -188,7 +188,7 @@ public class HpackDraft05Test {
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05#appendix-E.1.1 * http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05#appendix-E.1.1
*/ */
@Test public void readLiteralHeaderFieldWithIndexing() throws IOException { @Test public void readLiteralHeaderFieldWithIndexing() throws IOException {
OkBuffer out = new OkBuffer(); Buffer out = new Buffer();
out.writeByte(0x00); // Literal indexed out.writeByte(0x00); // Literal indexed
out.writeByte(0x0a); // Literal name (len = 10) out.writeByte(0x0a); // Literal name (len = 10)
@@ -217,7 +217,7 @@ public class HpackDraft05Test {
@Test public void literalHeaderFieldWithoutIndexingNewName() throws IOException { @Test public void literalHeaderFieldWithoutIndexingNewName() throws IOException {
List<Header> headerBlock = headerEntries("custom-key", "custom-header"); List<Header> headerBlock = headerEntries("custom-key", "custom-header");
OkBuffer expectedBytes = new OkBuffer(); Buffer expectedBytes = new Buffer();
expectedBytes.writeByte(0x40); // Not indexed expectedBytes.writeByte(0x40); // Not indexed
expectedBytes.writeByte(0x0a); // Literal name (len = 10) expectedBytes.writeByte(0x0a); // Literal name (len = 10)
@@ -244,7 +244,7 @@ public class HpackDraft05Test {
@Test public void literalHeaderFieldWithoutIndexingIndexedName() throws IOException { @Test public void literalHeaderFieldWithoutIndexingIndexedName() throws IOException {
List<Header> headerBlock = headerEntries(":path", "/sample/path"); List<Header> headerBlock = headerEntries(":path", "/sample/path");
OkBuffer expectedBytes = new OkBuffer(); Buffer expectedBytes = new Buffer();
expectedBytes.writeByte(0x44); // == Literal not indexed == expectedBytes.writeByte(0x44); // == Literal not indexed ==
// Indexed name (idx = 4) -> :path // Indexed name (idx = 4) -> :path
expectedBytes.writeByte(0x0c); // Literal value (len = 12) expectedBytes.writeByte(0x0c); // Literal value (len = 12)
@@ -369,7 +369,7 @@ public class HpackDraft05Test {
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05#appendix-E.2 * http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05#appendix-E.2
*/ */
@Test public void readRequestExamplesWithoutHuffman() throws IOException { @Test public void readRequestExamplesWithoutHuffman() throws IOException {
OkBuffer out = firstRequestWithoutHuffman(); Buffer out = firstRequestWithoutHuffman();
bytesIn.write(out, out.size()); bytesIn.write(out, out.size());
hpackReader.readHeaders(); hpackReader.readHeaders();
hpackReader.emitReferenceSet(); hpackReader.emitReferenceSet();
@@ -388,8 +388,8 @@ public class HpackDraft05Test {
checkReadThirdRequestWithoutHuffman(); checkReadThirdRequestWithoutHuffman();
} }
private OkBuffer firstRequestWithoutHuffman() { private Buffer firstRequestWithoutHuffman() {
OkBuffer out = new OkBuffer(); Buffer out = new Buffer();
out.writeByte(0x82); // == Indexed - Add == out.writeByte(0x82); // == Indexed - Add ==
// idx = 2 -> :method: GET // idx = 2 -> :method: GET
@@ -439,8 +439,8 @@ public class HpackDraft05Test {
":authority", "www.example.com"), hpackReader.getAndReset()); ":authority", "www.example.com"), hpackReader.getAndReset());
} }
private OkBuffer secondRequestWithoutHuffman() { private Buffer secondRequestWithoutHuffman() {
OkBuffer out = new OkBuffer(); Buffer out = new Buffer();
out.writeByte(0x1b); // == Literal indexed == out.writeByte(0x1b); // == Literal indexed ==
// Indexed name (idx = 27) -> cache-control // Indexed name (idx = 27) -> cache-control
@@ -490,8 +490,8 @@ public class HpackDraft05Test {
"cache-control", "no-cache"), hpackReader.getAndReset()); "cache-control", "no-cache"), hpackReader.getAndReset());
} }
private OkBuffer thirdRequestWithoutHuffman() { private Buffer thirdRequestWithoutHuffman() {
OkBuffer out = new OkBuffer(); Buffer out = new Buffer();
out.writeByte(0x80); // == Empty reference set == out.writeByte(0x80); // == Empty reference set ==
out.writeByte(0x85); // == Indexed - Add == out.writeByte(0x85); // == Indexed - Add ==
@@ -571,7 +571,7 @@ public class HpackDraft05Test {
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05#appendix-E.3 * http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05#appendix-E.3
*/ */
@Test public void readRequestExamplesWithHuffman() throws IOException { @Test public void readRequestExamplesWithHuffman() throws IOException {
OkBuffer out = firstRequestWithHuffman(); Buffer out = firstRequestWithHuffman();
bytesIn.write(out, out.size()); bytesIn.write(out, out.size());
hpackReader.readHeaders(); hpackReader.readHeaders();
hpackReader.emitReferenceSet(); hpackReader.emitReferenceSet();
@@ -590,8 +590,8 @@ public class HpackDraft05Test {
checkReadThirdRequestWithHuffman(); checkReadThirdRequestWithHuffman();
} }
private OkBuffer firstRequestWithHuffman() { private Buffer firstRequestWithHuffman() {
OkBuffer out = new OkBuffer(); Buffer out = new Buffer();
out.writeByte(0x82); // == Indexed - Add == out.writeByte(0x82); // == Indexed - Add ==
// idx = 2 -> :method: GET // idx = 2 -> :method: GET
@@ -646,8 +646,8 @@ public class HpackDraft05Test {
":authority", "www.example.com"), hpackReader.getAndReset()); ":authority", "www.example.com"), hpackReader.getAndReset());
} }
private OkBuffer secondRequestWithHuffman() { private Buffer secondRequestWithHuffman() {
OkBuffer out = new OkBuffer(); Buffer out = new Buffer();
out.writeByte(0x1b); // == Literal indexed == out.writeByte(0x1b); // == Literal indexed ==
// Indexed name (idx = 27) -> cache-control // Indexed name (idx = 27) -> cache-control
@@ -701,8 +701,8 @@ public class HpackDraft05Test {
"cache-control", "no-cache"), hpackReader.getAndReset()); "cache-control", "no-cache"), hpackReader.getAndReset());
} }
private OkBuffer thirdRequestWithHuffman() { private Buffer thirdRequestWithHuffman() {
OkBuffer out = new OkBuffer(); Buffer out = new Buffer();
out.writeByte(0x80); // == Empty reference set == out.writeByte(0x80); // == Empty reference set ==
out.writeByte(0x85); // == Indexed - Add == out.writeByte(0x85); // == Indexed - Add ==
@@ -848,12 +848,12 @@ public class HpackDraft05Test {
assertEquals(ByteString.EMPTY, newReader(byteStream(0)).readByteString(false)); assertEquals(ByteString.EMPTY, newReader(byteStream(0)).readByteString(false));
} }
private HpackDraft05.Reader newReader(OkBuffer source) { private HpackDraft05.Reader newReader(Buffer source) {
return new HpackDraft05.Reader(false, 4096, source); return new HpackDraft05.Reader(false, 4096, source);
} }
private OkBuffer byteStream(int... bytes) { private Buffer byteStream(int... bytes) {
return new OkBuffer().write(intArrayToByteArray(bytes)); return new Buffer().write(intArrayToByteArray(bytes));
} }
private void checkEntry(Header entry, String name, String value, int size) { private void checkEntry(Header entry, String name, String value, int size) {

View File

@@ -19,9 +19,9 @@ import com.squareup.okhttp.internal.Util;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import okio.Buffer;
import okio.BufferedSource; import okio.BufferedSource;
import okio.ByteString; import okio.ByteString;
import okio.OkBuffer;
import org.junit.Test; import org.junit.Test;
import static com.squareup.okhttp.internal.Util.headerEntries; import static com.squareup.okhttp.internal.Util.headerEntries;
@@ -34,7 +34,7 @@ public class Http20Draft09Test {
static final int expectedStreamId = 15; static final int expectedStreamId = 15;
@Test public void unknownFrameTypeIgnored() throws IOException { @Test public void unknownFrameTypeIgnored() throws IOException {
OkBuffer frame = new OkBuffer(); Buffer frame = new Buffer();
frame.writeShort(4); // has a 4-byte field frame.writeShort(4); // has a 4-byte field
frame.writeByte(99); // type 99 frame.writeByte(99); // type 99
@@ -51,11 +51,11 @@ public class Http20Draft09Test {
@Test public void onlyOneLiteralHeadersFrame() throws IOException { @Test public void onlyOneLiteralHeadersFrame() throws IOException {
final List<Header> sentHeaders = headerEntries("name", "value"); final List<Header> sentHeaders = headerEntries("name", "value");
OkBuffer frame = new OkBuffer(); Buffer frame = new Buffer();
// Write the headers frame, specifying no more frames are expected. // Write the headers frame, specifying no more frames are expected.
{ {
OkBuffer headerBytes = literalHeaders(sentHeaders); Buffer headerBytes = literalHeaders(sentHeaders);
frame.writeShort((int) headerBytes.size()); frame.writeShort((int) headerBytes.size());
frame.writeByte(Http20Draft09.TYPE_HEADERS); frame.writeByte(Http20Draft09.TYPE_HEADERS);
frame.writeByte(Http20Draft09.FLAG_END_HEADERS | Http20Draft09.FLAG_END_STREAM); frame.writeByte(Http20Draft09.FLAG_END_HEADERS | Http20Draft09.FLAG_END_STREAM);
@@ -84,12 +84,12 @@ public class Http20Draft09Test {
} }
@Test public void headersWithPriority() throws IOException { @Test public void headersWithPriority() throws IOException {
OkBuffer frame = new OkBuffer(); Buffer frame = new Buffer();
final List<Header> sentHeaders = headerEntries("name", "value"); final List<Header> sentHeaders = headerEntries("name", "value");
{ // Write the headers frame, specifying priority flag and value. { // Write the headers frame, specifying priority flag and value.
OkBuffer headerBytes = literalHeaders(sentHeaders); Buffer headerBytes = literalHeaders(sentHeaders);
frame.writeShort((int) (headerBytes.size() + 4)); frame.writeShort((int) (headerBytes.size() + 4));
frame.writeByte(Http20Draft09.TYPE_HEADERS); frame.writeByte(Http20Draft09.TYPE_HEADERS);
frame.writeByte(Http20Draft09.FLAG_END_HEADERS | Http20Draft09.FLAG_PRIORITY); frame.writeByte(Http20Draft09.FLAG_END_HEADERS | Http20Draft09.FLAG_PRIORITY);
@@ -121,10 +121,10 @@ public class Http20Draft09Test {
/** Headers are compressed, then framed. */ /** Headers are compressed, then framed. */
@Test public void headersFrameThenContinuation() throws IOException { @Test public void headersFrameThenContinuation() throws IOException {
OkBuffer frame = new OkBuffer(); Buffer frame = new Buffer();
// Decoding the first header will cross frame boundaries. // Decoding the first header will cross frame boundaries.
OkBuffer headerBlock = literalHeaders(headerEntries("foo", "barrr", "baz", "qux")); Buffer headerBlock = literalHeaders(headerEntries("foo", "barrr", "baz", "qux"));
{ // Write the first headers frame. { // Write the first headers frame.
frame.writeShort((int) (headerBlock.size() / 2)); frame.writeShort((int) (headerBlock.size() / 2));
frame.writeByte(Http20Draft09.TYPE_HEADERS); frame.writeByte(Http20Draft09.TYPE_HEADERS);
@@ -162,7 +162,7 @@ public class Http20Draft09Test {
} }
@Test public void pushPromise() throws IOException { @Test public void pushPromise() throws IOException {
OkBuffer frame = new OkBuffer(); Buffer frame = new Buffer();
final int expectedPromisedStreamId = 11; final int expectedPromisedStreamId = 11;
@@ -174,7 +174,7 @@ public class Http20Draft09Test {
); );
{ // Write the push promise frame, specifying the associated stream ID. { // Write the push promise frame, specifying the associated stream ID.
OkBuffer headerBytes = literalHeaders(pushPromise); Buffer headerBytes = literalHeaders(pushPromise);
frame.writeShort((int) (headerBytes.size() + 4)); frame.writeShort((int) (headerBytes.size() + 4));
frame.writeByte(Http20Draft09.TYPE_PUSH_PROMISE); frame.writeByte(Http20Draft09.TYPE_PUSH_PROMISE);
frame.writeByte(Http20Draft09.FLAG_END_PUSH_PROMISE); frame.writeByte(Http20Draft09.FLAG_END_PUSH_PROMISE);
@@ -198,7 +198,7 @@ public class Http20Draft09Test {
/** Headers are compressed, then framed. */ /** Headers are compressed, then framed. */
@Test public void pushPromiseThenContinuation() throws IOException { @Test public void pushPromiseThenContinuation() throws IOException {
OkBuffer frame = new OkBuffer(); Buffer frame = new Buffer();
final int expectedPromisedStreamId = 11; final int expectedPromisedStreamId = 11;
@@ -210,7 +210,7 @@ public class Http20Draft09Test {
); );
// Decoding the first header will cross frame boundaries. // Decoding the first header will cross frame boundaries.
OkBuffer headerBlock = literalHeaders(pushPromise); Buffer headerBlock = literalHeaders(pushPromise);
int firstFrameLength = (int) (headerBlock.size() - 1); int firstFrameLength = (int) (headerBlock.size() - 1);
{ // Write the first headers frame. { // Write the first headers frame.
frame.writeShort(firstFrameLength + 4); frame.writeShort(firstFrameLength + 4);
@@ -243,7 +243,7 @@ public class Http20Draft09Test {
} }
@Test public void readRstStreamFrame() throws IOException { @Test public void readRstStreamFrame() throws IOException {
OkBuffer frame = new OkBuffer(); Buffer frame = new Buffer();
frame.writeShort(4); frame.writeShort(4);
frame.writeByte(Http20Draft09.TYPE_RST_STREAM); frame.writeByte(Http20Draft09.TYPE_RST_STREAM);
@@ -263,7 +263,7 @@ public class Http20Draft09Test {
} }
@Test public void readSettingsFrame() throws IOException { @Test public void readSettingsFrame() throws IOException {
OkBuffer frame = new OkBuffer(); Buffer frame = new Buffer();
final int reducedTableSizeBytes = 16; final int reducedTableSizeBytes = 16;
@@ -289,7 +289,7 @@ public class Http20Draft09Test {
} }
@Test public void pingRoundTrip() throws IOException { @Test public void pingRoundTrip() throws IOException {
OkBuffer frame = new OkBuffer(); Buffer frame = new Buffer();
final int expectedPayload1 = 7; final int expectedPayload1 = 7;
final int expectedPayload2 = 8; final int expectedPayload2 = 8;
@@ -317,7 +317,7 @@ public class Http20Draft09Test {
} }
@Test public void maxLengthDataFrame() throws IOException { @Test public void maxLengthDataFrame() throws IOException {
OkBuffer frame = new OkBuffer(); Buffer frame = new Buffer();
final byte[] expectedData = new byte[16383]; final byte[] expectedData = new byte[16383];
Arrays.fill(expectedData, (byte) 2); Arrays.fill(expectedData, (byte) 2);
@@ -330,7 +330,7 @@ public class Http20Draft09Test {
frame.write(expectedData); frame.write(expectedData);
// Check writer sends the same bytes. // Check writer sends the same bytes.
assertEquals(frame, sendDataFrame(new OkBuffer().write(expectedData))); assertEquals(frame, sendDataFrame(new Buffer().write(expectedData)));
FrameReader fr = new Http20Draft09.Reader(frame, 4096, false); FrameReader fr = new Http20Draft09.Reader(frame, 4096, false);
@@ -350,7 +350,7 @@ public class Http20Draft09Test {
@Test public void tooLargeDataFrame() throws IOException { @Test public void tooLargeDataFrame() throws IOException {
try { try {
sendDataFrame(new OkBuffer().write(new byte[0x1000000])); sendDataFrame(new Buffer().write(new byte[0x1000000]));
fail(); fail();
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
assertEquals("FRAME_SIZE_ERROR length > 16383: 16777216", e.getMessage()); assertEquals("FRAME_SIZE_ERROR length > 16383: 16777216", e.getMessage());
@@ -358,7 +358,7 @@ public class Http20Draft09Test {
} }
@Test public void windowUpdateRoundTrip() throws IOException { @Test public void windowUpdateRoundTrip() throws IOException {
OkBuffer frame = new OkBuffer(); Buffer frame = new Buffer();
final long expectedWindowSizeIncrement = 0x7fffffff; final long expectedWindowSizeIncrement = 0x7fffffff;
@@ -400,7 +400,7 @@ public class Http20Draft09Test {
} }
@Test public void goAwayWithoutDebugDataRoundTrip() throws IOException { @Test public void goAwayWithoutDebugDataRoundTrip() throws IOException {
OkBuffer frame = new OkBuffer(); Buffer frame = new Buffer();
final ErrorCode expectedError = ErrorCode.PROTOCOL_ERROR; final ErrorCode expectedError = ErrorCode.PROTOCOL_ERROR;
@@ -428,7 +428,7 @@ public class Http20Draft09Test {
} }
@Test public void goAwayWithDebugDataRoundTrip() throws IOException { @Test public void goAwayWithDebugDataRoundTrip() throws IOException {
OkBuffer frame = new OkBuffer(); Buffer frame = new Buffer();
final ErrorCode expectedError = ErrorCode.PROTOCOL_ERROR; final ErrorCode expectedError = ErrorCode.PROTOCOL_ERROR;
final ByteString expectedData = ByteString.encodeUtf8("abcdefgh"); final ByteString expectedData = ByteString.encodeUtf8("abcdefgh");
@@ -458,7 +458,7 @@ public class Http20Draft09Test {
} }
@Test public void frameSizeError() throws IOException { @Test public void frameSizeError() throws IOException {
Http20Draft09.Writer writer = new Http20Draft09.Writer(new OkBuffer(), true); Http20Draft09.Writer writer = new Http20Draft09.Writer(new Buffer(), true);
try { try {
writer.frameHeader(16384, Http20Draft09.TYPE_DATA, Http20Draft09.FLAG_NONE, 0); writer.frameHeader(16384, Http20Draft09.TYPE_DATA, Http20Draft09.FLAG_NONE, 0);
@@ -469,7 +469,7 @@ public class Http20Draft09Test {
} }
@Test public void streamIdHasReservedBit() throws IOException { @Test public void streamIdHasReservedBit() throws IOException {
Http20Draft09.Writer writer = new Http20Draft09.Writer(new OkBuffer(), true); Http20Draft09.Writer writer = new Http20Draft09.Writer(new Buffer(), true);
try { try {
int streamId = 3; int streamId = 3;
@@ -481,34 +481,34 @@ public class Http20Draft09Test {
} }
} }
private OkBuffer literalHeaders(List<Header> sentHeaders) throws IOException { private Buffer literalHeaders(List<Header> sentHeaders) throws IOException {
OkBuffer out = new OkBuffer(); Buffer out = new Buffer();
new HpackDraft05.Writer(out).writeHeaders(sentHeaders); new HpackDraft05.Writer(out).writeHeaders(sentHeaders);
return out; return out;
} }
private OkBuffer sendPingFrame(boolean ack, int payload1, int payload2) throws IOException { private Buffer sendPingFrame(boolean ack, int payload1, int payload2) throws IOException {
OkBuffer out = new OkBuffer(); Buffer out = new Buffer();
new Http20Draft09.Writer(out, true).ping(ack, payload1, payload2); new Http20Draft09.Writer(out, true).ping(ack, payload1, payload2);
return out; return out;
} }
private OkBuffer sendGoAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData) private Buffer sendGoAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData)
throws IOException { throws IOException {
OkBuffer out = new OkBuffer(); Buffer out = new Buffer();
new Http20Draft09.Writer(out, true).goAway(lastGoodStreamId, errorCode, debugData); new Http20Draft09.Writer(out, true).goAway(lastGoodStreamId, errorCode, debugData);
return out; return out;
} }
private OkBuffer sendDataFrame(OkBuffer data) throws IOException { private Buffer sendDataFrame(Buffer data) throws IOException {
OkBuffer out = new OkBuffer(); Buffer out = new Buffer();
new Http20Draft09.Writer(out, true).dataFrame(expectedStreamId, Http20Draft09.FLAG_NONE, data, new Http20Draft09.Writer(out, true).dataFrame(expectedStreamId, Http20Draft09.FLAG_NONE, data,
(int) data.size()); (int) data.size());
return out; return out;
} }
private OkBuffer windowUpdate(long windowSizeIncrement) throws IOException { private Buffer windowUpdate(long windowSizeIncrement) throws IOException {
OkBuffer out = new OkBuffer(); Buffer out = new Buffer();
new Http20Draft09.Writer(out, true).windowUpdate(expectedStreamId, windowSizeIncrement); new Http20Draft09.Writer(out, true).windowUpdate(expectedStreamId, windowSizeIncrement);
return out; return out;
} }

View File

@@ -30,9 +30,9 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import okio.Buffer;
import okio.BufferedSource; import okio.BufferedSource;
import okio.ByteString; import okio.ByteString;
import okio.OkBuffer;
import okio.Okio; import okio.Okio;
/** Replays prerecorded outgoing frames and records incoming frames. */ /** Replays prerecorded outgoing frames and records incoming frames. */
@@ -40,7 +40,7 @@ public final class MockSpdyPeer implements Closeable {
private int frameCount = 0; private int frameCount = 0;
private boolean client = false; private boolean client = false;
private Variant variant = new Spdy3(); private Variant variant = new Spdy3();
private final OkBuffer bytesOut = new OkBuffer(); private final Buffer bytesOut = new Buffer();
private FrameWriter frameWriter = variant.newWriter(bytesOut, client); private FrameWriter frameWriter = variant.newWriter(bytesOut, client);
private final List<OutFrame> outFrames = new ArrayList<OutFrame>(); private final List<OutFrame> outFrames = new ArrayList<OutFrame>();
private final BlockingQueue<InFrame> inFrames = new LinkedBlockingQueue<InFrame>(); private final BlockingQueue<InFrame> inFrames = new LinkedBlockingQueue<InFrame>();

View File

@@ -17,8 +17,8 @@ package com.squareup.okhttp.internal.spdy;
import com.squareup.okhttp.internal.Util; import com.squareup.okhttp.internal.Util;
import java.io.IOException; import java.io.IOException;
import okio.Buffer;
import okio.ByteString; import okio.ByteString;
import okio.OkBuffer;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -29,7 +29,7 @@ public class Spdy3Test {
@Test public void tooLargeDataFrame() throws IOException { @Test public void tooLargeDataFrame() throws IOException {
try { try {
sendDataFrame(new OkBuffer().write(new byte[0x1000000])); sendDataFrame(new Buffer().write(new byte[0x1000000]));
fail(); fail();
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
assertEquals("FRAME_TOO_LARGE max size is 16Mib: " + 0x1000000L, e.getMessage()); assertEquals("FRAME_TOO_LARGE max size is 16Mib: " + 0x1000000L, e.getMessage());
@@ -53,7 +53,7 @@ public class Spdy3Test {
} }
@Test public void goAwayRoundTrip() throws IOException { @Test public void goAwayRoundTrip() throws IOException {
OkBuffer frame = new OkBuffer(); Buffer frame = new Buffer();
final ErrorCode expectedError = ErrorCode.PROTOCOL_ERROR; final ErrorCode expectedError = ErrorCode.PROTOCOL_ERROR;
@@ -83,18 +83,18 @@ public class Spdy3Test {
}); });
} }
private void sendDataFrame(OkBuffer source) throws IOException { private void sendDataFrame(Buffer source) throws IOException {
Spdy3.Writer writer = new Spdy3.Writer(new OkBuffer(), true); Spdy3.Writer writer = new Spdy3.Writer(new Buffer(), true);
writer.sendDataFrame(expectedStreamId, 0, source, (int) source.size()); writer.sendDataFrame(expectedStreamId, 0, source, (int) source.size());
} }
private void windowUpdate(long increment) throws IOException { private void windowUpdate(long increment) throws IOException {
new Spdy3.Writer(new OkBuffer(), true).windowUpdate(expectedStreamId, increment); new Spdy3.Writer(new Buffer(), true).windowUpdate(expectedStreamId, increment);
} }
private OkBuffer sendGoAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData) private Buffer sendGoAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData)
throws IOException { throws IOException {
OkBuffer out = new OkBuffer(); Buffer out = new Buffer();
new Spdy3.Writer(out, true).goAway(lastGoodStreamId, errorCode, debugData); new Spdy3.Writer(out, true).goAway(lastGoodStreamId, errorCode, debugData);
return out; return out;
} }

View File

@@ -23,10 +23,10 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import okio.Buffer;
import okio.BufferedSink; import okio.BufferedSink;
import okio.BufferedSource; import okio.BufferedSource;
import okio.ByteString; import okio.ByteString;
import okio.OkBuffer;
import okio.Okio; import okio.Okio;
import okio.Source; import okio.Source;
import org.junit.After; import org.junit.After;
@@ -67,7 +67,7 @@ public final class SpdyConnectionTest {
peer.acceptFrame(); // SYN_STREAM peer.acceptFrame(); // SYN_STREAM
peer.sendFrame() peer.sendFrame()
.synReply(false, 1, headerEntries("a", "android")); .synReply(false, 1, headerEntries("a", "android"));
peer.sendFrame().data(true, 1, new OkBuffer().writeUtf8("robot")); peer.sendFrame().data(true, 1, new Buffer().writeUtf8("robot"));
peer.acceptFrame(); // DATA peer.acceptFrame(); // DATA
peer.play(); peer.play();
@@ -436,7 +436,7 @@ public final class SpdyConnectionTest {
@Test public void bogusDataFrameDoesNotDisruptConnection() throws Exception { @Test public void bogusDataFrameDoesNotDisruptConnection() throws Exception {
// write the mocking script // write the mocking script
peer.sendFrame().data(true, 41, new OkBuffer().writeUtf8("bogus")); peer.sendFrame().data(true, 41, new Buffer().writeUtf8("bogus"));
peer.acceptFrame(); // RST_STREAM peer.acceptFrame(); // RST_STREAM
peer.sendFrame().ping(false, 2, 0); peer.sendFrame().ping(false, 2, 0);
peer.acceptFrame(); // PING peer.acceptFrame(); // PING
@@ -575,7 +575,7 @@ public final class SpdyConnectionTest {
BufferedSink out = Okio.buffer(stream.getSink()); BufferedSink out = Okio.buffer(stream.getSink());
in.close(); in.close();
try { try {
in.read(new OkBuffer(), 1); in.read(new Buffer(), 1);
fail(); fail();
} catch (IOException expected) { } catch (IOException expected) {
assertEquals("stream closed", expected.getMessage()); assertEquals("stream closed", expected.getMessage());
@@ -619,7 +619,7 @@ public final class SpdyConnectionTest {
BufferedSink out = Okio.buffer(stream.getSink()); BufferedSink out = Okio.buffer(stream.getSink());
source.close(); source.close();
try { try {
source.read(new OkBuffer(), 1); source.read(new Buffer(), 1);
fail(); fail();
} catch (IOException expected) { } catch (IOException expected) {
assertEquals("stream closed", expected.getMessage()); assertEquals("stream closed", expected.getMessage());
@@ -651,7 +651,7 @@ public final class SpdyConnectionTest {
// write the mocking script // write the mocking script
peer.acceptFrame(); // SYN_STREAM peer.acceptFrame(); // SYN_STREAM
peer.sendFrame().synReply(false, 1, headerEntries("b", "banana")); peer.sendFrame().synReply(false, 1, headerEntries("b", "banana"));
peer.sendFrame().data(true, 1, new OkBuffer().writeUtf8("square")); peer.sendFrame().data(true, 1, new Buffer().writeUtf8("square"));
peer.play(); peer.play();
// play it back // play it back
@@ -685,7 +685,7 @@ public final class SpdyConnectionTest {
assertEquals(headerEntries("a", "android"), stream.getResponseHeaders()); assertEquals(headerEntries("a", "android"), stream.getResponseHeaders());
connection.ping().roundTripTime(); // Ensure that the 2nd SYN REPLY has been received. connection.ping().roundTripTime(); // Ensure that the 2nd SYN REPLY has been received.
try { try {
stream.getSource().read(new OkBuffer(), 1); stream.getSource().read(new Buffer(), 1);
fail(); fail();
} catch (IOException expected) { } catch (IOException expected) {
assertEquals("stream was reset: STREAM_IN_USE", expected.getMessage()); assertEquals("stream was reset: STREAM_IN_USE", expected.getMessage());
@@ -738,8 +738,8 @@ public final class SpdyConnectionTest {
// write the mocking script // write the mocking script
peer.acceptFrame(); // SYN_STREAM peer.acceptFrame(); // SYN_STREAM
peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); peer.sendFrame().synReply(false, 1, headerEntries("a", "android"));
peer.sendFrame().data(true, 1, new OkBuffer().writeUtf8("robot")); peer.sendFrame().data(true, 1, new Buffer().writeUtf8("robot"));
peer.sendFrame().data(true, 1, new OkBuffer().writeUtf8("c3po")); // Ignored. peer.sendFrame().data(true, 1, new Buffer().writeUtf8("c3po")); // Ignored.
peer.sendFrame().ping(false, 2, 0); // Ping just to make sure the stream was fastforwarded. peer.sendFrame().ping(false, 2, 0); // Ping just to make sure the stream was fastforwarded.
peer.acceptFrame(); // PING peer.acceptFrame(); // PING
peer.play(); peer.play();
@@ -763,7 +763,7 @@ public final class SpdyConnectionTest {
// write the mocking script // write the mocking script
peer.acceptFrame(); // SYN_STREAM peer.acceptFrame(); // SYN_STREAM
peer.sendFrame().synReply(false, 1, headerEntries("b", "banana")); peer.sendFrame().synReply(false, 1, headerEntries("b", "banana"));
peer.sendFrame().data(false, 1, new OkBuffer().write(new byte[64 * 1024 + 1])); peer.sendFrame().data(false, 1, new Buffer().write(new byte[64 * 1024 + 1]));
peer.sendFrame().ping(false, 2, 0); // Ping just to make sure the stream was fastforwarded. peer.sendFrame().ping(false, 2, 0); // Ping just to make sure the stream was fastforwarded.
peer.acceptFrame(); // PING peer.acceptFrame(); // PING
peer.play(); peer.play();
@@ -946,7 +946,7 @@ public final class SpdyConnectionTest {
assertEquals("stream was reset: CANCEL", expected.getMessage()); assertEquals("stream was reset: CANCEL", expected.getMessage());
} }
try { try {
stream.getSource().read(new OkBuffer(), 1); stream.getSource().read(new Buffer(), 1);
fail(); fail();
} catch (IOException expected) { } catch (IOException expected) {
assertEquals("stream was reset: CANCEL", expected.getMessage()); assertEquals("stream was reset: CANCEL", expected.getMessage());
@@ -991,7 +991,7 @@ public final class SpdyConnectionTest {
Source source = stream.getSource(); Source source = stream.getSource();
long startNanos = System.nanoTime(); long startNanos = System.nanoTime();
try { try {
source.read(new OkBuffer(), 1); source.read(new Buffer(), 1);
fail(); fail();
} catch (IOException expected) { } catch (IOException expected) {
} }
@@ -1094,7 +1094,7 @@ public final class SpdyConnectionTest {
assertEquals(0, stream.unacknowledgedBytesRead); assertEquals(0, stream.unacknowledgedBytesRead);
assertEquals(headerEntries("a", "android"), stream.getResponseHeaders()); assertEquals(headerEntries("a", "android"), stream.getResponseHeaders());
Source in = stream.getSource(); Source in = stream.getSource();
OkBuffer buffer = new OkBuffer(); Buffer buffer = new Buffer();
while (in.read(buffer, 1024) != -1) { while (in.read(buffer, 1024) != -1) {
if (buffer.size() == 3 * windowUpdateThreshold) break; if (buffer.size() == 3 * windowUpdateThreshold) break;
} }
@@ -1115,8 +1115,8 @@ public final class SpdyConnectionTest {
} }
} }
private OkBuffer data(int byteCount) { private Buffer data(int byteCount) {
return new OkBuffer().write(new byte[byteCount]); return new Buffer().write(new byte[byteCount]);
} }
@Test public void serverSendsEmptyDataClientDoesntSendWindowUpdate() throws Exception { @Test public void serverSendsEmptyDataClientDoesntSendWindowUpdate() throws Exception {
@@ -1140,7 +1140,7 @@ public final class SpdyConnectionTest {
// Play it back. // Play it back.
SpdyConnection connection = connection(peer, variant); SpdyConnection connection = connection(peer, variant);
SpdyStream client = connection.newStream(headerEntries("b", "banana"), false, true); SpdyStream client = connection.newStream(headerEntries("b", "banana"), false, true);
assertEquals(-1, client.getSource().read(new OkBuffer(), 1)); assertEquals(-1, client.getSource().read(new Buffer(), 1));
// Verify the peer received what was expected. // Verify the peer received what was expected.
MockSpdyPeer.InFrame synStream = peer.takeFrame(); MockSpdyPeer.InFrame synStream = peer.takeFrame();
@@ -1416,7 +1416,7 @@ public final class SpdyConnectionTest {
// write the mocking script // write the mocking script
peer.acceptFrame(); // SYN_STREAM peer.acceptFrame(); // SYN_STREAM
peer.sendFrame(ByteString.decodeBase64(frame).toByteArray()); peer.sendFrame(ByteString.decodeBase64(frame).toByteArray());
peer.sendFrame().data(true, 1, new OkBuffer().writeUtf8("robot")); peer.sendFrame().data(true, 1, new Buffer().writeUtf8("robot"));
peer.acceptFrame(); // DATA peer.acceptFrame(); // DATA
peer.play(); peer.play();
@@ -1479,7 +1479,7 @@ public final class SpdyConnectionTest {
SpdyConnection connection = connectionBuilder(peer, HTTP_20_DRAFT_09) SpdyConnection connection = connectionBuilder(peer, HTTP_20_DRAFT_09)
.pushObserver(observer).build(); .pushObserver(observer).build();
SpdyStream client = connection.newStream(headerEntries("b", "banana"), false, true); SpdyStream client = connection.newStream(headerEntries("b", "banana"), false, true);
assertEquals(-1, client.getSource().read(new OkBuffer(), 1)); assertEquals(-1, client.getSource().read(new Buffer(), 1));
// verify the peer received what was expected // verify the peer received what was expected
assertEquals(TYPE_HEADERS, peer.takeFrame().type); assertEquals(TYPE_HEADERS, peer.takeFrame().type);
@@ -1565,7 +1565,7 @@ public final class SpdyConnectionTest {
} }
private void assertStreamData(String expected, Source source) throws IOException { private void assertStreamData(String expected, Source source) throws IOException {
OkBuffer buffer = new OkBuffer(); Buffer buffer = new Buffer();
while (source.read(buffer, Long.MAX_VALUE) != -1) { while (source.read(buffer, Long.MAX_VALUE) != -1) {
} }
String actual = buffer.readUtf8(buffer.size()); String actual = buffer.readUtf8(buffer.size());

View File

@@ -16,7 +16,6 @@
<dependency> <dependency>
<groupId>com.squareup.okio</groupId> <groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId> <artifactId>okio</artifactId>
<version>${project.version}</version>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@@ -46,7 +46,7 @@ import okio.ByteString;
* OkHttpClient for all of their HTTP requests - benefiting from a shared * OkHttpClient for all of their HTTP requests - benefiting from a shared
* response cache, thread pool, connection re-use, etc. * response cache, thread pool, connection re-use, etc.
* *
* Instances of OkHttpClient are intended to be fully configured before they're * <p>Instances of OkHttpClient are intended to be fully configured before they're
* shared - once shared they should be treated as immutable and can safely be used * shared - once shared they should be treated as immutable and can safely be used
* to concurrently open new connections. If required, threads can call * to concurrently open new connections. If required, threads can call
* {@link #clone()} to make a shallow copy of the OkHttpClient that can be * {@link #clone()} to make a shallow copy of the OkHttpClient that can be

View File

@@ -34,9 +34,9 @@ import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import okio.Buffer;
import okio.BufferedSink; import okio.BufferedSink;
import okio.BufferedSource; import okio.BufferedSource;
import okio.OkBuffer;
import okio.Okio; import okio.Okio;
/** /**
@@ -657,7 +657,7 @@ public final class DiskLruCache implements Closeable {
} }
private static String inputStreamToString(InputStream in) throws IOException { private static String inputStreamToString(InputStream in) throws IOException {
OkBuffer buffer = Util.readFully(Okio.source(in)); Buffer buffer = Util.readFully(Okio.source(in));
return buffer.readUtf8(buffer.size()); return buffer.readUtf8(buffer.size());
} }

View File

@@ -38,7 +38,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
import okio.ByteString; import okio.ByteString;
import okio.OkBuffer; import okio.Buffer;
import okio.Source; import okio.Source;
import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS;
@@ -210,8 +210,8 @@ public final class Util {
} }
/** Returns the remainder of 'source' as a buffer, closing it when done. */ /** Returns the remainder of 'source' as a buffer, closing it when done. */
public static OkBuffer readFully(Source source) throws IOException { public static Buffer readFully(Source source) throws IOException {
OkBuffer result = new OkBuffer(); Buffer result = new Buffer();
while (source.read(result, 2048) != -1) { while (source.read(result, 2048) != -1) {
} }
source.close(); source.close();
@@ -222,7 +222,7 @@ public final class Util {
public static boolean skipAll(Source in, int timeoutMillis) throws IOException { public static boolean skipAll(Source in, int timeoutMillis) throws IOException {
// TODO: Implement deadlines everywhere so they can do this work. // TODO: Implement deadlines everywhere so they can do this work.
long startNanos = System.nanoTime(); long startNanos = System.nanoTime();
OkBuffer skipBuffer = new OkBuffer(); Buffer skipBuffer = new Buffer();
while (NANOSECONDS.toMillis(System.nanoTime() - startNanos) < timeoutMillis) { while (NANOSECONDS.toMillis(System.nanoTime() - startNanos) < timeoutMillis) {
long read = in.read(skipBuffer, 2048); long read = in.read(skipBuffer, 2048);
if (read == -1) return true; // Successfully exhausted the stream. if (read == -1) return true; // Successfully exhausted the stream.

View File

@@ -28,13 +28,13 @@ import java.net.CacheRequest;
import java.net.ProtocolException; import java.net.ProtocolException;
import java.net.Socket; import java.net.Socket;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import okio.Buffer;
import okio.BufferedSink; import okio.BufferedSink;
import okio.BufferedSource; import okio.BufferedSource;
import okio.Deadline;
import okio.OkBuffer;
import okio.Okio; import okio.Okio;
import okio.Sink; import okio.Sink;
import okio.Source; import okio.Source;
import okio.Timeout;
import static com.squareup.okhttp.internal.Util.checkOffsetAndCount; import static com.squareup.okhttp.internal.Util.checkOffsetAndCount;
import static com.squareup.okhttp.internal.http.StatusLine.HTTP_CONTINUE; import static com.squareup.okhttp.internal.http.StatusLine.HTTP_CONTINUE;
@@ -277,11 +277,11 @@ public final class HttpConnection {
this.bytesRemaining = bytesRemaining; this.bytesRemaining = bytesRemaining;
} }
@Override public Sink deadline(Deadline deadline) { @Override public Timeout timeout() {
return this; // TODO: honor deadline. return sink.timeout();
} }
@Override public void write(OkBuffer source, long byteCount) throws IOException { @Override public void write(Buffer source, long byteCount) throws IOException {
if (closed) throw new IllegalStateException("closed"); if (closed) throw new IllegalStateException("closed");
checkOffsetAndCount(source.size(), 0, byteCount); checkOffsetAndCount(source.size(), 0, byteCount);
if (byteCount > bytesRemaining) { if (byteCount > bytesRemaining) {
@@ -322,11 +322,11 @@ public final class HttpConnection {
private boolean closed; private boolean closed;
@Override public Sink deadline(Deadline deadline) { @Override public Timeout timeout() {
return this; // TODO: honor deadline. return sink.timeout();
} }
@Override public void write(OkBuffer source, long byteCount) throws IOException { @Override public void write(Buffer source, long byteCount) throws IOException {
if (closed) throw new IllegalStateException("closed"); if (closed) throw new IllegalStateException("closed");
if (byteCount == 0) return; if (byteCount == 0) return;
@@ -377,9 +377,9 @@ public final class HttpConnection {
} }
/** Copy the last {@code byteCount} bytes of {@code source} to the cache body. */ /** Copy the last {@code byteCount} bytes of {@code source} to the cache body. */
protected final void cacheWrite(OkBuffer source, long byteCount) throws IOException { protected final void cacheWrite(Buffer source, long byteCount) throws IOException {
if (cacheBody != null) { if (cacheBody != null) {
Okio.copy(source, source.size() - byteCount, byteCount, cacheBody); source.copyTo(cacheBody, source.size() - byteCount, byteCount);
} }
} }
@@ -437,7 +437,7 @@ public final class HttpConnection {
} }
} }
@Override public long read(OkBuffer sink, long byteCount) @Override public long read(Buffer sink, long byteCount)
throws IOException { throws IOException {
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
if (closed) throw new IllegalStateException("closed"); if (closed) throw new IllegalStateException("closed");
@@ -457,9 +457,8 @@ public final class HttpConnection {
return read; return read;
} }
@Override public Source deadline(Deadline deadline) { @Override public Timeout timeout() {
source.deadline(deadline); return source.timeout();
return this;
} }
@Override public void close() throws IOException { @Override public void close() throws IOException {
@@ -486,7 +485,7 @@ public final class HttpConnection {
} }
@Override public long read( @Override public long read(
OkBuffer sink, long byteCount) throws IOException { Buffer sink, long byteCount) throws IOException {
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
if (closed) throw new IllegalStateException("closed"); if (closed) throw new IllegalStateException("closed");
if (!hasMoreChunks) return -1; if (!hasMoreChunks) return -1;
@@ -530,9 +529,8 @@ public final class HttpConnection {
} }
} }
@Override public Source deadline(Deadline deadline) { @Override public Timeout timeout() {
source.deadline(deadline); return source.timeout();
return this;
} }
@Override public void close() throws IOException { @Override public void close() throws IOException {
@@ -552,7 +550,7 @@ public final class HttpConnection {
super(cacheRequest); super(cacheRequest);
} }
@Override public long read(OkBuffer sink, long byteCount) @Override public long read(Buffer sink, long byteCount)
throws IOException { throws IOException {
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
if (closed) throw new IllegalStateException("closed"); if (closed) throw new IllegalStateException("closed");
@@ -568,9 +566,8 @@ public final class HttpConnection {
return read; return read;
} }
@Override public Source deadline(Deadline deadline) { @Override public Timeout timeout() {
source.deadline(deadline); return source.timeout();
return this;
} }
@Override public void close() throws IOException { @Override public void close() throws IOException {

View File

@@ -18,9 +18,9 @@ package com.squareup.okhttp.internal.http;
import java.io.IOException; import java.io.IOException;
import java.net.ProtocolException; import java.net.ProtocolException;
import okio.Buffer;
import okio.BufferedSink; import okio.BufferedSink;
import okio.Deadline; import okio.Timeout;
import okio.OkBuffer;
import okio.Sink; import okio.Sink;
import static com.squareup.okhttp.internal.Util.checkOffsetAndCount; import static com.squareup.okhttp.internal.Util.checkOffsetAndCount;
@@ -33,7 +33,7 @@ import static com.squareup.okhttp.internal.Util.checkOffsetAndCount;
final class RetryableSink implements Sink { final class RetryableSink implements Sink {
private boolean closed; private boolean closed;
private final int limit; private final int limit;
private final OkBuffer content = new OkBuffer(); private final Buffer content = new Buffer();
public RetryableSink(int limit) { public RetryableSink(int limit) {
this.limit = limit; this.limit = limit;
@@ -52,7 +52,7 @@ final class RetryableSink implements Sink {
} }
} }
@Override public void write(OkBuffer source, long byteCount) throws IOException { @Override public void write(Buffer source, long byteCount) throws IOException {
if (closed) throw new IllegalStateException("closed"); if (closed) throw new IllegalStateException("closed");
checkOffsetAndCount(source.size(), 0, byteCount); checkOffsetAndCount(source.size(), 0, byteCount);
if (limit != -1 && content.size() > limit - byteCount) { if (limit != -1 && content.size() > limit - byteCount) {
@@ -64,8 +64,8 @@ final class RetryableSink implements Sink {
@Override public void flush() throws IOException { @Override public void flush() throws IOException {
} }
@Override public Sink deadline(Deadline deadline) { @Override public Timeout timeout() {
return this; return Timeout.NONE;
} }
public long contentLength() throws IOException { public long contentLength() throws IOException {

View File

@@ -34,12 +34,11 @@ import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
import okio.Buffer;
import okio.ByteString; import okio.ByteString;
import okio.Deadline;
import okio.OkBuffer;
import okio.Okio;
import okio.Sink; import okio.Sink;
import okio.Source; import okio.Source;
import okio.Timeout;
import static com.squareup.okhttp.internal.spdy.Header.RESPONSE_STATUS; import static com.squareup.okhttp.internal.spdy.Header.RESPONSE_STATUS;
import static com.squareup.okhttp.internal.spdy.Header.TARGET_AUTHORITY; import static com.squareup.okhttp.internal.spdy.Header.TARGET_AUTHORITY;
@@ -262,7 +261,7 @@ public final class SpdyTransport implements Transport {
this.cacheRequest = cacheRequest; this.cacheRequest = cacheRequest;
} }
@Override public long read(OkBuffer sink, long byteCount) @Override public long read(Buffer sink, long byteCount)
throws IOException { throws IOException {
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
if (closed) throw new IllegalStateException("closed"); if (closed) throw new IllegalStateException("closed");
@@ -278,15 +277,14 @@ public final class SpdyTransport implements Transport {
} }
if (cacheBody != null) { if (cacheBody != null) {
Okio.copy(sink, sink.size() - read, read, cacheBody); sink.copyTo(cacheBody, sink.size() - read, read);
} }
return read; return read;
} }
@Override public Source deadline(Deadline deadline) { @Override public Timeout timeout() {
source.deadline(deadline); return source.timeout();
return this;
} }
@Override public void close() throws IOException { @Override public void close() throws IOException {

View File

@@ -19,7 +19,7 @@ package com.squareup.okhttp.internal.spdy;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import okio.OkBuffer; import okio.Buffer;
/** Writes transport frames for SPDY/3 or HTTP/2. */ /** Writes transport frames for SPDY/3 or HTTP/2. */
public interface FrameWriter extends Closeable { public interface FrameWriter extends Closeable {
@@ -60,9 +60,9 @@ public interface FrameWriter extends Closeable {
* *
* @param source the buffer to draw bytes from. May be null if byteCount is 0. * @param source the buffer to draw bytes from. May be null if byteCount is 0.
*/ */
void data(boolean outFinished, int streamId, OkBuffer source, int byteCount) throws IOException; void data(boolean outFinished, int streamId, Buffer source, int byteCount) throws IOException;
void data(boolean outFinished, int streamId, OkBuffer source) throws IOException; void data(boolean outFinished, int streamId, Buffer source) throws IOException;
/** Write okhttp's settings to the peer. */ /** Write okhttp's settings to the peer. */
void settings(Settings okHttpSettings) throws IOException; void settings(Settings okHttpSettings) throws IOException;

View File

@@ -8,9 +8,9 @@ import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import okio.Buffer;
import okio.BufferedSource; import okio.BufferedSource;
import okio.ByteString; import okio.ByteString;
import okio.OkBuffer;
import okio.Okio; import okio.Okio;
import okio.Source; import okio.Source;
@@ -396,9 +396,9 @@ final class HpackDraft05 {
} }
static final class Writer { static final class Writer {
private final OkBuffer out; private final Buffer out;
Writer(OkBuffer out) { Writer(Buffer out) {
this.out = out; this.out = out;
} }

View File

@@ -18,12 +18,12 @@ package com.squareup.okhttp.internal.spdy;
import com.squareup.okhttp.Protocol; import com.squareup.okhttp.Protocol;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import okio.Buffer;
import okio.BufferedSink; import okio.BufferedSink;
import okio.BufferedSource; import okio.BufferedSource;
import okio.ByteString; import okio.ByteString;
import okio.Deadline;
import okio.OkBuffer;
import okio.Source; import okio.Source;
import okio.Timeout;
/** /**
* Read and write http/2 v09 frames. * Read and write http/2 v09 frames.
@@ -291,14 +291,14 @@ public final class Http20Draft09 implements Variant {
static final class Writer implements FrameWriter { static final class Writer implements FrameWriter {
private final BufferedSink sink; private final BufferedSink sink;
private final boolean client; private final boolean client;
private final OkBuffer hpackBuffer; private final Buffer hpackBuffer;
private final HpackDraft05.Writer hpackWriter; private final HpackDraft05.Writer hpackWriter;
private boolean closed; private boolean closed;
Writer(BufferedSink sink, boolean client) { Writer(BufferedSink sink, boolean client) {
this.sink = sink; this.sink = sink;
this.client = client; this.client = client;
this.hpackBuffer = new OkBuffer(); this.hpackBuffer = new Buffer();
this.hpackWriter = new HpackDraft05.Writer(hpackBuffer); this.hpackWriter = new HpackDraft05.Writer(hpackBuffer);
} }
@@ -388,12 +388,12 @@ public final class Http20Draft09 implements Variant {
sink.flush(); sink.flush();
} }
@Override public synchronized void data(boolean outFinished, int streamId, OkBuffer source) @Override public synchronized void data(boolean outFinished, int streamId, Buffer source)
throws IOException { throws IOException {
data(outFinished, streamId, source, (int) source.size()); data(outFinished, streamId, source, (int) source.size());
} }
@Override public synchronized void data(boolean outFinished, int streamId, OkBuffer source, @Override public synchronized void data(boolean outFinished, int streamId, Buffer source,
int byteCount) throws IOException { int byteCount) throws IOException {
if (closed) throw new IOException("closed"); if (closed) throw new IOException("closed");
byte flags = FLAG_NONE; byte flags = FLAG_NONE;
@@ -401,7 +401,7 @@ public final class Http20Draft09 implements Variant {
dataFrame(streamId, flags, source, byteCount); dataFrame(streamId, flags, source, byteCount);
} }
void dataFrame(int streamId, byte flags, OkBuffer buffer, int byteCount) throws IOException { void dataFrame(int streamId, byte flags, Buffer buffer, int byteCount) throws IOException {
byte type = TYPE_DATA; byte type = TYPE_DATA;
frameHeader(byteCount, type, flags, streamId); frameHeader(byteCount, type, flags, streamId);
if (byteCount > 0) { if (byteCount > 0) {
@@ -508,7 +508,7 @@ public final class Http20Draft09 implements Variant {
this.source = source; this.source = source;
} }
@Override public long read(OkBuffer sink, long byteCount) throws IOException { @Override public long read(Buffer sink, long byteCount) throws IOException {
while (left == 0) { while (left == 0) {
if ((flags & FLAG_END_HEADERS) != 0) return -1; if ((flags & FLAG_END_HEADERS) != 0) return -1;
readContinuationHeader(); readContinuationHeader();
@@ -521,9 +521,8 @@ public final class Http20Draft09 implements Variant {
return read; return read;
} }
@Override public Source deadline(Deadline deadline) { @Override public Timeout timeout() {
source.deadline(deadline); return source.timeout();
return this;
} }
@Override public void close() throws IOException { @Override public void close() throws IOException {

View File

@@ -5,13 +5,13 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.zip.DataFormatException; import java.util.zip.DataFormatException;
import java.util.zip.Inflater; import java.util.zip.Inflater;
import okio.Buffer;
import okio.BufferedSource; import okio.BufferedSource;
import okio.ByteString; import okio.ByteString;
import okio.Deadline;
import okio.InflaterSource; import okio.InflaterSource;
import okio.OkBuffer;
import okio.Okio; import okio.Okio;
import okio.Source; import okio.Source;
import okio.Timeout;
/** /**
* Reads a SPDY/3 Name/Value header block. This class is made complicated by the * Reads a SPDY/3 Name/Value header block. This class is made complicated by the
@@ -37,7 +37,7 @@ class NameValueBlockReader {
// block. We cut the inflater off at its source because we can't predict the // block. We cut the inflater off at its source because we can't predict the
// ratio of compressed bytes to uncompressed bytes. // ratio of compressed bytes to uncompressed bytes.
Source throttleSource = new Source() { Source throttleSource = new Source() {
@Override public long read(OkBuffer sink, long byteCount) @Override public long read(Buffer sink, long byteCount)
throws IOException { throws IOException {
if (compressedLimit == 0) return -1; // Out of data for the current block. if (compressedLimit == 0) return -1; // Out of data for the current block.
long read = source.read(sink, Math.min(byteCount, compressedLimit)); long read = source.read(sink, Math.min(byteCount, compressedLimit));
@@ -50,9 +50,8 @@ class NameValueBlockReader {
source.close(); source.close();
} }
@Override public Source deadline(Deadline deadline) { @Override public Timeout timeout() {
source.deadline(deadline); return source.timeout();
return this;
} }
}; };

View File

@@ -22,11 +22,11 @@ import java.io.UnsupportedEncodingException;
import java.net.ProtocolException; import java.net.ProtocolException;
import java.util.List; import java.util.List;
import java.util.zip.Deflater; import java.util.zip.Deflater;
import okio.Buffer;
import okio.BufferedSink; import okio.BufferedSink;
import okio.BufferedSource; import okio.BufferedSource;
import okio.ByteString; import okio.ByteString;
import okio.DeflaterSink; import okio.DeflaterSink;
import okio.OkBuffer;
import okio.Okio; import okio.Okio;
/** /**
@@ -292,7 +292,7 @@ final class Spdy3 implements Variant {
/** Write spdy/3 frames. */ /** Write spdy/3 frames. */
static final class Writer implements FrameWriter { static final class Writer implements FrameWriter {
private final BufferedSink sink; private final BufferedSink sink;
private final OkBuffer headerBlockBuffer; private final Buffer headerBlockBuffer;
private final BufferedSink headerBlockOut; private final BufferedSink headerBlockOut;
private final boolean client; private final boolean client;
private boolean closed; private boolean closed;
@@ -303,7 +303,7 @@ final class Spdy3 implements Variant {
Deflater deflater = new Deflater(); Deflater deflater = new Deflater();
deflater.setDictionary(DICTIONARY); deflater.setDictionary(DICTIONARY);
headerBlockBuffer = new OkBuffer(); headerBlockBuffer = new Buffer();
headerBlockOut = Okio.buffer(new DeflaterSink(headerBlockBuffer, deflater)); headerBlockOut = Okio.buffer(new DeflaterSink(headerBlockBuffer, deflater));
} }
@@ -388,18 +388,18 @@ final class Spdy3 implements Variant {
sink.flush(); sink.flush();
} }
@Override public synchronized void data(boolean outFinished, int streamId, OkBuffer source) @Override public synchronized void data(boolean outFinished, int streamId, Buffer source)
throws IOException { throws IOException {
data(outFinished, streamId, source, (int) source.size()); data(outFinished, streamId, source, (int) source.size());
} }
@Override public synchronized void data(boolean outFinished, int streamId, OkBuffer source, @Override public synchronized void data(boolean outFinished, int streamId, Buffer source,
int byteCount) throws IOException { int byteCount) throws IOException {
int flags = (outFinished ? FLAG_FIN : 0); int flags = (outFinished ? FLAG_FIN : 0);
sendDataFrame(streamId, flags, source, byteCount); sendDataFrame(streamId, flags, source, byteCount);
} }
void sendDataFrame(int streamId, int flags, OkBuffer buffer, int byteCount) void sendDataFrame(int streamId, int flags, Buffer buffer, int byteCount)
throws IOException { throws IOException {
if (closed) throw new IOException("closed"); if (closed) throw new IOException("closed");
if (byteCount > 0xffffffL) { if (byteCount > 0xffffffL) {

View File

@@ -33,10 +33,10 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue; import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import okio.Buffer;
import okio.BufferedSink; import okio.BufferedSink;
import okio.BufferedSource; import okio.BufferedSource;
import okio.ByteString; import okio.ByteString;
import okio.OkBuffer;
import okio.Okio; import okio.Okio;
import static com.squareup.okhttp.internal.spdy.Settings.DEFAULT_INITIAL_WINDOW_SIZE; import static com.squareup.okhttp.internal.spdy.Settings.DEFAULT_INITIAL_WINDOW_SIZE;
@@ -287,7 +287,7 @@ public final class SpdyConnection implements Closeable {
* will not block. The only use case for zero {@code byteCount} is closing * will not block. The only use case for zero {@code byteCount} is closing
* a flushed output stream. * a flushed output stream.
*/ */
public void writeData(int streamId, boolean outFinished, OkBuffer buffer, long byteCount) public void writeData(int streamId, boolean outFinished, Buffer buffer, long byteCount)
throws IOException { throws IOException {
if (byteCount == 0) { // Empty data frames are not flow-controlled. if (byteCount == 0) { // Empty data frames are not flow-controlled.
frameWriter.data(outFinished, streamId, buffer, 0); frameWriter.data(outFinished, streamId, buffer, 0);
@@ -809,7 +809,7 @@ public final class SpdyConnection implements Closeable {
*/ */
private void pushDataLater(final int streamId, final BufferedSource source, final int byteCount, private void pushDataLater(final int streamId, final BufferedSource source, final int byteCount,
final boolean inFinished) throws IOException { final boolean inFinished) throws IOException {
final OkBuffer buffer = new OkBuffer(); final Buffer buffer = new Buffer();
source.require(byteCount); // Eagerly read the frame before firing client thread. source.require(byteCount); // Eagerly read the frame before firing client thread.
source.read(buffer, byteCount); source.read(buffer, byteCount);
if (buffer.size() != byteCount) throw new IOException(buffer.size() + " != " + byteCount); if (buffer.size() != byteCount) throw new IOException(buffer.size() + " != " + byteCount);

View File

@@ -22,11 +22,11 @@ import java.io.InterruptedIOException;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import okio.Buffer;
import okio.BufferedSource; import okio.BufferedSource;
import okio.Deadline;
import okio.OkBuffer;
import okio.Sink; import okio.Sink;
import okio.Source; import okio.Source;
import okio.Timeout;
import static com.squareup.okhttp.internal.spdy.Settings.DEFAULT_INITIAL_WINDOW_SIZE; import static com.squareup.okhttp.internal.spdy.Settings.DEFAULT_INITIAL_WINDOW_SIZE;
@@ -338,10 +338,10 @@ public final class SpdyStream {
*/ */
private final class SpdyDataSource implements Source { private final class SpdyDataSource implements Source {
/** Buffer to receive data from the network into. Only accessed by the reader thread. */ /** Buffer to receive data from the network into. Only accessed by the reader thread. */
private final OkBuffer receiveBuffer = new OkBuffer(); private final Buffer receiveBuffer = new Buffer();
/** Buffer with readable data. Guarded by SpdyStream.this. */ /** Buffer with readable data. Guarded by SpdyStream.this. */
private final OkBuffer readBuffer = new OkBuffer(); private final Buffer readBuffer = new Buffer();
/** Maximum number of bytes to buffer before reporting a flow control error. */ /** Maximum number of bytes to buffer before reporting a flow control error. */
private final long maxByteCount; private final long maxByteCount;
@@ -359,7 +359,7 @@ public final class SpdyStream {
this.maxByteCount = maxByteCount; this.maxByteCount = maxByteCount;
} }
@Override public long read(OkBuffer sink, long byteCount) @Override public long read(Buffer sink, long byteCount)
throws IOException { throws IOException {
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
@@ -462,9 +462,9 @@ public final class SpdyStream {
} }
} }
@Override public Source deadline(Deadline deadline) { @Override public Timeout timeout() {
// TODO: honor deadlines. // TODO: honor timeouts.
return this; return Timeout.NONE;
} }
@Override public void close() throws IOException { @Override public void close() throws IOException {
@@ -518,7 +518,7 @@ public final class SpdyStream {
*/ */
private boolean finished; private boolean finished;
@Override public void write(OkBuffer source, long byteCount) throws IOException { @Override public void write(Buffer source, long byteCount) throws IOException {
assert (!Thread.holdsLock(SpdyStream.this)); assert (!Thread.holdsLock(SpdyStream.this));
while (byteCount > 0) { while (byteCount > 0) {
long toWrite; long toWrite;
@@ -549,9 +549,9 @@ public final class SpdyStream {
connection.flush(); connection.flush();
} }
@Override public Sink deadline(Deadline deadline) { @Override public Timeout timeout() {
// TODO: honor deadlines. // TODO: honor timeouts.
return this; return Timeout.NONE;
} }
@Override public void close() throws IOException { @Override public void close() throws IOException {

View File

@@ -1,29 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.squareup.okhttp</groupId>
<artifactId>parent</artifactId>
<version>2.0.0-SNAPSHOT</version>
</parent>
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
<name>Okio</name>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-annotations</artifactId>
<version>1.10</version>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@@ -1,148 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/**
* @author Alexander Y. Kleymenov
*/
package okio;
import java.io.UnsupportedEncodingException;
final class Base64 {
private Base64() {
}
public static byte[] decode(String in) {
// Ignore trailing '=' padding and whitespace from the input.
int limit = in.length();
for (; limit > 0; limit--) {
char c = in.charAt(limit - 1);
if (c != '=' && c != '\n' && c != '\r' && c != ' ' && c != '\t') {
break;
}
}
// If the input includes whitespace, this output array will be longer than necessary.
byte[] out = new byte[(int) (limit * 6L / 8L)];
int outCount = 0;
int inCount = 0;
int word = 0;
for (int pos = 0; pos < limit; pos++) {
char c = in.charAt(pos);
int bits;
if (c >= 'A' && c <= 'Z') {
// char ASCII value
// A 65 0
// Z 90 25 (ASCII - 65)
bits = c - 65;
} else if (c >= 'a' && c <= 'z') {
// char ASCII value
// a 97 26
// z 122 51 (ASCII - 71)
bits = c - 71;
} else if (c >= '0' && c <= '9') {
// char ASCII value
// 0 48 52
// 9 57 61 (ASCII + 4)
bits = c + 4;
} else if (c == '+') {
bits = 62;
} else if (c == '/') {
bits = 63;
} else if (c == '\n' || c == '\r' || c == ' ' || c == '\t') {
continue;
} else {
return null;
}
// Append this char's 6 bits to the word.
word = (word << 6) | (byte) bits;
// For every 4 chars of input, we accumulate 24 bits of output. Emit 3 bytes.
inCount++;
if (inCount % 4 == 0) {
out[outCount++] = (byte) (word >> 16);
out[outCount++] = (byte) (word >> 8);
out[outCount++] = (byte) word;
}
}
int lastWordChars = inCount % 4;
if (lastWordChars == 1) {
// We read 1 char followed by "===". But 6 bits is a truncated byte! Fail.
return null;
} else if (lastWordChars == 2) {
// We read 2 chars followed by "==". Emit 1 byte with 8 of those 12 bits.
word = word << 12;
out[outCount++] = (byte) (word >> 16);
} else if (lastWordChars == 3) {
// We read 3 chars, followed by "=". Emit 2 bytes for 16 of those 18 bits.
word = word << 6;
out[outCount++] = (byte) (word >> 16);
out[outCount++] = (byte) (word >> 8);
}
// If we sized our out array perfectly, we're done.
if (outCount == out.length) return out;
// Copy the decoded bytes to a new, right-sized array.
byte[] prefix = new byte[outCount];
System.arraycopy(out, 0, prefix, 0, outCount);
return prefix;
}
private static final byte[] MAP = new byte[] {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4',
'5', '6', '7', '8', '9', '+', '/'
};
public static String encode(byte[] in) {
int length = (in.length + 2) * 4 / 3;
byte[] out = new byte[length];
int index = 0, end = in.length - in.length % 3;
for (int i = 0; i < end; i += 3) {
out[index++] = MAP[(in[i] & 0xff) >> 2];
out[index++] = MAP[((in[i] & 0x03) << 4) | ((in[i + 1] & 0xff) >> 4)];
out[index++] = MAP[((in[i + 1] & 0x0f) << 2) | ((in[i + 2] & 0xff) >> 6)];
out[index++] = MAP[(in[i + 2] & 0x3f)];
}
switch (in.length % 3) {
case 1:
out[index++] = MAP[(in[end] & 0xff) >> 2];
out[index++] = MAP[(in[end] & 0x03) << 4];
out[index++] = '=';
out[index++] = '=';
break;
case 2:
out[index++] = MAP[(in[end] & 0xff) >> 2];
out[index++] = MAP[((in[end] & 0x03) << 4) | ((in[end + 1] & 0xff) >> 4)];
out[index++] = MAP[((in[end + 1] & 0x0f) << 2)];
out[index++] = '=';
break;
}
try {
return new String(out, 0, index, "US-ASCII");
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
}
}

View File

@@ -1,72 +0,0 @@
/*
* Copyright (C) 2014 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 java.io.IOException;
import java.io.OutputStream;
/**
* A sink that keeps a buffer internally so that callers can do small writes
* without a performance penalty.
*/
public interface BufferedSink extends Sink {
/** Returns this sink's internal buffer. */
OkBuffer buffer();
BufferedSink write(ByteString byteString) throws IOException;
/**
* Like {@link OutputStream#write}, this writes a complete byte array to this
* sink.
*/
BufferedSink write(byte[] source) throws IOException;
/**
* Like {@link OutputStream#write}, this writes {@code byteCount} bytes
* of {@code source}, starting at {@code offset}.
*/
BufferedSink write(byte[] source, int offset, int byteCount) throws IOException;
/** Encodes {@code string} in UTF-8 and writes it to this sink. */
BufferedSink writeUtf8(String string) throws IOException;
/** Writes a byte to this sink. */
BufferedSink writeByte(int b) throws IOException;
/** Writes a big-endian short to this sink using two bytes. */
BufferedSink writeShort(int s) throws IOException;
/** Writes a little-endian short to this sink using two bytes. */
BufferedSink writeShortLe(int s) throws IOException;
/** Writes a big-endian int to this sink using four bytes. */
BufferedSink writeInt(int i) throws IOException;
/** Writes a little-endian int to this sink using four bytes. */
BufferedSink writeIntLe(int i) throws IOException;
/** Writes a big-endian long to this sink using eight bytes. */
BufferedSink writeLong(long v) throws IOException;
/** Writes a little-endian long to this sink using eight bytes. */
BufferedSink writeLongLe(long v) throws IOException;
/** Writes complete segments to this sink. Like {@link #flush}, but weaker. */
BufferedSink emitCompleteSegments() throws IOException;
/** Returns an output stream that writes to this sink. */
OutputStream outputStream();
}

View File

@@ -1,114 +0,0 @@
/*
* Copyright (C) 2014 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 java.io.IOException;
import java.io.InputStream;
/**
* A source that keeps a buffer internally so that callers can do small reads
* without a performance penalty. It also allows clients to read ahead,
* buffering as much as necessary before consuming input.
*/
public interface BufferedSource extends Source {
/** Returns this source's internal buffer. */
OkBuffer buffer();
/**
* Returns true if there are no more bytes in this source. This will block
* until there are bytes to read or the source is definitely exhausted.
*/
boolean exhausted() throws IOException;
/**
* Returns when the buffer contains at least {@code byteCount} bytes. Throws
* an {@link java.io.EOFException} if the source is exhausted before the
* required bytes can be read.
*/
void require(long byteCount) throws IOException;
/** Removes a byte from this source and returns it. */
byte readByte() throws IOException;
/** Removes two bytes from this source and returns a big-endian short. */
short readShort() throws IOException;
/** Removes two bytes from this source and returns a little-endian short. */
short readShortLe() throws IOException;
/** Removes four bytes from this source and returns a big-endian int. */
int readInt() throws IOException;
/** Removes four bytes from this source and returns a little-endian int. */
int readIntLe() throws IOException;
/** Removes eight bytes from this source and returns a big-endian long. */
long readLong() throws IOException;
/** Removes eight bytes from this source and returns a little-endian long. */
long readLongLe() throws IOException;
/**
* Reads and discards {@code byteCount} bytes from this source. Throws an
* {@link java.io.EOFException} if the source is exhausted before the
* requested bytes can be skipped.
*/
void skip(long byteCount) throws IOException;
/** Removes {@code byteCount} bytes from this and returns them as a byte string. */
ByteString readByteString(long byteCount) throws IOException;
/**
* Removes {@code byteCount} bytes from this, decodes them as UTF-8 and
* returns the string.
*/
String readUtf8(long byteCount) throws IOException;
/**
* Removes and returns characters up to but not including the next line break.
* A line break is either {@code "\n"} or {@code "\r\n"}; these characters are
* not included in the result.
*
* <p><strong>On the end of the stream this method returns null,</strong> just
* like {@link java.io.BufferedReader}. If the source doesn't end with a line
* break then an implicit line break is assumed. Null is returned once the
* source is exhausted. Use this for human-generated data, where a trailing
* line break is optional.
*/
String readUtf8Line() throws IOException;
/**
* Removes and returns characters up to but not including the next line break.
* A line break is either {@code "\n"} or {@code "\r\n"}; these characters are
* not included in the result.
*
* <p><strong>On the end of the stream this method throws.</strong> Every call
* must consume either '\r\n' or '\n'. If these characters are absent in the
* stream, an {@link java.io.EOFException} is thrown. Use this for
* machine-generated data where a missing line break implies truncated input.
*/
String readUtf8LineStrict() throws IOException;
/**
* Returns the index of {@code b} in the buffer, refilling it if necessary
* until it is found. This reads an unbounded number of bytes into the buffer.
* Returns -1 if the stream is exhausted before the requested byte is found.
*/
long indexOf(byte b) throws IOException;
/** Returns an input stream that reads from this source. */
InputStream inputStream();
}

View File

@@ -1,211 +0,0 @@
/*
* Copyright 2014 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 java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
/**
* An immutable sequence of bytes.
*
* <p><strong>Full disclosure:</strong> this class provides untrusted input and
* output streams with raw access to the underlying byte array. A hostile
* stream implementation could keep a reference to the mutable byte string,
* violating the immutable guarantee of this class. For this reason a byte
* string's immutability guarantee cannot be relied upon for security in applets
* and other environments that run both trusted and untrusted code in the same
* process.
*/
public final class ByteString {
private static final char[] HEX_DIGITS =
{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
/** A singleton empty {@code ByteString}. */
public static final ByteString EMPTY = ByteString.of();
final byte[] data;
private transient int hashCode; // Lazily computed; 0 if unknown.
private transient String utf8; // Lazily computed.
ByteString(byte[] data) {
this.data = data; // Trusted internal constructor doesn't clone data.
}
/**
* Returns a new byte string containing a clone of the bytes of {@code data}.
*/
public static ByteString of(byte... data) {
return new ByteString(data.clone());
}
/** Returns a new byte string containing the {@code UTF-8} bytes of {@code s}. */
public static ByteString encodeUtf8(String s) {
ByteString byteString = new ByteString(s.getBytes(Util.UTF_8));
byteString.utf8 = s;
return byteString;
}
/** Constructs a new {@code String} by decoding the bytes as {@code UTF-8}. */
public String utf8() {
String result = utf8;
// We don't care if we double-allocate in racy code.
return result != null ? result : (utf8 = new String(data, Util.UTF_8));
}
/**
* Returns this byte string encoded as <a
* href="http://www.ietf.org/rfc/rfc2045.txt">Base64</a>. In violation of the
* RFC, the returned string does not wrap lines at 76 columns.
*/
public String base64() {
return Base64.encode(data);
}
/**
* Decodes the Base64-encoded bytes and returns their value as a byte string.
* Returns null if {@code base64} is not a Base64-encoded sequence of bytes.
*/
public static ByteString decodeBase64(String base64) {
byte[] decoded = Base64.decode(base64);
return decoded != null ? new ByteString(decoded) : null;
}
/** Returns this byte string encoded in hexadecimal. */
public String hex() {
char[] result = new char[data.length * 2];
int c = 0;
for (byte b : data) {
result[c++] = HEX_DIGITS[(b >> 4) & 0xf];
result[c++] = HEX_DIGITS[b & 0xf];
}
return new String(result);
}
/** Decodes the hex-encoded bytes and returns their value a byte string. */
public static ByteString decodeHex(String hex) {
if (hex.length() % 2 != 0) throw new IllegalArgumentException("Unexpected hex string: " + hex);
byte[] result = new byte[hex.length() / 2];
for (int i = 0; i < result.length; i++) {
int d1 = decodeHexDigit(hex.charAt(i * 2)) << 4;
int d2 = decodeHexDigit(hex.charAt(i * 2 + 1));
result[i] = (byte) (d1 + d2);
}
return of(result);
}
private static int decodeHexDigit(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
throw new IllegalArgumentException("Unexpected hex digit: " + c);
}
/**
* Reads {@code count} bytes from {@code in} and returns the result.
*
* @throws java.io.EOFException if {@code in} has fewer than {@code count}
* bytes to read.
*/
public static ByteString read(InputStream in, int byteCount) throws IOException {
byte[] result = new byte[byteCount];
for (int offset = 0, read; offset < byteCount; offset += read) {
read = in.read(result, offset, byteCount - offset);
if (read == -1) throw new EOFException();
}
return new ByteString(result);
}
/**
* Returns a byte string equal to this byte string, but with the bytes 'A'
* through 'Z' replaced with the corresponding byte in 'a' through 'z'.
* Returns this byte string if it contains no bytes in 'A' through 'Z'.
*/
public ByteString toAsciiLowercase() {
// Search for an uppercase character. If we don't find one, return this.
for (int i = 0; i < data.length; i++) {
byte c = data[i];
if (c < 'A' || c > 'Z') continue;
// If we reach this point, this string is not not lowercase. Create and
// return a new byte string.
byte[] lowercase = data.clone();
lowercase[i++] = (byte) (c - ('A' - 'a'));
for (; i < lowercase.length; i++) {
c = lowercase[i];
if (c < 'A' || c > 'Z') continue;
lowercase[i] = (byte) (c - ('A' - 'a'));
}
return new ByteString(lowercase);
}
return this;
}
/** Returns the byte at {@code pos}. */
public byte getByte(int pos) {
return data[pos];
}
/**
* Returns the number of bytes in this ByteString.
*/
public int size() {
return data.length;
}
/**
* Returns a byte array containing a copy of the bytes in this {@code ByteString}.
*/
public byte[] toByteArray() {
return data.clone();
}
/** Writes the contents of this byte string to {@code out}. */
public void write(OutputStream out) throws IOException {
out.write(data);
}
@Override public boolean equals(Object o) {
return o == this || o instanceof ByteString && Arrays.equals(((ByteString) o).data, data);
}
@Override public int hashCode() {
int result = hashCode;
return result != 0 ? result : (hashCode = Arrays.hashCode(data));
}
@Override public String toString() {
if (data.length == 0) {
return "ByteString[size=0]";
}
if (data.length <= 16) {
return String.format("ByteString[size=%s data=%s]", data.length, hex());
}
try {
return String.format("ByteString[size=%s md5=%s]", data.length,
ByteString.of(MessageDigest.getInstance("MD5").digest(data)).hex());
} catch (NoSuchAlgorithmException e) {
throw new AssertionError();
}
}
}

View File

@@ -1,58 +0,0 @@
/*
* Copyright (C) 2014 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 java.io.IOException;
import java.io.InterruptedIOException;
import java.util.concurrent.TimeUnit;
/**
* 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 Deadline {
public static final Deadline NONE = new Deadline() {
@Override public Deadline start(long timeout, TimeUnit unit) {
throw new UnsupportedOperationException();
}
@Override public boolean reached() {
return false;
}
};
private long deadlineNanos;
public Deadline() {
}
public Deadline start(long timeout, TimeUnit unit) {
deadlineNanos = System.nanoTime() + unit.toNanos(timeout);
return this;
}
public boolean reached() {
return System.nanoTime() - deadlineNanos >= 0; // Subtract to avoid overflow!
}
public final void throwIfReached() throws IOException {
// TODO: a more catchable exception type?
if (reached()) throw new IOException("Deadline reached");
// If the thread is interrupted, do not proceed with further I/O.
if (Thread.interrupted()) throw new InterruptedIOException();
}
}

View File

@@ -1,138 +0,0 @@
/*
* Copyright (C) 2014 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 java.io.IOException;
import java.util.zip.Deflater;
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
import static okio.Util.checkOffsetAndCount;
/**
* A sink that uses <a href="http://tools.ietf.org/html/rfc1951">DEFLATE</a> to
* compress data written to another source.
*
* <h3>Sync flush</h3>
* Aggressive flushing of this stream may result in reduced compression. Each
* call to {@link #flush} immediately compresses all currently-buffered data;
* this early compression may be less effective than compression performed
* without flushing.
*
* <p>This is equivalent to using {@link Deflater} with the sync flush option.
* This class does not offer any partial flush mechanism. For best performance,
* only call {@link #flush} when application behavior requires it.
*/
public final class DeflaterSink implements Sink {
private final BufferedSink sink;
private final Deflater deflater;
private boolean closed;
public DeflaterSink(Sink sink, Deflater deflater) {
this.sink = Okio.buffer(sink);
this.deflater = deflater;
}
@Override public void write(OkBuffer source, long byteCount)
throws IOException {
checkOffsetAndCount(source.size, 0, byteCount);
while (byteCount > 0) {
// Share bytes from the head segment of 'source' with the deflater.
Segment head = source.head;
int toDeflate = (int) Math.min(byteCount, head.limit - head.pos);
deflater.setInput(head.data, head.pos, toDeflate);
// Deflate those bytes into sink.
deflate(false);
// Mark those bytes as read.
source.size -= toDeflate;
head.pos += toDeflate;
if (head.pos == head.limit) {
source.head = head.pop();
SegmentPool.INSTANCE.recycle(head);
}
byteCount -= toDeflate;
}
}
@IgnoreJRERequirement
private void deflate(boolean syncFlush) throws IOException {
OkBuffer buffer = sink.buffer();
while (true) {
Segment s = buffer.writableSegment(1);
// The 4-parameter overload of deflate() doesn't exist in the RI until
// Java 1.7, and is public (although with @hide) on Android since 2.3.
// The @hide tag means that this code won't compile against the Android
// 2.3 SDK, but it will run fine there.
int deflated = syncFlush
? deflater.deflate(s.data, s.limit, Segment.SIZE - s.limit, Deflater.SYNC_FLUSH)
: deflater.deflate(s.data, s.limit, Segment.SIZE - s.limit);
if (deflated > 0) {
s.limit += deflated;
buffer.size += deflated;
sink.emitCompleteSegments();
} else if (deflater.needsInput()) {
return;
}
}
}
@Override public void flush() throws IOException {
deflate(true);
sink.flush();
}
@Override public void close() throws IOException {
if (closed) return;
// Emit deflated data to the underlying sink. If this fails, we still need
// to close the deflater and the sink; otherwise we risk leaking resources.
Throwable thrown = null;
try {
deflater.finish();
deflate(false);
} catch (Throwable e) {
thrown = e;
}
try {
deflater.end();
} catch (Throwable e) {
if (thrown == null) thrown = e;
}
try {
sink.close();
} catch (Throwable e) {
if (thrown == null) thrown = e;
}
closed = true;
if (thrown != null) Util.sneakyRethrow(thrown);
}
@Override public Sink deadline(Deadline deadline) {
sink.deadline(deadline);
return this;
}
@Override public String toString() {
return "DeflaterSink(" + sink + ")";
}
}

View File

@@ -1,200 +0,0 @@
/*
* Copyright (C) 2014 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 java.io.EOFException;
import java.io.IOException;
import java.util.zip.CRC32;
import java.util.zip.Inflater;
public final class GzipSource implements Source {
private static final byte FHCRC = 1;
private static final byte FEXTRA = 2;
private static final byte FNAME = 3;
private static final byte FCOMMENT = 4;
private static final byte SECTION_HEADER = 0;
private static final byte SECTION_BODY = 1;
private static final byte SECTION_TRAILER = 2;
private static final byte SECTION_DONE = 3;
/** The current section. Always progresses forward. */
private int section = SECTION_HEADER;
/**
* Our source should yield a GZIP header (which we consume directly), followed
* by deflated bytes (which we consume via an InflaterSource), followed by a
* GZIP trailer (which we also consume directly).
*/
private final BufferedSource source;
/** The inflater used to decompress the deflated body. */
private final Inflater inflater;
/**
* The inflater source takes care of moving data between compressed source and
* decompressed sink buffers.
*/
private final InflaterSource inflaterSource;
/** Checksum used to check both the GZIP header and decompressed body. */
private final CRC32 crc = new CRC32();
public GzipSource(Source source) throws IOException {
this.inflater = new Inflater(true);
this.source = Okio.buffer(source);
this.inflaterSource = new InflaterSource(this.source, inflater);
}
@Override public long read(OkBuffer sink, long byteCount) throws IOException {
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
if (byteCount == 0) return 0;
// If we haven't consumed the header, we must consume it before anything else.
if (section == SECTION_HEADER) {
consumeHeader();
section = SECTION_BODY;
}
// Attempt to read at least a byte of the body. If we do, we're done.
if (section == SECTION_BODY) {
long offset = sink.size;
long result = inflaterSource.read(sink, byteCount);
if (result != -1) {
updateCrc(sink, offset, result);
return result;
}
section = SECTION_TRAILER;
}
// The body is exhausted; time to read the trailer. We always consume the
// trailer before returning a -1 exhausted result; that way if you read to
// the end of a GzipSource you guarantee that the CRC has been checked.
if (section == SECTION_TRAILER) {
consumeTrailer();
section = SECTION_DONE;
// Gzip streams self-terminate: they return -1 before their underlying
// source returns -1. Here we attempt to force the underlying stream to
// return -1 which may trigger it to release its resources. If it doesn't
// return -1, then our Gzip data finished prematurely!
if (!source.exhausted()) {
throw new IOException("gzip finished without exhausting source");
}
}
return -1;
}
private void consumeHeader() throws IOException {
// Read the 10-byte header. We peek at the flags byte first so we know if we
// need to CRC the entire header. Then we read the magic ID1ID2 sequence.
// We can skip everything else in the first 10 bytes.
// +---+---+---+---+---+---+---+---+---+---+
// |ID1|ID2|CM |FLG| MTIME |XFL|OS | (more-->)
// +---+---+---+---+---+---+---+---+---+---+
source.require(10);
byte flags = source.buffer().getByte(3);
boolean fhcrc = ((flags >> FHCRC) & 1) == 1;
if (fhcrc) updateCrc(source.buffer(), 0, 10);
short id1id2 = source.readShort();
checkEqual("ID1ID2", (short) 0x1f8b, id1id2);
source.skip(8);
// Skip optional extra fields.
// +---+---+=================================+
// | XLEN |...XLEN bytes of "extra field"...| (more-->)
// +---+---+=================================+
if (((flags >> FEXTRA) & 1) == 1) {
source.require(2);
if (fhcrc) updateCrc(source.buffer(), 0, 2);
int xlen = source.buffer().readShortLe();
source.require(xlen);
if (fhcrc) updateCrc(source.buffer(), 0, xlen);
source.skip(xlen);
}
// Skip an optional 0-terminated name.
// +=========================================+
// |...original file name, zero-terminated...| (more-->)
// +=========================================+
if (((flags >> FNAME) & 1) == 1) {
long index = source.indexOf((byte) 0);
if (index == -1) throw new EOFException();
if (fhcrc) updateCrc(source.buffer(), 0, index + 1);
source.skip(index + 1);
}
// Skip an optional 0-terminated comment.
// +===================================+
// |...file comment, zero-terminated...| (more-->)
// +===================================+
if (((flags >> FCOMMENT) & 1) == 1) {
long index = source.indexOf((byte) 0);
if (index == -1) throw new EOFException();
if (fhcrc) updateCrc(source.buffer(), 0, index + 1);
source.skip(index + 1);
}
// Confirm the optional header CRC.
// +---+---+
// | CRC16 |
// +---+---+
if (fhcrc) {
checkEqual("FHCRC", source.readShortLe(), (short) crc.getValue());
crc.reset();
}
}
private void consumeTrailer() throws IOException {
// Read the eight-byte trailer. Confirm the body's CRC and size.
// +---+---+---+---+---+---+---+---+
// | CRC32 | ISIZE |
// +---+---+---+---+---+---+---+---+
checkEqual("CRC", source.readIntLe(), (int) crc.getValue());
checkEqual("ISIZE", source.readIntLe(), inflater.getTotalOut());
}
@Override public Source deadline(Deadline deadline) {
source.deadline(deadline);
return this;
}
@Override public void close() throws IOException {
inflaterSource.close();
}
/** Updates the CRC with the given bytes. */
private void updateCrc(OkBuffer buffer, long offset, long byteCount) {
for (Segment s = buffer.head; byteCount > 0; s = s.next) {
int segmentByteCount = s.limit - s.pos;
if (offset < segmentByteCount) {
int toUpdate = (int) Math.min(byteCount, segmentByteCount - offset);
crc.update(s.data, (int) (s.pos + offset), toUpdate);
byteCount -= toUpdate;
}
offset -= segmentByteCount; // Track the offset of the current segment.
}
}
private void checkEqual(String name, int expected, int actual) throws IOException {
if (actual != expected) {
throw new IOException(String.format(
"%s: actual 0x%08x != expected 0x%08x", name, actual, expected));
}
}
}

View File

@@ -1,124 +0,0 @@
/*
* Copyright (C) 2014 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 java.io.EOFException;
import java.io.IOException;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
/**
* A source that uses <a href="http://tools.ietf.org/html/rfc1951">DEFLATE</a>
* to decompress data read from another source.
*/
public final class InflaterSource implements Source {
private final BufferedSource source;
private final Inflater inflater;
/**
* When we call Inflater.setInput(), the inflater keeps our byte array until
* it needs input again. This tracks how many bytes the inflater is currently
* holding on to.
*/
private int bufferBytesHeldByInflater;
private boolean closed;
public InflaterSource(Source source, Inflater inflater) {
this(Okio.buffer(source), inflater);
}
/**
* This package-private constructor shares a buffer with its trusted caller.
* In general we can't share a BufferedSource because the inflater holds input
* bytes until they are inflated.
*/
InflaterSource(BufferedSource source, Inflater inflater) {
if (source == null) throw new IllegalArgumentException("source == null");
if (inflater == null) throw new IllegalArgumentException("inflater == null");
this.source = source;
this.inflater = inflater;
}
@Override public long read(
OkBuffer sink, long byteCount) throws IOException {
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
if (closed) throw new IllegalStateException("closed");
if (byteCount == 0) return 0;
while (true) {
boolean sourceExhausted = refill();
// Decompress the inflater's compressed data into the sink.
try {
Segment tail = sink.writableSegment(1);
int bytesInflated = inflater.inflate(tail.data, tail.limit, Segment.SIZE - tail.limit);
if (bytesInflated > 0) {
tail.limit += bytesInflated;
sink.size += bytesInflated;
return bytesInflated;
}
if (inflater.finished() || inflater.needsDictionary()) {
releaseInflatedBytes();
return -1;
}
if (sourceExhausted) throw new EOFException("source exhausted prematurely");
} catch (DataFormatException e) {
throw new IOException(e);
}
}
}
/**
* Refills the inflater with compressed data if it needs input. (And only if
* it needs input). Returns true if the inflater required input but the source
* was exhausted.
*/
public boolean refill() throws IOException {
if (!inflater.needsInput()) return false;
releaseInflatedBytes();
if (inflater.getRemaining() != 0) throw new IllegalStateException("?"); // TODO: possible?
// If there are compressed bytes in the source, assign them to the inflater.
if (source.exhausted()) return true;
// Assign buffer bytes to the inflater.
Segment head = source.buffer().head;
bufferBytesHeldByInflater = head.limit - head.pos;
inflater.setInput(head.data, head.pos, bufferBytesHeldByInflater);
return false;
}
/** When the inflater has processed compressed data, remove it from the buffer. */
private void releaseInflatedBytes() throws IOException {
if (bufferBytesHeldByInflater == 0) return;
int toRelease = bufferBytesHeldByInflater - inflater.getRemaining();
bufferBytesHeldByInflater -= toRelease;
source.skip(toRelease);
}
@Override public Source deadline(Deadline deadline) {
source.deadline(deadline);
return this;
}
@Override public void close() throws IOException {
if (closed) return;
inflater.end();
closed = true;
source.close();
}
}

View File

@@ -1,745 +0,0 @@
/*
* Copyright (C) 2014 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 java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static okio.Util.UTF_8;
import static okio.Util.checkOffsetAndCount;
import static okio.Util.reverseBytesLong;
/**
* A collection of bytes in memory.
*
* <p><strong>Moving data from one OkBuffer to another is fast.</strong> Instead
* of copying bytes from one place in memory to another, this class just changes
* ownership of the underlying byte arrays.
*
* <p><strong>This buffer grows with your data.</strong> Just like ArrayList,
* each OkBuffer starts small. It consumes only the memory it needs to.
*
* <p><strong>This buffer pools its byte arrays.</strong> When you allocate a
* byte array in Java, the runtime must zero-fill the requested array before
* returning it to you. Even if you're going to write over that space anyway.
* This class avoids zero-fill and GC churn by pooling byte arrays.
*/
public final class OkBuffer implements BufferedSource, BufferedSink, Cloneable {
Segment head;
long size;
public OkBuffer() {
}
/** Returns the number of bytes currently in this buffer. */
public long size() {
return size;
}
@Override public OkBuffer buffer() {
return this;
}
@Override public OutputStream outputStream() {
return new OutputStream() {
@Override public void write(int b) {
writeByte((byte) b);
}
@Override public void write(byte[] data, int offset, int byteCount) {
OkBuffer.this.write(data, offset, byteCount);
}
@Override public void flush() {
}
@Override public void close() {
}
@Override public String toString() {
return this + ".outputStream()";
}
};
}
@Override public OkBuffer emitCompleteSegments() {
return this; // Nowhere to emit to!
}
@Override public boolean exhausted() {
return size == 0;
}
@Override public void require(long byteCount) throws EOFException {
if (this.size < byteCount) throw new EOFException();
}
@Override public InputStream inputStream() {
return new InputStream() {
@Override public int read() {
return readByte() & 0xff;
}
@Override public int read(byte[] sink, int offset, int byteCount) {
return OkBuffer.this.read(sink, offset, byteCount);
}
@Override public int available() {
return (int) Math.min(size, Integer.MAX_VALUE);
}
@Override public void close() {
}
@Override public String toString() {
return OkBuffer.this + ".inputStream()";
}
};
}
/**
* Returns the number of bytes in segments that are not writable. This is the
* number of bytes that can be flushed immediately to an underlying sink
* without harming throughput.
*/
public long completeSegmentByteCount() {
long result = size;
if (result == 0) return 0;
// Omit the tail if it's still writable.
Segment tail = head.prev;
if (tail.limit < Segment.SIZE) {
result -= tail.limit - tail.pos;
}
return result;
}
@Override public byte readByte() {
if (size == 0) throw new IllegalStateException("size == 0");
Segment segment = head;
int pos = segment.pos;
int limit = segment.limit;
byte[] data = segment.data;
byte b = data[pos++];
size -= 1;
if (pos == limit) {
head = segment.pop();
SegmentPool.INSTANCE.recycle(segment);
} else {
segment.pos = pos;
}
return b;
}
/** Returns the byte at {@code pos}. */
public byte getByte(long pos) {
checkOffsetAndCount(size, pos, 1);
for (Segment s = head; true; s = s.next) {
int segmentByteCount = s.limit - s.pos;
if (pos < segmentByteCount) return s.data[s.pos + (int) pos];
pos -= segmentByteCount;
}
}
@Override public short readShort() {
if (size < 2) throw new IllegalStateException("size < 2: " + size);
Segment segment = head;
int pos = segment.pos;
int limit = segment.limit;
// If the short is split across multiple segments, delegate to readByte().
if (limit - pos < 2) {
int s = (readByte() & 0xff) << 8
| (readByte() & 0xff);
return (short) s;
}
byte[] data = segment.data;
int s = (data[pos++] & 0xff) << 8
| (data[pos++] & 0xff);
size -= 2;
if (pos == limit) {
head = segment.pop();
SegmentPool.INSTANCE.recycle(segment);
} else {
segment.pos = pos;
}
return (short) s;
}
@Override public int readInt() {
if (size < 4) throw new IllegalStateException("size < 4: " + size);
Segment segment = head;
int pos = segment.pos;
int limit = segment.limit;
// If the int is split across multiple segments, delegate to readByte().
if (limit - pos < 4) {
return (readByte() & 0xff) << 24
| (readByte() & 0xff) << 16
| (readByte() & 0xff) << 8
| (readByte() & 0xff);
}
byte[] data = segment.data;
int i = (data[pos++] & 0xff) << 24
| (data[pos++] & 0xff) << 16
| (data[pos++] & 0xff) << 8
| (data[pos++] & 0xff);
size -= 4;
if (pos == limit) {
head = segment.pop();
SegmentPool.INSTANCE.recycle(segment);
} else {
segment.pos = pos;
}
return i;
}
@Override public long readLong() {
if (size < 8) throw new IllegalStateException("size < 8: " + size);
Segment segment = head;
int pos = segment.pos;
int limit = segment.limit;
// If the long is split across multiple segments, delegate to readInt().
if (limit - pos < 8) {
return (readInt() & 0xffffffffL) << 32
| (readInt() & 0xffffffffL);
}
byte[] data = segment.data;
long v = (data[pos++] & 0xffL) << 56
| (data[pos++] & 0xffL) << 48
| (data[pos++] & 0xffL) << 40
| (data[pos++] & 0xffL) << 32
| (data[pos++] & 0xffL) << 24
| (data[pos++] & 0xffL) << 16
| (data[pos++] & 0xffL) << 8
| (data[pos++] & 0xffL);
size -= 8;
if (pos == limit) {
head = segment.pop();
SegmentPool.INSTANCE.recycle(segment);
} else {
segment.pos = pos;
}
return v;
}
@Override public short readShortLe() {
return Util.reverseBytesShort(readShort());
}
@Override public int readIntLe() {
return Util.reverseBytesInt(readInt());
}
@Override public long readLongLe() {
return Util.reverseBytesLong(readLong());
}
@Override public ByteString readByteString(long byteCount) {
return new ByteString(readBytes(byteCount));
}
@Override public String readUtf8(long byteCount) {
checkOffsetAndCount(this.size, 0, byteCount);
if (byteCount > Integer.MAX_VALUE) {
throw new IllegalArgumentException("byteCount > Integer.MAX_VALUE: " + byteCount);
}
if (byteCount == 0) return "";
Segment head = this.head;
if (head.pos + byteCount > head.limit) {
// If the string spans multiple segments, delegate to readBytes().
return new String(readBytes(byteCount), Util.UTF_8);
}
String result = new String(head.data, head.pos, (int) byteCount, UTF_8);
head.pos += byteCount;
this.size -= byteCount;
if (head.pos == head.limit) {
this.head = head.pop();
SegmentPool.INSTANCE.recycle(head);
}
return result;
}
@Override public String readUtf8Line() throws IOException {
long newline = indexOf((byte) '\n');
if (newline == -1) {
return size != 0 ? readUtf8(size) : null;
}
return readUtf8Line(newline);
}
@Override public String readUtf8LineStrict() throws IOException {
long newline = indexOf((byte) '\n');
if (newline == -1) throw new EOFException();
return readUtf8Line(newline);
}
String readUtf8Line(long newline) {
if (newline > 0 && getByte(newline - 1) == '\r') {
// Read everything until '\r\n', then skip the '\r\n'.
String result = readUtf8((newline - 1));
skip(2);
return result;
} else {
// Read everything until '\n', then skip the '\n'.
String result = readUtf8(newline);
skip(1);
return result;
}
}
private byte[] readBytes(long byteCount) {
checkOffsetAndCount(this.size, 0, byteCount);
if (byteCount > Integer.MAX_VALUE) {
throw new IllegalArgumentException("byteCount > Integer.MAX_VALUE: " + byteCount);
}
int offset = 0;
byte[] result = new byte[(int) byteCount];
while (offset < byteCount) {
int toCopy = (int) Math.min(byteCount - offset, head.limit - head.pos);
System.arraycopy(head.data, head.pos, result, offset, toCopy);
offset += toCopy;
head.pos += toCopy;
if (head.pos == head.limit) {
Segment toRecycle = head;
head = toRecycle.pop();
SegmentPool.INSTANCE.recycle(toRecycle);
}
}
this.size -= byteCount;
return result;
}
/** Like {@link InputStream#read}. */
int read(byte[] sink, int offset, int byteCount) {
Segment s = this.head;
if (s == null) return -1;
int toCopy = Math.min(byteCount, s.limit - s.pos);
System.arraycopy(s.data, s.pos, sink, offset, toCopy);
s.pos += toCopy;
this.size -= toCopy;
if (s.pos == s.limit) {
this.head = s.pop();
SegmentPool.INSTANCE.recycle(s);
}
return toCopy;
}
/**
* Discards all bytes in this buffer. Calling this method when you're done
* with a buffer will return its segments to the pool.
*/
public void clear() {
skip(size);
}
/** Discards {@code byteCount} bytes from the head of this buffer. */
@Override public void skip(long byteCount) {
checkOffsetAndCount(this.size, 0, byteCount);
this.size -= byteCount;
while (byteCount > 0) {
int toSkip = (int) Math.min(byteCount, head.limit - head.pos);
byteCount -= toSkip;
head.pos += toSkip;
if (head.pos == head.limit) {
Segment toRecycle = head;
head = toRecycle.pop();
SegmentPool.INSTANCE.recycle(toRecycle);
}
}
}
@Override public OkBuffer write(ByteString byteString) {
return write(byteString.data, 0, byteString.data.length);
}
@Override public OkBuffer writeUtf8(String string) {
// TODO: inline UTF-8 encoding to save allocating a byte[]?
byte[] data = string.getBytes(Util.UTF_8);
return write(data, 0, data.length);
}
@Override public OkBuffer write(byte[] source) {
return write(source, 0, source.length);
}
@Override public OkBuffer write(byte[] source, int offset, int byteCount) {
int limit = offset + byteCount;
while (offset < limit) {
Segment tail = writableSegment(1);
int toCopy = Math.min(limit - offset, Segment.SIZE - tail.limit);
System.arraycopy(source, offset, tail.data, tail.limit, toCopy);
offset += toCopy;
tail.limit += toCopy;
}
this.size += byteCount;
return this;
}
@Override public OkBuffer writeByte(int b) {
Segment tail = writableSegment(1);
tail.data[tail.limit++] = (byte) b;
size += 1;
return this;
}
@Override public OkBuffer writeShort(int s) {
Segment tail = writableSegment(2);
byte[] data = tail.data;
int limit = tail.limit;
data[limit++] = (byte) ((s >>> 8) & 0xff);
data[limit++] = (byte) (s & 0xff);
tail.limit = limit;
size += 2;
return this;
}
@Override public BufferedSink writeShortLe(int s) {
return writeShort(Util.reverseBytesShort((short) s));
}
@Override public OkBuffer writeInt(int i) {
Segment tail = writableSegment(4);
byte[] data = tail.data;
int limit = tail.limit;
data[limit++] = (byte) ((i >>> 24) & 0xff);
data[limit++] = (byte) ((i >>> 16) & 0xff);
data[limit++] = (byte) ((i >>> 8) & 0xff);
data[limit++] = (byte) (i & 0xff);
tail.limit = limit;
size += 4;
return this;
}
@Override public BufferedSink writeIntLe(int i) {
return writeInt(Util.reverseBytesInt(i));
}
@Override public OkBuffer writeLong(long v) {
Segment tail = writableSegment(8);
byte[] data = tail.data;
int limit = tail.limit;
data[limit++] = (byte) ((v >>> 56L) & 0xff);
data[limit++] = (byte) ((v >>> 48L) & 0xff);
data[limit++] = (byte) ((v >>> 40L) & 0xff);
data[limit++] = (byte) ((v >>> 32L) & 0xff);
data[limit++] = (byte) ((v >>> 24L) & 0xff);
data[limit++] = (byte) ((v >>> 16L) & 0xff);
data[limit++] = (byte) ((v >>> 8L) & 0xff);
data[limit++] = (byte) (v & 0xff);
tail.limit = limit;
size += 8;
return this;
}
@Override public BufferedSink writeLongLe(long v) {
return writeLong(reverseBytesLong(v));
}
/**
* Returns a tail segment that we can write at least {@code minimumCapacity}
* bytes to, creating it if necessary.
*/
Segment writableSegment(int minimumCapacity) {
if (minimumCapacity < 1 || minimumCapacity > Segment.SIZE) throw new IllegalArgumentException();
if (head == null) {
head = SegmentPool.INSTANCE.take(); // Acquire a first segment.
return head.next = head.prev = head;
}
Segment tail = head.prev;
if (tail.limit + minimumCapacity > Segment.SIZE) {
tail = tail.push(SegmentPool.INSTANCE.take()); // Append a new empty segment to fill up.
}
return tail;
}
@Override public void write(OkBuffer source, long byteCount) {
// 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.
//
//
// Don't waste CPU (ie. don't copy data around).
//
// Copying large amounts of data is expensive. Instead, we prefer to
// reassign entire segments from one OkBuffer to the other.
//
//
// Don't waste memory.
//
// As an invariant, adjacent pairs of segments in an OkBuffer should be at
// least 50% full, except for the head segment and the tail segment.
//
// The head segment cannot maintain the invariant because the application is
// consuming bytes from this segment, decreasing its level.
//
// The tail segment cannot maintain the invariant because the application is
// producing bytes, which may require new nearly-empty tail segments to be
// appended.
//
//
// Moving segments between buffers
//
// When writing one buffer to another, we prefer to reassign entire segments
// over copying bytes into their most compact form. Suppose we have a buffer
// with these segment levels [91%, 61%]. If we append a buffer with a
// single [72%] segment, that yields [91%, 61%, 72%]. No bytes are copied.
//
// Or suppose we have a buffer with these segment levels: [100%, 2%], and we
// want to append it to a buffer with these segment levels [99%, 3%]. This
// operation will yield the following segments: [100%, 2%, 99%, 3%]. That
// is, we do not spend time copying bytes around to achieve more efficient
// memory use like [100%, 100%, 4%].
//
// When combining buffers, we will compact adjacent buffers when their
// 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
//
// Occasionally we write only part of a source buffer to a sink buffer. For
// example, given a sink [51%, 91%], we may want to write the first 30% of
// a source [92%, 82%] to it. To simplify, we first transform the source to
// an equivalent buffer [30%, 62%, 82%] and then move the head segment,
// yielding sink [51%, 91%, 30%] and source [62%, 82%].
if (source == this) {
throw new IllegalArgumentException("source == this");
}
checkOffsetAndCount(source.size, 0, byteCount);
while (byteCount > 0) {
// Is a prefix of the source's head segment all that we need to move?
if (byteCount < (source.head.limit - source.head.pos)) {
Segment tail = head != null ? head.prev : null;
if (tail == null || byteCount + (tail.limit - tail.pos) > Segment.SIZE) {
// We're going to need another segment. Split the source's head
// segment in two, then move the first of those two to this buffer.
source.head = source.head.split((int) byteCount);
} else {
// Our existing segments are sufficient. Move bytes from source's head to our tail.
source.head.writeTo(tail, (int) byteCount);
source.size -= byteCount;
this.size += byteCount;
return;
}
}
// Remove the source's head segment and append it to our tail.
Segment segmentToMove = source.head;
long movedByteCount = segmentToMove.limit - segmentToMove.pos;
source.head = segmentToMove.pop();
if (head == null) {
head = segmentToMove;
head.next = head.prev = head;
} else {
Segment tail = head.prev;
tail = tail.push(segmentToMove);
tail.compact();
}
source.size -= movedByteCount;
this.size += movedByteCount;
byteCount -= movedByteCount;
}
}
@Override public long read(OkBuffer sink, long byteCount) {
if (this.size == 0) return -1L;
if (byteCount > this.size) byteCount = this.size;
sink.write(this, byteCount);
return byteCount;
}
@Override public OkBuffer deadline(Deadline deadline) {
// All operations are in memory so this class doesn't need to honor deadlines.
return this;
}
@Override public long indexOf(byte b) {
return indexOf(b, 0);
}
/**
* Returns the index of {@code b} in this at or beyond {@code fromIndex}, or
* -1 if this buffer does not contain {@code b} in that range.
*/
public long indexOf(byte b, long fromIndex) {
Segment s = head;
if (s == null) return -1L;
long offset = 0L;
do {
int segmentByteCount = s.limit - s.pos;
if (fromIndex > segmentByteCount) {
fromIndex -= segmentByteCount;
} else {
byte[] data = s.data;
for (long pos = s.pos + fromIndex, limit = s.limit; pos < limit; pos++) {
if (data[(int) pos] == b) return offset + pos - s.pos;
}
fromIndex = 0;
}
offset += segmentByteCount;
s = s.next;
} while (s != head);
return -1L;
}
@Override public void flush() {
}
@Override public void close() {
}
/** For testing. This returns the sizes of the segments in this buffer. */
List<Integer> segmentSizes() {
if (head == null) return Collections.emptyList();
List<Integer> result = new ArrayList<Integer>();
result.add(head.limit - head.pos);
for (Segment s = head.next; s != head; s = s.next) {
result.add(s.limit - s.pos);
}
return result;
}
@Override public boolean equals(Object o) {
if (!(o instanceof OkBuffer)) return false;
OkBuffer that = (OkBuffer) o;
if (size != that.size) return false;
if (size == 0) return true; // Both buffers are empty.
Segment sa = this.head;
Segment sb = that.head;
int posA = sa.pos;
int posB = sb.pos;
for (long pos = 0, count; pos < size; pos += count) {
count = Math.min(sa.limit - posA, sb.limit - posB);
for (int i = 0; i < count; i++) {
if (sa.data[posA++] != sb.data[posB++]) return false;
}
if (posA == sa.limit) {
sa = sa.next;
posA = sa.pos;
}
if (posB == sb.limit) {
sb = sb.next;
posB = sb.pos;
}
}
return true;
}
@Override public int hashCode() {
Segment s = head;
if (s == null) return 0;
int result = 1;
do {
for (int pos = s.pos, limit = s.limit; pos < limit; pos++) {
result = 31 * result + s.data[pos];
}
s = s.next;
} while (s != head);
return result;
}
@Override public String toString() {
if (size == 0) {
return "OkBuffer[size=0]";
}
if (size <= 16) {
ByteString data = clone().readByteString(size);
return String.format("OkBuffer[size=%s data=%s]", size, data.hex());
}
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(head.data, head.pos, head.limit - head.pos);
for (Segment s = head.next; s != head; s = s.next) {
md5.update(s.data, s.pos, s.limit - s.pos);
}
return String.format("OkBuffer[size=%s md5=%s]",
size, ByteString.of(md5.digest()).hex());
} catch (NoSuchAlgorithmException e) {
throw new AssertionError();
}
}
/** Returns a deep copy of this buffer. */
@Override public OkBuffer clone() {
OkBuffer result = new OkBuffer();
if (size() == 0) return result;
result.write(head.data, head.pos, head.limit - head.pos);
for (Segment s = head.next; s != head; s = s.next) {
result.write(s.data, s.pos, s.limit - s.pos);
}
return result;
}
}

View File

@@ -1,135 +0,0 @@
/*
* Copyright (C) 2014 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 java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static okio.Util.checkOffsetAndCount;
public final class Okio {
private Okio() {
}
public static BufferedSource buffer(Source source) {
return new RealBufferedSource(source);
}
public static BufferedSink buffer(Sink sink) {
return new RealBufferedSink(sink);
}
/** Copies bytes from {@code source} to {@code sink}. */
public static void copy(OkBuffer source, long offset, long byteCount, OutputStream sink)
throws IOException {
checkOffsetAndCount(source.size, offset, byteCount);
// Skip segments that we aren't copying from.
Segment s = source.head;
while (offset >= (s.limit - s.pos)) {
offset -= (s.limit - s.pos);
s = s.next;
}
// Copy from one segment at a time.
while (byteCount > 0) {
int pos = (int) (s.pos + offset);
int toWrite = (int) Math.min(s.limit - pos, byteCount);
sink.write(s.data, pos, toWrite);
byteCount -= toWrite;
offset = 0;
}
}
/** Returns a sink that writes to {@code out}. */
public static Sink sink(final OutputStream out) {
return new Sink() {
private Deadline deadline = Deadline.NONE;
@Override public void write(OkBuffer source, long byteCount)
throws IOException {
checkOffsetAndCount(source.size, 0, byteCount);
while (byteCount > 0) {
deadline.throwIfReached();
Segment head = source.head;
int toCopy = (int) Math.min(byteCount, head.limit - head.pos);
out.write(head.data, head.pos, toCopy);
head.pos += toCopy;
byteCount -= toCopy;
source.size -= toCopy;
if (head.pos == head.limit) {
source.head = head.pop();
SegmentPool.INSTANCE.recycle(head);
}
}
}
@Override public void flush() throws IOException {
out.flush();
}
@Override public void close() throws IOException {
out.close();
}
@Override public Sink deadline(Deadline deadline) {
if (deadline == null) throw new IllegalArgumentException("deadline == null");
this.deadline = deadline;
return this;
}
@Override public String toString() {
return "sink(" + out + ")";
}
};
}
/** Returns a source that reads from {@code in}. */
public static Source source(final InputStream in) {
return new Source() {
private Deadline deadline = Deadline.NONE;
@Override public long read(OkBuffer sink, long byteCount) throws IOException {
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
deadline.throwIfReached();
Segment tail = sink.writableSegment(1);
int maxToCopy = (int) Math.min(byteCount, Segment.SIZE - tail.limit);
int bytesRead = in.read(tail.data, tail.limit, maxToCopy);
if (bytesRead == -1) return -1;
tail.limit += bytesRead;
sink.size += bytesRead;
return bytesRead;
}
@Override public void close() throws IOException {
in.close();
}
@Override public Source deadline(Deadline deadline) {
if (deadline == null) throw new IllegalArgumentException("deadline == null");
this.deadline = deadline;
return this;
}
@Override public String toString() {
return "source(" + in + ")";
}
};
}
}

View File

@@ -1,191 +0,0 @@
/*
* Copyright (C) 2014 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 java.io.IOException;
import java.io.OutputStream;
final class RealBufferedSink implements BufferedSink {
public final OkBuffer buffer;
public final Sink sink;
private boolean closed;
public RealBufferedSink(Sink sink, OkBuffer buffer) {
if (sink == null) throw new IllegalArgumentException("sink == null");
this.buffer = buffer;
this.sink = sink;
}
public RealBufferedSink(Sink sink) {
this(sink, new OkBuffer());
}
@Override public OkBuffer buffer() {
return buffer;
}
@Override public void write(OkBuffer source, long byteCount)
throws IOException {
if (closed) throw new IllegalStateException("closed");
buffer.write(source, byteCount);
emitCompleteSegments();
}
@Override public BufferedSink write(ByteString byteString) throws IOException {
if (closed) throw new IllegalStateException("closed");
buffer.write(byteString);
return emitCompleteSegments();
}
@Override public BufferedSink writeUtf8(String string) throws IOException {
if (closed) throw new IllegalStateException("closed");
buffer.writeUtf8(string);
return emitCompleteSegments();
}
@Override public BufferedSink write(byte[] source) throws IOException {
if (closed) throw new IllegalStateException("closed");
buffer.write(source);
return emitCompleteSegments();
}
@Override public BufferedSink write(byte[] source, int offset, int byteCount) throws IOException {
if (closed) throw new IllegalStateException("closed");
buffer.write(source, offset, byteCount);
return emitCompleteSegments();
}
@Override public BufferedSink writeByte(int b) throws IOException {
if (closed) throw new IllegalStateException("closed");
buffer.writeByte(b);
return emitCompleteSegments();
}
@Override public BufferedSink writeShort(int s) throws IOException {
if (closed) throw new IllegalStateException("closed");
buffer.writeShort(s);
return emitCompleteSegments();
}
@Override public BufferedSink writeShortLe(int s) throws IOException {
if (closed) throw new IllegalStateException("closed");
buffer.writeShortLe(s);
return emitCompleteSegments();
}
@Override public BufferedSink writeInt(int i) throws IOException {
if (closed) throw new IllegalStateException("closed");
buffer.writeInt(i);
return emitCompleteSegments();
}
@Override public BufferedSink writeIntLe(int i) throws IOException {
if (closed) throw new IllegalStateException("closed");
buffer.writeIntLe(i);
return emitCompleteSegments();
}
@Override public BufferedSink writeLong(long v) throws IOException {
if (closed) throw new IllegalStateException("closed");
buffer.writeLong(v);
return emitCompleteSegments();
}
@Override public BufferedSink writeLongLe(long v) throws IOException {
if (closed) throw new IllegalStateException("closed");
buffer.writeLongLe(v);
return emitCompleteSegments();
}
@Override public BufferedSink emitCompleteSegments() throws IOException {
if (closed) throw new IllegalStateException("closed");
long byteCount = buffer.completeSegmentByteCount();
if (byteCount > 0) sink.write(buffer, byteCount);
return this;
}
@Override public OutputStream outputStream() {
return new OutputStream() {
@Override public void write(int b) throws IOException {
if (closed) throw new IOException("closed");
buffer.writeByte((byte) b);
emitCompleteSegments();
}
@Override public void write(byte[] data, int offset, int byteCount) throws IOException {
if (closed) throw new IOException("closed");
buffer.write(data, offset, byteCount);
emitCompleteSegments();
}
@Override public void flush() throws IOException {
// For backwards compatibility, a flush() on a closed stream is a no-op.
if (!closed) {
RealBufferedSink.this.flush();
}
}
@Override public void close() throws IOException {
RealBufferedSink.this.close();
}
@Override public String toString() {
return RealBufferedSink.this + ".outputStream()";
}
};
}
@Override public void flush() throws IOException {
if (closed) throw new IllegalStateException("closed");
if (buffer.size > 0) {
sink.write(buffer, buffer.size);
}
sink.flush();
}
@Override public void close() throws IOException {
if (closed) return;
// Emit buffered data to the underlying sink. If this fails, we still need
// to close the sink; otherwise we risk leaking resources.
Throwable thrown = null;
try {
if (buffer.size > 0) {
sink.write(buffer, buffer.size);
}
} catch (Throwable e) {
thrown = e;
}
try {
sink.close();
} catch (Throwable e) {
if (thrown == null) thrown = e;
}
closed = true;
if (thrown != null) Util.sneakyRethrow(thrown);
}
@Override public Sink deadline(Deadline deadline) {
sink.deadline(deadline);
return this;
}
@Override public String toString() {
return "buffer(" + sink + ")";
}
}

View File

@@ -1,205 +0,0 @@
/*
* Copyright (C) 2014 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 java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import static okio.Util.checkOffsetAndCount;
final class RealBufferedSource implements BufferedSource {
public final OkBuffer buffer;
public final Source source;
private boolean closed;
public RealBufferedSource(Source source, OkBuffer buffer) {
if (source == null) throw new IllegalArgumentException("source == null");
this.buffer = buffer;
this.source = source;
}
public RealBufferedSource(Source source) {
this(source, new OkBuffer());
}
@Override public OkBuffer buffer() {
return buffer;
}
@Override public long read(OkBuffer sink, long byteCount) throws IOException {
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
if (closed) throw new IllegalStateException("closed");
if (buffer.size == 0) {
long read = source.read(buffer, Segment.SIZE);
if (read == -1) return -1;
}
long toRead = Math.min(byteCount, buffer.size);
return buffer.read(sink, toRead);
}
@Override public boolean exhausted() throws IOException {
if (closed) throw new IllegalStateException("closed");
return buffer.exhausted() && source.read(buffer, Segment.SIZE) == -1;
}
@Override public void require(long byteCount) throws IOException {
if (closed) throw new IllegalStateException("closed");
while (buffer.size < byteCount) {
if (source.read(buffer, Segment.SIZE) == -1) throw new EOFException();
}
}
@Override public byte readByte() throws IOException {
require(1);
return buffer.readByte();
}
@Override public ByteString readByteString(long byteCount) throws IOException {
require(byteCount);
return buffer.readByteString(byteCount);
}
@Override public String readUtf8(long byteCount) throws IOException {
require(byteCount);
return buffer.readUtf8(byteCount);
}
@Override public String readUtf8Line() throws IOException {
long newline = indexOf((byte) '\n');
if (newline == -1) {
return buffer.size != 0 ? readUtf8(buffer.size) : null;
}
return buffer.readUtf8Line(newline);
}
@Override public String readUtf8LineStrict() throws IOException {
long newline = indexOf((byte) '\n');
if (newline == -1L) throw new EOFException();
return buffer.readUtf8Line(newline);
}
@Override public short readShort() throws IOException {
require(2);
return buffer.readShort();
}
@Override public short readShortLe() throws IOException {
require(2);
return buffer.readShortLe();
}
@Override public int readInt() throws IOException {
require(4);
return buffer.readInt();
}
@Override public int readIntLe() throws IOException {
require(4);
return buffer.readIntLe();
}
@Override public long readLong() throws IOException {
require(8);
return buffer.readLong();
}
@Override public long readLongLe() throws IOException {
require(8);
return buffer.readLongLe();
}
@Override public void skip(long byteCount) throws IOException {
if (closed) throw new IllegalStateException("closed");
while (byteCount > 0) {
if (buffer.size == 0 && source.read(buffer, Segment.SIZE) == -1) {
throw new EOFException();
}
long toSkip = Math.min(byteCount, buffer.size());
buffer.skip(toSkip);
byteCount -= toSkip;
}
}
@Override public long indexOf(byte b) throws IOException {
if (closed) throw new IllegalStateException("closed");
long start = 0;
long index;
while ((index = buffer.indexOf(b, start)) == -1) {
start = buffer.size;
if (source.read(buffer, Segment.SIZE) == -1) return -1L;
}
return index;
}
@Override public InputStream inputStream() {
return new InputStream() {
@Override public int read() throws IOException {
if (closed) throw new IOException("closed");
if (buffer.size == 0) {
long count = source.read(buffer, Segment.SIZE);
if (count == -1) return -1;
}
return buffer.readByte() & 0xff;
}
@Override public int read(byte[] data, int offset, int byteCount) throws IOException {
if (closed) throw new IOException("closed");
checkOffsetAndCount(data.length, offset, byteCount);
if (buffer.size == 0) {
long count = source.read(buffer, Segment.SIZE);
if (count == -1) return -1;
}
return buffer.read(data, offset, byteCount);
}
@Override public int available() throws IOException {
if (closed) throw new IOException("closed");
return (int) Math.min(buffer.size, Integer.MAX_VALUE);
}
@Override public void close() throws IOException {
RealBufferedSource.this.close();
}
@Override public String toString() {
return RealBufferedSource.this + ".inputStream()";
}
};
}
@Override public Source deadline(Deadline deadline) {
source.deadline(deadline);
return this;
}
@Override public void close() throws IOException {
if (closed) return;
closed = true;
source.close();
buffer.clear();
}
@Override public String toString() {
return "buffer(" + source + ")";
}
}

View File

@@ -1,135 +0,0 @@
/*
* Copyright (C) 2014 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;
/**
* A segment of an OkBuffer.
*
* <p>Each segment in an OkBuffer is a circularly-linked list node referencing
* the following and preceding segments in the buffer.
*
* <p>Each segment in the pool is a singly-linked list node referencing the rest
* of segments in the pool.
*/
final class Segment {
/** The size of all segments in bytes. */
// TODO: Using fixed-size segments makes pooling easier. But it harms memory
// efficiency and encourages copying. Try variable sized segments?
// TODO: Is 2 KiB a good default segment size?
static final int SIZE = 2048;
final byte[] data = new byte[SIZE];
/** The next byte of application data byte to read in this segment. */
int pos;
/** The first byte of available data ready to be written to. */
int limit;
/** Next segment in a linked or circularly-linked list. */
Segment next;
/** Previous segment in a circularly-linked list. */
Segment prev;
/**
* Removes this segment of a circularly-linked list and returns its successor.
* Returns null if the list is now empty.
*/
public Segment pop() {
Segment result = next != this ? next : null;
prev.next = next;
next.prev = prev;
next = null;
prev = null;
return result;
}
/**
* Appends {@code segment} after this segment in the circularly-linked list.
* Returns the pushed segment.
*/
public Segment push(Segment segment) {
segment.prev = this;
segment.next = next;
next.prev = segment;
next = segment;
return segment;
}
/**
* Splits this head of a circularly-linked list into two segments. The first
* segment contains the data in {@code [pos..pos+byteCount)}. The second
* segment contains the data in {@code [pos+byteCount..limit)}. This can be
* useful when moving partial segments from one OkBuffer to another.
*
* <p>Returns the new head of the circularly-linked list.
*/
public Segment split(int byteCount) {
int aSize = byteCount;
int bSize = (limit - pos) - byteCount;
if (aSize <= 0 || bSize <= 0) throw new IllegalArgumentException();
// Which side of the split is larger? We want to copy as few bytes as possible.
if (aSize < bSize) {
// Create a segment of size 'aSize' before this segment.
Segment before = SegmentPool.INSTANCE.take();
System.arraycopy(data, pos, before.data, before.pos, aSize);
pos += aSize;
before.limit += aSize;
prev.push(before);
return before;
} else {
// Create a new segment of size 'bSize' after this segment.
Segment after = SegmentPool.INSTANCE.take();
System.arraycopy(data, pos + aSize, after.data, after.pos, bSize);
limit -= bSize;
after.limit += bSize;
push(after);
return this;
}
}
/**
* Call this when the tail and its predecessor may both be less than half
* full. This will copy data so that segments can be recycled.
*/
public void compact() {
if (prev == this) throw new IllegalStateException();
if ((prev.limit - prev.pos) + (limit - pos) > SIZE) return; // Cannot compact.
writeTo(prev, limit - pos);
pop();
SegmentPool.INSTANCE.recycle(this);
}
/** Moves {@code byteCount} bytes from {@code sink} to this segment. */
// TODO: if sink has fewer bytes than this, it may be cheaper to reverse the
// direction of the copy and swap the segments!
public void writeTo(Segment sink, int byteCount) {
if (byteCount + (sink.limit - sink.pos) > SIZE) throw new IllegalArgumentException();
if (sink.limit + byteCount > SIZE) {
// We can't fit byteCount bytes at the sink's current position. Compact sink first.
System.arraycopy(sink.data, sink.pos, sink.data, 0, sink.limit - sink.pos);
sink.limit -= sink.pos;
sink.pos = 0;
}
System.arraycopy(data, pos, sink.data, sink.limit, byteCount);
sink.limit += byteCount;
pos += byteCount;
}
}

View File

@@ -1,61 +0,0 @@
/*
* Copyright (C) 2014 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;
/**
* A collection of unused segments, necessary to avoid GC churn and zero-fill.
* This pool is a thread-safe static singleton.
*/
final class SegmentPool {
static final SegmentPool INSTANCE = new SegmentPool();
/** The maximum number of bytes to pool. */
// TODO: Is 64 KiB a good maximum size? Do we ever have that many idle segments?
static final long MAX_SIZE = 64 * 1024; // 64 KiB.
/** Singly-linked list of segments. */
private Segment next;
/** Total bytes in this pool. */
long byteCount;
private SegmentPool() {
}
Segment take() {
synchronized (this) {
if (next != null) {
Segment result = next;
next = result.next;
result.next = null;
byteCount -= Segment.SIZE;
return result;
}
}
return new Segment(); // Pool is empty. Don't zero-fill while holding a lock.
}
void recycle(Segment segment) {
if (segment.next != null || segment.prev != null) throw new IllegalArgumentException();
synchronized (this) {
if (byteCount + Segment.SIZE > MAX_SIZE) return; // Pool is full.
byteCount += Segment.SIZE;
segment.next = next;
segment.pos = segment.limit = 0;
next = segment;
}
}
}

View File

@@ -1,69 +0,0 @@
/*
* Copyright (C) 2014 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 java.io.Closeable;
import java.io.IOException;
/**
* Receives a stream of bytes. Use this interface to write data wherever it's
* needed: to the network, storage, or a buffer in memory. Sinks may be layered
* to transform received data, such as to compress, encrypt, throttle, or add
* protocol framing.
*
* <p>Most application code shouldn't operate on a sink directly, but rather
* {@link BufferedSink} which is both more efficient and more convenient. Use
* {@link Okio#buffer(Sink)} to wrap any sink with a buffer.
*
* <p>Sinks are easy to test: just use an {@link OkBuffer} in your tests, and
* read from it to confirm it received the data that was expected.
*
* <h3>Comparison with OutputStream</h3>
* This interface is functionally equivalent to {@link java.io.OutputStream}.
*
* <p>{@code OutputStream} requires multiple layers when emitted data is
* heterogeneous: a {@code DataOutputStream} for primitive values, a {@code
* BufferedOutputStream} for buffering, and {@code OutputStreamWriter} for
* charset encoding. This class uses {@code BufferedSink} for all of the above.
*
* <p>Sink is also easier to layer: there is no {@link
* java.io.OutputStream#write(int) single-byte write} method that is awkward to
* implement efficiently.
*
* <h3>Interop with OutputStream</h3>
* Use {@link Okio#sink} to adapt an {@code OutputStream} to a sink. Use {@link
* BufferedSink#outputStream} to adapt a sink to an {@code OutputStream}.
*/
public interface Sink extends Closeable {
/** Removes {@code byteCount} bytes from {@code source} and appends them to this. */
void write(OkBuffer source, long byteCount) throws IOException;
/** Pushes all buffered bytes to their final destination. */
void flush() throws IOException;
/**
* Sets the deadline for all operations on this sink.
* @return this sink.
*/
Sink deadline(Deadline deadline);
/**
* 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.
*/
@Override void close() throws IOException;
}

View File

@@ -1,81 +0,0 @@
/*
* Copyright (C) 2014 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 java.io.Closeable;
import java.io.IOException;
/**
* Supplies a stream of bytes. Use this interface to read data from wherever
* it's located: from the network, storage, or a buffer in memory. Sources may
* be layered to transform supplied data, such as to decompress, decrypt, or
* remove protocol framing.
*
* <p>Most applications shouldn't operate on a source directly, but rather
* {@link BufferedSource} which is both more efficient and more convenient. Use
* {@link Okio#buffer(Source)} to wrap any source with a buffer.
*
* <p>Sources are easy to test: just use an {@link OkBuffer} in your tests, and
* fill it with the data your application is to read.
*
* <h3>Comparison with InputStream</h3>
* This interface is functionally equivalent to {@link java.io.InputStream}.
*
* <p>{@code InputStream} requires multiple layers when consumed data is
* heterogeneous: a {@code DataOutputStream} for primitive values, a {@code
* BufferedInputStream} for buffering, and {@code InputStreamReader} for
* strings. This class uses {@code BufferedSource} for all of the above.
*
* <p>Source avoids the impossible-to-implement {@link
* java.io.InputStream#available available()} method. Instead callers specify
* how many bytes they {@link BufferedSource#require require}.
*
* <p>Source omits the unsafe-to-compose {@link java.io.InputStream#mark mark
* and reset} state that's tracked by {@code InputStream}; callers instead just
* buffer what they need.
*
* <p>When implementing a source, you need not worry about the {@link
* java.io.InputStream#read single-byte read} method that is awkward to
* implement efficiently and that returns one of 257 possible values.
*
* <p>And source has a stronger {@code skip} method: {@link BufferedSource#skip}
* won't return prematurely.
*
* <h3>Interop with InputStream</h3>
* Use {@link Okio#source} to adapt an {@code InputStream} to a source. Use
* {@link BufferedSource#inputStream} to adapt a source to an {@code
* InputStream}.
*/
public interface Source extends Closeable {
/**
* Removes at least 1, and up to {@code byteCount} bytes from this and appends
* them to {@code sink}. Returns the number of bytes read, or -1 if this
* source is exhausted.
*/
long read(OkBuffer sink, long byteCount) throws IOException;
/**
* Sets the deadline for all operations on this source.
* @return this source.
*/
Source deadline(Deadline deadline);
/**
* 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.
*/
@Override void close() throws IOException;
}

View File

@@ -1,71 +0,0 @@
/*
* Copyright (C) 2014 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 java.nio.charset.Charset;
final class Util {
/** A cheap and type-safe constant for the UTF-8 Charset. */
public static final Charset UTF_8 = Charset.forName("UTF-8");
private Util() {
}
public static void checkOffsetAndCount(long arrayLength, long offset, long count) {
if ((offset | count) < 0 || offset > arrayLength || arrayLength - offset < count) {
throw new ArrayIndexOutOfBoundsException();
}
}
public static short reverseBytesShort(short s) {
int i = s & 0xffff;
int reversed = (i & 0xff00) >>> 8
| (i & 0x00ff) << 8;
return (short) reversed;
}
public static int reverseBytesInt(int i) {
return (i & 0xff000000) >>> 24
| (i & 0x00ff0000) >>> 8
| (i & 0x0000ff00) << 8
| (i & 0x000000ff) << 24;
}
public static long reverseBytesLong(long v) {
return (v & 0xff00000000000000L) >>> 56
| (v & 0x00ff000000000000L) >>> 40
| (v & 0x0000ff0000000000L) >>> 24
| (v & 0x000000ff00000000L) >>> 8
| (v & 0x00000000ff000000L) << 8
| (v & 0x0000000000ff0000L) << 24
| (v & 0x000000000000ff00L) << 40
| (v & 0x00000000000000ffL) << 56;
}
/**
* Throws {@code t}, even if the declared throws clause doesn't permit it.
* This is a terrible but terribly convenient hack that makes it easy to
* catch and rethrow exceptions after cleanup. See Java Puzzlers #43.
*/
public static void sneakyRethrow(Throwable t) {
Util.<Error>sneakyThrow2(t);
}
@SuppressWarnings("unchecked")
private static <T extends Throwable> void sneakyThrow2(Throwable t) throws T {
throw (T) t;
}
}

View File

@@ -1,182 +0,0 @@
/*
* Copyright 2014 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 java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Arrays;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class ByteStringTest {
@Test public void getByte() throws Exception {
ByteString byteString = ByteString.decodeHex("ab12");
assertEquals(-85, byteString.getByte(0));
assertEquals(18, byteString.getByte(1));
}
@Test public void getByteOutOfBounds() throws Exception {
ByteString byteString = ByteString.decodeHex("ab12");
try {
byteString.getByte(2);
fail();
} catch (IndexOutOfBoundsException expected) {
}
}
@Test public void equals() throws Exception {
ByteString byteString = ByteString.decodeHex("000102");
assertTrue(byteString.equals(byteString));
assertTrue(byteString.equals(ByteString.decodeHex("000102")));
assertTrue(ByteString.of().equals(ByteString.EMPTY));
assertTrue(ByteString.EMPTY.equals(ByteString.of()));
assertFalse(byteString.equals(new Object()));
assertFalse(byteString.equals(ByteString.decodeHex("000201")));
}
private final String bronzeHorseman = "На берегу пустынных волн";
@Test public void utf8() throws Exception {
ByteString byteString = ByteString.encodeUtf8(bronzeHorseman);
assertByteArraysEquals(byteString.toByteArray(), bronzeHorseman.getBytes(Util.UTF_8));
assertTrue(byteString.equals(ByteString.of(bronzeHorseman.getBytes(Util.UTF_8))));
assertEquals(byteString.utf8(), bronzeHorseman);
}
@Test public void testHashCode() throws Exception {
ByteString byteString = ByteString.decodeHex("0102");
assertEquals(byteString.hashCode(), byteString.hashCode());
assertEquals(byteString.hashCode(), ByteString.decodeHex("0102").hashCode());
}
@Test public void read() throws Exception {
InputStream in = new ByteArrayInputStream("abc".getBytes(Util.UTF_8));
assertEquals(ByteString.decodeHex("6162"), ByteString.read(in, 2));
assertEquals(ByteString.decodeHex("63"), ByteString.read(in, 1));
assertEquals(ByteString.of(), ByteString.read(in, 0));
}
@Test public void readLowerCase() throws Exception {
InputStream in = new ByteArrayInputStream("ABC".getBytes(Util.UTF_8));
assertEquals(ByteString.encodeUtf8("ab"), ByteString.read(in, 2).toAsciiLowercase());
assertEquals(ByteString.encodeUtf8("c"), ByteString.read(in, 1).toAsciiLowercase());
assertEquals(ByteString.EMPTY, ByteString.read(in, 0).toAsciiLowercase());
}
@Test public void toAsciiLowerCaseNoUppercase() throws Exception {
ByteString s = ByteString.encodeUtf8("a1_+");
assertSame(s, s.toAsciiLowercase());
}
@Test public void toAsciiAllUppercase() throws Exception {
assertEquals(ByteString.encodeUtf8("ab"), ByteString.encodeUtf8("AB").toAsciiLowercase());
}
@Test public void toAsciiStartsLowercaseEndsUppercase() throws Exception {
assertEquals(ByteString.encodeUtf8("abcd"), ByteString.encodeUtf8("abCD").toAsciiLowercase());
}
@Test public void write() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteString.decodeHex("616263").write(out);
assertByteArraysEquals(new byte[] { 0x61, 0x62, 0x63 }, out.toByteArray());
}
@Test public void encodeBase64() {
assertEquals("", ByteString.encodeUtf8("").base64());
assertEquals("AA==", ByteString.encodeUtf8("\u0000").base64());
assertEquals("AAA=", ByteString.encodeUtf8("\u0000\u0000").base64());
assertEquals("AAAA", ByteString.encodeUtf8("\u0000\u0000\u0000").base64());
assertEquals("V2UncmUgZ29ubmEgbWFrZSBhIGZvcnR1bmUgd2l0aCB0aGlzIHBsYWNlLg==",
ByteString.encodeUtf8("We're gonna make a fortune with this place.").base64());
}
@Test public void ignoreUnnecessaryPadding() {
assertEquals("", ByteString.decodeBase64("====").utf8());
assertEquals("\u0000\u0000\u0000", ByteString.decodeBase64("AAAA====").utf8());
}
@Test public void decodeBase64() {
assertEquals("", ByteString.decodeBase64("").utf8());
assertEquals(null, ByteString.decodeBase64("/===")); // Can't do anything with 6 bits!
assertEquals(ByteString.decodeHex("ff"), ByteString.decodeBase64("//=="));
assertEquals(ByteString.decodeHex("ffff"), ByteString.decodeBase64("///="));
assertEquals(ByteString.decodeHex("ffffff"), ByteString.decodeBase64("////"));
assertEquals(ByteString.decodeHex("ffffffffffff"), ByteString.decodeBase64("////////"));
assertEquals("What's to be scared about? It's just a little hiccup in the power...",
ByteString.decodeBase64("V2hhdCdzIHRvIGJlIHNjYXJlZCBhYm91dD8gSXQncyBqdXN0IGEgbGl0dGxlIGhpY2"
+ "N1cCBpbiB0aGUgcG93ZXIuLi4=").utf8());
}
@Test public void decodeBase64WithWhitespace() {
assertEquals("\u0000\u0000\u0000", ByteString.decodeBase64(" AA AA ").utf8());
assertEquals("\u0000\u0000\u0000", ByteString.decodeBase64(" AA A\r\nA ").utf8());
assertEquals("\u0000\u0000\u0000", ByteString.decodeBase64("AA AA").utf8());
assertEquals("\u0000\u0000\u0000", ByteString.decodeBase64(" AA AA ").utf8());
assertEquals("\u0000\u0000\u0000", ByteString.decodeBase64(" AA A\r\nA ").utf8());
assertEquals("\u0000\u0000\u0000", ByteString.decodeBase64("A AAA").utf8());
assertEquals("", ByteString.decodeBase64(" ").utf8());
}
@Test public void encodeHex() throws Exception {
assertEquals("000102", ByteString.of((byte) 0x0, (byte) 0x1, (byte) 0x2).hex());
}
@Test public void decodeHex() throws Exception {
assertEquals(ByteString.of((byte) 0x0, (byte) 0x1, (byte) 0x2), ByteString.decodeHex("000102"));
}
@Test public void decodeHexOddNumberOfChars() throws Exception {
try {
ByteString.decodeHex("aaa");
fail();
} catch (IllegalArgumentException expected) {
}
}
@Test public void decodeHexInvalidChar() throws Exception {
try {
ByteString.decodeHex("a\u0000");
fail();
} catch (IllegalArgumentException expected) {
}
}
@Test public void toStringOnEmptyByteString() {
assertEquals("ByteString[size=0]", ByteString.of().toString());
}
@Test public void toStringOnSmallByteStringIncludesContents() {
assertEquals("ByteString[size=16 data=a1b2c3d4e5f61a2b3c4d5e6f10203040]",
ByteString.decodeHex("a1b2c3d4e5f61a2b3c4d5e6f10203040").toString());
}
@Test public void toStringOnLargeByteStringIncludesMd5() {
assertEquals("ByteString[size=17 md5=2c9728a2138b2f25e9f89f99bdccf8db]",
ByteString.encodeUtf8("12345678901234567").toString());
}
private static void assertByteArraysEquals(byte[] a, byte[] b) {
assertEquals(Arrays.toString(a), Arrays.toString(b));
}
}

View File

@@ -1,141 +0,0 @@
/*
* Copyright (C) 2014 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 java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Random;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public final class DeflaterSinkTest {
@Test public void deflateWithClose() throws Exception {
OkBuffer data = new OkBuffer();
String original = "They're moving in herds. They do move in herds.";
data.writeUtf8(original);
OkBuffer sink = new OkBuffer();
DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater());
deflaterSink.write(data, data.size());
deflaterSink.close();
OkBuffer inflated = inflate(sink);
assertEquals(original, inflated.readUtf8(inflated.size()));
}
@Test public void deflateWithSyncFlush() throws Exception {
String original = "Yes, yes, yes. That's why we're taking extreme precautions.";
OkBuffer data = new OkBuffer();
data.writeUtf8(original);
OkBuffer sink = new OkBuffer();
DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater());
deflaterSink.write(data, data.size());
deflaterSink.flush();
OkBuffer inflated = inflate(sink);
assertEquals(original, inflated.readUtf8(inflated.size()));
}
@Test public void deflateWellCompressed() throws IOException {
String original = repeat('a', 1024 * 1024);
OkBuffer data = new OkBuffer();
data.writeUtf8(original);
OkBuffer sink = new OkBuffer();
DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater());
deflaterSink.write(data, data.size());
deflaterSink.close();
OkBuffer inflated = inflate(sink);
assertEquals(original, inflated.readUtf8(inflated.size()));
}
@Test public void deflatePoorlyCompressed() throws IOException {
ByteString original = randomBytes(1024 * 1024);
OkBuffer data = new OkBuffer();
data.write(original);
OkBuffer sink = new OkBuffer();
DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater());
deflaterSink.write(data, data.size());
deflaterSink.close();
OkBuffer inflated = inflate(sink);
assertEquals(original, inflated.readByteString(inflated.size()));
}
@Test public void multipleSegmentsWithoutCompression() throws IOException {
OkBuffer buffer = new OkBuffer();
Deflater deflater = new Deflater();
deflater.setLevel(Deflater.NO_COMPRESSION);
DeflaterSink deflaterSink = new DeflaterSink(buffer, deflater);
int byteCount = Segment.SIZE * 4;
deflaterSink.write(new OkBuffer().writeUtf8(repeat('a', byteCount)), byteCount);
deflaterSink.close();
assertEquals(repeat('a', byteCount), inflate(buffer).readUtf8(byteCount));
}
/**
* This test deflates a single segment of without compression because that's
* the easiest way to force close() to emit a large amount of data to the
* underlying sink.
*/
@Test public void closeWithExceptionWhenWritingAndClosing() throws IOException {
MockSink mockSink = new MockSink();
mockSink.scheduleThrow(0, new IOException("first"));
mockSink.scheduleThrow(1, new IOException("second"));
Deflater deflater = new Deflater();
deflater.setLevel(Deflater.NO_COMPRESSION);
DeflaterSink deflaterSink = new DeflaterSink(mockSink, deflater);
deflaterSink.write(new OkBuffer().writeUtf8(repeat('a', Segment.SIZE)), Segment.SIZE);
try {
deflaterSink.close();
fail();
} catch (IOException expected) {
assertEquals("first", expected.getMessage());
}
mockSink.assertLogContains("close()");
}
/**
* Uses streaming decompression to inflate {@code deflated}. The input must
* either be finished or have a trailing sync flush.
*/
private OkBuffer inflate(OkBuffer deflated) throws IOException {
InputStream deflatedIn = deflated.inputStream();
Inflater inflater = new Inflater();
InputStream inflatedIn = new InflaterInputStream(deflatedIn, inflater);
OkBuffer result = new OkBuffer();
byte[] buffer = new byte[8192];
while (!inflater.needsInput() || deflated.size() > 0 || deflatedIn.available() > 0) {
int count = inflatedIn.read(buffer, 0, buffer.length);
result.write(buffer, 0, count);
}
return result;
}
private ByteString randomBytes(int length) {
Random random = new Random(0);
byte[] randomBytes = new byte[length];
random.nextBytes(randomBytes);
return ByteString.of(randomBytes);
}
private String repeat(char c, int count) {
char[] array = new char[count];
Arrays.fill(array, c);
return new String(array);
}
}

View File

@@ -1,234 +0,0 @@
/*
* Copyright (C) 2014 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 java.io.IOException;
import java.util.zip.CRC32;
import org.junit.Test;
import static okio.Util.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class GzipSourceTest {
@Test public void gunzip() throws Exception {
OkBuffer gzipped = new OkBuffer();
gzipped.write(gzipHeader);
gzipped.write(deflated);
gzipped.write(gzipTrailer);
assertGzipped(gzipped);
}
@Test public void gunzip_withHCRC() throws Exception {
CRC32 hcrc = new CRC32();
ByteString gzipHeader = gzipHeaderWithFlags((byte) 0x02);
hcrc.update(gzipHeader.toByteArray());
OkBuffer gzipped = new OkBuffer();
gzipped.write(gzipHeader);
gzipped.writeShort(Util.reverseBytesShort((short) hcrc.getValue())); // little endian
gzipped.write(deflated);
gzipped.write(gzipTrailer);
assertGzipped(gzipped);
}
@Test public void gunzip_withExtra() throws Exception {
OkBuffer gzipped = new OkBuffer();
gzipped.write(gzipHeaderWithFlags((byte) 0x04));
gzipped.writeShort(Util.reverseBytesShort((short) 7)); // little endian extra length
gzipped.write("blubber".getBytes(UTF_8), 0, 7);
gzipped.write(deflated);
gzipped.write(gzipTrailer);
assertGzipped(gzipped);
}
@Test public void gunzip_withName() throws Exception {
OkBuffer gzipped = new OkBuffer();
gzipped.write(gzipHeaderWithFlags((byte) 0x08));
gzipped.write("foo.txt".getBytes(UTF_8), 0, 7);
gzipped.writeByte(0); // zero-terminated
gzipped.write(deflated);
gzipped.write(gzipTrailer);
assertGzipped(gzipped);
}
@Test public void gunzip_withComment() throws Exception {
OkBuffer gzipped = new OkBuffer();
gzipped.write(gzipHeaderWithFlags((byte) 0x10));
gzipped.write("rubbish".getBytes(UTF_8), 0, 7);
gzipped.writeByte(0); // zero-terminated
gzipped.write(deflated);
gzipped.write(gzipTrailer);
assertGzipped(gzipped);
}
/**
* For portability, it is a good idea to export the gzipped bytes and try running gzip. Ex.
* {@code echo gzipped | base64 --decode | gzip -l -v}
*/
@Test public void gunzip_withAll() throws Exception {
OkBuffer gzipped = new OkBuffer();
gzipped.write(gzipHeaderWithFlags((byte) 0x1c));
gzipped.writeShort(Util.reverseBytesShort((short) 7)); // little endian extra length
gzipped.write("blubber".getBytes(UTF_8), 0, 7);
gzipped.write("foo.txt".getBytes(UTF_8), 0, 7);
gzipped.writeByte(0); // zero-terminated
gzipped.write("rubbish".getBytes(UTF_8), 0, 7);
gzipped.writeByte(0); // zero-terminated
gzipped.write(deflated);
gzipped.write(gzipTrailer);
assertGzipped(gzipped);
}
private void assertGzipped(OkBuffer gzipped) throws IOException {
OkBuffer gunzipped = gunzip(gzipped);
assertEquals("It's a UNIX system! I know this!", gunzipped.readUtf8(gunzipped.size()));
}
/**
* Note that you cannot test this with old versions of gzip, as they interpret flag bit 1 as
* CONTINUATION, not HCRC. For example, this is the case with the default gzip on osx.
*/
@Test public void gunzipWhenHeaderCRCIncorrect() throws Exception {
OkBuffer gzipped = new OkBuffer();
gzipped.write(gzipHeaderWithFlags((byte) 0x02));
gzipped.writeShort((short) 0); // wrong HCRC!
gzipped.write(deflated);
gzipped.write(gzipTrailer);
try {
gunzip(gzipped);
fail();
} catch (IOException e) {
assertEquals("FHCRC: actual 0x0000261d != expected 0x00000000", e.getMessage());
}
}
@Test public void gunzipWhenCRCIncorrect() throws Exception {
OkBuffer gzipped = new OkBuffer();
gzipped.write(gzipHeader);
gzipped.write(deflated);
gzipped.writeInt(Util.reverseBytesInt(0x1234567)); // wrong CRC
gzipped.write(gzipTrailer.toByteArray(), 3, 4);
try {
gunzip(gzipped);
fail();
} catch (IOException e) {
assertEquals("CRC: actual 0x37ad8f8d != expected 0x01234567", e.getMessage());
}
}
@Test public void gunzipWhenLengthIncorrect() throws Exception {
OkBuffer gzipped = new OkBuffer();
gzipped.write(gzipHeader);
gzipped.write(deflated);
gzipped.write(gzipTrailer.toByteArray(), 0, 4);
gzipped.writeInt(Util.reverseBytesInt(0x123456)); // wrong length
try {
gunzip(gzipped);
fail();
} catch (IOException e) {
assertEquals("ISIZE: actual 0x00000020 != expected 0x00123456", e.getMessage());
}
}
@Test public void gunzipExhaustsSource() throws Exception {
OkBuffer gzippedSource = new OkBuffer()
.write(ByteString.decodeHex("1f8b08000000000000004b4c4a0600c241243503000000")); // 'abc'
ExhaustableSource exhaustableSource = new ExhaustableSource(gzippedSource);
BufferedSource gunzippedSource = Okio.buffer(new GzipSource(exhaustableSource));
assertEquals('a', gunzippedSource.readByte());
assertEquals('b', gunzippedSource.readByte());
assertEquals('c', gunzippedSource.readByte());
assertFalse(exhaustableSource.exhausted);
assertEquals(-1, gunzippedSource.read(new OkBuffer(), 1));
assertTrue(exhaustableSource.exhausted);
}
@Test public void gunzipThrowsIfSourceIsNotExhausted() throws Exception {
OkBuffer gzippedSource = new OkBuffer()
.write(ByteString.decodeHex("1f8b08000000000000004b4c4a0600c241243503000000")); // 'abc'
gzippedSource.writeByte('d'); // This byte shouldn't be here!
BufferedSource gunzippedSource = Okio.buffer(new GzipSource(gzippedSource));
assertEquals('a', gunzippedSource.readByte());
assertEquals('b', gunzippedSource.readByte());
assertEquals('c', gunzippedSource.readByte());
try {
gunzippedSource.readByte();
fail();
} catch (IOException expected) {
}
}
private ByteString gzipHeaderWithFlags(byte flags) {
byte[] result = gzipHeader.toByteArray();
result[3] = flags;
return ByteString.of(result);
}
private final ByteString gzipHeader = ByteString.decodeHex("1f8b0800000000000000");
// Deflated "It's a UNIX system! I know this!"
private final ByteString deflated = ByteString.decodeHex(
"f32c512f56485408f5f38c5028ae2c2e49cd5554f054c8cecb2f5728c9c82c560400");
private final ByteString gzipTrailer = ByteString.decodeHex(""
+ "8d8fad37" // Checksum of deflated.
+ "20000000" // 32 in little endian.
);
private OkBuffer gunzip(OkBuffer gzipped) throws IOException {
OkBuffer result = new OkBuffer();
GzipSource source = new GzipSource(gzipped);
while (source.read(result, Integer.MAX_VALUE) != -1) {
}
return result;
}
/** This source keeps track of whether its read have returned -1. */
static class ExhaustableSource implements Source {
private final Source source;
private boolean exhausted;
ExhaustableSource(Source source) {
this.source = source;
}
@Override public long read(OkBuffer sink, long byteCount) throws IOException {
long result = source.read(sink, byteCount);
if (result == -1) exhausted = true;
return result;
}
@Override public Source deadline(Deadline deadline) {
source.deadline(deadline);
return this;
}
@Override public void close() throws IOException {
source.close();
}
}
}

View File

@@ -1,115 +0,0 @@
/*
* Copyright (C) 2014 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 java.io.EOFException;
import java.io.IOException;
import java.util.Arrays;
import java.util.Random;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.Inflater;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public final class InflaterSourceTest {
@Test public void inflate() throws Exception {
OkBuffer deflated = decodeBase64("eJxzz09RyEjNKVAoLdZRKE9VL0pVyMxTKMlIVchIzEspVshPU0jNS8/MS00tK"
+ "tYDAF6CD5s=");
OkBuffer inflated = inflate(deflated);
assertEquals("God help us, we're in the hands of engineers.", readUtf8(inflated));
}
@Test public void inflateTruncated() throws Exception {
OkBuffer deflated = decodeBase64("eJxzz09RyEjNKVAoLdZRKE9VL0pVyMxTKMlIVchIzEspVshPU0jNS8/MS00tK"
+ "tYDAF6CDw==");
try {
inflate(deflated);
fail();
} catch (EOFException expected) {
}
}
@Test public void inflateWellCompressed() throws Exception {
OkBuffer deflated = decodeBase64("eJztwTEBAAAAwqCs61/CEL5AAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8B"
+ "tFeWvE=\n");
String original = repeat('a', 1024 * 1024);
OkBuffer inflated = inflate(deflated);
assertEquals(original, readUtf8(inflated));
}
@Test public void inflatePoorlyCompressed() throws Exception {
ByteString original = randomBytes(1024 * 1024);
OkBuffer deflated = deflate(original);
OkBuffer inflated = inflate(deflated);
assertEquals(original, inflated.readByteString(inflated.size()));
}
private OkBuffer decodeBase64(String s) {
return new OkBuffer().write(ByteString.decodeBase64(s));
}
private String readUtf8(OkBuffer buffer) {
return buffer.readUtf8(buffer.size());
}
/** Use DeflaterOutputStream to deflate source. */
private OkBuffer deflate(ByteString source) throws IOException {
OkBuffer result = new OkBuffer();
Sink sink = Okio.sink(new DeflaterOutputStream(result.outputStream()));
sink.write(new OkBuffer().write(source), source.size());
sink.close();
return result;
}
/** Returns a new buffer containing the inflated contents of {@code deflated}. */
private OkBuffer inflate(OkBuffer deflated) throws IOException {
OkBuffer result = new OkBuffer();
InflaterSource source = new InflaterSource(deflated, new Inflater());
while (source.read(result, Integer.MAX_VALUE) != -1) {
}
return result;
}
private ByteString randomBytes(int length) {
Random random = new Random(0);
byte[] randomBytes = new byte[length];
random.nextBytes(randomBytes);
return ByteString.of(randomBytes);
}
private String repeat(char c, int count) {
char[] array = new char[count];
Arrays.fill(array, c);
return new String(array);
}
}

View File

@@ -1,70 +0,0 @@
/*
* Copyright (C) 2014 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 java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/** A scriptable sink. Like Mockito, but worse and requiring less configuration. */
class MockSink implements Sink {
private final List<String> log = new ArrayList<String>();
private final Map<Integer, IOException> callThrows = new LinkedHashMap<Integer, IOException>();
public void assertLog(String... messages) {
assertEquals(Arrays.asList(messages), log);
}
public void assertLogContains(String message) {
assertTrue(log.contains(message));
}
public void scheduleThrow(int call, IOException e) {
callThrows.put(call, e);
}
private void throwIfScheduled() throws IOException {
IOException exception = callThrows.get(log.size() - 1);
if (exception != null) throw exception;
}
@Override public void write(OkBuffer source, long byteCount) throws IOException {
log.add("write(" + source + ", " + byteCount + ")");
source.skip(byteCount);
throwIfScheduled();
}
@Override public void flush() throws IOException {
log.add("flush()");
throwIfScheduled();
}
@Override public Sink deadline(Deadline deadline) {
log.add("deadline()");
return this;
}
@Override public void close() throws IOException {
log.add("close()");
throwIfScheduled();
}
}

View File

@@ -1,22 +0,0 @@
/*
* Copyright (C) 2014 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;
public final class OkBufferReadUtf8LineTest extends ReadUtf8LineTest {
@Override protected BufferedSource newSource(String s) {
return new OkBuffer().writeUtf8(s);
}
}

View File

@@ -1,682 +0,0 @@
/*
* Copyright (C) 2014 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 java.util.Arrays;
import java.util.List;
import java.util.Random;
import org.junit.Test;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public final class OkBufferTest {
@Test public void readAndWriteUtf8() throws Exception {
OkBuffer buffer = new OkBuffer();
buffer.writeUtf8("ab");
assertEquals(2, buffer.size());
buffer.writeUtf8("cdef");
assertEquals(6, buffer.size());
assertEquals("abcd", buffer.readUtf8(4));
assertEquals(2, buffer.size());
assertEquals("ef", buffer.readUtf8(2));
assertEquals(0, buffer.size());
try {
buffer.readUtf8(1);
fail();
} catch (ArrayIndexOutOfBoundsException expected) {
}
}
@Test public void completeSegmentByteCountOnEmptyBuffer() throws Exception {
OkBuffer buffer = new OkBuffer();
assertEquals(0, buffer.completeSegmentByteCount());
}
@Test public void completeSegmentByteCountOnBufferWithFullSegments() throws Exception {
OkBuffer buffer = new OkBuffer();
buffer.writeUtf8(repeat('a', Segment.SIZE * 4));
assertEquals(Segment.SIZE * 4, buffer.completeSegmentByteCount());
}
@Test public void completeSegmentByteCountOnBufferWithIncompleteTailSegment() throws Exception {
OkBuffer buffer = new OkBuffer();
buffer.writeUtf8(repeat('a', Segment.SIZE * 4 - 10));
assertEquals(Segment.SIZE * 3, buffer.completeSegmentByteCount());
}
@Test public void readUtf8SpansSegments() throws Exception {
OkBuffer buffer = new OkBuffer();
buffer.writeUtf8(repeat('a', Segment.SIZE * 2));
buffer.readUtf8(Segment.SIZE - 1);
assertEquals("aa", buffer.readUtf8(2));
}
@Test public void readUtf8EntireBuffer() throws Exception {
OkBuffer buffer = new OkBuffer();
buffer.writeUtf8(repeat('a', Segment.SIZE));
assertEquals(repeat('a', Segment.SIZE), buffer.readUtf8(Segment.SIZE));
}
@Test public void toStringOnEmptyBuffer() throws Exception {
OkBuffer buffer = new OkBuffer();
assertEquals("OkBuffer[size=0]", buffer.toString());
}
@Test public void toStringOnSmallBufferIncludesContents() throws Exception {
OkBuffer buffer = new OkBuffer();
buffer.write(ByteString.decodeHex("a1b2c3d4e5f61a2b3c4d5e6f10203040"));
assertEquals("OkBuffer[size=16 data=a1b2c3d4e5f61a2b3c4d5e6f10203040]", buffer.toString());
}
@Test public void toStringOnLargeBufferIncludesMd5() throws Exception {
OkBuffer buffer = new OkBuffer();
buffer.write(ByteString.encodeUtf8("12345678901234567"));
assertEquals("OkBuffer[size=17 md5=2c9728a2138b2f25e9f89f99bdccf8db]", buffer.toString());
}
@Test public void toStringOnMultipleSegmentBuffer() throws Exception {
OkBuffer buffer = new OkBuffer();
buffer.writeUtf8(repeat('a', 6144));
assertEquals("OkBuffer[size=6144 md5=d890021f28522533c1cc1b9b1f83ce73]", buffer.toString());
}
@Test public void multipleSegmentBuffers() throws Exception {
OkBuffer buffer = new OkBuffer();
buffer.writeUtf8(repeat('a', 1000));
buffer.writeUtf8(repeat('b', 2500));
buffer.writeUtf8(repeat('c', 5000));
buffer.writeUtf8(repeat('d', 10000));
buffer.writeUtf8(repeat('e', 25000));
buffer.writeUtf8(repeat('f', 50000));
assertEquals(repeat('a', 999), buffer.readUtf8(999)); // a...a
assertEquals("a" + repeat('b', 2500) + "c", buffer.readUtf8(2502)); // ab...bc
assertEquals(repeat('c', 4998), buffer.readUtf8(4998)); // c...c
assertEquals("c" + repeat('d', 10000) + "e", buffer.readUtf8(10002)); // cd...de
assertEquals(repeat('e', 24998), buffer.readUtf8(24998)); // e...e
assertEquals("e" + repeat('f', 50000), buffer.readUtf8(50001)); // ef...f
assertEquals(0, buffer.size());
}
@Test public void fillAndDrainPool() throws Exception {
OkBuffer buffer = new OkBuffer();
// Take 2 * MAX_SIZE segments. This will drain the pool, even if other tests filled it.
buffer.write(new byte[(int) SegmentPool.MAX_SIZE]);
buffer.write(new byte[(int) SegmentPool.MAX_SIZE]);
assertEquals(0, SegmentPool.INSTANCE.byteCount);
// Recycle MAX_SIZE segments. They're all in the pool.
buffer.readByteString(SegmentPool.MAX_SIZE);
assertEquals(SegmentPool.MAX_SIZE, SegmentPool.INSTANCE.byteCount);
// Recycle MAX_SIZE more segments. The pool is full so they get garbage collected.
buffer.readByteString(SegmentPool.MAX_SIZE);
assertEquals(SegmentPool.MAX_SIZE, SegmentPool.INSTANCE.byteCount);
// Take MAX_SIZE segments to drain the pool.
buffer.write(new byte[(int) SegmentPool.MAX_SIZE]);
assertEquals(0, SegmentPool.INSTANCE.byteCount);
// Take MAX_SIZE more segments. The pool is drained so these will need to be allocated.
buffer.write(new byte[(int) SegmentPool.MAX_SIZE]);
assertEquals(0, SegmentPool.INSTANCE.byteCount);
}
@Test public void moveBytesBetweenBuffersShareSegment() throws Exception {
int size = (Segment.SIZE / 2) - 1;
List<Integer> segmentSizes = moveBytesBetweenBuffers(repeat('a', size), repeat('b', size));
assertEquals(asList(size * 2), segmentSizes);
}
@Test public void moveBytesBetweenBuffersReassignSegment() throws Exception {
int size = (Segment.SIZE / 2) + 1;
List<Integer> segmentSizes = moveBytesBetweenBuffers(repeat('a', size), repeat('b', size));
assertEquals(asList(size, size), segmentSizes);
}
@Test public void moveBytesBetweenBuffersMultipleSegments() throws Exception {
int size = 3 * Segment.SIZE + 1;
List<Integer> segmentSizes = moveBytesBetweenBuffers(repeat('a', size), repeat('b', size));
assertEquals(asList(Segment.SIZE, Segment.SIZE, Segment.SIZE, 1,
Segment.SIZE, Segment.SIZE, Segment.SIZE, 1), segmentSizes);
}
private List<Integer> moveBytesBetweenBuffers(String... contents) {
StringBuilder expected = new StringBuilder();
OkBuffer buffer = new OkBuffer();
for (String s : contents) {
OkBuffer source = new OkBuffer();
source.writeUtf8(s);
buffer.write(source, source.size());
expected.append(s);
}
List<Integer> segmentSizes = buffer.segmentSizes();
assertEquals(expected.toString(), buffer.readUtf8(expected.length()));
return segmentSizes;
}
/** The big part of source's first segment is being moved. */
@Test public void writeSplitSourceBufferLeft() throws Exception {
int writeSize = Segment.SIZE / 2 + 1;
OkBuffer sink = new OkBuffer();
sink.writeUtf8(repeat('b', Segment.SIZE - 10));
OkBuffer source = new OkBuffer();
source.writeUtf8(repeat('a', Segment.SIZE * 2));
sink.write(source, writeSize);
assertEquals(asList(Segment.SIZE - 10, writeSize), sink.segmentSizes());
assertEquals(asList(Segment.SIZE - writeSize, Segment.SIZE), source.segmentSizes());
}
/** The big part of source's first segment is staying put. */
@Test public void writeSplitSourceBufferRight() throws Exception {
int writeSize = Segment.SIZE / 2 - 1;
OkBuffer sink = new OkBuffer();
sink.writeUtf8(repeat('b', Segment.SIZE - 10));
OkBuffer source = new OkBuffer();
source.writeUtf8(repeat('a', Segment.SIZE * 2));
sink.write(source, writeSize);
assertEquals(asList(Segment.SIZE - 10, writeSize), sink.segmentSizes());
assertEquals(asList(Segment.SIZE - writeSize, Segment.SIZE), source.segmentSizes());
}
@Test public void writePrefixDoesntSplit() throws Exception {
OkBuffer sink = new OkBuffer();
sink.writeUtf8(repeat('b', 10));
OkBuffer source = new OkBuffer();
source.writeUtf8(repeat('a', Segment.SIZE * 2));
sink.write(source, 20);
assertEquals(asList(30), sink.segmentSizes());
assertEquals(asList(Segment.SIZE - 20, Segment.SIZE), source.segmentSizes());
assertEquals(30, sink.size());
assertEquals(Segment.SIZE * 2 - 20, source.size());
}
@Test public void writePrefixDoesntSplitButRequiresCompact() throws Exception {
OkBuffer sink = new OkBuffer();
sink.writeUtf8(repeat('b', Segment.SIZE - 10)); // limit = size - 10
sink.readUtf8(Segment.SIZE - 20); // pos = size = 20
OkBuffer source = new OkBuffer();
source.writeUtf8(repeat('a', Segment.SIZE * 2));
sink.write(source, 20);
assertEquals(asList(30), sink.segmentSizes());
assertEquals(asList(Segment.SIZE - 20, Segment.SIZE), source.segmentSizes());
assertEquals(30, sink.size());
assertEquals(Segment.SIZE * 2 - 20, source.size());
}
@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));
assertEquals(10, sink.size());
assertEquals(0, source.size());
}
@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));
assertEquals(10, sink.size());
assertEquals(0, source.size());
}
@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));
assertEquals(20, sink.size());
assertEquals(5, source.size());
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));
assertEquals(30, sink.size());
assertEquals(0, source.size());
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'));
// The segment has one value.
buffer.writeUtf8("a"); // a
assertEquals(0, buffer.indexOf((byte) 'a'));
assertEquals(-1, buffer.indexOf((byte) 'b'));
// The segment has lots of data.
buffer.writeUtf8(repeat('b', Segment.SIZE - 2)); // ab...b
assertEquals(0, buffer.indexOf((byte) 'a'));
assertEquals(1, buffer.indexOf((byte) 'b'));
assertEquals(-1, buffer.indexOf((byte) 'c'));
// The segment doesn't start at 0, it starts at 2.
buffer.readUtf8(2); // b...b
assertEquals(-1, buffer.indexOf((byte) 'a'));
assertEquals(0, buffer.indexOf((byte) 'b'));
assertEquals(-1, buffer.indexOf((byte) 'c'));
// The segment is full.
buffer.writeUtf8("c"); // b...bc
assertEquals(-1, buffer.indexOf((byte) 'a'));
assertEquals(0, buffer.indexOf((byte) 'b'));
assertEquals(Segment.SIZE - 3, buffer.indexOf((byte) 'c'));
// The segment doesn't start at 2, it starts at 4.
buffer.readUtf8(2); // b...bc
assertEquals(-1, buffer.indexOf((byte) 'a'));
assertEquals(0, buffer.indexOf((byte) 'b'));
assertEquals(Segment.SIZE - 5, buffer.indexOf((byte) 'c'));
// 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'));
assertEquals(-1, buffer.indexOf((byte) 'e'));
}
@Test public void indexOfWithOffset() throws Exception {
OkBuffer buffer = new OkBuffer();
int halfSegment = Segment.SIZE / 2;
buffer.writeUtf8(repeat('a', halfSegment));
buffer.writeUtf8(repeat('b', halfSegment));
buffer.writeUtf8(repeat('c', halfSegment));
buffer.writeUtf8(repeat('d', halfSegment));
assertEquals(0, buffer.indexOf((byte) 'a', 0));
assertEquals(halfSegment - 1, buffer.indexOf((byte) 'a', halfSegment - 1));
assertEquals(halfSegment, buffer.indexOf((byte) 'b', halfSegment - 1));
assertEquals(halfSegment * 2, buffer.indexOf((byte) 'c', halfSegment - 1));
assertEquals(halfSegment * 3, buffer.indexOf((byte) 'd', halfSegment - 1));
assertEquals(halfSegment * 3, buffer.indexOf((byte) 'd', halfSegment * 2));
assertEquals(halfSegment * 3, buffer.indexOf((byte) 'd', halfSegment * 3));
assertEquals(halfSegment * 4 - 1, buffer.indexOf((byte) 'd', halfSegment * 4 - 1));
}
@Test public void writeBytes() throws Exception {
OkBuffer data = new OkBuffer();
data.writeByte(0xab);
data.writeByte(0xcd);
assertEquals("OkBuffer[size=2 data=abcd]", data.toString());
}
@Test public void writeLastByteInSegment() throws Exception {
OkBuffer data = new OkBuffer();
data.writeUtf8(repeat('a', Segment.SIZE - 1));
data.writeByte(0x20);
data.writeByte(0x21);
assertEquals(asList(Segment.SIZE, 1), data.segmentSizes());
assertEquals(repeat('a', Segment.SIZE - 1), data.readUtf8(Segment.SIZE - 1));
assertEquals("OkBuffer[size=2 data=2021]", data.toString());
}
@Test public void writeShort() throws Exception {
OkBuffer data = new OkBuffer();
data.writeShort(0xabcd);
data.writeShort(0x4321);
assertEquals("OkBuffer[size=4 data=abcd4321]", data.toString());
}
@Test public void writeShortLe() throws Exception {
OkBuffer data = new OkBuffer();
data.writeShortLe(0xabcd);
data.writeShortLe(0x4321);
assertEquals("OkBuffer[size=4 data=cdab2143]", data.toString());
}
@Test public void writeInt() throws Exception {
OkBuffer data = new OkBuffer();
data.writeInt(0xabcdef01);
data.writeInt(0x87654321);
assertEquals("OkBuffer[size=8 data=abcdef0187654321]", data.toString());
}
@Test public void writeLastIntegerInSegment() throws Exception {
OkBuffer data = new OkBuffer();
data.writeUtf8(repeat('a', Segment.SIZE - 4));
data.writeInt(0xabcdef01);
data.writeInt(0x87654321);
assertEquals(asList(Segment.SIZE, 4), data.segmentSizes());
assertEquals(repeat('a', Segment.SIZE - 4), data.readUtf8(Segment.SIZE - 4));
assertEquals("OkBuffer[size=8 data=abcdef0187654321]", data.toString());
}
@Test public void writeIntegerDoesntQuiteFitInSegment() throws Exception {
OkBuffer data = new OkBuffer();
data.writeUtf8(repeat('a', Segment.SIZE - 3));
data.writeInt(0xabcdef01);
data.writeInt(0x87654321);
assertEquals(asList(Segment.SIZE - 3, 8), data.segmentSizes());
assertEquals(repeat('a', Segment.SIZE - 3), data.readUtf8(Segment.SIZE - 3));
assertEquals("OkBuffer[size=8 data=abcdef0187654321]", data.toString());
}
@Test public void writeIntLe() throws Exception {
OkBuffer data = new OkBuffer();
data.writeIntLe(0xabcdef01);
data.writeIntLe(0x87654321);
assertEquals("OkBuffer[size=8 data=01efcdab21436587]", data.toString());
}
@Test public void writeLong() throws Exception {
OkBuffer data = new OkBuffer();
data.writeLong(0xabcdef0187654321L);
data.writeLong(0xcafebabeb0b15c00L);
assertEquals("OkBuffer[size=16 data=abcdef0187654321cafebabeb0b15c00]", data.toString());
}
@Test public void writeLongLe() throws Exception {
OkBuffer data = new OkBuffer();
data.writeLongLe(0xabcdef0187654321L);
data.writeLongLe(0xcafebabeb0b15c00L);
assertEquals("OkBuffer[size=16 data=2143658701efcdab005cb1b0bebafeca]", data.toString());
}
@Test public void readByte() throws Exception {
OkBuffer data = new OkBuffer();
data.write(new byte[] { (byte) 0xab, (byte) 0xcd });
assertEquals(0xab, data.readByte() & 0xff);
assertEquals(0xcd, data.readByte() & 0xff);
assertEquals(0, data.size());
}
@Test public void readShort() throws Exception {
OkBuffer data = new OkBuffer();
data.write(new byte[] {
(byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x01
});
assertEquals((short) 0xabcd, data.readShort());
assertEquals((short) 0xef01, data.readShort());
assertEquals(0, data.size());
}
@Test public void readShortLe() throws Exception {
OkBuffer data = new OkBuffer();
data.write(new byte[] {
(byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x10
});
assertEquals((short) 0xcdab, data.readShortLe());
assertEquals((short) 0x10ef, data.readShortLe());
assertEquals(0, data.size());
}
@Test public void readShortSplitAcrossMultipleSegments() throws Exception {
OkBuffer data = new OkBuffer();
data.writeUtf8(repeat('a', Segment.SIZE - 1));
data.write(new byte[] { (byte) 0xab, (byte) 0xcd });
data.readUtf8(Segment.SIZE - 1);
assertEquals((short) 0xabcd, data.readShort());
assertEquals(0, data.size());
}
@Test public void readInt() throws Exception {
OkBuffer data = new OkBuffer();
data.write(new byte[] {
(byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x01,
(byte) 0x87, (byte) 0x65, (byte) 0x43, (byte) 0x21
});
assertEquals(0xabcdef01, data.readInt());
assertEquals(0x87654321, data.readInt());
assertEquals(0, data.size());
}
@Test public void readIntLe() throws Exception {
OkBuffer data = new OkBuffer();
data.write(new byte[] {
(byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x10,
(byte) 0x87, (byte) 0x65, (byte) 0x43, (byte) 0x21
});
assertEquals(0x10efcdab, data.readIntLe());
assertEquals(0x21436587, data.readIntLe());
assertEquals(0, data.size());
}
@Test public void readIntSplitAcrossMultipleSegments() throws Exception {
OkBuffer data = new OkBuffer();
data.writeUtf8(repeat('a', Segment.SIZE - 3));
data.write(new byte[] {
(byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x01
});
data.readUtf8(Segment.SIZE - 3);
assertEquals(0xabcdef01, data.readInt());
assertEquals(0, data.size());
}
@Test public void readLong() throws Exception {
OkBuffer data = new OkBuffer();
data.write(new byte[] {
(byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x10,
(byte) 0x87, (byte) 0x65, (byte) 0x43, (byte) 0x21,
(byte) 0x36, (byte) 0x47, (byte) 0x58, (byte) 0x69,
(byte) 0x12, (byte) 0x23, (byte) 0x34, (byte) 0x45
});
assertEquals(0xabcdef1087654321L, data.readLong());
assertEquals(0x3647586912233445L, data.readLong());
assertEquals(0, data.size());
}
@Test public void readLongLe() throws Exception {
OkBuffer data = new OkBuffer();
data.write(new byte[] {
(byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x10,
(byte) 0x87, (byte) 0x65, (byte) 0x43, (byte) 0x21,
(byte) 0x36, (byte) 0x47, (byte) 0x58, (byte) 0x69,
(byte) 0x12, (byte) 0x23, (byte) 0x34, (byte) 0x45
});
assertEquals(0x2143658710efcdabL, data.readLongLe());
assertEquals(0x4534231269584736L, data.readLongLe());
assertEquals(0, data.size());
}
@Test public void readLongSplitAcrossMultipleSegments() throws Exception {
OkBuffer data = new OkBuffer();
data.writeUtf8(repeat('a', Segment.SIZE - 7));
data.write(new byte[] {
(byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x01,
(byte) 0x87, (byte) 0x65, (byte) 0x43, (byte) 0x21,
});
data.readUtf8(Segment.SIZE - 7);
assertEquals(0xabcdef0187654321L, data.readLong());
assertEquals(0, data.size());
}
@Test public void byteAt() throws Exception {
OkBuffer buffer = new OkBuffer();
buffer.writeUtf8("a");
buffer.writeUtf8(repeat('b', Segment.SIZE));
buffer.writeUtf8("c");
assertEquals('a', buffer.getByte(0));
assertEquals('a', buffer.getByte(0)); // getByte doesn't mutate!
assertEquals('c', buffer.getByte(buffer.size - 1));
assertEquals('b', buffer.getByte(buffer.size - 2));
assertEquals('b', buffer.getByte(buffer.size - 3));
}
@Test public void getByteOfEmptyBuffer() throws Exception {
OkBuffer buffer = new OkBuffer();
try {
buffer.getByte(0);
fail();
} catch (IndexOutOfBoundsException expected) {
}
}
@Test public void skip() throws Exception {
OkBuffer buffer = new OkBuffer();
buffer.writeUtf8("a");
buffer.writeUtf8(repeat('b', Segment.SIZE));
buffer.writeUtf8("c");
buffer.skip(1);
assertEquals('b', buffer.readByte() & 0xff);
buffer.skip(Segment.SIZE - 2);
assertEquals('b', buffer.readByte() & 0xff);
buffer.skip(1);
assertEquals(0, buffer.size());
}
@Test public void testWritePrefixToEmptyBuffer() {
OkBuffer sink = new OkBuffer();
OkBuffer source = new OkBuffer();
source.writeUtf8("abcd");
sink.write(source, 2);
assertEquals("ab", sink.readUtf8(2));
}
@Test public void cloneDoesNotObserveWritesToOriginal() throws Exception {
OkBuffer original = new OkBuffer();
OkBuffer clone = original.clone();
original.writeUtf8("abc");
assertEquals(0, clone.size());
}
@Test public void cloneDoesNotObserveReadsFromOriginal() throws Exception {
OkBuffer original = new OkBuffer();
original.writeUtf8("abc");
OkBuffer clone = original.clone();
assertEquals("abc", original.readUtf8(3));
assertEquals(3, clone.size());
assertEquals("ab", clone.readUtf8(2));
}
@Test public void originalDoesNotObserveWritesToClone() throws Exception {
OkBuffer original = new OkBuffer();
OkBuffer clone = original.clone();
clone.writeUtf8("abc");
assertEquals(0, original.size());
}
@Test public void originalDoesNotObserveReadsFromClone() throws Exception {
OkBuffer original = new OkBuffer();
original.writeUtf8("abc");
OkBuffer clone = original.clone();
assertEquals("abc", clone.readUtf8(3));
assertEquals(3, original.size());
assertEquals("ab", original.readUtf8(2));
}
@Test public void cloneMultipleSegments() throws Exception {
OkBuffer original = new OkBuffer();
original.writeUtf8(repeat('a', Segment.SIZE * 3));
OkBuffer clone = original.clone();
original.writeUtf8(repeat('b', Segment.SIZE * 3));
clone.writeUtf8(repeat('c', Segment.SIZE * 3));
assertEquals(repeat('a', Segment.SIZE * 3) + repeat('b', Segment.SIZE * 3),
original.readUtf8(Segment.SIZE * 6));
assertEquals(repeat('a', Segment.SIZE * 3) + repeat('c', Segment.SIZE * 3),
clone.readUtf8(Segment.SIZE * 6));
}
@Test public void testEqualsAndHashCodeEmpty() throws Exception {
OkBuffer a = new OkBuffer();
OkBuffer b = new OkBuffer();
assertTrue(a.equals(b));
assertTrue(a.hashCode() == b.hashCode());
}
@Test public void testEqualsAndHashCode() throws Exception {
OkBuffer a = new OkBuffer().writeUtf8("dog");
OkBuffer b = new OkBuffer().writeUtf8("hotdog");
assertFalse(a.equals(b));
assertFalse(a.hashCode() == b.hashCode());
b.readUtf8(3); // Leaves b containing 'dog'.
assertTrue(a.equals(b));
assertTrue(a.hashCode() == b.hashCode());
}
@Test public void testEqualsAndHashCodeSpanningSegments() throws Exception {
byte[] data = new byte[1024 * 1024];
Random dice = new Random(0);
dice.nextBytes(data);
OkBuffer a = bufferWithRandomSegmentLayout(dice, data);
OkBuffer b = bufferWithRandomSegmentLayout(dice, data);
assertTrue(a.equals(b));
assertTrue(a.hashCode() == b.hashCode());
data[data.length / 2]++; // Change a single byte.
OkBuffer c = bufferWithRandomSegmentLayout(dice, data);
assertFalse(a.equals(c));
assertFalse(a.hashCode() == c.hashCode());
}
/**
* Returns a new buffer containing the data in {@code data}, and a segment
* layout determined by {@code dice}.
*/
private OkBuffer bufferWithRandomSegmentLayout(Random dice, byte[] data) {
OkBuffer result = new OkBuffer();
// Writing to result directly will yield packed segments. Instead, write to
// other buffers, then write those buffers to result.
for (int pos = 0, byteCount; pos < data.length; pos += byteCount) {
byteCount = (Segment.SIZE / 2) + dice.nextInt(Segment.SIZE / 2);
if (byteCount > data.length - pos) byteCount = data.length - pos;
int offset = dice.nextInt(Segment.SIZE - byteCount);
OkBuffer segment = new OkBuffer();
segment.write(new byte[offset]);
segment.write(data, pos, byteCount);
segment.skip(offset);
result.write(segment, byteCount);
}
return result;
}
private String repeat(char c, int count) {
char[] array = new char[count];
Arrays.fill(array, c);
return new String(array);
}
}

View File

@@ -1,81 +0,0 @@
/*
* Copyright (C) 2014 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 java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Arrays;
import org.junit.Test;
import static okio.Util.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public final class OkioTest {
@Test public void sinkFromOutputStream() throws Exception {
OkBuffer data = new OkBuffer();
data.writeUtf8("a");
data.writeUtf8(repeat('b', 9998));
data.writeUtf8("c");
ByteArrayOutputStream out = new ByteArrayOutputStream();
Sink sink = Okio.sink(out);
sink.write(data, 3);
assertEquals("abb", out.toString("UTF-8"));
sink.write(data, data.size());
assertEquals("a" + repeat('b', 9998) + "c", out.toString("UTF-8"));
}
@Test public void sourceFromInputStream() throws Exception {
InputStream in = new ByteArrayInputStream(
("a" + repeat('b', Segment.SIZE * 2) + "c").getBytes(UTF_8));
// Source: ab...bc
Source source = Okio.source(in);
OkBuffer sink = new OkBuffer();
// Source: b...bc. Sink: abb.
assertEquals(3, source.read(sink, 3));
assertEquals("abb", sink.readUtf8(3));
// Source: b...bc. Sink: b...b.
assertEquals(Segment.SIZE, source.read(sink, 20000));
assertEquals(repeat('b', Segment.SIZE), sink.readUtf8(sink.size()));
// Source: b...bc. Sink: b...bc.
assertEquals(Segment.SIZE - 1, source.read(sink, 20000));
assertEquals(repeat('b', Segment.SIZE - 2) + "c", sink.readUtf8(sink.size()));
// Source and sink are empty.
assertEquals(-1, source.read(sink, 1));
}
@Test public void sourceFromInputStreamBounds() throws Exception {
Source source = Okio.source(new ByteArrayInputStream(new byte[100]));
try {
source.read(new OkBuffer(), -1);
fail();
} catch (IllegalArgumentException expected) {
}
}
private String repeat(char c, int count) {
char[] array = new char[count];
Arrays.fill(array, c);
return new String(array);
}
}

View File

@@ -1,68 +0,0 @@
/*
* Copyright (C) 2014 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 java.io.EOFException;
import java.io.IOException;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public abstract class ReadUtf8LineTest {
protected abstract BufferedSource newSource(String s);
@Test public void readLines() throws IOException {
BufferedSource source = newSource("abc\ndef\n");
assertEquals("abc", source.readUtf8LineStrict());
assertEquals("def", source.readUtf8LineStrict());
try {
source.readUtf8LineStrict();
fail();
} catch (EOFException expected) {
}
}
@Test public void emptyLines() throws IOException {
BufferedSource source = newSource("\n\n\n");
assertEquals("", source.readUtf8LineStrict());
assertEquals("", source.readUtf8LineStrict());
assertEquals("", source.readUtf8LineStrict());
assertTrue(source.exhausted());
}
@Test public void crDroppedPrecedingLf() throws IOException {
BufferedSource source = newSource("abc\r\ndef\r\nghi\rjkl\r\n");
assertEquals("abc", source.readUtf8LineStrict());
assertEquals("def", source.readUtf8LineStrict());
assertEquals("ghi\rjkl", source.readUtf8LineStrict());
}
@Test public void bufferedReaderCompatible() throws IOException {
BufferedSource source = newSource("abc\ndef");
assertEquals("abc", source.readUtf8Line());
assertEquals("def", source.readUtf8Line());
assertEquals(null, source.readUtf8Line());
}
@Test public void bufferedReaderCompatibleWithTrailingNewline() throws IOException {
BufferedSource source = newSource("abc\ndef\n");
assertEquals("abc", source.readUtf8Line());
assertEquals("def", source.readUtf8Line());
assertEquals(null, source.readUtf8Line());
}
}

View File

@@ -1,215 +0,0 @@
/*
* Copyright (C) 2014 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 java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import org.junit.Test;
import static okio.Util.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public final class RealBufferedSinkTest {
@Test public void outputStreamFromSink() throws Exception {
OkBuffer sink = new OkBuffer();
OutputStream out = new RealBufferedSink(sink).outputStream();
out.write('a');
out.write(repeat('b', 9998).getBytes(UTF_8));
out.write('c');
out.flush();
assertEquals("a" + repeat('b', 9998) + "c", sink.readUtf8(10000));
}
@Test public void outputStreamFromSinkBounds() throws Exception {
OkBuffer sink = new OkBuffer();
OutputStream out = new RealBufferedSink(sink).outputStream();
try {
out.write(new byte[100], 50, 51);
fail();
} catch (ArrayIndexOutOfBoundsException expected) {
}
}
@Test public void bufferedSinkEmitsTailWhenItIsComplete() throws IOException {
OkBuffer sink = new OkBuffer();
BufferedSink bufferedSink = new RealBufferedSink(sink);
bufferedSink.writeUtf8(repeat('a', Segment.SIZE - 1));
assertEquals(0, sink.size());
bufferedSink.writeByte(0);
assertEquals(Segment.SIZE, sink.size());
assertEquals(0, bufferedSink.buffer().size());
}
@Test public void bufferedSinkEmitZero() throws IOException {
OkBuffer sink = new OkBuffer();
BufferedSink bufferedSink = new RealBufferedSink(sink);
bufferedSink.writeUtf8("");
assertEquals(0, sink.size());
}
@Test public void bufferedSinkEmitMultipleSegments() throws IOException {
OkBuffer sink = new OkBuffer();
BufferedSink bufferedSink = new RealBufferedSink(sink);
bufferedSink.writeUtf8(repeat('a', Segment.SIZE * 4 - 1));
assertEquals(Segment.SIZE * 3, sink.size());
assertEquals(Segment.SIZE - 1, bufferedSink.buffer().size());
}
@Test public void bufferedSinkFlush() throws IOException {
OkBuffer sink = new OkBuffer();
BufferedSink bufferedSink = new RealBufferedSink(sink);
bufferedSink.writeByte('a');
assertEquals(0, sink.size());
bufferedSink.flush();
assertEquals(0, bufferedSink.buffer().size());
assertEquals(1, sink.size());
}
@Test public void bytesEmittedToSinkWithFlush() throws Exception {
OkBuffer sink = new OkBuffer();
BufferedSink bufferedSink = new RealBufferedSink(sink);
bufferedSink.writeUtf8("abc");
bufferedSink.flush();
assertEquals(3, sink.size());
}
@Test public void bytesNotEmittedToSinkWithoutFlush() throws Exception {
OkBuffer sink = new OkBuffer();
BufferedSink bufferedSink = new RealBufferedSink(sink);
bufferedSink.writeUtf8("abc");
assertEquals(0, sink.size());
}
@Test public void completeSegmentsEmitted() throws Exception {
OkBuffer sink = new OkBuffer();
BufferedSink bufferedSink = new RealBufferedSink(sink);
bufferedSink.writeUtf8(repeat('a', Segment.SIZE * 3));
assertEquals(Segment.SIZE * 3, sink.size());
}
@Test public void incompleteSegmentsNotEmitted() throws Exception {
OkBuffer sink = new OkBuffer();
BufferedSink bufferedSink = new RealBufferedSink(sink);
bufferedSink.writeUtf8(repeat('a', Segment.SIZE * 3 - 1));
assertEquals(Segment.SIZE * 2, sink.size());
}
@Test public void closeEmitsBufferedBytes() throws IOException {
OkBuffer sink = new OkBuffer();
BufferedSink bufferedSink = new RealBufferedSink(sink);
bufferedSink.writeByte('a');
bufferedSink.close();
assertEquals('a', sink.readByte());
}
@Test public void closeWithExceptionWhenWriting() throws IOException {
MockSink mockSink = new MockSink();
mockSink.scheduleThrow(0, new IOException());
BufferedSink bufferedSink = new RealBufferedSink(mockSink);
bufferedSink.writeByte('a');
try {
bufferedSink.close();
fail();
} catch (IOException expected) {
}
mockSink.assertLog("write(OkBuffer[size=1 data=61], 1)", "close()");
}
@Test public void closeWithExceptionWhenClosing() throws IOException {
MockSink mockSink = new MockSink();
mockSink.scheduleThrow(1, new IOException());
BufferedSink bufferedSink = new RealBufferedSink(mockSink);
bufferedSink.writeByte('a');
try {
bufferedSink.close();
fail();
} catch (IOException expected) {
}
mockSink.assertLog("write(OkBuffer[size=1 data=61], 1)", "close()");
}
@Test public void closeWithExceptionWhenWritingAndClosing() throws IOException {
MockSink mockSink = new MockSink();
mockSink.scheduleThrow(0, new IOException("first"));
mockSink.scheduleThrow(1, new IOException("second"));
BufferedSink bufferedSink = new RealBufferedSink(mockSink);
bufferedSink.writeByte('a');
try {
bufferedSink.close();
fail();
} catch (IOException expected) {
assertEquals("first", expected.getMessage());
}
mockSink.assertLog("write(OkBuffer[size=1 data=61], 1)", "close()");
}
@Test public void operationsAfterClose() throws IOException {
MockSink mockSink = new MockSink();
BufferedSink bufferedSink = new RealBufferedSink(mockSink);
bufferedSink.writeByte('a');
bufferedSink.close();
// Test a sample set of methods.
try {
bufferedSink.writeByte('a');
fail();
} catch (IllegalStateException expected) {
}
try {
bufferedSink.write(new byte[10]);
fail();
} catch (IllegalStateException expected) {
}
try {
bufferedSink.emitCompleteSegments();
fail();
} catch (IllegalStateException expected) {
}
try {
bufferedSink.flush();
fail();
} catch (IllegalStateException expected) {
}
// Test a sample set of methods on the OutputStream.
OutputStream os = bufferedSink.outputStream();
try {
os.write('a');
fail();
} catch (IOException expected) {
}
try {
os.write(new byte[10]);
fail();
} catch (IOException expected) {
}
// Permitted
os.flush();
}
private String repeat(char c, int count) {
char[] array = new char[count];
Arrays.fill(array, c);
return new String(array);
}
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright (C) 2014 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 java.io.IOException;
public final class RealBufferedSourceReadUtf8LineTest extends ReadUtf8LineTest {
/** Returns a buffered source that gets bytes of {@code data} one at a time. */
@Override protected BufferedSource newSource(String s) {
final OkBuffer buffer = new OkBuffer().writeUtf8(s);
Source slowSource = new Source() {
@Override public long read(OkBuffer sink, long byteCount) throws IOException {
return buffer.read(sink, Math.min(1, byteCount));
}
@Override public Source deadline(Deadline deadline) {
throw new UnsupportedOperationException();
}
@Override public void close() throws IOException {
throw new UnsupportedOperationException();
}
};
return Okio.buffer(slowSource);
}
}

View File

@@ -1,208 +0,0 @@
/*
* Copyright (C) 2014 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 java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import org.junit.Test;
import static okio.Util.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public final class RealBufferedSourceTest {
@Test public void inputStreamFromSource() throws Exception {
OkBuffer source = new OkBuffer();
source.writeUtf8("a");
source.writeUtf8(repeat('b', Segment.SIZE));
source.writeUtf8("c");
InputStream in = new RealBufferedSource(source).inputStream();
assertEquals(0, in.available());
assertEquals(Segment.SIZE + 2, source.size());
// Reading one byte buffers a full segment.
assertEquals('a', in.read());
assertEquals(Segment.SIZE - 1, in.available());
assertEquals(2, source.size());
// Reading as much as possible reads the rest of that buffered segment.
byte[] data = new byte[Segment.SIZE * 2];
assertEquals(Segment.SIZE - 1, in.read(data, 0, data.length));
assertEquals(repeat('b', Segment.SIZE - 1), new String(data, 0, Segment.SIZE - 1, UTF_8));
assertEquals(2, source.size());
// Continuing to read buffers the next segment.
assertEquals('b', in.read());
assertEquals(1, in.available());
assertEquals(0, source.size());
// Continuing to read reads from the buffer.
assertEquals('c', in.read());
assertEquals(0, in.available());
assertEquals(0, source.size());
// Once we've exhausted the source, we're done.
assertEquals(-1, in.read());
assertEquals(0, source.size());
}
@Test public void inputStreamFromSourceBounds() throws IOException {
OkBuffer source = new OkBuffer();
source.writeUtf8(repeat('a', 100));
InputStream in = new RealBufferedSource(source).inputStream();
try {
in.read(new byte[100], 50, 51);
fail();
} catch (ArrayIndexOutOfBoundsException expected) {
}
}
@Test public void requireTracksBufferFirst() throws Exception {
OkBuffer source = new OkBuffer();
source.writeUtf8("bb");
BufferedSource bufferedSource = new RealBufferedSource(source);
bufferedSource.buffer().writeUtf8("aa");
bufferedSource.require(2);
assertEquals(2, bufferedSource.buffer().size());
assertEquals(2, source.size());
}
@Test public void requireIncludesBufferBytes() throws Exception {
OkBuffer source = new OkBuffer();
source.writeUtf8("b");
BufferedSource bufferedSource = new RealBufferedSource(source);
bufferedSource.buffer().writeUtf8("a");
bufferedSource.require(2);
assertEquals("ab", bufferedSource.buffer().readUtf8(2));
}
@Test public void requireInsufficientData() throws Exception {
OkBuffer source = new OkBuffer();
source.writeUtf8("a");
BufferedSource bufferedSource = new RealBufferedSource(source);
try {
bufferedSource.require(2);
fail();
} catch (EOFException expected) {
}
}
@Test public void requireReadsOneSegmentAtATime() throws Exception {
OkBuffer source = new OkBuffer();
source.writeUtf8(repeat('a', Segment.SIZE));
source.writeUtf8(repeat('b', Segment.SIZE));
BufferedSource bufferedSource = new RealBufferedSource(source);
bufferedSource.require(2);
assertEquals(Segment.SIZE, source.size());
assertEquals(Segment.SIZE, bufferedSource.buffer().size());
}
@Test public void skipInsufficientData() throws Exception {
OkBuffer source = new OkBuffer();
source.writeUtf8("a");
BufferedSource bufferedSource = new RealBufferedSource(source);
try {
bufferedSource.skip(2);
fail();
} catch (EOFException expected) {
}
}
@Test public void skipReadsOneSegmentAtATime() throws Exception {
OkBuffer source = new OkBuffer();
source.writeUtf8(repeat('a', Segment.SIZE));
source.writeUtf8(repeat('b', Segment.SIZE));
BufferedSource bufferedSource = new RealBufferedSource(source);
bufferedSource.skip(2);
assertEquals(Segment.SIZE, source.size());
assertEquals(Segment.SIZE - 2, bufferedSource.buffer().size());
}
@Test public void skipTracksBufferFirst() throws Exception {
OkBuffer source = new OkBuffer();
source.writeUtf8("bb");
BufferedSource bufferedSource = new RealBufferedSource(source);
bufferedSource.buffer().writeUtf8("aa");
bufferedSource.skip(2);
assertEquals(0, bufferedSource.buffer().size());
assertEquals(2, source.size());
}
@Test public void operationsAfterClose() throws IOException {
OkBuffer source = new OkBuffer();
BufferedSource bufferedSource = new RealBufferedSource(source);
bufferedSource.close();
// Test a sample set of methods.
try {
bufferedSource.indexOf((byte) 1);
fail();
} catch (IllegalStateException expected) {
}
try {
bufferedSource.skip(1);
fail();
} catch (IllegalStateException expected) {
}
try {
bufferedSource.readByte();
fail();
} catch (IllegalStateException expected) {
}
try {
bufferedSource.readByteString(10);
fail();
} catch (IllegalStateException expected) {
}
// Test a sample set of methods on the InputStream.
InputStream is = bufferedSource.inputStream();
try {
is.read();
fail();
} catch (IOException expected) {
}
try {
is.read(new byte[10]);
fail();
} catch (IOException expected) {
}
}
private String repeat(char c, int count) {
char[] array = new char[count];
Arrays.fill(array, c);
return new String(array);
}
}

View File

@@ -23,7 +23,6 @@
<module>okhttp-apache</module> <module>okhttp-apache</module>
<module>okhttp-tests</module> <module>okhttp-tests</module>
<module>okcurl</module> <module>okcurl</module>
<module>okio</module>
<module>mockwebserver</module> <module>mockwebserver</module>
<module>samples</module> <module>samples</module>
<module>benchmarks</module> <module>benchmarks</module>
@@ -34,6 +33,7 @@
<!-- Compilation --> <!-- Compilation -->
<java.version>1.6</java.version> <java.version>1.6</java.version>
<okio.version>0.6.0</okio.version>
<npn.version>8.1.2.v20120308</npn.version> <npn.version>8.1.2.v20120308</npn.version>
<bouncycastle.version>1.48</bouncycastle.version> <bouncycastle.version>1.48</bouncycastle.version>
<gson.version>2.2.3</gson.version> <gson.version>2.2.3</gson.version>
@@ -66,6 +66,11 @@
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<dependency>
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
<version>${okio.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.mortbay.jetty.npn</groupId> <groupId>org.mortbay.jetty.npn</groupId>
<artifactId>npn-boot</artifactId> <artifactId>npn-boot</artifactId>