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:
@@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>();
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
29
okio/pom.xml
29
okio/pom.xml
@@ -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>
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 + ")";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
7
pom.xml
7
pom.xml
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user