blob: 0ba4d152bdecfb8a2c043d91250561d68c2f219c [file] [log] [blame]
/*
* Copyright (C) 2019 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 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.
*/
expect interface BufferedSource : Source {
/** This source's internal buffer. */
val buffer: 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.
*/
fun exhausted(): Boolean
/**
* Returns when the buffer contains at least `byteCount` bytes. Throws an
* [java.io.EOFException] if the source is exhausted before the required bytes can be read.
*/
fun require(byteCount: Long)
/**
* Returns true when the buffer contains at least `byteCount` bytes, expanding it as
* necessary. Returns false if the source is exhausted before the requested bytes can be read.
*/
fun request(byteCount: Long): Boolean
/** Removes a byte from this source and returns it. */
fun readByte(): Byte
/**
* Removes two bytes from this source and returns a big-endian short.
* ```
* Buffer buffer = new Buffer()
* .writeByte(0x7f)
* .writeByte(0xff)
* .writeByte(0x00)
* .writeByte(0x0f);
* assertEquals(4, buffer.size());
*
* assertEquals(32767, buffer.readShort());
* assertEquals(2, buffer.size());
*
* assertEquals(15, buffer.readShort());
* assertEquals(0, buffer.size());
* ```
*/
fun readShort(): Short
/**
* Removes two bytes from this source and returns a little-endian short.
* ```
* Buffer buffer = new Buffer()
* .writeByte(0xff)
* .writeByte(0x7f)
* .writeByte(0x0f)
* .writeByte(0x00);
* assertEquals(4, buffer.size());
*
* assertEquals(32767, buffer.readShortLe());
* assertEquals(2, buffer.size());
*
* assertEquals(15, buffer.readShortLe());
* assertEquals(0, buffer.size());
* ```
*/
fun readShortLe(): Short
/**
* Removes four bytes from this source and returns a big-endian int.
* ```
* Buffer buffer = new Buffer()
* .writeByte(0x7f)
* .writeByte(0xff)
* .writeByte(0xff)
* .writeByte(0xff)
* .writeByte(0x00)
* .writeByte(0x00)
* .writeByte(0x00)
* .writeByte(0x0f);
* assertEquals(8, buffer.size());
*
* assertEquals(2147483647, buffer.readInt());
* assertEquals(4, buffer.size());
*
* assertEquals(15, buffer.readInt());
* assertEquals(0, buffer.size());
* ```
*/
fun readInt(): Int
/**
* Removes four bytes from this source and returns a little-endian int.
* ```
* Buffer buffer = new Buffer()
* .writeByte(0xff)
* .writeByte(0xff)
* .writeByte(0xff)
* .writeByte(0x7f)
* .writeByte(0x0f)
* .writeByte(0x00)
* .writeByte(0x00)
* .writeByte(0x00);
* assertEquals(8, buffer.size());
*
* assertEquals(2147483647, buffer.readIntLe());
* assertEquals(4, buffer.size());
*
* assertEquals(15, buffer.readIntLe());
* assertEquals(0, buffer.size());
* ```
*/
fun readIntLe(): Int
/**
* Removes eight bytes from this source and returns a big-endian long.
* ```
* Buffer buffer = new Buffer()
* .writeByte(0x7f)
* .writeByte(0xff)
* .writeByte(0xff)
* .writeByte(0xff)
* .writeByte(0xff)
* .writeByte(0xff)
* .writeByte(0xff)
* .writeByte(0xff)
* .writeByte(0x00)
* .writeByte(0x00)
* .writeByte(0x00)
* .writeByte(0x00)
* .writeByte(0x00)
* .writeByte(0x00)
* .writeByte(0x00)
* .writeByte(0x0f);
* assertEquals(16, buffer.size());
*
* assertEquals(9223372036854775807L, buffer.readLong());
* assertEquals(8, buffer.size());
*
* assertEquals(15, buffer.readLong());
* assertEquals(0, buffer.size());
* ```
*/
fun readLong(): Long
/**
* Removes eight bytes from this source and returns a little-endian long.
* ```
* Buffer buffer = new Buffer()
* .writeByte(0xff)
* .writeByte(0xff)
* .writeByte(0xff)
* .writeByte(0xff)
* .writeByte(0xff)
* .writeByte(0xff)
* .writeByte(0xff)
* .writeByte(0x7f)
* .writeByte(0x0f)
* .writeByte(0x00)
* .writeByte(0x00)
* .writeByte(0x00)
* .writeByte(0x00)
* .writeByte(0x00)
* .writeByte(0x00)
* .writeByte(0x00);
* assertEquals(16, buffer.size());
*
* assertEquals(9223372036854775807L, buffer.readLongLe());
* assertEquals(8, buffer.size());
*
* assertEquals(15, buffer.readLongLe());
* assertEquals(0, buffer.size());
* ```
*/
fun readLongLe(): Long
/**
* Reads a long from this source in signed decimal form (i.e., as a string in base 10 with
* optional leading '-'). This will iterate until a non-digit character is found.
* ```
* Buffer buffer = new Buffer()
* .writeUtf8("8675309 -123 00001");
*
* assertEquals(8675309L, buffer.readDecimalLong());
* assertEquals(' ', buffer.readByte());
* assertEquals(-123L, buffer.readDecimalLong());
* assertEquals(' ', buffer.readByte());
* assertEquals(1L, buffer.readDecimalLong());
* ```
*
* @throws NumberFormatException if the found digits do not fit into a `long` or a decimal
* number was not present.
*/
fun readDecimalLong(): Long
/**
* Reads a long form this source in hexadecimal form (i.e., as a string in base 16). This will
* iterate until a non-hexadecimal character is found.
* ```
* Buffer buffer = new Buffer()
* .writeUtf8("ffff CAFEBABE 10");
*
* assertEquals(65535L, buffer.readHexadecimalUnsignedLong());
* assertEquals(' ', buffer.readByte());
* assertEquals(0xcafebabeL, buffer.readHexadecimalUnsignedLong());
* assertEquals(' ', buffer.readByte());
* assertEquals(0x10L, buffer.readHexadecimalUnsignedLong());
* ```
*
* @throws NumberFormatException if the found hexadecimal does not fit into a `long` or
* hexadecimal was not found.
*/
fun readHexadecimalUnsignedLong(): Long
/**
* Reads and discards `byteCount` bytes from this source. Throws an [java.io.EOFException] if the
* source is exhausted before the requested bytes can be skipped.
*/
fun skip(byteCount: Long)
/** Removes all bytes from this and returns them as a byte string. */
fun readByteString(): ByteString
/** Removes `byteCount` bytes from this and returns them as a byte string. */
fun readByteString(byteCount: Long): ByteString
/**
* Finds the first string in `options` that is a prefix of this buffer, consumes it from this
* buffer, and returns its index. If no byte string in `options` is a prefix of this buffer this
* returns -1 and no bytes are consumed.
*
* This can be used as an alternative to [readByteString] or even [readUtf8] if the set of
* expected values is known in advance.
* ```
* Options FIELDS = Options.of(
* ByteString.encodeUtf8("depth="),
* ByteString.encodeUtf8("height="),
* ByteString.encodeUtf8("width="));
*
* Buffer buffer = new Buffer()
* .writeUtf8("width=640\n")
* .writeUtf8("height=480\n");
*
* assertEquals(2, buffer.select(FIELDS));
* assertEquals(640, buffer.readDecimalLong());
* assertEquals('\n', buffer.readByte());
* assertEquals(1, buffer.select(FIELDS));
* assertEquals(480, buffer.readDecimalLong());
* assertEquals('\n', buffer.readByte());
* ```
*/
fun select(options: Options): Int
/** Removes all bytes from this and returns them as a byte array. */
fun readByteArray(): ByteArray
/** Removes `byteCount` bytes from this and returns them as a byte array. */
fun readByteArray(byteCount: Long): ByteArray
/**
* Removes up to `sink.length` bytes from this and copies them into `sink`. Returns the number of
* bytes read, or -1 if this source is exhausted.
*/
fun read(sink: ByteArray): Int
/**
* Removes exactly `sink.length` bytes from this and copies them into `sink`. Throws an
* [java.io.EOFException] if the requested number of bytes cannot be read.
*/
fun readFully(sink: ByteArray)
/**
* Removes up to `byteCount` bytes from this and copies them into `sink` at `offset`. Returns the
* number of bytes read, or -1 if this source is exhausted.
*/
fun read(sink: ByteArray, offset: Int, byteCount: Int): Int
/**
* Removes exactly `byteCount` bytes from this and appends them to `sink`. Throws an
* [java.io.EOFException] if the requested number of bytes cannot be read.
*/
fun readFully(sink: Buffer, byteCount: Long)
/**
* Removes all bytes from this and appends them to `sink`. Returns the total number of bytes
* written to `sink` which will be 0 if this is exhausted.
*/
fun readAll(sink: Sink): Long
/**
* Removes all bytes from this, decodes them as UTF-8, and returns the string. Returns the empty
* string if this source is empty.
* ```
* Buffer buffer = new Buffer()
* .writeUtf8("Uh uh uh!")
* .writeByte(' ')
* .writeUtf8("You didn't say the magic word!");
*
* assertEquals("Uh uh uh! You didn't say the magic word!", buffer.readUtf8());
* assertEquals(0, buffer.size());
*
* assertEquals("", buffer.readUtf8());
* assertEquals(0, buffer.size());
* ```
*/
fun readUtf8(): String
/**
* Removes `byteCount` bytes from this, decodes them as UTF-8, and returns the string.
* ```
* Buffer buffer = new Buffer()
* .writeUtf8("Uh uh uh!")
* .writeByte(' ')
* .writeUtf8("You didn't say the magic word!");
* assertEquals(40, buffer.size());
*
* assertEquals("Uh uh uh! You ", buffer.readUtf8(14));
* assertEquals(26, buffer.size());
*
* assertEquals("didn't say the", buffer.readUtf8(14));
* assertEquals(12, buffer.size());
*
* assertEquals(" magic word!", buffer.readUtf8(12));
* assertEquals(0, buffer.size());
* ```
*/
fun readUtf8(byteCount: Long): String
/**
* Removes and returns characters up to but not including the next line break. A line break is
* either `"\n"` or `"\r\n"`; these characters are not included in the result.
* ```
* Buffer buffer = new Buffer()
* .writeUtf8("I'm a hacker!\n")
* .writeUtf8("That's what I said: you're a nerd.\n")
* .writeUtf8("I prefer to be called a hacker!\n");
* assertEquals(81, buffer.size());
*
* assertEquals("I'm a hacker!", buffer.readUtf8Line());
* assertEquals(67, buffer.size());
*
* assertEquals("That's what I said: you're a nerd.", buffer.readUtf8Line());
* assertEquals(32, buffer.size());
*
* assertEquals("I prefer to be called a hacker!", buffer.readUtf8Line());
* assertEquals(0, buffer.size());
*
* assertEquals(null, buffer.readUtf8Line());
* assertEquals(0, buffer.size());
* ```
*
* **On the end of the stream this method returns null,** just like [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.
*/
fun readUtf8Line(): String?
/**
* Removes and returns characters up to but not including the next line break. A line break is
* either `"\n"` or `"\r\n"`; these characters are not included in the result.
*
* **On the end of the stream this method throws.** Every call must consume either
* '\r\n' or '\n'. If these characters are absent in the stream, an [java.io.EOFException]
* is thrown. Use this for machine-generated data where a missing line break implies truncated
* input.
*/
fun readUtf8LineStrict(): String
/**
* Like [readUtf8LineStrict], except this allows the caller to specify the longest allowed match.
* Use this to protect against streams that may not include `"\n"` or `"\r\n"`.
*
* The returned string will have at most `limit` UTF-8 bytes, and the maximum number of bytes
* scanned is `limit + 2`. If `limit == 0` this will always throw an `EOFException` because no
* bytes will be scanned.
*
* This method is safe. No bytes are discarded if the match fails, and the caller is free to try
* another match:
* ```
* Buffer buffer = new Buffer();
* buffer.writeUtf8("12345\r\n");
*
* // This will throw! There must be \r\n or \n at the limit or before it.
* buffer.readUtf8LineStrict(4);
*
* // No bytes have been consumed so the caller can retry.
* assertEquals("12345", buffer.readUtf8LineStrict(5));
* ```
*/
fun readUtf8LineStrict(limit: Long): String
/**
* Removes and returns a single UTF-8 code point, reading between 1 and 4 bytes as necessary.
*
* If this source is exhausted before a complete code point can be read, this throws an
* [java.io.EOFException] and consumes no input.
*
* If this source doesn't start with a properly-encoded UTF-8 code point, this method will remove
* 1 or more non-UTF-8 bytes and return the replacement character (`U+FFFD`). This covers encoding
* problems (the input is not properly-encoded UTF-8), characters out of range (beyond the
* 0x10ffff limit of Unicode), code points for UTF-16 surrogates (U+d800..U+dfff) and overlong
* encodings (such as `0xc080` for the NUL character in modified UTF-8).
*/
fun readUtf8CodePoint(): Int
/** Equivalent to [indexOf(b, 0)][indexOf]. */
fun indexOf(b: Byte): Long
/**
* Returns the index of the first `b` in the buffer at or after `fromIndex`. This expands the
* buffer as necessary until `b` 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.
* ```
* Buffer buffer = new Buffer();
* buffer.writeUtf8("Don't move! He can't see us if we don't move.");
*
* byte m = 'm';
* assertEquals(6, buffer.indexOf(m));
* assertEquals(40, buffer.indexOf(m, 12));
* ```
*/
fun indexOf(b: Byte, fromIndex: Long): Long
/**
* Returns the index of `b` if it is found in the range of `fromIndex` inclusive to `toIndex`
* exclusive. If `b` isn't found, or if `fromIndex == toIndex`, then -1 is returned.
*
* The scan terminates at either `toIndex` or the end of the buffer, whichever comes first. The
* maximum number of bytes scanned is `toIndex-fromIndex`.
*/
fun indexOf(b: Byte, fromIndex: Long, toIndex: Long): Long
/** Equivalent to [indexOf(bytes, 0)][indexOf]. */
fun indexOf(bytes: ByteString): Long
/**
* Returns the index of the first match for `bytes` in the buffer at or after `fromIndex`. This
* expands the buffer as necessary until `bytes` is found. This reads an unbounded number of
* bytes into the buffer. Returns -1 if the stream is exhausted before the requested bytes are
* found.
* ```
* ByteString MOVE = ByteString.encodeUtf8("move");
*
* Buffer buffer = new Buffer();
* buffer.writeUtf8("Don't move! He can't see us if we don't move.");
*
* assertEquals(6, buffer.indexOf(MOVE));
* assertEquals(40, buffer.indexOf(MOVE, 12));
* ```
*/
fun indexOf(bytes: ByteString, fromIndex: Long): Long
/** Equivalent to [indexOfElement(targetBytes, 0)][indexOfElement]. */
fun indexOfElement(targetBytes: ByteString): Long
/**
* Returns the first index in this buffer that is at or after `fromIndex` and that contains any of
* the bytes in `targetBytes`. This expands the buffer as necessary until a target byte 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.
* ```
* ByteString ANY_VOWEL = ByteString.encodeUtf8("AEOIUaeoiu");
*
* Buffer buffer = new Buffer();
* buffer.writeUtf8("Dr. Alan Grant");
*
* assertEquals(4, buffer.indexOfElement(ANY_VOWEL)); // 'A' in 'Alan'.
* assertEquals(11, buffer.indexOfElement(ANY_VOWEL, 9)); // 'a' in 'Grant'.
* ```
*/
fun indexOfElement(targetBytes: ByteString, fromIndex: Long): Long
/**
* Returns true if the bytes at `offset` in this source equal `bytes`. This expands the buffer as
* necessary until a byte does not match, all bytes are matched, or if the stream is exhausted
* before enough bytes could determine a match.
* ```
* ByteString simonSays = ByteString.encodeUtf8("Simon says:");
*
* Buffer standOnOneLeg = new Buffer().writeUtf8("Simon says: Stand on one leg.");
* assertTrue(standOnOneLeg.rangeEquals(0, simonSays));
*
* Buffer payMeMoney = new Buffer().writeUtf8("Pay me $1,000,000.");
* assertFalse(payMeMoney.rangeEquals(0, simonSays));
* ```
*/
fun rangeEquals(offset: Long, bytes: ByteString): Boolean
/**
* Returns true if `byteCount` bytes at `offset` in this source equal `bytes` at `bytesOffset`.
* This expands the buffer as necessary until a byte does not match, all bytes are matched, or if
* the stream is exhausted before enough bytes could determine a match.
*/
fun rangeEquals(offset: Long, bytes: ByteString, bytesOffset: Int, byteCount: Int): Boolean
/**
* Returns a new `BufferedSource` that can read data from this `BufferedSource` without consuming
* it. The returned source becomes invalid once this source is next read or closed.
*
* For example, we can use `peek()` to lookahead and read the same data multiple times.
*
* ```
* val buffer = Buffer()
* buffer.writeUtf8("abcdefghi")
*
* buffer.readUtf8(3) // returns "abc", buffer contains "defghi"
*
* val peek = buffer.peek()
* peek.readUtf8(3) // returns "def", buffer contains "defghi"
* peek.readUtf8(3) // returns "ghi", buffer contains "defghi"
*
* buffer.readUtf8(3) // returns "def", buffer contains "ghi"
* ```
*/
fun peek(): BufferedSource
}