blob: 0edad9cb17d10e0309a5a40f00d8ae6cdd12bcc5 [file] [log] [blame]
/*
* 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 okio.internal.commonBase64
import okio.internal.commonBase64Url
import okio.internal.commonCompareTo
import okio.internal.commonDecodeBase64
import okio.internal.commonDecodeHex
import okio.internal.commonEncodeUtf8
import okio.internal.commonEndsWith
import okio.internal.commonEquals
import okio.internal.commonGetByte
import okio.internal.commonGetSize
import okio.internal.commonHashCode
import okio.internal.commonHex
import okio.internal.commonIndexOf
import okio.internal.commonInternalArray
import okio.internal.commonLastIndexOf
import okio.internal.commonOf
import okio.internal.commonRangeEquals
import okio.internal.commonStartsWith
import okio.internal.commonSubstring
import okio.internal.commonToAsciiLowercase
import okio.internal.commonToAsciiUppercase
import okio.internal.commonToByteArray
import okio.internal.commonToByteString
import okio.internal.commonToString
import okio.internal.commonUtf8
import okio.internal.commonWrite
import java.io.EOFException
import java.io.IOException
import java.io.InputStream
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.io.OutputStream
import java.io.Serializable
import java.nio.ByteBuffer
import java.nio.charset.Charset
import java.security.InvalidKeyException
import java.security.MessageDigest
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
actual open class ByteString
internal actual constructor(
internal actual val data: ByteArray
) : Serializable, Comparable<ByteString> {
@Transient internal actual var hashCode: Int = 0 // Lazily computed; 0 if unknown.
@Transient internal actual var utf8: String? = null // Lazily computed.
actual open fun utf8(): String = commonUtf8()
/** Constructs a new `String` by decoding the bytes using `charset`. */
open fun string(charset: Charset) = String(data, charset)
actual open fun base64() = commonBase64()
actual fun md5() = digest("MD5")
actual fun sha1() = digest("SHA-1")
actual fun sha256() = digest("SHA-256")
actual fun sha512() = digest("SHA-512")
internal open fun digest(algorithm: String): ByteString {
val digestBytes = MessageDigest.getInstance(algorithm).run {
update(data, 0, size)
digest()
}
return ByteString(digestBytes)
}
/** Returns the 160-bit SHA-1 HMAC of this byte string. */
actual open fun hmacSha1(key: ByteString) = hmac("HmacSHA1", key)
/** Returns the 256-bit SHA-256 HMAC of this byte string. */
actual open fun hmacSha256(key: ByteString) = hmac("HmacSHA256", key)
/** Returns the 512-bit SHA-512 HMAC of this byte string. */
actual open fun hmacSha512(key: ByteString) = hmac("HmacSHA512", key)
internal open fun hmac(algorithm: String, key: ByteString): ByteString {
try {
val mac = Mac.getInstance(algorithm)
mac.init(SecretKeySpec(key.toByteArray(), algorithm))
return ByteString(mac.doFinal(data))
} catch (e: InvalidKeyException) {
throw IllegalArgumentException(e)
}
}
actual open fun base64Url() = commonBase64Url()
actual open fun hex(): String = commonHex()
actual open fun toAsciiLowercase(): ByteString = commonToAsciiLowercase()
actual open fun toAsciiUppercase(): ByteString = commonToAsciiUppercase()
@JvmOverloads
actual open fun substring(beginIndex: Int, endIndex: Int): ByteString =
commonSubstring(beginIndex, endIndex)
internal actual open fun internalGet(pos: Int) = commonGetByte(pos)
@JvmName("getByte")
actual operator fun get(index: Int): Byte = internalGet(index)
actual val size
@JvmName("size") get() = getSize()
internal actual open fun getSize() = commonGetSize()
actual open fun toByteArray() = commonToByteArray()
internal actual open fun internalArray() = commonInternalArray()
/** Returns a `ByteBuffer` view of the bytes in this `ByteString`. */
open fun asByteBuffer(): ByteBuffer = ByteBuffer.wrap(data).asReadOnlyBuffer()
/** Writes the contents of this byte string to `out`. */
@Throws(IOException::class)
open fun write(out: OutputStream) {
out.write(data)
}
internal actual open fun write(buffer: Buffer, offset: Int, byteCount: Int) =
commonWrite(buffer, offset, byteCount)
actual open fun rangeEquals(
offset: Int,
other: ByteString,
otherOffset: Int,
byteCount: Int
): Boolean = commonRangeEquals(offset, other, otherOffset, byteCount)
actual open fun rangeEquals(
offset: Int,
other: ByteArray,
otherOffset: Int,
byteCount: Int
): Boolean = commonRangeEquals(offset, other, otherOffset, byteCount)
actual fun startsWith(prefix: ByteString) = commonStartsWith(prefix)
actual fun startsWith(prefix: ByteArray) = commonStartsWith(prefix)
actual fun endsWith(suffix: ByteString) = commonEndsWith(suffix)
actual fun endsWith(suffix: ByteArray) = commonEndsWith(suffix)
@JvmOverloads
actual fun indexOf(other: ByteString, fromIndex: Int) = indexOf(other.internalArray(), fromIndex)
@JvmOverloads
actual open fun indexOf(other: ByteArray, fromIndex: Int) = commonIndexOf(other, fromIndex)
@JvmOverloads
actual fun lastIndexOf(other: ByteString, fromIndex: Int) = commonLastIndexOf(other, fromIndex)
@JvmOverloads
actual open fun lastIndexOf(other: ByteArray, fromIndex: Int) = commonLastIndexOf(other, fromIndex)
actual override fun equals(other: Any?) = commonEquals(other)
actual override fun hashCode() = commonHashCode()
actual override fun compareTo(other: ByteString) = commonCompareTo(other)
actual override fun toString() = commonToString()
@Throws(IOException::class)
private fun readObject(`in`: ObjectInputStream) {
val dataLength = `in`.readInt()
val byteString = `in`.readByteString(dataLength)
val field = ByteString::class.java.getDeclaredField("data")
field.isAccessible = true
field.set(this, byteString.data)
}
@Throws(IOException::class)
private fun writeObject(out: ObjectOutputStream) {
out.writeInt(data.size)
out.write(data)
}
@JvmName("-deprecated_getByte")
@Deprecated(
message = "moved to operator function",
replaceWith = ReplaceWith(expression = "this[index]"),
level = DeprecationLevel.ERROR
)
fun getByte(index: Int) = this[index]
@JvmName("-deprecated_size")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "size"),
level = DeprecationLevel.ERROR
)
fun size() = size
actual companion object {
private const val serialVersionUID = 1L
@JvmField
actual val EMPTY: ByteString = ByteString(byteArrayOf())
@JvmStatic
actual fun of(vararg data: Byte) = commonOf(data)
@JvmStatic
@JvmName("of")
actual fun ByteArray.toByteString(offset: Int, byteCount: Int): ByteString =
commonToByteString(offset, byteCount)
/** Returns a [ByteString] containing a copy of this [ByteBuffer]. */
@JvmStatic
@JvmName("of")
fun ByteBuffer.toByteString(): ByteString {
val copy = ByteArray(remaining())
get(copy)
return ByteString(copy)
}
@JvmStatic
actual fun String.encodeUtf8(): ByteString = commonEncodeUtf8()
/** Returns a new [ByteString] containing the `charset`-encoded bytes of this [String]. */
@JvmStatic
@JvmName("encodeString")
fun String.encode(charset: Charset = Charsets.UTF_8) = ByteString(toByteArray(charset))
@JvmStatic
actual fun String.decodeBase64() = commonDecodeBase64()
@JvmStatic
actual fun String.decodeHex() = commonDecodeHex()
/**
* Reads `count` bytes from this [InputStream] and returns the result.
*
* @throws java.io.EOFException if `in` has fewer than `count` bytes to read.
*/
@Throws(IOException::class)
@JvmStatic
@JvmName("read")
fun InputStream.readByteString(byteCount: Int): ByteString {
require(byteCount >= 0) { "byteCount < 0: $byteCount" }
val result = ByteArray(byteCount)
var offset = 0
var read: Int
while (offset < byteCount) {
read = read(result, offset, byteCount - offset)
if (read == -1) throw EOFException()
offset += read
}
return ByteString(result)
}
@JvmName("-deprecated_decodeBase64")
@Deprecated(
message = "moved to extension function",
replaceWith = ReplaceWith(
expression = "string.decodeBase64()",
imports = ["okio.ByteString.Companion.decodeBase64"]
),
level = DeprecationLevel.ERROR
)
fun decodeBase64(string: String) = string.decodeBase64()
@JvmName("-deprecated_decodeHex")
@Deprecated(
message = "moved to extension function",
replaceWith = ReplaceWith(
expression = "string.decodeHex()",
imports = ["okio.ByteString.Companion.decodeHex"]
),
level = DeprecationLevel.ERROR
)
fun decodeHex(string: String) = string.decodeHex()
@JvmName("-deprecated_encodeString")
@Deprecated(
message = "moved to extension function",
replaceWith = ReplaceWith(
expression = "string.encode(charset)",
imports = ["okio.ByteString.Companion.encode"]
),
level = DeprecationLevel.ERROR
)
fun encodeString(string: String, charset: Charset) = string.encode(charset)
@JvmName("-deprecated_encodeUtf8")
@Deprecated(
message = "moved to extension function",
replaceWith = ReplaceWith(
expression = "string.encodeUtf8()",
imports = ["okio.ByteString.Companion.encodeUtf8"]
),
level = DeprecationLevel.ERROR
)
fun encodeUtf8(string: String) = string.encodeUtf8()
@JvmName("-deprecated_of")
@Deprecated(
message = "moved to extension function",
replaceWith = ReplaceWith(
expression = "buffer.toByteString()",
imports = ["okio.ByteString.Companion.toByteString"]
),
level = DeprecationLevel.ERROR
)
fun of(buffer: ByteBuffer) = buffer.toByteString()
@JvmName("-deprecated_of")
@Deprecated(
message = "moved to extension function",
replaceWith = ReplaceWith(
expression = "array.toByteString(offset, byteCount)",
imports = ["okio.ByteString.Companion.toByteString"]
),
level = DeprecationLevel.ERROR
)
fun of(array: ByteArray, offset: Int, byteCount: Int) = array.toByteString(offset, byteCount)
@JvmName("-deprecated_read")
@Deprecated(
message = "moved to extension function",
replaceWith = ReplaceWith(
expression = "inputstream.readByteString(byteCount)",
imports = ["okio.ByteString.Companion.readByteString"]
),
level = DeprecationLevel.ERROR
)
fun read(inputstream: InputStream, byteCount: Int) = inputstream.readByteString(byteCount)
}
}