blob: 857c983e549c6aff19425c1a656edf8ef6295fbd [file] [log] [blame]
/*
* 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 okio.internal.commonClear
import okio.internal.commonClose
import okio.internal.commonCompleteSegmentByteCount
import okio.internal.commonCopy
import okio.internal.commonCopyTo
import okio.internal.commonEquals
import okio.internal.commonExpandBuffer
import okio.internal.commonGet
import okio.internal.commonHashCode
import okio.internal.commonIndexOf
import okio.internal.commonIndexOfElement
import okio.internal.commonNext
import okio.internal.commonRangeEquals
import okio.internal.commonRead
import okio.internal.commonReadAll
import okio.internal.commonReadAndWriteUnsafe
import okio.internal.commonReadByte
import okio.internal.commonReadByteArray
import okio.internal.commonReadByteString
import okio.internal.commonReadDecimalLong
import okio.internal.commonReadFully
import okio.internal.commonReadHexadecimalUnsignedLong
import okio.internal.commonReadInt
import okio.internal.commonReadLong
import okio.internal.commonReadShort
import okio.internal.commonReadUnsafe
import okio.internal.commonReadUtf8CodePoint
import okio.internal.commonReadUtf8Line
import okio.internal.commonReadUtf8LineStrict
import okio.internal.commonResizeBuffer
import okio.internal.commonSeek
import okio.internal.commonSelect
import okio.internal.commonSkip
import okio.internal.commonSnapshot
import okio.internal.commonWritableSegment
import okio.internal.commonWrite
import okio.internal.commonWriteAll
import okio.internal.commonWriteByte
import okio.internal.commonWriteDecimalLong
import okio.internal.commonWriteHexadecimalUnsignedLong
import okio.internal.commonWriteInt
import okio.internal.commonWriteLong
import okio.internal.commonWriteShort
import okio.internal.commonWriteUtf8
import okio.internal.commonWriteUtf8CodePoint
import java.io.Closeable
import java.io.EOFException
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.nio.ByteBuffer
import java.nio.channels.ByteChannel
import java.nio.charset.Charset
import java.security.InvalidKeyException
import java.security.MessageDigest
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
actual class Buffer : BufferedSource, BufferedSink, Cloneable, ByteChannel {
@JvmField internal actual var head: Segment? = null
@get:JvmName("size")
actual var size: Long = 0L
internal set
override fun buffer() = this
actual override val buffer get() = this
override fun outputStream(): OutputStream {
return object : OutputStream() {
override fun write(b: Int) {
writeByte(b)
}
override fun write(data: ByteArray, offset: Int, byteCount: Int) {
this@Buffer.write(data, offset, byteCount)
}
override fun flush() {}
override fun close() {}
override fun toString(): String = "${this@Buffer}.outputStream()"
}
}
actual override fun emitCompleteSegments() = this // Nowhere to emit to!
actual override fun emit() = this // Nowhere to emit to!
override fun exhausted() = size == 0L
@Throws(EOFException::class)
override fun require(byteCount: Long) {
if (size < byteCount) throw EOFException()
}
override fun request(byteCount: Long) = size >= byteCount
override fun peek(): BufferedSource {
return PeekSource(this).buffer()
}
override fun inputStream(): InputStream {
return object : InputStream() {
override fun read(): Int {
return if (size > 0L) {
readByte() and 0xff
} else {
-1
}
}
override fun read(sink: ByteArray, offset: Int, byteCount: Int): Int {
return this@Buffer.read(sink, offset, byteCount)
}
override fun available() = minOf(size, Integer.MAX_VALUE).toInt()
override fun close() {}
override fun toString() = "${this@Buffer}.inputStream()"
}
}
/** Copy `byteCount` bytes from this, starting at `offset`, to `out`. */
@Throws(IOException::class)
@JvmOverloads
fun copyTo(
out: OutputStream,
offset: Long = 0L,
byteCount: Long = size - offset
): Buffer {
var offset = offset
var byteCount = byteCount
checkOffsetAndCount(size, offset, byteCount)
if (byteCount == 0L) return this
// Skip segments that we aren't copying from.
var s = head
while (offset >= s!!.limit - s.pos) {
offset -= (s.limit - s.pos).toLong()
s = s.next
}
// Copy from one segment at a time.
while (byteCount > 0L) {
val pos = (s!!.pos + offset).toInt()
val toCopy = minOf(s.limit - pos, byteCount).toInt()
out.write(s.data, pos, toCopy)
byteCount -= toCopy.toLong()
offset = 0L
s = s.next
}
return this
}
actual fun copyTo(
out: Buffer,
offset: Long,
byteCount: Long
): Buffer = commonCopyTo(out, offset, byteCount)
actual fun copyTo(
out: Buffer,
offset: Long
): Buffer = copyTo(out, offset, size - offset)
/** Write `byteCount` bytes from this to `out`. */
@Throws(IOException::class)
@JvmOverloads
fun writeTo(out: OutputStream, byteCount: Long = size): Buffer {
var byteCount = byteCount
checkOffsetAndCount(size, 0, byteCount)
var s = head
while (byteCount > 0L) {
val toCopy = minOf(byteCount, s!!.limit - s.pos).toInt()
out.write(s.data, s.pos, toCopy)
s.pos += toCopy
size -= toCopy.toLong()
byteCount -= toCopy.toLong()
if (s.pos == s.limit) {
val toRecycle = s
s = toRecycle.pop()
head = s
SegmentPool.recycle(toRecycle)
}
}
return this
}
/** Read and exhaust bytes from `input` into this. */
@Throws(IOException::class)
fun readFrom(input: InputStream): Buffer {
readFrom(input, Long.MAX_VALUE, true)
return this
}
/** Read `byteCount` bytes from `input` into this. */
@Throws(IOException::class)
fun readFrom(input: InputStream, byteCount: Long): Buffer {
require(byteCount >= 0L) { "byteCount < 0: $byteCount" }
readFrom(input, byteCount, false)
return this
}
@Throws(IOException::class)
private fun readFrom(input: InputStream, byteCount: Long, forever: Boolean) {
var byteCount = byteCount
while (byteCount > 0L || forever) {
val tail = writableSegment(1)
val maxToCopy = minOf(byteCount, Segment.SIZE - tail.limit).toInt()
val bytesRead = input.read(tail.data, tail.limit, maxToCopy)
if (bytesRead == -1) {
if (tail.pos == tail.limit) {
// We allocated a tail segment, but didn't end up needing it. Recycle!
head = tail.pop()
SegmentPool.recycle(tail)
}
if (forever) return
throw EOFException()
}
tail.limit += bytesRead
size += bytesRead.toLong()
byteCount -= bytesRead.toLong()
}
}
actual fun completeSegmentByteCount(): Long = commonCompleteSegmentByteCount()
@Throws(EOFException::class)
override fun readByte(): Byte = commonReadByte()
@JvmName("getByte")
actual operator fun get(pos: Long): Byte = commonGet(pos)
@Throws(EOFException::class)
override fun readShort(): Short = commonReadShort()
@Throws(EOFException::class)
override fun readInt(): Int = commonReadInt()
@Throws(EOFException::class)
override fun readLong(): Long = commonReadLong()
@Throws(EOFException::class)
override fun readShortLe() = readShort().reverseBytes()
@Throws(EOFException::class)
override fun readIntLe() = readInt().reverseBytes()
@Throws(EOFException::class)
override fun readLongLe() = readLong().reverseBytes()
@Throws(EOFException::class)
override fun readDecimalLong(): Long = commonReadDecimalLong()
@Throws(EOFException::class)
override fun readHexadecimalUnsignedLong(): Long = commonReadHexadecimalUnsignedLong()
override fun readByteString(): ByteString = commonReadByteString()
@Throws(EOFException::class)
override fun readByteString(byteCount: Long) = commonReadByteString(byteCount)
override fun select(options: Options): Int = commonSelect(options)
@Throws(EOFException::class)
override fun readFully(sink: Buffer, byteCount: Long): Unit = commonReadFully(sink, byteCount)
@Throws(IOException::class)
override fun readAll(sink: Sink): Long = commonReadAll(sink)
override fun readUtf8() = readString(size, Charsets.UTF_8)
@Throws(EOFException::class)
override fun readUtf8(byteCount: Long) = readString(byteCount, Charsets.UTF_8)
override fun readString(charset: Charset) = readString(size, charset)
@Throws(EOFException::class)
override fun readString(byteCount: Long, charset: Charset): String {
require(byteCount >= 0 && byteCount <= Integer.MAX_VALUE) { "byteCount: $byteCount" }
if (size < byteCount) throw EOFException()
if (byteCount == 0L) return ""
val s = head!!
if (s.pos + byteCount > s.limit) {
// If the string spans multiple segments, delegate to readBytes().
return String(readByteArray(byteCount), charset)
}
val result = String(s.data, s.pos, byteCount.toInt(), charset)
s.pos += byteCount.toInt()
size -= byteCount
if (s.pos == s.limit) {
head = s.pop()
SegmentPool.recycle(s)
}
return result
}
@Throws(EOFException::class)
override fun readUtf8Line(): String? = commonReadUtf8Line()
@Throws(EOFException::class)
override fun readUtf8LineStrict() = readUtf8LineStrict(Long.MAX_VALUE)
@Throws(EOFException::class)
override fun readUtf8LineStrict(limit: Long): String = commonReadUtf8LineStrict(limit)
@Throws(EOFException::class)
override fun readUtf8CodePoint(): Int = commonReadUtf8CodePoint()
override fun readByteArray() = commonReadByteArray()
@Throws(EOFException::class)
override fun readByteArray(byteCount: Long): ByteArray = commonReadByteArray(byteCount)
override fun read(sink: ByteArray) = commonRead(sink)
@Throws(EOFException::class)
override fun readFully(sink: ByteArray) = commonReadFully(sink)
override fun read(sink: ByteArray, offset: Int, byteCount: Int): Int =
commonRead(sink, offset, byteCount)
@Throws(IOException::class)
override fun read(sink: ByteBuffer): Int {
val s = head ?: return -1
val toCopy = minOf(sink.remaining(), s.limit - s.pos)
sink.put(s.data, s.pos, toCopy)
s.pos += toCopy
size -= toCopy.toLong()
if (s.pos == s.limit) {
head = s.pop()
SegmentPool.recycle(s)
}
return toCopy
}
actual fun clear() = commonClear()
@Throws(EOFException::class)
actual override fun skip(byteCount: Long) = commonSkip(byteCount)
actual override fun write(byteString: ByteString): Buffer = commonWrite(byteString)
actual override fun write(byteString: ByteString, offset: Int, byteCount: Int) =
commonWrite(byteString, offset, byteCount)
actual override fun writeUtf8(string: String): Buffer = writeUtf8(string, 0, string.length)
actual override fun writeUtf8(string: String, beginIndex: Int, endIndex: Int): Buffer =
commonWriteUtf8(string, beginIndex, endIndex)
actual override fun writeUtf8CodePoint(codePoint: Int): Buffer =
commonWriteUtf8CodePoint(codePoint)
override fun writeString(string: String, charset: Charset) = writeString(
string, 0, string.length,
charset
)
override fun writeString(
string: String,
beginIndex: Int,
endIndex: Int,
charset: Charset
): Buffer {
require(beginIndex >= 0) { "beginIndex < 0: $beginIndex" }
require(endIndex >= beginIndex) { "endIndex < beginIndex: $endIndex < $beginIndex" }
require(endIndex <= string.length) { "endIndex > string.length: $endIndex > ${string.length}" }
if (charset == Charsets.UTF_8) return writeUtf8(string, beginIndex, endIndex)
val data = string.substring(beginIndex, endIndex).toByteArray(charset)
return write(data, 0, data.size)
}
actual override fun write(source: ByteArray): Buffer = commonWrite(source)
actual override fun write(
source: ByteArray,
offset: Int,
byteCount: Int
): Buffer = commonWrite(source, offset, byteCount)
@Throws(IOException::class)
override fun write(source: ByteBuffer): Int {
val byteCount = source.remaining()
var remaining = byteCount
while (remaining > 0) {
val tail = writableSegment(1)
val toCopy = minOf(remaining, Segment.SIZE - tail.limit)
source.get(tail.data, tail.limit, toCopy)
remaining -= toCopy
tail.limit += toCopy
}
size += byteCount.toLong()
return byteCount
}
@Throws(IOException::class)
override fun writeAll(source: Source): Long = commonWriteAll(source)
@Throws(IOException::class)
actual override fun write(source: Source, byteCount: Long): Buffer =
commonWrite(source, byteCount)
actual override fun writeByte(b: Int): Buffer = commonWriteByte(b)
actual override fun writeShort(s: Int): Buffer = commonWriteShort(s)
actual override fun writeShortLe(s: Int) = writeShort(s.toShort().reverseBytes().toInt())
actual override fun writeInt(i: Int): Buffer = commonWriteInt(i)
actual override fun writeIntLe(i: Int) = writeInt(i.reverseBytes())
actual override fun writeLong(v: Long): Buffer = commonWriteLong(v)
actual override fun writeLongLe(v: Long) = writeLong(v.reverseBytes())
actual override fun writeDecimalLong(v: Long): Buffer = commonWriteDecimalLong(v)
actual override fun writeHexadecimalUnsignedLong(v: Long): Buffer =
commonWriteHexadecimalUnsignedLong(v)
internal actual fun writableSegment(minimumCapacity: Int): Segment =
commonWritableSegment(minimumCapacity)
override fun write(source: Buffer, byteCount: Long): Unit = commonWrite(source, byteCount)
override fun read(sink: Buffer, byteCount: Long): Long = commonRead(sink, byteCount)
override fun indexOf(b: Byte) = indexOf(b, 0, Long.MAX_VALUE)
/**
* Returns the index of `b` in this at or beyond `fromIndex`, or -1 if this buffer does not
* contain `b` in that range.
*/
override fun indexOf(b: Byte, fromIndex: Long) = indexOf(b, fromIndex, Long.MAX_VALUE)
override fun indexOf(b: Byte, fromIndex: Long, toIndex: Long): Long = commonIndexOf(b, fromIndex, toIndex)
@Throws(IOException::class)
override fun indexOf(bytes: ByteString): Long = indexOf(bytes, 0)
@Throws(IOException::class)
override fun indexOf(bytes: ByteString, fromIndex: Long): Long = commonIndexOf(bytes, fromIndex)
override fun indexOfElement(targetBytes: ByteString) = indexOfElement(targetBytes, 0L)
override fun indexOfElement(targetBytes: ByteString, fromIndex: Long): Long =
commonIndexOfElement(targetBytes, fromIndex)
override fun rangeEquals(offset: Long, bytes: ByteString) =
rangeEquals(offset, bytes, 0, bytes.size)
override fun rangeEquals(
offset: Long,
bytes: ByteString,
bytesOffset: Int,
byteCount: Int
): Boolean = commonRangeEquals(offset, bytes, bytesOffset, byteCount)
override fun flush() {}
override fun isOpen() = true
override fun close() {}
override fun timeout() = Timeout.NONE
/** Returns the 128-bit MD5 hash of this buffer. */
actual fun md5() = digest("MD5")
/** Returns the 160-bit SHA-1 hash of this buffer. */
actual fun sha1() = digest("SHA-1")
/** Returns the 256-bit SHA-256 hash of this buffer. */
actual fun sha256() = digest("SHA-256")
/** Returns the 512-bit SHA-512 hash of this buffer. */
actual fun sha512() = digest("SHA-512")
private fun digest(algorithm: String): ByteString {
val messageDigest = MessageDigest.getInstance(algorithm)
head?.let { head ->
messageDigest.update(head.data, head.pos, head.limit - head.pos)
var s = head.next!!
while (s !== head) {
messageDigest.update(s.data, s.pos, s.limit - s.pos)
s = s.next!!
}
}
return ByteString(messageDigest.digest())
}
/** Returns the 160-bit SHA-1 HMAC of this buffer. */
actual fun hmacSha1(key: ByteString) = hmac("HmacSHA1", key)
/** Returns the 256-bit SHA-256 HMAC of this buffer. */
actual fun hmacSha256(key: ByteString) = hmac("HmacSHA256", key)
/** Returns the 512-bit SHA-512 HMAC of this buffer. */
actual fun hmacSha512(key: ByteString) = hmac("HmacSHA512", key)
private fun hmac(algorithm: String, key: ByteString): ByteString {
try {
val mac = Mac.getInstance(algorithm)
mac.init(SecretKeySpec(key.internalArray(), algorithm))
head?.let { head ->
mac.update(head.data, head.pos, head.limit - head.pos)
var s = head.next!!
while (s !== head) {
mac.update(s.data, s.pos, s.limit - s.pos)
s = s.next!!
}
}
return ByteString(mac.doFinal())
} catch (e: InvalidKeyException) {
throw IllegalArgumentException(e)
}
}
override fun equals(other: Any?): Boolean = commonEquals(other)
override fun hashCode(): Int = commonHashCode()
/**
* Returns a human-readable string that describes the contents of this buffer. Typically this
* is a string like `[text=Hello]` or `[hex=0000ffff]`.
*/
override fun toString() = snapshot().toString()
actual fun copy(): Buffer = commonCopy()
/** Returns a deep copy of this buffer. */
public override fun clone(): Buffer = copy()
actual fun snapshot(): ByteString = commonSnapshot()
actual fun snapshot(byteCount: Int): ByteString = commonSnapshot(byteCount)
@JvmOverloads
actual fun readUnsafe(unsafeCursor: UnsafeCursor): UnsafeCursor = commonReadUnsafe(unsafeCursor)
@JvmOverloads
actual fun readAndWriteUnsafe(unsafeCursor: UnsafeCursor): UnsafeCursor =
commonReadAndWriteUnsafe(unsafeCursor)
@JvmName("-deprecated_getByte")
@Deprecated(
message = "moved to operator function",
replaceWith = ReplaceWith(expression = "this[index]"),
level = DeprecationLevel.ERROR
)
fun getByte(index: Long) = this[index]
@JvmName("-deprecated_size")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "size"),
level = DeprecationLevel.ERROR
)
fun size() = size
actual class UnsafeCursor : Closeable {
@JvmField actual var buffer: Buffer? = null
@JvmField actual var readWrite: Boolean = false
internal actual var segment: Segment? = null
@JvmField actual var offset = -1L
@JvmField actual var data: ByteArray? = null
@JvmField actual var start = -1
@JvmField actual var end = -1
actual fun next(): Int = commonNext()
actual fun seek(offset: Long): Int = commonSeek(offset)
actual fun resizeBuffer(newSize: Long): Long = commonResizeBuffer(newSize)
actual fun expandBuffer(minByteCount: Int): Long = commonExpandBuffer(minByteCount)
actual override fun close() {
commonClose()
}
}
}