From d6c533f3cf46386b09c0c0fb7392737425555647 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Tue, 20 Jun 2023 11:40:03 +0200 Subject: [PATCH 01/32] Implement basic ByteString API --- bytestring/api/kotlinx-io-bytestring.api | 47 + bytestring/build.gradle.kts | 57 + .../src/commonMain/kotlin/ByteString.kt | 423 ++++++++ .../kotlinx/io/bytestring/ByteStringTest.kt | 983 ++++++++++++++++++ .../src/jvmMain/kotlin/ByteStringJVM.kt | 56 + settings.gradle.kts | 2 + 6 files changed, 1568 insertions(+) create mode 100644 bytestring/api/kotlinx-io-bytestring.api create mode 100644 bytestring/build.gradle.kts create mode 100644 bytestring/src/commonMain/kotlin/ByteString.kt create mode 100644 bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt create mode 100644 bytestring/src/jvmMain/kotlin/ByteStringJVM.kt diff --git a/bytestring/api/kotlinx-io-bytestring.api b/bytestring/api/kotlinx-io-bytestring.api new file mode 100644 index 000000000..378f51ef0 --- /dev/null +++ b/bytestring/api/kotlinx-io-bytestring.api @@ -0,0 +1,47 @@ +public final class kotlinx/io/bytestring/ByteString : java/lang/Comparable { + public static final field Companion Lkotlinx/io/bytestring/ByteString$Companion; + public fun ([B)V + public fun ([BII)V + public synthetic fun ([BIIILkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun compareTo (Ljava/lang/Object;)I + public fun compareTo (Lkotlinx/io/bytestring/ByteString;)I + public final fun copyInto ([BIII)V + public static synthetic fun copyInto$default (Lkotlinx/io/bytestring/ByteString;[BIIIILjava/lang/Object;)V + public fun equals (Ljava/lang/Object;)Z + public final fun get (I)B + public final fun getSize ()I + public fun hashCode ()I + public final fun substring (II)Lkotlinx/io/bytestring/ByteString; + public static synthetic fun substring$default (Lkotlinx/io/bytestring/ByteString;IIILjava/lang/Object;)Lkotlinx/io/bytestring/ByteString; + public final fun toByteArray (II)[B + public static synthetic fun toByteArray$default (Lkotlinx/io/bytestring/ByteString;IIILjava/lang/Object;)[B + public fun toString ()Ljava/lang/String; + public final fun toString (Z)Ljava/lang/String; + public static synthetic fun toString$default (Lkotlinx/io/bytestring/ByteString;ZILjava/lang/Object;)Ljava/lang/String; +} + +public final class kotlinx/io/bytestring/ByteString$Companion { + public final fun getEMPTY ()Lkotlinx/io/bytestring/ByteString; +} + +public final class kotlinx/io/bytestring/ByteStringKt { + public static final fun endsWith (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;)Z + public static final fun endsWith (Lkotlinx/io/bytestring/ByteString;[B)Z + public static final fun indexOf (Lkotlinx/io/bytestring/ByteString;BI)I + public static final fun indexOf (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;I)I + public static final fun indexOf (Lkotlinx/io/bytestring/ByteString;[BI)I + public static synthetic fun indexOf$default (Lkotlinx/io/bytestring/ByteString;BIILjava/lang/Object;)I + public static synthetic fun indexOf$default (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;IILjava/lang/Object;)I + public static synthetic fun indexOf$default (Lkotlinx/io/bytestring/ByteString;[BIILjava/lang/Object;)I + public static final fun indices (Lkotlinx/io/bytestring/ByteString;)Lkotlin/ranges/IntRange; + public static final fun isEmpty (Lkotlinx/io/bytestring/ByteString;)Z + public static final fun lastIndexOf (Lkotlinx/io/bytestring/ByteString;BI)I + public static final fun lastIndexOf (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;I)I + public static final fun lastIndexOf (Lkotlinx/io/bytestring/ByteString;[BI)I + public static synthetic fun lastIndexOf$default (Lkotlinx/io/bytestring/ByteString;BIILjava/lang/Object;)I + public static synthetic fun lastIndexOf$default (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;IILjava/lang/Object;)I + public static synthetic fun lastIndexOf$default (Lkotlinx/io/bytestring/ByteString;[BIILjava/lang/Object;)I + public static final fun startsWith (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;)Z + public static final fun startsWith (Lkotlinx/io/bytestring/ByteString;[B)Z +} + diff --git a/bytestring/build.gradle.kts b/bytestring/build.gradle.kts new file mode 100644 index 000000000..904cc80a3 --- /dev/null +++ b/bytestring/build.gradle.kts @@ -0,0 +1,57 @@ +import org.jetbrains.dokka.gradle.DokkaTaskPartial +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet + +plugins { + kotlin("multiplatform") + id("org.jetbrains.kotlinx.kover") version "0.7.0" + id("org.jetbrains.dokka") version "1.8.10" +} + +kotlin { + jvm { + withJava() + testRuns["test"].executionTask.configure { + useJUnitPlatform() + } + } + + configureNativePlatforms() + sourceSets { + val commonMain by getting + val commonTest by getting { + dependencies { + implementation(kotlin("test")) + } + } + val jvmMain by getting + val jvmTest by getting + + createSourceSet("nativeMain", parent = commonMain, children = nativeTargets) + createSourceSet("nativeTest", parent = commonTest, children = nativeTargets) + } + + explicitApi() + sourceSets.configureEach { + configureSourceSet() + } +} + +fun KotlinSourceSet.configureSourceSet() { + val srcDir = if (name.endsWith("Main")) "src" else "test" + val platform = name.dropLast(4) + kotlin.srcDir("$platform/$srcDir") + if (name == "jvmMain") { + resources.srcDir("$platform/resources") + } else if (name == "jvmTest") { + resources.srcDir("$platform/test-resources") + } + languageSettings { + progressiveMode = true + } +} + +tasks.withType().configureEach { + dokkaSourceSets.configureEach { + includes.from("Module.md") + } +} \ No newline at end of file diff --git a/bytestring/src/commonMain/kotlin/ByteString.kt b/bytestring/src/commonMain/kotlin/ByteString.kt new file mode 100644 index 000000000..e3c563f6a --- /dev/null +++ b/bytestring/src/commonMain/kotlin/ByteString.kt @@ -0,0 +1,423 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. + */ + +/* + * Copyright (C) 2018 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 kotlinx.io.bytestring + +import kotlin.math.max +import kotlin.math.min + +private val HEX_DIGITS = "0123456789ABCDEF".toCharArray() + +/** + * ByteString is an immutable wrapper around a byte sequence providing [String] like functionality. + * + * + */ +public class ByteString private constructor( + private val data: ByteArray, + @Suppress("UNUSED_PARAMETER") dummy: Any? +) : Comparable { + private var hashCode: Int = 0 + + public companion object { + /** + * An empty ByteString. + */ + public val EMPTY: ByteString = ByteString() + } + + /** + * Returns size of this ByteString. + */ + public val size: Int + get(): Int = data.size + + /** + * Wraps a copy of [data] subarray starting at [startIndex] and ending at [endIndex] into a byte string. + * + * @param data the array whose subarray should be copied and wrapped into a byte string. + * @param startIndex the start index (inclusive) of a subarray to copy, `0` by default. + * @param endIndex the end index (exclusive) of a subarray to copy, `data.size` be default. + * + * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [data] array indices. + * @throws IllegalArgumentException when `startIndex > endIndex`. + */ + public constructor(data: ByteArray, startIndex: Int = 0, endIndex: Int = data.size) : + this(data.copyOfRange(startIndex, endIndex), null) + + /** + * Wraps given [bytes] into a byte string. + * + * @param bytes a sequence of bytes to be wrapped. + */ + public constructor(vararg bytes: Byte) : this(bytes, null) + + /** + * Returns `true` if [other] is a byte string containing exactly the same byte sequence. + * + * @param other the other object to compare this byte string for equality to. + */ + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as ByteString + + if (other.data.size != data.size) return false + if (other.hashCode != 0 && hashCode != 0 && other.hashCode != hashCode) return false + return data.contentEquals(other.data) + } + + /** + * Returns a hash code based on the content of this byte string. + */ + override fun hashCode(): Int { + var hc = hashCode + if (hc == 0) { + hc = data.contentHashCode() + hashCode = hc + } + return hc + } + + /** + * Returns a byte at the given index in this byte string. + * + * @param index the index to retrieve the byte at. + * + * @throws IndexOutOfBoundsException when [index] is negative or greater or equal to the [size]. + */ + public operator fun get(index: Int): Byte = data[index] + + /** + * Returns a copy of subsequence starting at [startIndex] and ending at [endIndex] of a byte sequence + * wrapped by this byte string. + * + * @param startIndex the start index (inclusive) of a subsequence to copy, `0` by default. + * @param endIndex the end index (exclusive) of a subsequence to copy, [size] be default. + * + * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of byte string indices. + * @throws IllegalArgumentException when `startIndex > endIndex`. + */ + public fun toByteArray(startIndex: Int = 0, endIndex: Int = size): ByteArray { + require(startIndex <= endIndex) { "startIndex: $startIndex, endIndex: $endIndex" } + return data.copyOfRange(startIndex, endIndex) + } + + /** + * Copies a subsequence starting at [startIndex] and ending at [endIndex] of a byte sequence + * wrapped by this byte string and writes it into [destination] array starting at [destinationOffset] offset. + * + * @param destination the array to copy data into. + * @param destinationOffset the offset starting from which data copy should be written to [destination]. + * @param startIndex the start index (inclusive) of a subsequence to copy, `0` by default. + * @param endIndex the end index (exclusive) of a subsequence to copy, [size] be default. + * + * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of byte string indices. + * @throws IndexOutOfBoundsException when the subrange doesn't fit into the [destination] array starting at + * the specified [destinationOffset], or when that index is out of the [destination] array indices range. + * @throws IllegalArgumentException when `startIndex > endIndex`. + */ + public fun copyInto( + destination: ByteArray, destinationOffset: Int = 0, + startIndex: Int = 0, endIndex: Int = size + ) { + require(startIndex <= endIndex) { + "destinationOffset: $destinationOffset, startIndex: $startIndex, endIndex: $endIndex" + } + data.copyInto(destination, destinationOffset, startIndex, endIndex) + } + + /** + * Returns a new byte string wrapping a subsequence of bytes wrapped by this byte string starting from + * [startIndex] and ending at [endIndex]. + * + * @param startIndex the start index (inclusive) of a subsequence to copy. + * @param endIndex the end index (exclusive) of a subsequence to copy, [size] be default. + * + * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of byte string indices. + * @throws IllegalArgumentException when `startIndex > endIndex`. + */ + public fun substring(startIndex: Int, endIndex: Int = size): ByteString = if (startIndex == endIndex) { + EMPTY + } else { + ByteString(data, startIndex, endIndex) + } + + /** + * Compares a byte sequence wrapped by this byte string to a byte sequence wrapped by [other] + * in lexicographical order. + * Byte values are compared as unsigned integers. + * + * @param other the byte string to compare this string to. + */ + override fun compareTo(other: ByteString): Int { + if (other === this) return 0 + + for (i in 0 until min(size, other.size)) { + val cmp = data[i].toUByte().compareTo(other[i].toUByte()) + if (cmp != 0) return cmp + } + + return size.compareTo(other.size) + } + + /** + * Returns a string representation of this byte string. A string representation consists of [size] and + * prefix of the byte sequence wrapped by this byte string encoded to hexadecimal string. + * + * By default, for short byte string, the string representation looks like `[size=3 hex=ABCDEF]`, + * for empty strings it's always `[size=0]` and for long string only a prefix of data will be included: + * `[size=128 hex=0123456789…]`. + * This behavior can be changed by setting [full] to `true`, in that case a string representation will include + * hexadecimal string encoding full byte string's content. + * + * @param full the flag indicating whether the resulting string will include the whole byte string or only + * its prefix. + */ + public fun toString(full: Boolean = false): String { + if (isEmpty()) { + return "[size=0]" + } + // format: "[size=XXX hex=YYYY]" + val sizeStr = size.toString() + val hexLen = if (full) size else min(64, size) + val len = 12 + sizeStr.length + hexLen * 2 + return with(StringBuilder(len)) { + append("[size=") + append(sizeStr) + append(" hex=") + for (i in 0 until hexLen) { + val b = this@ByteString[i].toInt() + append(HEX_DIGITS[(b ushr 4) and 0xf]) + append(HEX_DIGITS[b and 0xf]) + } + if (!full && size > 64) { + append('…') + } + append(']') + }.toString() + } + + /** + * Returns a string representation of this byte string. + * This call is equivalent to `toString(false)`. + */ + override fun toString(): String = toString(false) + + internal fun getByteArray(): ByteArray = data +} + +/** + * Returns the range of valid byte indices for this byte string. + */ +public fun ByteString.indices(): IntRange = 0 until size + +/** + * Returns the index within this byte string of the first occurrence of the specified [byte], + * starting from the specified [startIndex]. + * + * Behavior of this method is compatible with [CharSequence.indexOf]. + * + * @param byte the value to search for. + * @param startIndex the index (inclusive) starting from which the [byte] should be searched. + */ +public fun ByteString.indexOf(byte: Byte, startIndex: Int = 0): Int { + for (i in max(startIndex, 0) until size) { + if (this[i] == byte) { + return i + } + } + return -1 +} + +/** + * Returns the index within this byte string of the first occurrence of the specified [byteString], + * starting from the specified [startIndex]. + * + * Behavior of this method is compatible with [CharSequence.indexOf]. + * + * @param byteString the value to search for. + * @param startIndex the index (inclusive) starting from which the [byteString] should be searched. + */ +public fun ByteString.indexOf(byteString: ByteString, startIndex: Int = 0): Int { + if (byteString.isEmpty()) return max(min(startIndex, size), 0) + for (i in max(startIndex, 0)..size - byteString.size) { + if (this[i] == byteString[0] && rangeEquals(i, byteString)) { + return i + } + } + return -1 +} + +/** + * Returns the index within this byte string of the first occurrence of the specified [byteArray], + * starting from the specified [startIndex]. + * + * Behavior of this method is compatible with [CharSequence.indexOf]. + * + * @param byteArray the value to search for. + * @param startIndex the index (inclusive) starting from which the [byteArray] should be searched. + */ +public fun ByteString.indexOf(byteArray: ByteArray, startIndex: Int = 0): Int { + if (byteArray.isEmpty()) return max(min(startIndex, size), 0) + for (i in max(0, startIndex)..size - byteArray.size) { + if (this[i] == byteArray[0] && rangeEquals(i, byteArray)) { + return i + } + } + return -1 +} + +/** + * Returns the index within this char sequence of the last occurrence of the specified [byte], + * starting from the specified [startIndex]. + * + * Behavior of this method is compatible with [CharSequence.lastIndexOf]. + * + * @param byte the value to search for. + * @param startIndex the index (inclusive) starting from which the [byte] should be searched. + */ +public fun ByteString.lastIndexOf(byte: Byte, startIndex: Int = 0): Int { + for (i in size - 1 downTo max(0, startIndex)) { + if (this[i] == byte) { + return i + } + } + return -1 +} + +/** + * Returns the index within this char sequence of the last occurrence of the specified [byteString], + * starting from the specified [startIndex]. + * + * Behavior of this method is compatible with [CharSequence.lastIndexOf]. + * + * @param byteString the value to search for. + * @param startIndex the index (inclusive) starting from which the [byteString] should be searched. + */ +public fun ByteString.lastIndexOf(byteString: ByteString, startIndex: Int = 0): Int { + if (byteString.isEmpty()) return size + for (idx in (size - byteString.size) downTo max(0, startIndex)) { + if (rangeEquals(idx, byteString, 0)) { + return idx + } + } + return -1 +} + +/** + * Returns the index within this char sequence of the last occurrence of the specified [byteArray], + * starting from the specified [startIndex]. + * + * Behavior of this method is compatible with [CharSequence.lastIndexOf]. + * + * @param byteArray the value to search for. + * @param startIndex the index (inclusive) starting from which the [byteArray] should be searched. + */ +public fun ByteString.lastIndexOf(byteArray: ByteArray, startIndex: Int = 0): Int { + if (byteArray.isEmpty()) return size + for (idx in (size - byteArray.size) downTo max(0, startIndex)) { + if (rangeEquals(idx, byteArray, 0)) { + return idx + } + } + return -1 +} + +/** + * Returns true if this byte string starts with the prefix specified by the [byteArray]. + * + * Behavior of this method is compatible with [CharSequence.startsWith]. + * + * @param byteArray the prefix to check for. + */ +public fun ByteString.startsWith(byteArray: ByteArray): Boolean = when { + byteArray.size > size -> false + else -> rangeEquals(0, byteArray) +} + +/** + * Returns true if this byte string starts with the prefix specified by the [byteString]. + * + * Behavior of this method is compatible with [CharSequence.startsWith]. + * + * @param byteString the prefix to check for. + */ +public fun ByteString.startsWith(byteString: ByteString): Boolean = when { + byteString.size > size -> false + byteString.size == size -> equals(byteString) + else -> rangeEquals(0, byteString) +} + +/** + * Returns true if this byte string ends with the suffix specified by the [byteArray]. + * + * Behavior of this method is compatible with [CharSequence.endsWith]. + * + * @param byteArray the suffix to check for. + */ +public fun ByteString.endsWith(byteArray: ByteArray): Boolean = when { + byteArray.size > size -> false + else -> rangeEquals(size - byteArray.size, byteArray) +} + +/** + * Returns true if this byte string ends with the suffix specified by the [byteString]. + * + * Behavior of this method is compatible with [CharSequence.endsWith]. + * + * @param byteString the suffix to check for. + */ +public fun ByteString.endsWith(byteString: ByteString): Boolean = when { + byteString.size > size -> false + byteString.size == size -> equals(byteString) + else -> rangeEquals(size - byteString.size, byteString) +} + +private fun ByteString.rangeEquals( + offset: Int, other: ByteString, otherOffset: Int = 0, + byteCount: Int = other.size - otherOffset +): Boolean { + for (i in 0 until byteCount) { + if (this[offset + i] != other[otherOffset + i]) { + return false + } + } + return true +} + +private fun ByteString.rangeEquals( + offset: Int, other: ByteArray, otherOffset: Int = 0, + byteCount: Int = other.size - otherOffset +): Boolean { + for (i in 0 until byteCount) { + if (this[offset + i] != other[otherOffset + i]) { + return false + } + } + return true +} + +/** + * Returns `true` if this byte string is empty. + */ +public fun ByteString.isEmpty(): Boolean = size == 0 diff --git a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt new file mode 100644 index 000000000..7bbd26fa4 --- /dev/null +++ b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt @@ -0,0 +1,983 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. + */ + +package kotlinx.io.bytestring + +import kotlin.test.* + +fun hexChar2Int(char: Char): Int = when (char) { + in '0'..'9' -> char.code - '0'.code + in 'a'..'f' -> char.code - 'a'.code + 10 + in 'A'..'F' -> char.code - 'A'.code + 10 + else -> throw IllegalArgumentException("Not a hex digit: $char") +} + +fun String.decodeHex(): ByteString { + val hex = this + if (hex.isEmpty()) { + return ByteString.EMPTY + } + val data = if (hex.length % 2 == 1) { + ByteArray((hex.length + 1) / 2) + } else { + ByteArray(hex.length / 2) + } + + var idx = 0 + var builderIdx = 0 + if (hex.length % 2 == 1) { + data[builderIdx++] = hexChar2Int(hex[idx++]).toByte() + } + while (idx < hex.length) { + var b = hexChar2Int(hex[idx++]) shl 4 + b = b or hexChar2Int(hex[idx++]) + data[builderIdx++] = b.toByte() + } + return ByteString(data) +} + +class ByteStringTest { + @Test + fun get() { + val actual = ByteString("abc".encodeToByteArray()) + assertEquals(3, actual.size) + assertEquals(actual[0], 'a'.code.toByte()) + assertEquals(actual[1], 'b'.code.toByte()) + assertEquals(actual[2], 'c'.code.toByte()) + } + + @Test + fun getWithInvalidIndex() { + val str = ByteString(0, 1, 2) + assertFailsWith { str[-1] } + assertFailsWith { str[3] } + } + + @Test + fun equalsAndHashCode() { + with(ByteString(1, 2, 3)) { checkEqualsAndHashCodeAreSame(this, this) } + checkEqualsAndHashCodeAreSame(ByteString.EMPTY, ByteString(byteArrayOf())) + checkEqualsAndHashCodeAreSame(ByteString(1, 2, 3), ByteString(1, 2, 3)) + + assertNotEquals(ByteString(1, 2, 3), ByteString(3, 2, 1)) + assertNotEquals(ByteString(1, 2, 3).hashCode(), ByteString(3, 2, 1).hashCode()) + } + + private fun checkEqualsAndHashCodeAreSame(first: ByteString, second: ByteString) { + assertEquals(first, second) + assertEquals(first.hashCode(), second.hashCode()) + } + + @Test + fun toByteArray() { + val str = ByteString(1, 2, 3, 4, 5, 6) + assertContentEquals(byteArrayOf(1, 2, 3, 4, 5, 6), str.toByteArray()) + assertContentEquals(byteArrayOf(), str.toByteArray(0, 0)) + assertContentEquals(byteArrayOf(1, 2, 3), str.toByteArray(endIndex = 3)) + assertContentEquals(byteArrayOf(4, 5, 6), str.toByteArray(startIndex = 3)) + assertContentEquals(byteArrayOf(2, 3, 4), str.toByteArray(startIndex = 1, endIndex = 4)) + } + + @Test + fun toByteArrayWithInvalidIndex() { + val str = ByteString(1, 2, 3) + assertFailsWith { str.toByteArray(-1, 1) } + assertFailsWith { str.toByteArray(1, 4) } + assertFailsWith { str.toByteArray(-1, 4) } + assertFailsWith { str.toByteArray(2, 0) } + } + + @Test + fun copyTo() { + val str = ByteString(1, 2, 3, 4, 5, 6) + val dest = ByteArray(10) + + str.copyInto(dest) + assertContentEquals(byteArrayOf(1, 2, 3, 4, 5, 6, 0, 0, 0, 0), dest) + + dest.fill(0) + str.copyInto(dest, 2) + assertContentEquals(byteArrayOf(0, 0, 1, 2, 3, 4, 5, 6, 0, 0), dest) + + dest.fill(0) + str.copyInto(dest, destinationOffset = 0, startIndex = 1) + assertContentEquals(byteArrayOf(2, 3, 4, 5, 6, 0, 0, 0, 0, 0), dest) + + dest.fill(0) + str.copyInto(dest, destinationOffset = 0, endIndex = 3) + assertContentEquals(byteArrayOf(1, 2, 3, 0, 0, 0, 0, 0, 0, 0), dest) + + dest.fill(0) + str.copyInto(dest, destinationOffset = 0, startIndex = 3, endIndex = 5) + assertContentEquals(byteArrayOf(4, 5, 0, 0, 0, 0, 0, 0, 0, 0), dest) + + dest.fill(0) + str.copyInto(dest, destinationOffset = 5, endIndex = 5) + assertContentEquals(byteArrayOf(0, 0, 0, 0, 0, 1, 2, 3, 4, 5), dest) + + dest.fill(0) + str.copyInto(dest, startIndex = 3, endIndex = 3) + assertContentEquals(ByteArray(10), dest) + } + + @Test + fun copyToWithInvalidArguments() { + val str = ByteString(1, 2, 3) + val dest = ByteArray(10) + + assertFailsWith { str.copyInto(dest, 0, startIndex = 1, endIndex = 0) } + assertFailsWith { str.copyInto(dest, 9) } + assertFailsWith { str.copyInto(dest, -1) } + assertFailsWith { str.copyInto(dest, 0, startIndex = -1) } + assertFailsWith { str.copyInto(dest, 0, endIndex = 5) } + } + + @Test + fun substring() { + val str = ByteString(1, 2, 3, 4, 5) + + assertEquals(ByteString.EMPTY, str.substring(0, 0)) + assertEquals(ByteString(1, 2, 3), str.substring(startIndex = 0, endIndex = 3)) + assertEquals(ByteString(3, 4, 5), str.substring(startIndex = 2)) + assertEquals(ByteString(2, 3, 4), str.substring(startIndex = 1, endIndex = 4)) + } + + @Test + fun substringWithInvalidArgs() { + val str = ByteString(1, 2, 3) + + assertFailsWith { str.substring(2, 1) } + assertFailsWith { str.substring(-1) } + assertFailsWith { str.substring(0, 10) } + assertFailsWith { str.substring(-10, 10) } + } + + @Test + fun compareTo() { + assertEquals(0, ByteString.EMPTY.compareTo(ByteString.EMPTY)) + assertEquals(0, ByteString(1, 2, 3).compareTo(ByteString(1, 2, 3))) + assertEquals(-1, ByteString(1, 2).compareTo(ByteString(1, 2, 3))) + assertEquals(-1, ByteString(0, 1, 2).compareTo(ByteString(0, 1, 3))) + assertEquals(1, ByteString(1, 2, 3).compareTo(ByteString(1, 2))) + assertEquals(1, ByteString(1, 2, 3).compareTo(ByteString(0, 1, 2))) + assertEquals(1, ByteString(0xFF.toByte()).compareTo(ByteString(0))) + assertEquals(-1, ByteString(1).compareTo(ByteString(0x81.toByte()))) + } + + @Test + fun size() { + assertEquals(0, ByteString.EMPTY.size) + assertEquals(1, ByteString(0).size) + assertEquals(12345, ByteString(ByteArray(12345)).size) + } + + @Test + fun indices() { + assertEquals(0 until 10, ByteString(ByteArray(10)).indices()) + assertTrue(ByteString.EMPTY.indices().isEmpty()) + } + + @Test + fun isEmpty() { + assertTrue(ByteString.EMPTY.isEmpty()) + assertTrue(ByteString(byteArrayOf()).isEmpty()) + assertFalse(ByteString(byteArrayOf(0)).isEmpty()) + } + + @Test + fun indexOfByte() { + val str = ByteString(1, 2, 3, 4) + for (idx in str.indices()) { + assertEquals(idx, str.indexOf(str[idx])) + } + + assertEquals(-1, str.indexOf(0)) + assertEquals(-1, str.indexOf(1, 1)) + assertEquals(-1, str.indexOf(4, 4)) + assertEquals(0, str.indexOf(1, -10)) + assertEquals(1, ByteString(0, 1, 1, 1).indexOf(1)) + + assertEquals(-1, ByteString.EMPTY.indexOf(0)) + assertEquals(-1, ByteString.EMPTY.indexOf(0, 100500)) + assertEquals(-1, str.indexOf(1, 100500)) + } + + @Test + fun indexOfByteArray() { + val str = ByteString(1, 2, 3, 4, 5) + + assertEquals(0, str.indexOf(byteArrayOf(1, 2, 3, 4, 5))) + assertEquals(0, str.indexOf(byteArrayOf(1, 2, 3))) + assertEquals(0, str.indexOf(byteArrayOf(1))) + + assertEquals(2, str.indexOf(byteArrayOf(3, 4, 5))) + assertEquals(-1, str.indexOf(byteArrayOf(3, 4, 5, 6))) + assertEquals(0, str.indexOf(byteArrayOf())) + assertEquals(-1, str.indexOf(byteArrayOf(-1))) + + assertEquals(-1, str.indexOf(byteArrayOf(1, 2, 3, 4, 5), 1)) + assertEquals(3, str.indexOf(byteArrayOf(4, 5), 3)) + + assertEquals(0, str.indexOf(byteArrayOf(1, 2, 3), -1000)) + assertEquals(1, str.indexOf(byteArrayOf(2, 3), -1)) + + assertEquals(1, ByteString(0, 1, 0, 1, 0, 1).indexOf(byteArrayOf(1, 0))) + + assertEquals(0, ByteString.EMPTY.indexOf(byteArrayOf())) + assertEquals(0, ByteString.EMPTY.indexOf(byteArrayOf(), -100500)) + assertEquals(0, ByteString.EMPTY.indexOf(byteArrayOf(), 100500)) + assertEquals(-1, str.indexOf(byteArrayOf(1, 2, 3), 100500)) + assertEquals(-1, ByteString.EMPTY.indexOf(byteArrayOf(1, 2, 3, 4, 5))) + } + + @Test + fun indexOfByteString() { + val str = ByteString(1, 2, 3, 4, 5) + + assertEquals(0, str.indexOf(ByteString(1, 2, 3, 4, 5))) + assertEquals(0, str.indexOf(ByteString(1, 2, 3))) + assertEquals(0, str.indexOf(ByteString(1))) + + assertEquals(2, str.indexOf(ByteString(3, 4, 5))) + assertEquals(-1, str.indexOf(ByteString(3, 4, 5, 6))) + assertEquals(0, str.indexOf(ByteString.EMPTY)) + assertEquals(-1, str.indexOf(ByteString(-1))) + + assertEquals(-1, str.indexOf(ByteString(1, 2, 3, 4, 5), 1)) + assertEquals(3, str.indexOf(ByteString(4, 5), 3)) + + assertEquals(0, str.indexOf(ByteString(1, 2, 3), -1000)) + assertEquals(1, str.indexOf(ByteString(2, 3), -1)) + + assertEquals(1, ByteString(0, 1, 0, 1, 0, 1).indexOf(ByteString(1, 0))) + + assertEquals(0, ByteString.EMPTY.indexOf(ByteString.EMPTY)) + assertEquals(0, ByteString.EMPTY.indexOf(ByteString.EMPTY, -100500)) + assertEquals(0, ByteString.EMPTY.indexOf(ByteString.EMPTY, 100500)) + assertEquals(-1, str.indexOf(ByteString(1, 2, 3), 100500)) + assertEquals(-1, ByteString.EMPTY.indexOf(ByteString(1, 2, 3, 4, 5))) + } + + @Test + fun lastIndexOfByte() { + val str = ByteString(1, 2, 3, 4) + for (idx in str.indices()) { + assertEquals(idx, str.lastIndexOf(str[idx])) + } + + assertEquals(-1, str.lastIndexOf(0)) + assertEquals(-1, str.lastIndexOf(1, 1)) + assertEquals(-1, str.lastIndexOf(4, 4)) + assertEquals(0, str.lastIndexOf(1, -10)) + assertEquals(3, ByteString(0, 1, 1, 1, 0).lastIndexOf(1)) + + assertEquals(-1, ByteString.EMPTY.lastIndexOf(0)) + assertEquals(-1, ByteString.EMPTY.lastIndexOf(0, 100500)) + assertEquals(-1, str.lastIndexOf(1, 1005000)) + } + + @Test + fun lastIndexOfByteArray() { + val str = ByteString(1, 2, 3, 4, 5) + + assertEquals(0, str.lastIndexOf(byteArrayOf(1, 2, 3, 4, 5))) + assertEquals(0, str.lastIndexOf(byteArrayOf(1, 2, 3))) + assertEquals(-1, str.lastIndexOf(byteArrayOf(0, 1, 2))) + assertEquals(2, str.lastIndexOf(byteArrayOf(3, 4, 5))) + + assertEquals(-1, str.lastIndexOf(byteArrayOf(1, 2, 3), 1)) + assertEquals(1, str.lastIndexOf(byteArrayOf(2, 3, 4), 1)) + + assertEquals(str.size, str.lastIndexOf(byteArrayOf())) + assertEquals(str.size, str.lastIndexOf(byteArrayOf())) + + assertEquals(2, str.lastIndexOf(byteArrayOf(3, 4), -1000)) + assertEquals(0, str.lastIndexOf(byteArrayOf(1), -1)) + + assertEquals(4, ByteString(1, 1, 1, 1, 1).lastIndexOf(byteArrayOf(1))) + assertEquals(3, ByteString(0, 1, 0, 1, 0).lastIndexOf(byteArrayOf(1, 0))) + + assertEquals(0, ByteString.EMPTY.lastIndexOf(byteArrayOf())) + assertEquals(0, ByteString.EMPTY.lastIndexOf(byteArrayOf(), -100500)) + assertEquals(0, ByteString.EMPTY.lastIndexOf(byteArrayOf(), 100500)) + assertEquals(-1, str.lastIndexOf(byteArrayOf(1, 2, 3), 100500)) + assertEquals(-1, ByteString.EMPTY.lastIndexOf(byteArrayOf(1, 2, 3))) + } + + @Test + fun lastIndexOfByteString() { + val str = ByteString(1, 2, 3, 4, 5) + + assertEquals(0, str.lastIndexOf(ByteString(1, 2, 3, 4, 5))) + assertEquals(0, str.lastIndexOf(ByteString(1, 2, 3))) + assertEquals(-1, str.lastIndexOf(ByteString(0, 1, 2))) + assertEquals(2, str.lastIndexOf(ByteString(3, 4, 5))) + + assertEquals(-1, str.lastIndexOf(ByteString(1, 2, 3), 1)) + assertEquals(1, str.lastIndexOf(ByteString(2, 3, 4), 1)) + + assertEquals(str.size, str.lastIndexOf(ByteString.EMPTY)) + assertEquals(str.size, str.lastIndexOf(ByteString.EMPTY)) + + assertEquals(2, str.lastIndexOf(ByteString(3, 4), -1000)) + assertEquals(0, str.lastIndexOf(ByteString(1), -1)) + + assertEquals(4, ByteString(1, 1, 1, 1, 1).lastIndexOf(ByteString(1))) + assertEquals(3, ByteString(0, 1, 0, 1, 0).lastIndexOf(ByteString(1, 0))) + + assertEquals(0, ByteString.EMPTY.lastIndexOf(ByteString.EMPTY)) + assertEquals(0, ByteString.EMPTY.lastIndexOf(ByteString.EMPTY, -100500)) + assertEquals(0, ByteString.EMPTY.lastIndexOf(ByteString.EMPTY, 100500)) + assertEquals(-1, str.lastIndexOf(ByteString(1, 2, 3), 100500)) + assertEquals(-1, ByteString.EMPTY.lastIndexOf(ByteString(1, 2, 3))) + } + + @Test + fun startsWithByteArray() { + val str = ByteString(1, 2, 3, 4, 5) + + assertTrue(str.startsWith(byteArrayOf(1, 2, 3, 4, 5))) + assertTrue(str.startsWith(byteArrayOf(1, 2, 3))) + + assertTrue(str.startsWith(byteArrayOf())) + + assertFalse(str.startsWith(byteArrayOf(0, 1, 2, 3))) + assertFalse(str.startsWith(byteArrayOf(2, 3, 4))) + assertFalse(str.startsWith(byteArrayOf(1, 2, 3, 4, 5, 6))) + + assertTrue(ByteString.EMPTY.startsWith(byteArrayOf())) + } + + @Test + fun startWithByteString() { + val str = ByteString(1, 2, 3, 4, 5) + + assertTrue(str.startsWith(ByteString(1, 2, 3, 4, 5))) + assertTrue(str.startsWith(ByteString(1, 2, 3))) + + assertTrue(str.startsWith(ByteString.EMPTY)) + + assertFalse(str.startsWith(ByteString(0, 1, 2, 3))) + assertFalse(str.startsWith(ByteString(2, 3, 4))) + assertFalse(str.startsWith(ByteString(1, 2, 3, 4, 5, 6))) + + assertTrue(ByteString.EMPTY.startsWith(ByteString.EMPTY)) + } + + @Test + fun endsWithByteArray() { + val str = ByteString(1, 2, 3, 4, 5) + + assertTrue(str.endsWith(byteArrayOf(1, 2, 3, 4, 5))) + assertTrue(str.endsWith(byteArrayOf(3, 4, 5))) + + assertTrue(str.endsWith(byteArrayOf())) + + assertFalse(str.endsWith(byteArrayOf(3, 4, 5, 6))) + assertFalse(str.endsWith(byteArrayOf(0, 1, 2, 3, 4, 5))) + assertFalse(str.endsWith(byteArrayOf(2, 3, 4))) + + assertTrue(ByteString.EMPTY.endsWith(byteArrayOf())) + } + + @Test + fun endsWithByteString() { + val str = ByteString(1, 2, 3, 4, 5) + + assertTrue(str.endsWith(ByteString(1, 2, 3, 4, 5))) + assertTrue(str.endsWith(ByteString(3, 4, 5))) + + assertTrue(str.endsWith(ByteString.EMPTY)) + + assertFalse(str.endsWith(ByteString(3, 4, 5, 6))) + assertFalse(str.endsWith(ByteString(0, 1, 2, 3, 4, 5))) + assertFalse(str.endsWith(ByteString(2, 3, 4))) + + assertTrue(ByteString.EMPTY.endsWith(ByteString.EMPTY)) + } + + @Test + fun testToString() { + assertEquals("[size=0]", ByteString.EMPTY.toString()) + assertEquals("[size=1 hex=00]", ByteString(0).toString()) + assertEquals("[size=16 hex=000102030405060708090A0B0C0D0E0F]", + ByteString(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15).toString()) + assertEquals("[size=64 hex=0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000]", + ByteString(ByteArray(64)).toString()) + assertEquals("[size=65 hex=0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000…]", + ByteString(ByteArray(65)).toString()) + assertEquals("[size=65 hex=0000000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000000000]", + ByteString(ByteArray(65)).toString(true)) + } + +// @Test +// fun startsWithByteString() { +// val byteString = "112233".decodeHex() +// assertTrue(byteString.startsWith("".decodeHex())) +// assertTrue(byteString.startsWith("11".decodeHex())) +// assertTrue(byteString.startsWith("1122".decodeHex())) +// assertTrue(byteString.startsWith("112233".decodeHex())) +// assertFalse(byteString.startsWith("2233".decodeHex())) +// assertFalse(byteString.startsWith("11223344".decodeHex())) +// assertFalse(byteString.startsWith("112244".decodeHex())) +// } +// +// @Test +// fun endsWithByteString() { +// val byteString = "112233".decodeHex() +// assertTrue(byteString.endsWith("".decodeHex())) +// assertTrue(byteString.endsWith("33".decodeHex())) +// assertTrue(byteString.endsWith("2233".decodeHex())) +// assertTrue(byteString.endsWith("112233".decodeHex())) +// assertFalse(byteString.endsWith("1122".decodeHex())) +// assertFalse(byteString.endsWith("00112233".decodeHex())) +// assertFalse(byteString.endsWith("002233".decodeHex())) +// } +// +// @Test +// fun startsWithByteArray() { +// val byteString = "112233".decodeHex() +// assertTrue(byteString.startsWith("".decodeHex().toByteArray())) +// assertTrue(byteString.startsWith("11".decodeHex().toByteArray())) +// assertTrue(byteString.startsWith("1122".decodeHex().toByteArray())) +// assertTrue(byteString.startsWith("112233".decodeHex().toByteArray())) +// assertFalse(byteString.startsWith("2233".decodeHex().toByteArray())) +// assertFalse(byteString.startsWith("11223344".decodeHex().toByteArray())) +// assertFalse(byteString.startsWith("112244".decodeHex().toByteArray())) +// } +// +// @Test +// fun endsWithByteArray() { +// val byteString = "112233".decodeHex() +// assertTrue(byteString.endsWith("".decodeHex().toByteArray())) +// assertTrue(byteString.endsWith("33".decodeHex().toByteArray())) +// assertTrue(byteString.endsWith("2233".decodeHex().toByteArray())) +// assertTrue(byteString.endsWith("112233".decodeHex().toByteArray())) +// assertFalse(byteString.endsWith("1122".decodeHex().toByteArray())) +// assertFalse(byteString.endsWith("00112233".decodeHex().toByteArray())) +// assertFalse(byteString.endsWith("002233".decodeHex().toByteArray())) +// } +// +// @Test +// fun indexOfByteString() { +// val byteString = "112233".decodeHex() +// assertEquals(0, byteString.indexOf("112233".decodeHex()).toLong()) +// assertEquals(0, byteString.indexOf("1122".decodeHex()).toLong()) +// assertEquals(0, byteString.indexOf("11".decodeHex()).toLong()) +// assertEquals(0, byteString.indexOf("11".decodeHex(), 0).toLong()) +// assertEquals(0, byteString.indexOf("".decodeHex()).toLong()) +// assertEquals(0, byteString.indexOf("".decodeHex(), 0).toLong()) +// assertEquals(1, byteString.indexOf("2233".decodeHex()).toLong()) +// assertEquals(1, byteString.indexOf("22".decodeHex()).toLong()) +// assertEquals(1, byteString.indexOf("22".decodeHex(), 1).toLong()) +// assertEquals(1, byteString.indexOf("".decodeHex(), 1).toLong()) +// assertEquals(2, byteString.indexOf("33".decodeHex()).toLong()) +// assertEquals(2, byteString.indexOf("33".decodeHex(), 2).toLong()) +// assertEquals(2, byteString.indexOf("".decodeHex(), 2).toLong()) +// assertEquals(3, byteString.indexOf("".decodeHex(), 3).toLong()) +// assertEquals(-1, byteString.indexOf("112233".decodeHex(), 1).toLong()) +// assertEquals(-1, byteString.indexOf("44".decodeHex()).toLong()) +// assertEquals(-1, byteString.indexOf("11223344".decodeHex()).toLong()) +// assertEquals(-1, byteString.indexOf("112244".decodeHex()).toLong()) +// assertEquals(-1, byteString.indexOf("112233".decodeHex(), 1).toLong()) +// assertEquals(-1, byteString.indexOf("2233".decodeHex(), 2).toLong()) +// assertEquals(-1, byteString.indexOf("33".decodeHex(), 3).toLong()) +// assertEquals(3, byteString.indexOf("".decodeHex(), 4).toLong()) +// } +// +// @Test +// fun indexOfWithOffset() { +// val byteString = "112233112233".decodeHex() +// assertEquals(0, byteString.indexOf("112233".decodeHex(), -1).toLong()) +// assertEquals(0, byteString.indexOf("112233".decodeHex(), 0).toLong()) +// assertEquals(0, byteString.indexOf("112233".decodeHex()).toLong()) +// assertEquals(3, byteString.indexOf("112233".decodeHex(), 1).toLong()) +// assertEquals(3, byteString.indexOf("112233".decodeHex(), 2).toLong()) +// assertEquals(3, byteString.indexOf("112233".decodeHex(), 3).toLong()) +// assertEquals(-1, byteString.indexOf("112233".decodeHex(), 4).toLong()) +// } +// +// @Test +// fun indexOfByteArray() { +// val byteString = "112233".decodeHex() +// assertEquals(0, byteString.indexOf("112233".decodeHex().toByteArray())) +// assertEquals(1, byteString.indexOf("2233".decodeHex().toByteArray())) +// assertEquals(2, byteString.indexOf("33".decodeHex().toByteArray())) +// assertEquals(-1, byteString.indexOf("112244".decodeHex().toByteArray())) +// } +// +// @Test +// fun lastIndexOfByteString() { +// val byteString = "112233".decodeHex() +// assertEquals(0, byteString.lastIndexOf("112233".decodeHex()).toLong()) +// assertEquals(0, byteString.lastIndexOf("1122".decodeHex()).toLong()) +// assertEquals(0, byteString.lastIndexOf("11".decodeHex()).toLong()) +// assertEquals(0, byteString.lastIndexOf("11".decodeHex(), 3).toLong()) +// assertEquals(0, byteString.lastIndexOf("11".decodeHex(), 0).toLong()) +// assertEquals(0, byteString.lastIndexOf("".decodeHex(), 0).toLong()) +// assertEquals(1, byteString.lastIndexOf("2233".decodeHex()).toLong()) +// assertEquals(1, byteString.lastIndexOf("22".decodeHex()).toLong()) +// assertEquals(1, byteString.lastIndexOf("22".decodeHex(), 3).toLong()) +// assertEquals(1, byteString.lastIndexOf("22".decodeHex(), 1).toLong()) +// assertEquals(1, byteString.lastIndexOf("".decodeHex(), 1).toLong()) +// assertEquals(2, byteString.lastIndexOf("33".decodeHex()).toLong()) +// assertEquals(2, byteString.lastIndexOf("33".decodeHex(), 3).toLong()) +// assertEquals(2, byteString.lastIndexOf("33".decodeHex(), 2).toLong()) +// assertEquals(2, byteString.lastIndexOf("".decodeHex(), 2).toLong()) +// assertEquals(3, byteString.lastIndexOf("".decodeHex(), 3).toLong()) +// assertEquals(3, byteString.lastIndexOf("".decodeHex()).toLong()) +// assertEquals(-1, byteString.lastIndexOf("112233".decodeHex(), -1).toLong()) +// assertEquals(-1, byteString.lastIndexOf("112233".decodeHex(), -2).toLong()) +// assertEquals(-1, byteString.lastIndexOf("44".decodeHex()).toLong()) +// assertEquals(-1, byteString.lastIndexOf("11223344".decodeHex()).toLong()) +// assertEquals(-1, byteString.lastIndexOf("112244".decodeHex()).toLong()) +// assertEquals(-1, byteString.lastIndexOf("2233".decodeHex(), 0).toLong()) +// assertEquals(-1, byteString.lastIndexOf("33".decodeHex(), 1).toLong()) +// assertEquals(-1, byteString.lastIndexOf("".decodeHex(), -1).toLong()) +// } +// +// @Test +// fun lastIndexOfByteArray() { +// val byteString = "112233".decodeHex() +// assertEquals(0, byteString.lastIndexOf("112233".decodeHex().toByteArray())) +// assertEquals(1, byteString.lastIndexOf("2233".decodeHex().toByteArray())) +// assertEquals(2, byteString.lastIndexOf("33".decodeHex().toByteArray())) +// assertEquals(3, byteString.lastIndexOf("".decodeHex().toByteArray())) +// } +// +// @Test +// fun equalsTest() { +// val byteString = "000102".decodeHex() +// assertEquals(byteString, byteString) +// assertEquals(byteString, "000102".decodeHex()) +// assertNotEquals(byteString, Any()) +// assertNotEquals(byteString, "000201".decodeHex()) +// } +// +// @Ignore +// @Test +// fun equalsEmptyTest() { +// // assertEquals("".decodeHex(), ByteString.EMPTY) +// // assertEquals("".decodeHex(), ByteString.of()) +// // assertEquals(ByteString.EMPTY, factory.decodeHex("")) +// // assertEquals(ByteString.of(), factory.decodeHex("")) +// } +// +// private val bronzeHorseman = "На берегу пустынных волн" +// +// /* +// @Test fun utf8() { +// val byteString = factory.encodeUtf8(bronzeHorseman) +// assertEquals(byteString.toByteArray().toList(), bronzeHorseman.commonAsUtf8ToByteArray().toList()) +// assertTrue(byteString == ByteString.of(*bronzeHorseman.commonAsUtf8ToByteArray())) +// assertEquals( +// byteString, +// ( +// "d09dd0b020d0b1d0b5d180d0b5d0b3d18320d0bfd183d181" + +// "d182d18bd0bdd0bdd18bd18520d0b2d0bed0bbd0bd" +// ).decodeHex(), +// ) +// assertEquals(byteString.utf8(), bronzeHorseman) +// } +// +// */ +// +// @Test +// fun testHashCode() { +// val byteString = "0102".decodeHex() +// assertEquals(byteString.hashCode().toLong(), byteString.hashCode().toLong()) +// assertEquals(byteString.hashCode().toLong(), "0102".decodeHex().hashCode().toLong()) +// } +// +// /* +// @Test fun toAsciiLowerCaseNoUppercase() { +// val s = "a1_+".encodeUtf8() +// assertEquals(s, s.toAsciiLowercase()) +// if (factory === ByteStringFactory.BYTE_STRING) { +// assertSame(s, s.toAsciiLowercase()) +// } +// } +// +// @Test fun toAsciiAllUppercase() { +// assertEquals("ab".encodeUtf8(), factory.encodeUtf8("AB").toAsciiLowercase()) +// } +// +// @Test fun toAsciiStartsLowercaseEndsUppercase() { +// assertEquals("abcd".encodeUtf8(), factory.encodeUtf8("abCD").toAsciiLowercase()) +// } +// +// @Test fun toAsciiStartsUppercaseEndsLowercase() { +// assertEquals("ABCD".encodeUtf8(), factory.encodeUtf8("ABcd").toAsciiUppercase()) +// } +// */ +// +// @Test +// fun substring() { +// val byteString = ByteString("Hello, World!".encodeToByteArray()) +// +// assertEquals(byteString.substring(0), byteString) +// assertEquals(byteString.substring(0, 5), ByteString("Hello".encodeToByteArray())) +// assertEquals(byteString.substring(7), ByteString("World!".encodeToByteArray())) +// assertEquals(byteString.substring(6, 6), ByteString("".encodeToByteArray())) +// } +// +// @Test +// fun substringWithInvalidBounds() { +// val byteString = ByteString("Hello, World!".encodeToByteArray()) +// +// assertFailsWith { +// byteString.substring(-1) +// } +// +// assertFailsWith { +// byteString.substring(0, 14) +// } +// +// assertFailsWith { +// byteString.substring(8, 7) +// } +// } +// +// /* +// @Test fun encodeBase64() { +// assertEquals("", factory.encodeUtf8("").base64()) +// assertEquals("AA==", factory.encodeUtf8("\u0000").base64()) +// assertEquals("AAA=", factory.encodeUtf8("\u0000\u0000").base64()) +// assertEquals("AAAA", factory.encodeUtf8("\u0000\u0000\u0000").base64()) +// assertEquals( +// "SG93IG1hbnkgbGluZXMgb2YgY29kZSBhcmUgdGhlcmU/ICdib3V0IDIgbWlsbGlvbi4=", +// factory.encodeUtf8("How many lines of code are there? 'bout 2 million.").base64(), +// ) +// } +// +// @Test fun encodeBase64Url() { +// assertEquals("", factory.encodeUtf8("").base64Url()) +// assertEquals("AA==", factory.encodeUtf8("\u0000").base64Url()) +// assertEquals("AAA=", factory.encodeUtf8("\u0000\u0000").base64Url()) +// assertEquals("AAAA", factory.encodeUtf8("\u0000\u0000\u0000").base64Url()) +// assertEquals( +// "SG93IG1hbnkgbGluZXMgb2YgY29kZSBhcmUgdGhlcmU_ICdib3V0IDIgbWlsbGlvbi4=", +// factory.encodeUtf8("How many lines of code are there? 'bout 2 million.").base64Url(), +// ) +// } +// +// @Test fun ignoreUnnecessaryPadding() { +// assertEquals("", "====".decodeBase64()!!.utf8()) +// assertEquals("\u0000\u0000\u0000", "AAAA====".decodeBase64()!!.utf8()) +// } +// +// @Test fun decodeBase64() { +// assertEquals("", "".decodeBase64()!!.utf8()) +// assertEquals(null, "/===".decodeBase64()) // Can't do anything with 6 bits! +// assertEquals("ff".decodeHex(), "//==".decodeBase64()) +// assertEquals("ff".decodeHex(), "__==".decodeBase64()) +// assertEquals("ffff".decodeHex(), "///=".decodeBase64()) +// assertEquals("ffff".decodeHex(), "___=".decodeBase64()) +// assertEquals("ffffff".decodeHex(), "////".decodeBase64()) +// assertEquals("ffffff".decodeHex(), "____".decodeBase64()) +// assertEquals("ffffffffffff".decodeHex(), "////////".decodeBase64()) +// assertEquals("ffffffffffff".decodeHex(), "________".decodeBase64()) +// assertEquals( +// "What's to be scared about? It's just a little hiccup in the power...", +// ( +// "V2hhdCdzIHRvIGJlIHNjYXJlZCBhYm91dD8gSXQncyBqdXN0IGEgbGl0dGxlIGhpY2" + +// "N1cCBpbiB0aGUgcG93ZXIuLi4=" +// ).decodeBase64()!!.utf8(), +// ) +// // Uses two encoding styles. Malformed, but supported as a side-effect. +// assertEquals("ffffff".decodeHex(), "__//".decodeBase64()) +// } +// +// @Test fun decodeBase64WithWhitespace() { +// assertEquals("\u0000\u0000\u0000", " AA AA ".decodeBase64()!!.utf8()) +// assertEquals("\u0000\u0000\u0000", " AA A\r\nA ".decodeBase64()!!.utf8()) +// assertEquals("\u0000\u0000\u0000", "AA AA".decodeBase64()!!.utf8()) +// assertEquals("\u0000\u0000\u0000", " AA AA ".decodeBase64()!!.utf8()) +// assertEquals("\u0000\u0000\u0000", " AA A\r\nA ".decodeBase64()!!.utf8()) +// assertEquals("\u0000\u0000\u0000", "A AAA".decodeBase64()!!.utf8()) +// assertEquals("", " ".decodeBase64()!!.utf8()) +// } +// +// */ +// +// @Test +// fun encodeHex() { +// assertEquals("000102", ByteString(0x0, 0x1, 0x2).hex()) +// } +// +// @Test +// fun decodeHex() { +// val actual = "CAFEBABE".decodeHex() +// val expected = ByteString(-54, -2, -70, -66) +// assertEquals(expected, actual) +// } +// +// @Test +// fun decodeHexOddNumberOfChars() { +// assertFailsWith { +// "aaa".decodeHex() +// } +// } +// +// @Test +// fun decodeHexInvalidChar() { +// assertFailsWith { +// "a\u0000".decodeHex() +// } +// } +// +// @Test +// fun toStringOnEmpty() { +// assertEquals("[size=0]", "".decodeHex().toString()) +// } +// +// @Test +// fun toStringOnShortText() { +// assertEquals( +// "[text=Tyrannosaur]", +// ByteString("Tyrannosaur".encodeToByteArray()).toString(), +// ) +// //assertEquals( +// //"[text=təˈranəˌsôr]", +// //factory.decodeHex("74c999cb8872616ec999cb8c73c3b472").toString(), +// //) +// TODO() +// } +// +// @Test +// fun toStringOnLongTextIsTruncated() { +// /* +// val raw = ( +// "Um, I'll tell you the problem with the scientific power that you're using here, " + +// "it didn't require any discipline to attain it. You read what others had done and you " + +// "took the next step. You didn't earn the knowledge for yourselves, so you don't take any " + +// "responsibility for it. You stood on the shoulders of geniuses to accomplish something " + +// "as fast as you could, and before you even knew what you had, you patented it, and " + +// "packaged it, and slapped it on a plastic lunchbox, and now you're selling it, you wanna " + +// "sell it." +// ) +// +// */ +// //assertEquals( +// // "[size=517 text=Um, I'll tell you the problem with the scientific power that " + +// // "you…]", +// // factory.encodeUtf8(raw).toString(), +// //) +// /* +// val war = ( +// "Սm, I'll 𝓽𝖾ll ᶌօ𝘂 ᴛℎ℮ 𝜚𝕣०bl𝖾m wі𝕥𝒽 𝘵𝘩𝐞 𝓼𝙘𝐢𝔢𝓷𝗍𝜄𝚏𝑖c 𝛠𝝾w𝚎𝑟 𝕥h⍺𝞃 𝛄𝓸𝘂'𝒓𝗲 υ𝖘𝓲𝗇ɡ 𝕙𝚎𝑟e, " + +// "𝛊𝓽 ⅆ𝕚𝐝𝝿'𝗍 𝔯𝙚𝙦ᴜ𝜾𝒓𝘦 𝔞𝘯𝐲 ԁ𝜄𝑠𝚌ι𝘱lι𝒏e 𝑡𝜎 𝕒𝚝𝖙𝓪і𝞹 𝔦𝚝. 𝒀ο𝗎 𝔯𝑒⍺𝖉 w𝐡𝝰𝔱 𝞂𝞽һ𝓮𝓇ƽ հ𝖺𝖉 ⅾ𝛐𝝅ⅇ 𝝰πԁ 𝔂ᴑᴜ 𝓉ﮨ၀𝚔 " + +// "т𝒽𝑒 𝗇𝕖ⅹ𝚝 𝔰𝒕е𝓅. 𝘠ⲟ𝖚 𝖉ⅰԁ𝝕'τ 𝙚𝚊r𝞹 𝘵Ꮒ𝖾 𝝒𝐧هwl𝑒𝖉ƍ𝙚 𝓯૦r 𝔂𝞼𝒖𝕣𝑠𝕖l𝙫𝖊𝓼, 𐑈о y𝘰𝒖 ⅆە𝗇't 𝜏α𝒌𝕖 𝛂𝟉ℽ " + +// "𝐫ⅇ𝗌ⲣ๐ϖ𝖘ꙇᖯ𝓲l𝓲𝒕𝘆 𝐟𝞼𝘳 𝚤𝑡. 𝛶𝛔𝔲 s𝕥σσ𝐝 ﮩ𝕟 𝒕𝗁𝔢 𝘴𝐡𝜎ᴜlⅾ𝓮𝔯𝚜 𝛐𝙛 ᶃ𝚎ᴨᎥս𝚜𝘦𝓈 𝓽𝞸 a𝒄𝚌𝞸mρl𝛊ꜱ𝐡 𝓈𝚘m𝚎𝞃𝔥⍳𝞹𝔤 𝐚𝗌 𝖋a𝐬𝒕 " + +// "αs γ𝛐𝕦 𝔠ﻫ𝛖lԁ, 𝚊π𝑑 Ь𝑒𝙛૦𝓇𝘦 𝓎٥𝖚 ⅇvℯ𝝅 𝜅ո𝒆w w𝗵𝒂𝘁 ᶌ੦𝗎 h𝐚𝗱, 𝜸ﮨ𝒖 𝓹𝝰𝔱𝖾𝗇𝓽𝔢ⅆ і𝕥, 𝚊𝜛𝓭 𝓹𝖺ⅽϰ𝘢ℊеᏧ 𝑖𝞃, " + +// "𝐚𝛑ꓒ 𝙨l𝔞р𝘱𝔢𝓭 ɩ𝗍 ہ𝛑 𝕒 pl𝛂ѕᴛ𝗂𝐜 l𝞄ℼ𝔠𝒽𝑏ﮪ⨯, 𝔞ϖ𝒹 n𝛔w 𝛾𝐨𝞄'𝗿𝔢 ꜱ℮ll𝙞nɡ ɩ𝘁, 𝙮𝕠𝛖 w𝑎ℼ𝚗𝛂 𝕤𝓮ll 𝙞𝓉." +// ) +// +// */ +// //assertEquals( +// // "[size=1496 text=Սm, I'll 𝓽𝖾ll ᶌօ𝘂 ᴛℎ℮ 𝜚𝕣०bl𝖾m wі𝕥𝒽 𝘵𝘩𝐞 𝓼𝙘𝐢𝔢𝓷𝗍𝜄𝚏𝑖c 𝛠𝝾w𝚎𝑟 𝕥h⍺𝞃 " + +// // "𝛄𝓸𝘂…]", +// // factory.encodeUtf8(war).toString(), +// //) +// TODO() +// } +// +// @Test +// fun toStringOnTextWithNewlines() { +// // Instead of emitting a literal newline in the toString(), these are escaped as "\n". +// //assertEquals( +// // "[text=a\\r\\nb\\nc\\rd\\\\e]", +// // factory.encodeUtf8("a\r\nb\nc\rd\\e").toString(), +// // ) +// TODO() +// } +// +// @Test +// fun toStringOnData() { +// /* +// val byteString = factory.decodeHex( +// "" + +// "60b420bb3851d9d47acb933dbe70399bf6c92da33af01d4fb770e98c0325f41d3ebaf8986da712c82bcd4d55" + +// "4bf0b54023c29b624de9ef9c2f931efc580f9afb", +// ) +// assertEquals( +// "[hex=" + +// "60b420bb3851d9d47acb933dbe70399bf6c92da33af01d4fb770e98c0325f41d3ebaf8986da712c82bcd4d55" + +// "4bf0b54023c29b624de9ef9c2f931efc580f9afb]", +// byteString.toString(), +// ) +// */ +// TODO() +// } +// +// @Test +// fun toStringOnLongDataIsTruncated() { +// /* +// val byteString = factory.decodeHex( +// "" + +// "60b420bb3851d9d47acb933dbe70399bf6c92da33af01d4fb770e98c0325f41d3ebaf8986da712c82bcd4d55" + +// "4bf0b54023c29b624de9ef9c2f931efc580f9afba1", +// ) +// assertEquals( +// "[size=65 hex=" + +// "60b420bb3851d9d47acb933dbe70399bf6c92da33af01d4fb770e98c0325f41d3ebaf8986da712c82bcd4d55" + +// "4bf0b54023c29b624de9ef9c2f931efc580f9afb…]", +// byteString.toString(), +// ) +// */ +// TODO() +// } +// +// @Test +// fun compareToSingleBytes() { +// val originalByteStrings = listOf( +// "00".decodeHex(), +// "01".decodeHex(), +// "7e".decodeHex(), +// "7f".decodeHex(), +// "80".decodeHex(), +// "81".decodeHex(), +// "fe".decodeHex(), +// "ff".decodeHex(), +// ) +// +// val sortedByteStrings = originalByteStrings.toMutableList() +// sortedByteStrings.shuffle(Random(0)) +// assertNotEquals(originalByteStrings, sortedByteStrings) +// +// sortedByteStrings.sort() +// assertEquals(originalByteStrings, sortedByteStrings) +// } +// +// @Test +// fun compareToMultipleBytes() { +// val originalByteStrings = listOf( +// "".decodeHex(), +// "00".decodeHex(), +// "0000".decodeHex(), +// "000000".decodeHex(), +// "00000000".decodeHex(), +// "0000000000".decodeHex(), +// "0000000001".decodeHex(), +// "000001".decodeHex(), +// "00007f".decodeHex(), +// "0000ff".decodeHex(), +// "000100".decodeHex(), +// "000101".decodeHex(), +// "007f00".decodeHex(), +// "00ff00".decodeHex(), +// "010000".decodeHex(), +// "010001".decodeHex(), +// "01007f".decodeHex(), +// "0100ff".decodeHex(), +// "010100".decodeHex(), +// "01010000".decodeHex(), +// "0101000000".decodeHex(), +// "0101000001".decodeHex(), +// "010101".decodeHex(), +// "7f0000".decodeHex(), +// "7f0000ffff".decodeHex(), +// "ffffff".decodeHex(), +// ) +// +// val sortedByteStrings = originalByteStrings.toMutableList() +// sortedByteStrings.shuffle(Random(0)) +// assertNotEquals(originalByteStrings, sortedByteStrings) +// +// sortedByteStrings.sort() +// assertEquals(originalByteStrings, sortedByteStrings) +// } +// +// /* +// @Test fun testHash() = with(factory.encodeUtf8("Kevin")) { +// assertEquals("e043899daa0c7add37bc99792b2c045d6abbc6dc", sha1().hex()) +// assertEquals("f1cd318e412b5f7226e5f377a9544ff7", md5().hex()) +// assertEquals("0e4dd66217fc8d2e298b78c8cd9392870dcd065d0ff675d0edff5bcd227837e9", sha256().hex()) +// assertEquals("483676b93c4417198b465083d196ec6a9fab8d004515874b8ff47e041f5f56303cc08179625030b8b5b721c09149a18f0f59e64e7ae099518cea78d3d83167e1", sha512().hex()) +// } +//*/ +// +// @Test +// fun copyInto() { +// val byteString = ByteString("abcdefgh".encodeToByteArray()) +// val byteArray = "WwwwXxxxYyyyZzzz".encodeToByteArray() +// byteString.copyInto(byteArray, 0, 0, 5) +// assertEquals("abcdexxxYyyyZzzz", byteArray.decodeToString()) +// } +// +// @Test +// fun copyIntoFullRange() { +// val byteString = ByteString("abcdefghijklmnop".encodeToByteArray()) +// val byteArray = "WwwwXxxxYyyyZzzz".encodeToByteArray() +// byteString.copyInto(byteArray, 0, 0, 16) +// assertEquals("abcdefghijklmnop", byteArray.decodeToString()) +// } +// +// @Test +// fun copyIntoWithTargetOffset() { +// val byteString = ByteString("abcdefgh".encodeToByteArray()) +// val byteArray = "WwwwXxxxYyyyZzzz".encodeToByteArray() +// byteString.copyInto(byteArray, 11, 0, 5) +// assertEquals("WwwwXxxxYyyabcde", byteArray.decodeToString()) +// } +// +// @Test +// fun copyIntoWithSourceOffset() { +// val byteString = ByteString("abcdefgh".encodeToByteArray()) +// val byteArray = "WwwwXxxxYyyyZzzz".encodeToByteArray() +// byteString.copyInto(byteArray, 0, 3, 5) +// assertEquals("defghxxxYyyyZzzz", byteArray.decodeToString()) +// } +// +// /* +// @Test fun copyIntoWithAllParameters() { +// val byteString = factory.encodeUtf8("abcdefgh") +// val byteArray = "WwwwXxxxYyyyZzzz".encodeToByteArray() +// byteString.copyInto(offset = 3, target = byteArray, targetOffset = 11, byteCount = 5) +// assertEquals("WwwwXxxxYyydefgh", byteArray.decodeToString()) +// } +// +// */ +// +// /* +// TODO +// @Test fun copyIntoBoundsChecks() { +// val byteString = ByteString("abcdefgh".encodeToByteArray()) +// val byteArray = "WwwwXxxxYyyyZzzz".encodeToByteArray() +// assertFailsWith { +// byteString.copyInto(offset = -1, target = byteArray, targetOffset = 1, byteCount = 1) +// } +// assertFailsWith { +// byteString.copyInto(offset = 9, target = byteArray, targetOffset = 0, byteCount = 0) +// } +// assertFailsWith { +// byteString.copyInto(offset = 1, target = byteArray, targetOffset = -1, byteCount = 1) +// } +// assertFailsWith { +// byteString.copyInto(offset = 1, target = byteArray, targetOffset = 17, byteCount = 1) +// } +// assertFailsWith { +// byteString.copyInto(offset = 7, target = byteArray, targetOffset = 1, byteCount = 2) +// } +// assertFailsWith { +// byteString.copyInto(offset = 1, target = byteArray, targetOffset = 15, byteCount = 2) +// } +// } +// +// @Test fun copyEmptyAtBounds() { +// val byteString = ByteString("abcdefgh".encodeToByteArray()) +// val byteArray = "WwwwXxxxYyyyZzzz".encodeToByteArray() +// byteString.copyInto(offset = 0, target = byteArray, targetOffset = 0, byteCount = 0) +// assertEquals("WwwwXxxxYyyyZzzz", byteArray.decodeToString()) +// byteString.copyInto(offset = 0, target = byteArray, targetOffset = 16, byteCount = 0) +// assertEquals("WwwwXxxxYyyyZzzz", byteArray.decodeToString()) +// byteString.copyInto(offset = 8, target = byteArray, targetOffset = 0, byteCount = 0) +// assertEquals("WwwwXxxxYyyyZzzz", byteArray.decodeToString()) +// } +// */ +} \ No newline at end of file diff --git a/bytestring/src/jvmMain/kotlin/ByteStringJVM.kt b/bytestring/src/jvmMain/kotlin/ByteStringJVM.kt new file mode 100644 index 000000000..6691cf165 --- /dev/null +++ b/bytestring/src/jvmMain/kotlin/ByteStringJVM.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. + */ + +package kotlinx.io.bytestring + +/* +public fun ByteString.encodeToString(charset: Charset = Charsets.UTF_8): String = getByteArray().toString(charset) +public fun ByteString.Companion.fromString(string: String, charset: Charset = Charsets.UTF_8): ByteString = + ByteString(string.toByteArray(charset)) + */ + +/* +public fun ByteString.writeTo(outputStream: OutputStream) { + writeCopyTo(object : ByteString.Reader { + override fun read(data: ByteArray, beginIndex: Int, endIndex: Int) { + outputStream.write(data, beginIndex, endIndex - beginIndex) + } + }) +} + +@OptIn(UnsafeApi::class) +public fun ByteString.writeTo(outputStream: ByteArrayOutputStream) { + writeUnsafeTo(object : ByteString.Reader { + override fun read(data: ByteArray, beginIndex: Int, endIndex: Int) { + outputStream.write(data, beginIndex, endIndex - beginIndex) + } + }) +} + +@OptIn(UnsafeApi::class) +public fun ByteString.md5(): ByteString { + val md = MessageDigest.getInstance("MD5") + writeUnsafeTo(object : ByteString.Reader { + override fun read(data: ByteArray, beginIndex: Int, endIndex: Int) { + md.update(data, beginIndex, endIndex - beginIndex) + } + }) + md.digest() + return ByteString(md.digest()) +} + +@OptIn(UnsafeApi::class) +public fun ByteString.toString(charset: Charset = Charsets.UTF_8): String { + var string: String? = null + + writeUnsafeTo(object : ByteString.Reader { + override fun read(data: ByteArray, beginIndex: Int, endIndex: Int) { + string = String(data, beginIndex, endIndex - beginIndex, charset) + } + }) + + return string!! +} + */ \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 9d18495a5..5e0a8c5d0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,5 +15,7 @@ rootProject.name = "kotlinx-io" include(":kotlinx-io-core") include(":kotlinx-io-benchmarks") +include(":kotlinx-io-bytestring") project(":kotlinx-io-core").projectDir = file("./core") project(":kotlinx-io-benchmarks").projectDir = file("./benchmarks") +project(":kotlinx-io-bytestring").projectDir = file("./bytestring") \ No newline at end of file From 41b19624ac8817e1acfdcd455a743ca1ef6a3ec5 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Wed, 21 Jun 2023 17:34:25 +0200 Subject: [PATCH 02/32] Support ByteString in kotlinx-io --- .../src/commonMain/kotlin/ByteString.kt | 42 ++++- .../commonMain/kotlin/ByteStringBuilder.kt | 112 +++++++++++++ core/build.gradle.kts | 6 +- core/common/src/Buffer.kt | 7 +- core/common/src/BufferExt.kt | 29 ++++ core/common/src/ByteStringExt.kt | 137 ++++++++++++++++ core/common/test/AbstractSinkTest.kt | 13 ++ core/common/test/AbstractSourceTest.kt | 152 ++++++++++++++++++ core/common/test/ByteStringExtensionsTest.kt | 45 ++++++ core/common/test/CommonBufferTest.kt | 11 ++ core/common/test/util.kt | 3 + core/jvm/test/md5.kt | 23 +++ 12 files changed, 572 insertions(+), 8 deletions(-) create mode 100644 bytestring/src/commonMain/kotlin/ByteStringBuilder.kt create mode 100644 core/common/src/BufferExt.kt create mode 100644 core/common/src/ByteStringExt.kt create mode 100644 core/common/test/ByteStringExtensionsTest.kt create mode 100644 core/jvm/test/md5.kt diff --git a/bytestring/src/commonMain/kotlin/ByteString.kt b/bytestring/src/commonMain/kotlin/ByteString.kt index e3c563f6a..5633a5991 100644 --- a/bytestring/src/commonMain/kotlin/ByteString.kt +++ b/bytestring/src/commonMain/kotlin/ByteString.kt @@ -27,9 +27,27 @@ import kotlin.math.min private val HEX_DIGITS = "0123456789ABCDEF".toCharArray() /** - * ByteString is an immutable wrapper around a byte sequence providing [String] like functionality. - * + * Marks declarations whose usage may brake some ByteString invariants. * + * Consider using other APIs instead when possible. + * Otherwise, make sure to read documentation describing an unsafe API. + */ +@MustBeDocumented +@Retention(AnnotationRetention.BINARY) +@RequiresOptIn( + level = RequiresOptIn.Level.ERROR, + message = "This is a delicate API and its use requires care. " + + "Make sure you fully read and understand documentation of the declaration that is marked as a delicate API." +) +public annotation class UnsafeByteStringApi + +/** + * Constructs empty byte string. + */ +public fun ByteString(): ByteString = ByteString.EMPTY + +/** + * An immutable wrapper around a byte sequence providing [String] like functionality. */ public class ByteString private constructor( private val data: ByteArray, @@ -41,7 +59,16 @@ public class ByteString private constructor( /** * An empty ByteString. */ - public val EMPTY: ByteString = ByteString() + public val EMPTY: ByteString = ByteString(ByteArray(0), null) + + /** + * Creates a new byte string by wrapping [byteArray] without copying it. + * Make sure that the wrapped array won't be modified during the lifespan of the returned byte string. + * + * @param byteArray the array to wrap into the byte string. + */ + @UnsafeByteStringApi + public fun wrapUnsafe(byteArray: ByteArray): ByteString = ByteString(byteArray, null) } /** @@ -223,7 +250,14 @@ public class ByteString private constructor( */ override fun toString(): String = toString(false) - internal fun getByteArray(): ByteArray = data + /** + * Returns a reference to the underlying array. + * + * These methods return reference to the underlying array, not to its copy. + * Consider using [toByteArray] if it's impossible to guarantee that the array won't be modified. + */ + @UnsafeByteStringApi + public fun getByteArrayUnsafe(): ByteArray = data } /** diff --git a/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt b/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt new file mode 100644 index 000000000..09edcbbd1 --- /dev/null +++ b/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt @@ -0,0 +1,112 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. + */ + +package kotlinx.io.bytestring + +import kotlin.math.max + +/** + * A helper class facilitating [ByteString] construction. + * + * A builder is characterized by the [capacity] - the number of bytes that an instance of builder + * can receive before extending an underlying byte sequence, and [size] - the number of bytes being written + * to the builder. + * + * The builder avoids additional copies and allocations when `size == capacity` when [toByteString] called, + * thus it's recommended to specify expected [ByteString] size as `initialCapacity` when creating a builder. + * + * When a builder runs out of available capacity, a new byte sequence with extended capacity + * will be allocated and previously written data will be copied into it. + * + * @param initialCapacity the initial size of an underlying byte sequence. + */ +public class ByteStringBuilder(initialCapacity: Int = 0) { + private var buffer = ByteArray(initialCapacity) + private var offset: Int = 0 + + /** + * The number of bytes being written to this builder. + */ + public val size: Int + get() = offset + + /** + * The number of bytes this builder can store without an extension of an internal buffer. + */ + public val capacity: Int + get() = buffer.size + + /** + * Returns a new [ByteString] wrapping all bytes written to this builder. + * + * There will be no additional allocations or copying of data when `size == capacity`. + */ + @OptIn(UnsafeByteStringApi::class) + public fun toByteString(): ByteString { + if (size == 0) { + return ByteString.EMPTY + } + if (buffer.size == size) { + return ByteString.wrapUnsafe(buffer) + } + return ByteString(buffer, 0, size) + } + + /** + * Append a single byte to this builder. + * + * @param byte the byte to append. + */ + public fun append(byte: Byte) { + ensureCapacity(size + 1) + buffer[offset++] = byte + } + + /** + * Appends a subarray of [array] starting at [startIndex] and ending at [endIndex] to this builder. + * + * @param array the array whose subarray should be appended. + * @param startIndex the first index (inclusive) to copy data from the [array]. + * @param endIndex the last index (exclusive) to copy data from the [array] + * + * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [array] array indices. + * @throws IllegalArgumentException when `startIndex > endIndex`. + */ + public fun append(array: ByteArray, startIndex: Int = 0, endIndex: Int = array.size) { + require(startIndex <= endIndex) { "startIndex: $startIndex, endIndex: $endIndex" } + if (startIndex < 0 || endIndex > array.size) { + throw IndexOutOfBoundsException("startIndex: $startIndex, endIndex: $endIndex") + } + ensureCapacity(offset + endIndex - startIndex) + + array.copyInto(buffer, offset, startIndex, endIndex) + offset += endIndex - startIndex + } + + private fun ensureCapacity(requiredCapacity: Int) { + if (buffer.size <= requiredCapacity) { + return + } + + var desiredSize = if (buffer.isEmpty()) 16 else (buffer.size * 1.5).toInt() + desiredSize = max(desiredSize, requiredCapacity) + val newBuffer = ByteArray(desiredSize) + buffer.copyInto(newBuffer) + buffer = newBuffer + } +} + +/** + * Appends unsigned byte to this builder. + */ +public fun ByteStringBuilder.append(byte: UByte): Unit = append(byte.toByte()) + +/** + * Appends a byte string to this builder. + */ +@OptIn(UnsafeByteStringApi::class) +public fun ByteStringBuilder.append(byteString: ByteString) { + append(byteString.getByteArrayUnsafe()) +} \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 850c033e9..1bde4e349 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -40,7 +40,11 @@ kotlin { configureNativePlatforms() sourceSets { - val commonMain by getting + val commonMain by getting { + dependencies { + implementation(project(":kotlinx-io-bytestring")) + } + } val commonTest by getting { dependencies { implementation(kotlin("test")) diff --git a/core/common/src/Buffer.kt b/core/common/src/Buffer.kt index 975473abd..39f5bbfba 100644 --- a/core/common/src/Buffer.kt +++ b/core/common/src/Buffer.kt @@ -322,9 +322,10 @@ public class Buffer : Source, Sink { val s = head ?: return -1 val toCopy = minOf(endIndex - startIndex, s.limit - s.pos) - s.data.copyInto( - destination = sink, destinationOffset = startIndex, startIndex = s.pos, endIndex = s.pos + toCopy - ) + println("${s.limit} ${s.pos}") + s.data.copyInto( + destination = sink, destinationOffset = startIndex, startIndex = s.pos, endIndex = s.pos + toCopy + ) s.pos += toCopy size -= toCopy.toLong() diff --git a/core/common/src/BufferExt.kt b/core/common/src/BufferExt.kt new file mode 100644 index 000000000..cd963a3ef --- /dev/null +++ b/core/common/src/BufferExt.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. + */ + +package kotlinx.io + +import kotlinx.io.bytestring.ByteString +import kotlinx.io.bytestring.ByteStringBuilder + +/** + * Creates a byte string containing a copy of all the data from this buffer. + * + * This call doesn't consume data from the buffer, but instead copies it. + */ +public fun Buffer.snapshot(): ByteString { + if (size == 0L) return ByteString.EMPTY + + val bufferSize = this@snapshot.size + return with (ByteStringBuilder(bufferSize.toInt())) { + var curr = head + do { + check(curr != null) { "Current segment is null" } + append(curr.data, curr.pos, curr.limit) + curr = curr.next + } while (curr !== head) + toByteString() + } +} \ No newline at end of file diff --git a/core/common/src/ByteStringExt.kt b/core/common/src/ByteStringExt.kt new file mode 100644 index 000000000..c612ec9d6 --- /dev/null +++ b/core/common/src/ByteStringExt.kt @@ -0,0 +1,137 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. + */ + +package kotlinx.io + +import kotlinx.io.bytestring.ByteString +import kotlinx.io.bytestring.UnsafeByteStringApi +import kotlinx.io.bytestring.indices +import kotlinx.io.bytestring.isEmpty +import kotlinx.io.internal.commonAsUtf8ToByteArray +import kotlinx.io.internal.commonToUtf8String +import kotlin.math.min + + +/** + * Decodes content of a byte string into a string using UTF-8 encoding. + */ +@OptIn(UnsafeByteStringApi::class) +public fun ByteString.toUtf8(): String { + return getByteArrayUnsafe().commonToUtf8String() +} + +/** + * Encodes a string into a byte sequence using UTF8-encoding and wraps it into a byte string. + * + * @param string the string to be encoded. + */ +@OptIn(UnsafeByteStringApi::class) +public fun ByteString.Companion.fromUtf8(string: String): ByteString { + return wrapUnsafe(string.commonAsUtf8ToByteArray()) +} + +/** + * Writes subsequence of data from [byteString] starting at [startIndex] and ending at [endIndex] into a sink. + * + * @param byteString the byte string whose subsequence should be written to a sink. + * @param startIndex the first index (inclusive) to copy data from the [byteString]. + * @param endIndex the last index (exclusive) to copy data from the [byteString] + * + * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [byteString] indices. + * @throws IllegalArgumentException when `startIndex > endIndex`. + * @throws IllegalStateException if the sink is closed. + */ +@OptIn(DelicateIoApi::class) +public fun Sink.write(byteString: ByteString, startIndex: Int = 0, endIndex: Int = byteString.size) { + checkBounds(byteString.size, startIndex, endIndex) + + writeToInternalBuffer { buffer -> + var offset = startIndex + while (offset < endIndex) { + val bytesToWrite = min(endIndex - offset, Segment.SIZE) + val seg = buffer.writableSegment(bytesToWrite) + byteString.copyInto(seg.data, seg.limit, offset, offset + bytesToWrite) + seg.limit += bytesToWrite + buffer.size += bytesToWrite + offset += bytesToWrite + } + } +} + +/** + * Consumes all bytes from this source and wraps it into a byte string. + * + * @throws IllegalStateException if the source is closed. + */ +@OptIn(UnsafeByteStringApi::class) +public fun Source.readByteString(): ByteString { + return ByteString.wrapUnsafe(readByteArray()) +} + +/** + * Consumes exactly [byteCount] bytes from this source and wraps it into a byte string. + * + * @param byteCount the number of bytes to read from the source. + * + * @throws EOFException when the source is exhausted before reading [byteCount] bytes from it. + * @throws IllegalArgumentException when [byteCount] is negative. + * @throws IllegalStateException if the source is closed. + */ +@OptIn(UnsafeByteStringApi::class) +public fun Source.readByteString(byteCount: Int): ByteString { + return ByteString.wrapUnsafe(readByteArray(byteCount)) +} + +/** + * Returns the index of the first match for [byteString] in the source at or after [startIndex]. This + * expands the source's buffer as necessary until [byteString] 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. + * + * @param byteString the sequence of bytes to find within the source. + * @param startIndex the index into the source to start searching from. + * + * @throws IllegalArgumentException if [startIndex] is negative. + * @throws IllegalStateException if the source is closed. + */ +@OptIn(InternalIoApi::class) +public fun Source.indexOf(byteString: ByteString, startIndex: Long = 0): Long { + require(startIndex >= 0) { "startIndex: $startIndex" } + + if (byteString.isEmpty()) { + return 0 + } + + var offset = startIndex + val peek = peek() + if (!request(startIndex)) { + return -1L + } + peek.skip(offset) + while (!peek.exhausted()) { + val index = peek.indexOf(byteString[0]) + if (index == -1L) { + return -1L + } + offset += index + peek.skip(index) + if (!peek.request(byteString.size.toLong())) { + return -1L + } + + var matches = true + for (idx in byteString.indices()) { + if (byteString[idx] != peek.buffer[idx.toLong()]) { + matches = false + offset++ + peek.skip(1) + break + } + } + if (matches) { + return offset + } + } + return -1L +} \ No newline at end of file diff --git a/core/common/test/AbstractSinkTest.kt b/core/common/test/AbstractSinkTest.kt index 38882154b..10714ed66 100644 --- a/core/common/test/AbstractSinkTest.kt +++ b/core/common/test/AbstractSinkTest.kt @@ -21,6 +21,7 @@ package kotlinx.io +import kotlinx.io.bytestring.ByteString import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -446,4 +447,16 @@ abstract class AbstractSinkTest internal constructor( sink.flush() assertEquals("Buffer(size=8 hex=efcdab9078563412)", data.toString()) } + + @Test fun writeByteString() { + sink.write("təˈranəˌsôr".encodeUtf8()) + sink.flush() + assertEquals(ByteString("74c999cb8872616ec999cb8c73c3b472".decodeHex()), data.readByteString()) + } + + @Test fun writeByteStringOffset() { + sink.write("təˈranəˌsôr".encodeUtf8(), 5, 10) + sink.flush() + assertEquals(ByteString("72616ec999".decodeHex()), data.readByteString()) + } } diff --git a/core/common/test/AbstractSourceTest.kt b/core/common/test/AbstractSourceTest.kt index e89f418b5..7f5d7fb4a 100644 --- a/core/common/test/AbstractSourceTest.kt +++ b/core/common/test/AbstractSourceTest.kt @@ -21,6 +21,7 @@ package kotlinx.io +import kotlinx.io.bytestring.ByteString import kotlin.test.* private const val SEGMENT_SIZE = Segment.SIZE @@ -1536,4 +1537,155 @@ abstract class AbstractBufferedSourceTest internal constructor( } assertTrue(source.request(7)) } + @Test fun readByteString() { + with (sink) { + writeUtf8("abcd") + writeUtf8("e".repeat(Segment.SIZE)) + emit() + } + assertEquals("abcd" + "e".repeat(Segment.SIZE), source.readByteString().toUtf8()) + } + + @Test fun readByteStringPartial() { + with (sink) { + writeUtf8("abcd") + writeUtf8("e".repeat(Segment.SIZE)) + emit() + } + assertEquals("abc", source.readByteString(3).toUtf8()) + assertEquals("d", source.readUtf8(1)) + } + + @Test fun readByteStringTooShortThrows() { + sink.writeUtf8("abc") + sink.emit() + assertFailsWith { source.readByteString(4) } + + assertEquals("abc", source.readUtf8()) // The read shouldn't consume any data. + } + + @Test fun indexOfByteString() { + assertEquals(-1, source.indexOf("flop".encodeUtf8())) + + sink.writeUtf8("flip flop") + sink.emit() + assertEquals(5, source.indexOf("flop".encodeUtf8())) + source.readUtf8() // Clear stream. + + // Make sure we backtrack and resume searching after partial match. + sink.writeUtf8("hi hi hi hey") + sink.emit() + assertEquals(3, source.indexOf("hi hi hey".encodeUtf8())) + } + + @Test fun indexOfByteStringAtSegmentBoundary() { + sink.writeUtf8("a".repeat(Segment.SIZE - 1)) + sink.writeUtf8("bcd") + sink.emit() + assertEquals( + (Segment.SIZE - 3).toLong(), + source.indexOf("aabc".encodeUtf8(), (Segment.SIZE - 4).toLong()), + ) + assertEquals( + (Segment.SIZE - 3).toLong(), + source.indexOf("aabc".encodeUtf8(), (Segment.SIZE - 3).toLong()), + ) + assertEquals( + (Segment.SIZE - 2).toLong(), + source.indexOf("abcd".encodeUtf8(), (Segment.SIZE - 2).toLong()), + ) + assertEquals( + (Segment.SIZE - 2).toLong(), + source.indexOf("abc".encodeUtf8(), (Segment.SIZE - 2).toLong()), + ) + assertEquals( + (Segment.SIZE - 2).toLong(), + source.indexOf("abc".encodeUtf8(), (Segment.SIZE - 2).toLong()), + ) + assertEquals( + (Segment.SIZE - 2).toLong(), + source.indexOf("ab".encodeUtf8(), (Segment.SIZE - 2).toLong()), + ) + assertEquals( + (Segment.SIZE - 2).toLong(), + source.indexOf("a".encodeUtf8(), (Segment.SIZE - 2).toLong()), + ) + assertEquals( + (Segment.SIZE - 1).toLong(), + source.indexOf("bc".encodeUtf8(), (Segment.SIZE - 2).toLong()), + ) + assertEquals( + (Segment.SIZE - 1).toLong(), + source.indexOf("b".encodeUtf8(), (Segment.SIZE - 2).toLong()), + ) + assertEquals( + Segment.SIZE.toLong(), + source.indexOf("c".encodeUtf8(), (Segment.SIZE - 2).toLong()), + ) + assertEquals( + Segment.SIZE.toLong(), + source.indexOf("c".encodeUtf8(), Segment.SIZE.toLong()), + ) + assertEquals( + (Segment.SIZE + 1).toLong(), + source.indexOf("d".encodeUtf8(), (Segment.SIZE - 2).toLong()), + ) + assertEquals( + (Segment.SIZE + 1).toLong(), + source.indexOf("d".encodeUtf8(), (Segment.SIZE + 1).toLong()), + ) + } + + @Test fun indexOfDoesNotWrapAround() { + sink.writeUtf8("a".repeat(Segment.SIZE - 1)) + sink.writeUtf8("bcd") + sink.emit() + assertEquals(-1, source.indexOf("abcda".encodeUtf8(), (Segment.SIZE - 3).toLong())) + } + + @Test fun indexOfByteStringWithOffset() { + assertEquals(-1, source.indexOf("flop".encodeUtf8(), 1)) + + sink.writeUtf8("flop flip flop") + sink.emit() + assertEquals(10, source.indexOf("flop".encodeUtf8(), 1)) + source.readUtf8() // Clear stream + + // Make sure we backtrack and resume searching after partial match. + sink.writeUtf8("hi hi hi hi hey") + sink.emit() + assertEquals(6, source.indexOf("hi hi hey".encodeUtf8(), 1)) + } + + @Test fun indexOfEmptyByteString() { + assertEquals(0, source.indexOf(ByteString.EMPTY)) + } + + @Test fun indexOfByteStringInvalidArgumentsThrows() { + assertFailsWith { + source.indexOf("hi".encodeUtf8(), -1) + } + } + + /** + * With [BufferedSourceFactory.ONE_BYTE_AT_A_TIME_BUFFERED_SOURCE], this code was extremely slow. + * https://github.com/square/okio/issues/171 + */ + @Test fun indexOfByteStringAcrossSegmentBoundaries() { + sink.writeUtf8("a".repeat(Segment.SIZE * 2 - 3)) + sink.writeUtf8("bcdefg") + sink.emit() + assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("ab".encodeUtf8())) + assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abc".encodeUtf8())) + assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcd".encodeUtf8())) + assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcde".encodeUtf8())) + assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcdef".encodeUtf8())) + assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcdefg".encodeUtf8())) + assertEquals((Segment.SIZE * 2 - 3).toLong(), source.indexOf("bcdefg".encodeUtf8())) + assertEquals((Segment.SIZE * 2 - 2).toLong(), source.indexOf("cdefg".encodeUtf8())) + assertEquals((Segment.SIZE * 2 - 1).toLong(), source.indexOf("defg".encodeUtf8())) + assertEquals((Segment.SIZE * 2).toLong(), source.indexOf("efg".encodeUtf8())) + assertEquals((Segment.SIZE * 2 + 1).toLong(), source.indexOf("fg".encodeUtf8())) + assertEquals((Segment.SIZE * 2 + 2).toLong(), source.indexOf("g".encodeUtf8())) + } } diff --git a/core/common/test/ByteStringExtensionsTest.kt b/core/common/test/ByteStringExtensionsTest.kt new file mode 100644 index 000000000..dfee62603 --- /dev/null +++ b/core/common/test/ByteStringExtensionsTest.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. + */ + +/* + * Copyright (C) 2018 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 kotlinx.io + +import kotlinx.io.bytestring.ByteString +import kotlinx.io.internal.commonAsUtf8ToByteArray +import kotlin.test.Test +import kotlin.test.assertEquals + +class ByteStringExtensionsTest { + private val bronzeHorseman = "На берегу пустынных волн" + + @Test + fun utf8() { + val byteString = ByteString.fromUtf8(bronzeHorseman) + assertEquals(byteString.toByteArray().toList(), bronzeHorseman.commonAsUtf8ToByteArray().toList()) + assertEquals(byteString, ByteString(*bronzeHorseman.commonAsUtf8ToByteArray())) + assertEquals( + byteString, + ByteString( + "d09dd0b020d0b1d0b5d180d0b5d0b3d18320d0bfd183d181d182d18bd0bdd0bdd18bd18520d0b2d0bed0bbd0bd".decodeHex() + ) + ) + assertEquals(byteString.toUtf8(), bronzeHorseman) + } +} \ No newline at end of file diff --git a/core/common/test/CommonBufferTest.kt b/core/common/test/CommonBufferTest.kt index abd257895..85b00c99d 100644 --- a/core/common/test/CommonBufferTest.kt +++ b/core/common/test/CommonBufferTest.kt @@ -20,6 +20,7 @@ */ package kotlinx.io +import kotlinx.io.bytestring.ByteString import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -584,4 +585,14 @@ class CommonBufferTest { copy.transferTo(buffer) assertArrayEquals(byteArrayOf(42, 42), buffer.readByteArray()) } + + @Test + fun snapshot() { + val buffer = Buffer() + assertEquals(ByteString(), buffer.snapshot()) + buffer.writeUtf8("hello") + assertEquals("hello".encodeUtf8(), buffer.snapshot()) + buffer.clear() + assertEquals(ByteString(), buffer.snapshot()) + } } diff --git a/core/common/test/util.kt b/core/common/test/util.kt index c621b3c17..fd21ef562 100644 --- a/core/common/test/util.kt +++ b/core/common/test/util.kt @@ -20,6 +20,7 @@ */ package kotlinx.io +import kotlinx.io.bytestring.ByteString import kotlin.test.assertEquals fun segmentSizes(buffer: Buffer): List { @@ -67,3 +68,5 @@ fun Char.repeat(count: Int): String { fun assertArrayEquals(a: ByteArray, b: ByteArray) { assertEquals(a.contentToString(), b.contentToString()) } + +fun String.encodeUtf8(): ByteString = ByteString.fromUtf8(this) diff --git a/core/jvm/test/md5.kt b/core/jvm/test/md5.kt new file mode 100644 index 000000000..1f99d9adb --- /dev/null +++ b/core/jvm/test/md5.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. + */ + +package kotlinx.io + +import java.nio.ByteBuffer +import java.security.MessageDigest +import kotlin.test.Test + + +class T { + @Test + fun test() { + val md = MessageDigest.getInstance("MD5") + var bb = ByteBuffer.allocate(1024) + bb.put(ByteArray(1024)) + bb = bb.asReadOnlyBuffer() + bb.position(0) + md.update(bb) + } +} \ No newline at end of file From 40f59edca00c9a15a6f17e0cd8e3de741e54fec3 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Wed, 21 Jun 2023 17:43:03 +0200 Subject: [PATCH 03/32] Cleanup --- .../src/jvmMain/kotlin/ByteStringJVM.kt | 56 ------------------- 1 file changed, 56 deletions(-) delete mode 100644 bytestring/src/jvmMain/kotlin/ByteStringJVM.kt diff --git a/bytestring/src/jvmMain/kotlin/ByteStringJVM.kt b/bytestring/src/jvmMain/kotlin/ByteStringJVM.kt deleted file mode 100644 index 6691cf165..000000000 --- a/bytestring/src/jvmMain/kotlin/ByteStringJVM.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.bytestring - -/* -public fun ByteString.encodeToString(charset: Charset = Charsets.UTF_8): String = getByteArray().toString(charset) -public fun ByteString.Companion.fromString(string: String, charset: Charset = Charsets.UTF_8): ByteString = - ByteString(string.toByteArray(charset)) - */ - -/* -public fun ByteString.writeTo(outputStream: OutputStream) { - writeCopyTo(object : ByteString.Reader { - override fun read(data: ByteArray, beginIndex: Int, endIndex: Int) { - outputStream.write(data, beginIndex, endIndex - beginIndex) - } - }) -} - -@OptIn(UnsafeApi::class) -public fun ByteString.writeTo(outputStream: ByteArrayOutputStream) { - writeUnsafeTo(object : ByteString.Reader { - override fun read(data: ByteArray, beginIndex: Int, endIndex: Int) { - outputStream.write(data, beginIndex, endIndex - beginIndex) - } - }) -} - -@OptIn(UnsafeApi::class) -public fun ByteString.md5(): ByteString { - val md = MessageDigest.getInstance("MD5") - writeUnsafeTo(object : ByteString.Reader { - override fun read(data: ByteArray, beginIndex: Int, endIndex: Int) { - md.update(data, beginIndex, endIndex - beginIndex) - } - }) - md.digest() - return ByteString(md.digest()) -} - -@OptIn(UnsafeApi::class) -public fun ByteString.toString(charset: Charset = Charsets.UTF_8): String { - var string: String? = null - - writeUnsafeTo(object : ByteString.Reader { - override fun read(data: ByteArray, beginIndex: Int, endIndex: Int) { - string = String(data, beginIndex, endIndex - beginIndex, charset) - } - }) - - return string!! -} - */ \ No newline at end of file From 2d8174339f240bb85b438f38a512584b40b5de96 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Wed, 21 Jun 2023 17:43:15 +0200 Subject: [PATCH 04/32] Update API dump --- bytestring/api/kotlinx-io-bytestring.api | 24 ++++++++++++++++++++++++ core/api/kotlinx-io-core.api | 15 +++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/bytestring/api/kotlinx-io-bytestring.api b/bytestring/api/kotlinx-io-bytestring.api index 378f51ef0..dc091ac1a 100644 --- a/bytestring/api/kotlinx-io-bytestring.api +++ b/bytestring/api/kotlinx-io-bytestring.api @@ -3,12 +3,14 @@ public final class kotlinx/io/bytestring/ByteString : java/lang/Comparable { public fun ([B)V public fun ([BII)V public synthetic fun ([BIIILkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun ([BLjava/lang/Object;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public synthetic fun compareTo (Ljava/lang/Object;)I public fun compareTo (Lkotlinx/io/bytestring/ByteString;)I public final fun copyInto ([BIII)V public static synthetic fun copyInto$default (Lkotlinx/io/bytestring/ByteString;[BIIIILjava/lang/Object;)V public fun equals (Ljava/lang/Object;)Z public final fun get (I)B + public final fun getByteArrayUnsafe ()[B public final fun getSize ()I public fun hashCode ()I public final fun substring (II)Lkotlinx/io/bytestring/ByteString; @@ -22,9 +24,28 @@ public final class kotlinx/io/bytestring/ByteString : java/lang/Comparable { public final class kotlinx/io/bytestring/ByteString$Companion { public final fun getEMPTY ()Lkotlinx/io/bytestring/ByteString; + public final fun wrapUnsafe ([B)Lkotlinx/io/bytestring/ByteString; +} + +public final class kotlinx/io/bytestring/ByteStringBuilder { + public fun ()V + public fun (I)V + public synthetic fun (IILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun append (B)V + public final fun append ([BII)V + public static synthetic fun append$default (Lkotlinx/io/bytestring/ByteStringBuilder;[BIIILjava/lang/Object;)V + public final fun getCapacity ()I + public final fun getSize ()I + public final fun toByteString ()Lkotlinx/io/bytestring/ByteString; +} + +public final class kotlinx/io/bytestring/ByteStringBuilderKt { + public static final fun append (Lkotlinx/io/bytestring/ByteStringBuilder;Lkotlinx/io/bytestring/ByteString;)V + public static final fun append-EK-6454 (Lkotlinx/io/bytestring/ByteStringBuilder;B)V } public final class kotlinx/io/bytestring/ByteStringKt { + public static final fun ByteString ()Lkotlinx/io/bytestring/ByteString; public static final fun endsWith (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;)Z public static final fun endsWith (Lkotlinx/io/bytestring/ByteString;[B)Z public static final fun indexOf (Lkotlinx/io/bytestring/ByteString;BI)I @@ -45,3 +66,6 @@ public final class kotlinx/io/bytestring/ByteStringKt { public static final fun startsWith (Lkotlinx/io/bytestring/ByteString;[B)Z } +public abstract interface annotation class kotlinx/io/bytestring/UnsafeByteStringApi : java/lang/annotation/Annotation { +} + diff --git a/core/api/kotlinx-io-core.api b/core/api/kotlinx-io-core.api index 75e8d9085..f38d9d028 100644 --- a/core/api/kotlinx-io-core.api +++ b/core/api/kotlinx-io-core.api @@ -47,6 +47,21 @@ public final class kotlinx/io/BufferExtJvmKt { public static final fun write (Lkotlinx/io/Buffer;Ljava/io/InputStream;J)Lkotlinx/io/Buffer; } +public final class kotlinx/io/BufferExtKt { + public static final fun snapshot (Lkotlinx/io/Buffer;)Lkotlinx/io/bytestring/ByteString; +} + +public final class kotlinx/io/ByteStringExtKt { + public static final fun fromUtf8 (Lkotlinx/io/bytestring/ByteString$Companion;Ljava/lang/String;)Lkotlinx/io/bytestring/ByteString; + public static final fun indexOf (Lkotlinx/io/Source;Lkotlinx/io/bytestring/ByteString;J)J + public static synthetic fun indexOf$default (Lkotlinx/io/Source;Lkotlinx/io/bytestring/ByteString;JILjava/lang/Object;)J + public static final fun readByteString (Lkotlinx/io/Source;)Lkotlinx/io/bytestring/ByteString; + public static final fun readByteString (Lkotlinx/io/Source;I)Lkotlinx/io/bytestring/ByteString; + public static final fun toUtf8 (Lkotlinx/io/bytestring/ByteString;)Ljava/lang/String; + public static final fun write (Lkotlinx/io/Sink;Lkotlinx/io/bytestring/ByteString;II)V + public static synthetic fun write$default (Lkotlinx/io/Sink;Lkotlinx/io/bytestring/ByteString;IIILjava/lang/Object;)V +} + public final class kotlinx/io/CoreKt { public static final fun buffered (Lkotlinx/io/RawSink;)Lkotlinx/io/Sink; public static final fun buffered (Lkotlinx/io/RawSource;)Lkotlinx/io/Source; From d99ff1ce4fe41e29383a767c152da49b14b88d32 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Thu, 22 Jun 2023 10:12:02 +0200 Subject: [PATCH 05/32] Added tests on ByteStringBuilder --- .../commonMain/kotlin/ByteStringBuilder.kt | 2 +- .../io/bytestring/ByteStringBuilderTest.kt | 145 ++++++++++++++++++ 2 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringBuilderTest.kt diff --git a/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt b/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt index 09edcbbd1..b3e994f7c 100644 --- a/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt +++ b/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt @@ -86,7 +86,7 @@ public class ByteStringBuilder(initialCapacity: Int = 0) { } private fun ensureCapacity(requiredCapacity: Int) { - if (buffer.size <= requiredCapacity) { + if (buffer.size >= requiredCapacity) { return } diff --git a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringBuilderTest.kt b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringBuilderTest.kt new file mode 100644 index 000000000..f8e4257d8 --- /dev/null +++ b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringBuilderTest.kt @@ -0,0 +1,145 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. + */ + +package kotlinx.io.bytestring + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertTrue + +class ByteStringBuilderTest { + @Test + fun emptyString() { + assertTrue(ByteStringBuilder().toByteString().isEmpty()) + assertTrue(ByteStringBuilder(1024).toByteString().isEmpty()) + } + + @Test + fun appendByte() { + val builder = ByteStringBuilder() + with (builder) { + append(1) + append(2) + append(3) + } + assertEquals(ByteString(1, 2, 3), builder.toByteString()) + } + + @Test + fun appendUByte() { + val builder = ByteStringBuilder() + with (builder) { + append(0x80U) + append(0x81U) + append(0x82U) + } + assertEquals(ByteString(0x80U.toByte(), 0x81U.toByte(), 0x82U.toByte()), builder.toByteString()) + } + + @Test + fun appendArray() { + with (ByteStringBuilder()) { + append(byteArrayOf(1, 2, 3, 4)) + assertEquals(ByteString(1, 2, 3, 4), toByteString()) + } + + with (ByteStringBuilder()) { + append(byteArrayOf(1, 2, 3, 4), startIndex = 2) + assertEquals(ByteString(3, 4), toByteString()) + } + + with (ByteStringBuilder()) { + append(byteArrayOf(1, 2, 3, 4), endIndex = 2) + assertEquals(ByteString(1, 2), toByteString()) + } + + with (ByteStringBuilder()) { + append(byteArrayOf(1, 2, 3, 4), startIndex = 1, endIndex = 3) + assertEquals(ByteString(2, 3), toByteString()) + } + + with (ByteStringBuilder()) { + append(byteArrayOf(1, 2, 3, 4), startIndex = 1, endIndex = 1) + assertEquals(ByteString(), toByteString()) + } + } + + @Test + fun testAppendByteArrayWithInvalidIndices() { + val builder = ByteStringBuilder() + val array = ByteArray(10) + assertFailsWith { builder.append(array, 2, 0) } + assertFailsWith { builder.append(array, -1, 2) } + assertFailsWith { builder.append(array, 0, 1000) } + assertFailsWith { builder.append(array, 1000, 1001) } + } + + @Test + fun appendByteString() { + val builder = ByteStringBuilder() + builder.append(ByteString(1, 2, 3, 4)) + assertEquals(ByteString(1, 2, 3, 4), builder.toByteString()) + } + + @Test + fun appendMultipleValues() { + val string = with (ByteStringBuilder()) { + append(42) + append(ByteArray(10) { it.toByte() }) + append(42) + append(ByteString(10, 5, 57)) + toByteString() + } + + assertEquals(ByteString(42, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 42, 10, 5, 57), string) + } + + @Test + fun resizeMultipleTimes() { + val builder = ByteStringBuilder() + builder.append(ByteArray(1)) + builder.append(ByteArray(32)) + builder.append(ByteArray(120)) + builder.append(ByteArray(1024)) + + assertEquals(ByteString(ByteArray(1 + 32 + 120 + 1024)), builder.toByteString()) + } + + @Test + fun testSize() { + val builder = ByteStringBuilder() + assertEquals(0, builder.size) + builder.append(1) + assertEquals(1, builder.size) + builder.append(ByteArray(33)) + assertEquals(34, builder.size) + } + + @Test + fun testCapacity() { + assertEquals(0, ByteStringBuilder().capacity) + assertEquals(10, ByteStringBuilder(10).capacity) + + with (ByteStringBuilder()) { + append(1) + assertTrue(capacity >= 1) + append(ByteArray(1024)) + assertTrue(capacity >= 1025) + } + } + + @Test + fun createMultipleByteStrings() { + val builder = ByteStringBuilder() + builder.append(1) + val str0 = builder.toByteString() + assertEquals(ByteString(1), str0) + assertEquals(ByteString(1), builder.toByteString()) + builder.append(2) + assertEquals(ByteString(1, 2), builder.toByteString()) + assertEquals(ByteString(1), str0) + } +} \ No newline at end of file From 6d3bd05f0c470ebc10ecee8bca8e5e73085c25b3 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Thu, 22 Jun 2023 10:23:41 +0200 Subject: [PATCH 06/32] Improve test coverage --- .../kotlinx/io/bytestring/ByteStringTest.kt | 662 ++---------------- 1 file changed, 39 insertions(+), 623 deletions(-) diff --git a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt index 7bbd26fa4..afa700236 100644 --- a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt +++ b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt @@ -7,37 +7,6 @@ package kotlinx.io.bytestring import kotlin.test.* -fun hexChar2Int(char: Char): Int = when (char) { - in '0'..'9' -> char.code - '0'.code - in 'a'..'f' -> char.code - 'a'.code + 10 - in 'A'..'F' -> char.code - 'A'.code + 10 - else -> throw IllegalArgumentException("Not a hex digit: $char") -} - -fun String.decodeHex(): ByteString { - val hex = this - if (hex.isEmpty()) { - return ByteString.EMPTY - } - val data = if (hex.length % 2 == 1) { - ByteArray((hex.length + 1) / 2) - } else { - ByteArray(hex.length / 2) - } - - var idx = 0 - var builderIdx = 0 - if (hex.length % 2 == 1) { - data[builderIdx++] = hexChar2Int(hex[idx++]).toByte() - } - while (idx < hex.length) { - var b = hexChar2Int(hex[idx++]) shl 4 - b = b or hexChar2Int(hex[idx++]) - data[builderIdx++] = b.toByte() - } - return ByteString(data) -} - class ByteStringTest { @Test fun get() { @@ -63,6 +32,17 @@ class ByteStringTest { assertNotEquals(ByteString(1, 2, 3), ByteString(3, 2, 1)) assertNotEquals(ByteString(1, 2, 3).hashCode(), ByteString(3, 2, 1).hashCode()) + + assertNotEquals(ByteString(1, 2, 3, 4), ByteString(1, 2, 3)) + + val str1 = ByteString(1, 2, 3) + val str2 = ByteString(2, 3, 4) + // force hashCode computation + assertNotEquals(str1.hashCode(), str2.hashCode()) + assertNotEquals(str1, str2) + + assertFalse(ByteString().equals(null)) + assertFalse(ByteString().equals(byteArrayOf(1, 2, 3))) } private fun checkEqualsAndHashCodeAreSame(first: ByteString, second: ByteString) { @@ -230,6 +210,7 @@ class ByteStringTest { assertEquals(0, ByteString.EMPTY.indexOf(byteArrayOf(), 100500)) assertEquals(-1, str.indexOf(byteArrayOf(1, 2, 3), 100500)) assertEquals(-1, ByteString.EMPTY.indexOf(byteArrayOf(1, 2, 3, 4, 5))) + assertEquals(-1, str.indexOf(byteArrayOf(2, 3, 5))) } @Test @@ -239,25 +220,21 @@ class ByteStringTest { assertEquals(0, str.indexOf(ByteString(1, 2, 3, 4, 5))) assertEquals(0, str.indexOf(ByteString(1, 2, 3))) assertEquals(0, str.indexOf(ByteString(1))) - assertEquals(2, str.indexOf(ByteString(3, 4, 5))) assertEquals(-1, str.indexOf(ByteString(3, 4, 5, 6))) assertEquals(0, str.indexOf(ByteString.EMPTY)) assertEquals(-1, str.indexOf(ByteString(-1))) - assertEquals(-1, str.indexOf(ByteString(1, 2, 3, 4, 5), 1)) assertEquals(3, str.indexOf(ByteString(4, 5), 3)) - assertEquals(0, str.indexOf(ByteString(1, 2, 3), -1000)) assertEquals(1, str.indexOf(ByteString(2, 3), -1)) - assertEquals(1, ByteString(0, 1, 0, 1, 0, 1).indexOf(ByteString(1, 0))) - assertEquals(0, ByteString.EMPTY.indexOf(ByteString.EMPTY)) assertEquals(0, ByteString.EMPTY.indexOf(ByteString.EMPTY, -100500)) assertEquals(0, ByteString.EMPTY.indexOf(ByteString.EMPTY, 100500)) assertEquals(-1, str.indexOf(ByteString(1, 2, 3), 100500)) assertEquals(-1, ByteString.EMPTY.indexOf(ByteString(1, 2, 3, 4, 5))) + assertEquals(-1, str.indexOf(ByteString(2, 3, 5))) } @Test @@ -286,24 +263,20 @@ class ByteStringTest { assertEquals(0, str.lastIndexOf(byteArrayOf(1, 2, 3))) assertEquals(-1, str.lastIndexOf(byteArrayOf(0, 1, 2))) assertEquals(2, str.lastIndexOf(byteArrayOf(3, 4, 5))) - assertEquals(-1, str.lastIndexOf(byteArrayOf(1, 2, 3), 1)) assertEquals(1, str.lastIndexOf(byteArrayOf(2, 3, 4), 1)) - assertEquals(str.size, str.lastIndexOf(byteArrayOf())) assertEquals(str.size, str.lastIndexOf(byteArrayOf())) - assertEquals(2, str.lastIndexOf(byteArrayOf(3, 4), -1000)) assertEquals(0, str.lastIndexOf(byteArrayOf(1), -1)) - assertEquals(4, ByteString(1, 1, 1, 1, 1).lastIndexOf(byteArrayOf(1))) assertEquals(3, ByteString(0, 1, 0, 1, 0).lastIndexOf(byteArrayOf(1, 0))) - assertEquals(0, ByteString.EMPTY.lastIndexOf(byteArrayOf())) assertEquals(0, ByteString.EMPTY.lastIndexOf(byteArrayOf(), -100500)) assertEquals(0, ByteString.EMPTY.lastIndexOf(byteArrayOf(), 100500)) assertEquals(-1, str.lastIndexOf(byteArrayOf(1, 2, 3), 100500)) assertEquals(-1, ByteString.EMPTY.lastIndexOf(byteArrayOf(1, 2, 3))) + assertEquals(-1, str.lastIndexOf(byteArrayOf(2, 3, 5))) } @Test @@ -314,24 +287,20 @@ class ByteStringTest { assertEquals(0, str.lastIndexOf(ByteString(1, 2, 3))) assertEquals(-1, str.lastIndexOf(ByteString(0, 1, 2))) assertEquals(2, str.lastIndexOf(ByteString(3, 4, 5))) - assertEquals(-1, str.lastIndexOf(ByteString(1, 2, 3), 1)) assertEquals(1, str.lastIndexOf(ByteString(2, 3, 4), 1)) - assertEquals(str.size, str.lastIndexOf(ByteString.EMPTY)) assertEquals(str.size, str.lastIndexOf(ByteString.EMPTY)) - assertEquals(2, str.lastIndexOf(ByteString(3, 4), -1000)) assertEquals(0, str.lastIndexOf(ByteString(1), -1)) - assertEquals(4, ByteString(1, 1, 1, 1, 1).lastIndexOf(ByteString(1))) assertEquals(3, ByteString(0, 1, 0, 1, 0).lastIndexOf(ByteString(1, 0))) - assertEquals(0, ByteString.EMPTY.lastIndexOf(ByteString.EMPTY)) assertEquals(0, ByteString.EMPTY.lastIndexOf(ByteString.EMPTY, -100500)) assertEquals(0, ByteString.EMPTY.lastIndexOf(ByteString.EMPTY, 100500)) assertEquals(-1, str.lastIndexOf(ByteString(1, 2, 3), 100500)) assertEquals(-1, ByteString.EMPTY.lastIndexOf(ByteString(1, 2, 3))) + assertEquals(-1, str.lastIndexOf(ByteString(2, 3, 5))) } @Test @@ -402,582 +371,29 @@ class ByteStringTest { fun testToString() { assertEquals("[size=0]", ByteString.EMPTY.toString()) assertEquals("[size=1 hex=00]", ByteString(0).toString()) - assertEquals("[size=16 hex=000102030405060708090A0B0C0D0E0F]", - ByteString(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15).toString()) - assertEquals("[size=64 hex=0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000]", - ByteString(ByteArray(64)).toString()) - assertEquals("[size=65 hex=0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000…]", - ByteString(ByteArray(65)).toString()) - assertEquals("[size=65 hex=0000000000000000000000000000000000000000000000000000000000000000" + - "000000000000000000000000000000000000000000000000000000000000000000]", - ByteString(ByteArray(65)).toString(true)) + assertEquals( + "[size=16 hex=000102030405060708090A0B0C0D0E0F]", + ByteString(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15).toString() + ) + assertEquals( + "[size=64 hex=0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000]", + ByteString(ByteArray(64)).toString() + ) + assertEquals( + "[size=65 hex=0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000…]", + ByteString(ByteArray(65)).toString() + ) + assertEquals( + "[size=65 hex=0000000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000000000]", + ByteString(ByteArray(65)).toString(true) + ) + assertEquals( + "[size=65 hex=0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000…]", + ByteString(ByteArray(65)).toString(false) + ) } - -// @Test -// fun startsWithByteString() { -// val byteString = "112233".decodeHex() -// assertTrue(byteString.startsWith("".decodeHex())) -// assertTrue(byteString.startsWith("11".decodeHex())) -// assertTrue(byteString.startsWith("1122".decodeHex())) -// assertTrue(byteString.startsWith("112233".decodeHex())) -// assertFalse(byteString.startsWith("2233".decodeHex())) -// assertFalse(byteString.startsWith("11223344".decodeHex())) -// assertFalse(byteString.startsWith("112244".decodeHex())) -// } -// -// @Test -// fun endsWithByteString() { -// val byteString = "112233".decodeHex() -// assertTrue(byteString.endsWith("".decodeHex())) -// assertTrue(byteString.endsWith("33".decodeHex())) -// assertTrue(byteString.endsWith("2233".decodeHex())) -// assertTrue(byteString.endsWith("112233".decodeHex())) -// assertFalse(byteString.endsWith("1122".decodeHex())) -// assertFalse(byteString.endsWith("00112233".decodeHex())) -// assertFalse(byteString.endsWith("002233".decodeHex())) -// } -// -// @Test -// fun startsWithByteArray() { -// val byteString = "112233".decodeHex() -// assertTrue(byteString.startsWith("".decodeHex().toByteArray())) -// assertTrue(byteString.startsWith("11".decodeHex().toByteArray())) -// assertTrue(byteString.startsWith("1122".decodeHex().toByteArray())) -// assertTrue(byteString.startsWith("112233".decodeHex().toByteArray())) -// assertFalse(byteString.startsWith("2233".decodeHex().toByteArray())) -// assertFalse(byteString.startsWith("11223344".decodeHex().toByteArray())) -// assertFalse(byteString.startsWith("112244".decodeHex().toByteArray())) -// } -// -// @Test -// fun endsWithByteArray() { -// val byteString = "112233".decodeHex() -// assertTrue(byteString.endsWith("".decodeHex().toByteArray())) -// assertTrue(byteString.endsWith("33".decodeHex().toByteArray())) -// assertTrue(byteString.endsWith("2233".decodeHex().toByteArray())) -// assertTrue(byteString.endsWith("112233".decodeHex().toByteArray())) -// assertFalse(byteString.endsWith("1122".decodeHex().toByteArray())) -// assertFalse(byteString.endsWith("00112233".decodeHex().toByteArray())) -// assertFalse(byteString.endsWith("002233".decodeHex().toByteArray())) -// } -// -// @Test -// fun indexOfByteString() { -// val byteString = "112233".decodeHex() -// assertEquals(0, byteString.indexOf("112233".decodeHex()).toLong()) -// assertEquals(0, byteString.indexOf("1122".decodeHex()).toLong()) -// assertEquals(0, byteString.indexOf("11".decodeHex()).toLong()) -// assertEquals(0, byteString.indexOf("11".decodeHex(), 0).toLong()) -// assertEquals(0, byteString.indexOf("".decodeHex()).toLong()) -// assertEquals(0, byteString.indexOf("".decodeHex(), 0).toLong()) -// assertEquals(1, byteString.indexOf("2233".decodeHex()).toLong()) -// assertEquals(1, byteString.indexOf("22".decodeHex()).toLong()) -// assertEquals(1, byteString.indexOf("22".decodeHex(), 1).toLong()) -// assertEquals(1, byteString.indexOf("".decodeHex(), 1).toLong()) -// assertEquals(2, byteString.indexOf("33".decodeHex()).toLong()) -// assertEquals(2, byteString.indexOf("33".decodeHex(), 2).toLong()) -// assertEquals(2, byteString.indexOf("".decodeHex(), 2).toLong()) -// assertEquals(3, byteString.indexOf("".decodeHex(), 3).toLong()) -// assertEquals(-1, byteString.indexOf("112233".decodeHex(), 1).toLong()) -// assertEquals(-1, byteString.indexOf("44".decodeHex()).toLong()) -// assertEquals(-1, byteString.indexOf("11223344".decodeHex()).toLong()) -// assertEquals(-1, byteString.indexOf("112244".decodeHex()).toLong()) -// assertEquals(-1, byteString.indexOf("112233".decodeHex(), 1).toLong()) -// assertEquals(-1, byteString.indexOf("2233".decodeHex(), 2).toLong()) -// assertEquals(-1, byteString.indexOf("33".decodeHex(), 3).toLong()) -// assertEquals(3, byteString.indexOf("".decodeHex(), 4).toLong()) -// } -// -// @Test -// fun indexOfWithOffset() { -// val byteString = "112233112233".decodeHex() -// assertEquals(0, byteString.indexOf("112233".decodeHex(), -1).toLong()) -// assertEquals(0, byteString.indexOf("112233".decodeHex(), 0).toLong()) -// assertEquals(0, byteString.indexOf("112233".decodeHex()).toLong()) -// assertEquals(3, byteString.indexOf("112233".decodeHex(), 1).toLong()) -// assertEquals(3, byteString.indexOf("112233".decodeHex(), 2).toLong()) -// assertEquals(3, byteString.indexOf("112233".decodeHex(), 3).toLong()) -// assertEquals(-1, byteString.indexOf("112233".decodeHex(), 4).toLong()) -// } -// -// @Test -// fun indexOfByteArray() { -// val byteString = "112233".decodeHex() -// assertEquals(0, byteString.indexOf("112233".decodeHex().toByteArray())) -// assertEquals(1, byteString.indexOf("2233".decodeHex().toByteArray())) -// assertEquals(2, byteString.indexOf("33".decodeHex().toByteArray())) -// assertEquals(-1, byteString.indexOf("112244".decodeHex().toByteArray())) -// } -// -// @Test -// fun lastIndexOfByteString() { -// val byteString = "112233".decodeHex() -// assertEquals(0, byteString.lastIndexOf("112233".decodeHex()).toLong()) -// assertEquals(0, byteString.lastIndexOf("1122".decodeHex()).toLong()) -// assertEquals(0, byteString.lastIndexOf("11".decodeHex()).toLong()) -// assertEquals(0, byteString.lastIndexOf("11".decodeHex(), 3).toLong()) -// assertEquals(0, byteString.lastIndexOf("11".decodeHex(), 0).toLong()) -// assertEquals(0, byteString.lastIndexOf("".decodeHex(), 0).toLong()) -// assertEquals(1, byteString.lastIndexOf("2233".decodeHex()).toLong()) -// assertEquals(1, byteString.lastIndexOf("22".decodeHex()).toLong()) -// assertEquals(1, byteString.lastIndexOf("22".decodeHex(), 3).toLong()) -// assertEquals(1, byteString.lastIndexOf("22".decodeHex(), 1).toLong()) -// assertEquals(1, byteString.lastIndexOf("".decodeHex(), 1).toLong()) -// assertEquals(2, byteString.lastIndexOf("33".decodeHex()).toLong()) -// assertEquals(2, byteString.lastIndexOf("33".decodeHex(), 3).toLong()) -// assertEquals(2, byteString.lastIndexOf("33".decodeHex(), 2).toLong()) -// assertEquals(2, byteString.lastIndexOf("".decodeHex(), 2).toLong()) -// assertEquals(3, byteString.lastIndexOf("".decodeHex(), 3).toLong()) -// assertEquals(3, byteString.lastIndexOf("".decodeHex()).toLong()) -// assertEquals(-1, byteString.lastIndexOf("112233".decodeHex(), -1).toLong()) -// assertEquals(-1, byteString.lastIndexOf("112233".decodeHex(), -2).toLong()) -// assertEquals(-1, byteString.lastIndexOf("44".decodeHex()).toLong()) -// assertEquals(-1, byteString.lastIndexOf("11223344".decodeHex()).toLong()) -// assertEquals(-1, byteString.lastIndexOf("112244".decodeHex()).toLong()) -// assertEquals(-1, byteString.lastIndexOf("2233".decodeHex(), 0).toLong()) -// assertEquals(-1, byteString.lastIndexOf("33".decodeHex(), 1).toLong()) -// assertEquals(-1, byteString.lastIndexOf("".decodeHex(), -1).toLong()) -// } -// -// @Test -// fun lastIndexOfByteArray() { -// val byteString = "112233".decodeHex() -// assertEquals(0, byteString.lastIndexOf("112233".decodeHex().toByteArray())) -// assertEquals(1, byteString.lastIndexOf("2233".decodeHex().toByteArray())) -// assertEquals(2, byteString.lastIndexOf("33".decodeHex().toByteArray())) -// assertEquals(3, byteString.lastIndexOf("".decodeHex().toByteArray())) -// } -// -// @Test -// fun equalsTest() { -// val byteString = "000102".decodeHex() -// assertEquals(byteString, byteString) -// assertEquals(byteString, "000102".decodeHex()) -// assertNotEquals(byteString, Any()) -// assertNotEquals(byteString, "000201".decodeHex()) -// } -// -// @Ignore -// @Test -// fun equalsEmptyTest() { -// // assertEquals("".decodeHex(), ByteString.EMPTY) -// // assertEquals("".decodeHex(), ByteString.of()) -// // assertEquals(ByteString.EMPTY, factory.decodeHex("")) -// // assertEquals(ByteString.of(), factory.decodeHex("")) -// } -// -// private val bronzeHorseman = "На берегу пустынных волн" -// -// /* -// @Test fun utf8() { -// val byteString = factory.encodeUtf8(bronzeHorseman) -// assertEquals(byteString.toByteArray().toList(), bronzeHorseman.commonAsUtf8ToByteArray().toList()) -// assertTrue(byteString == ByteString.of(*bronzeHorseman.commonAsUtf8ToByteArray())) -// assertEquals( -// byteString, -// ( -// "d09dd0b020d0b1d0b5d180d0b5d0b3d18320d0bfd183d181" + -// "d182d18bd0bdd0bdd18bd18520d0b2d0bed0bbd0bd" -// ).decodeHex(), -// ) -// assertEquals(byteString.utf8(), bronzeHorseman) -// } -// -// */ -// -// @Test -// fun testHashCode() { -// val byteString = "0102".decodeHex() -// assertEquals(byteString.hashCode().toLong(), byteString.hashCode().toLong()) -// assertEquals(byteString.hashCode().toLong(), "0102".decodeHex().hashCode().toLong()) -// } -// -// /* -// @Test fun toAsciiLowerCaseNoUppercase() { -// val s = "a1_+".encodeUtf8() -// assertEquals(s, s.toAsciiLowercase()) -// if (factory === ByteStringFactory.BYTE_STRING) { -// assertSame(s, s.toAsciiLowercase()) -// } -// } -// -// @Test fun toAsciiAllUppercase() { -// assertEquals("ab".encodeUtf8(), factory.encodeUtf8("AB").toAsciiLowercase()) -// } -// -// @Test fun toAsciiStartsLowercaseEndsUppercase() { -// assertEquals("abcd".encodeUtf8(), factory.encodeUtf8("abCD").toAsciiLowercase()) -// } -// -// @Test fun toAsciiStartsUppercaseEndsLowercase() { -// assertEquals("ABCD".encodeUtf8(), factory.encodeUtf8("ABcd").toAsciiUppercase()) -// } -// */ -// -// @Test -// fun substring() { -// val byteString = ByteString("Hello, World!".encodeToByteArray()) -// -// assertEquals(byteString.substring(0), byteString) -// assertEquals(byteString.substring(0, 5), ByteString("Hello".encodeToByteArray())) -// assertEquals(byteString.substring(7), ByteString("World!".encodeToByteArray())) -// assertEquals(byteString.substring(6, 6), ByteString("".encodeToByteArray())) -// } -// -// @Test -// fun substringWithInvalidBounds() { -// val byteString = ByteString("Hello, World!".encodeToByteArray()) -// -// assertFailsWith { -// byteString.substring(-1) -// } -// -// assertFailsWith { -// byteString.substring(0, 14) -// } -// -// assertFailsWith { -// byteString.substring(8, 7) -// } -// } -// -// /* -// @Test fun encodeBase64() { -// assertEquals("", factory.encodeUtf8("").base64()) -// assertEquals("AA==", factory.encodeUtf8("\u0000").base64()) -// assertEquals("AAA=", factory.encodeUtf8("\u0000\u0000").base64()) -// assertEquals("AAAA", factory.encodeUtf8("\u0000\u0000\u0000").base64()) -// assertEquals( -// "SG93IG1hbnkgbGluZXMgb2YgY29kZSBhcmUgdGhlcmU/ICdib3V0IDIgbWlsbGlvbi4=", -// factory.encodeUtf8("How many lines of code are there? 'bout 2 million.").base64(), -// ) -// } -// -// @Test fun encodeBase64Url() { -// assertEquals("", factory.encodeUtf8("").base64Url()) -// assertEquals("AA==", factory.encodeUtf8("\u0000").base64Url()) -// assertEquals("AAA=", factory.encodeUtf8("\u0000\u0000").base64Url()) -// assertEquals("AAAA", factory.encodeUtf8("\u0000\u0000\u0000").base64Url()) -// assertEquals( -// "SG93IG1hbnkgbGluZXMgb2YgY29kZSBhcmUgdGhlcmU_ICdib3V0IDIgbWlsbGlvbi4=", -// factory.encodeUtf8("How many lines of code are there? 'bout 2 million.").base64Url(), -// ) -// } -// -// @Test fun ignoreUnnecessaryPadding() { -// assertEquals("", "====".decodeBase64()!!.utf8()) -// assertEquals("\u0000\u0000\u0000", "AAAA====".decodeBase64()!!.utf8()) -// } -// -// @Test fun decodeBase64() { -// assertEquals("", "".decodeBase64()!!.utf8()) -// assertEquals(null, "/===".decodeBase64()) // Can't do anything with 6 bits! -// assertEquals("ff".decodeHex(), "//==".decodeBase64()) -// assertEquals("ff".decodeHex(), "__==".decodeBase64()) -// assertEquals("ffff".decodeHex(), "///=".decodeBase64()) -// assertEquals("ffff".decodeHex(), "___=".decodeBase64()) -// assertEquals("ffffff".decodeHex(), "////".decodeBase64()) -// assertEquals("ffffff".decodeHex(), "____".decodeBase64()) -// assertEquals("ffffffffffff".decodeHex(), "////////".decodeBase64()) -// assertEquals("ffffffffffff".decodeHex(), "________".decodeBase64()) -// assertEquals( -// "What's to be scared about? It's just a little hiccup in the power...", -// ( -// "V2hhdCdzIHRvIGJlIHNjYXJlZCBhYm91dD8gSXQncyBqdXN0IGEgbGl0dGxlIGhpY2" + -// "N1cCBpbiB0aGUgcG93ZXIuLi4=" -// ).decodeBase64()!!.utf8(), -// ) -// // Uses two encoding styles. Malformed, but supported as a side-effect. -// assertEquals("ffffff".decodeHex(), "__//".decodeBase64()) -// } -// -// @Test fun decodeBase64WithWhitespace() { -// assertEquals("\u0000\u0000\u0000", " AA AA ".decodeBase64()!!.utf8()) -// assertEquals("\u0000\u0000\u0000", " AA A\r\nA ".decodeBase64()!!.utf8()) -// assertEquals("\u0000\u0000\u0000", "AA AA".decodeBase64()!!.utf8()) -// assertEquals("\u0000\u0000\u0000", " AA AA ".decodeBase64()!!.utf8()) -// assertEquals("\u0000\u0000\u0000", " AA A\r\nA ".decodeBase64()!!.utf8()) -// assertEquals("\u0000\u0000\u0000", "A AAA".decodeBase64()!!.utf8()) -// assertEquals("", " ".decodeBase64()!!.utf8()) -// } -// -// */ -// -// @Test -// fun encodeHex() { -// assertEquals("000102", ByteString(0x0, 0x1, 0x2).hex()) -// } -// -// @Test -// fun decodeHex() { -// val actual = "CAFEBABE".decodeHex() -// val expected = ByteString(-54, -2, -70, -66) -// assertEquals(expected, actual) -// } -// -// @Test -// fun decodeHexOddNumberOfChars() { -// assertFailsWith { -// "aaa".decodeHex() -// } -// } -// -// @Test -// fun decodeHexInvalidChar() { -// assertFailsWith { -// "a\u0000".decodeHex() -// } -// } -// -// @Test -// fun toStringOnEmpty() { -// assertEquals("[size=0]", "".decodeHex().toString()) -// } -// -// @Test -// fun toStringOnShortText() { -// assertEquals( -// "[text=Tyrannosaur]", -// ByteString("Tyrannosaur".encodeToByteArray()).toString(), -// ) -// //assertEquals( -// //"[text=təˈranəˌsôr]", -// //factory.decodeHex("74c999cb8872616ec999cb8c73c3b472").toString(), -// //) -// TODO() -// } -// -// @Test -// fun toStringOnLongTextIsTruncated() { -// /* -// val raw = ( -// "Um, I'll tell you the problem with the scientific power that you're using here, " + -// "it didn't require any discipline to attain it. You read what others had done and you " + -// "took the next step. You didn't earn the knowledge for yourselves, so you don't take any " + -// "responsibility for it. You stood on the shoulders of geniuses to accomplish something " + -// "as fast as you could, and before you even knew what you had, you patented it, and " + -// "packaged it, and slapped it on a plastic lunchbox, and now you're selling it, you wanna " + -// "sell it." -// ) -// -// */ -// //assertEquals( -// // "[size=517 text=Um, I'll tell you the problem with the scientific power that " + -// // "you…]", -// // factory.encodeUtf8(raw).toString(), -// //) -// /* -// val war = ( -// "Սm, I'll 𝓽𝖾ll ᶌօ𝘂 ᴛℎ℮ 𝜚𝕣०bl𝖾m wі𝕥𝒽 𝘵𝘩𝐞 𝓼𝙘𝐢𝔢𝓷𝗍𝜄𝚏𝑖c 𝛠𝝾w𝚎𝑟 𝕥h⍺𝞃 𝛄𝓸𝘂'𝒓𝗲 υ𝖘𝓲𝗇ɡ 𝕙𝚎𝑟e, " + -// "𝛊𝓽 ⅆ𝕚𝐝𝝿'𝗍 𝔯𝙚𝙦ᴜ𝜾𝒓𝘦 𝔞𝘯𝐲 ԁ𝜄𝑠𝚌ι𝘱lι𝒏e 𝑡𝜎 𝕒𝚝𝖙𝓪і𝞹 𝔦𝚝. 𝒀ο𝗎 𝔯𝑒⍺𝖉 w𝐡𝝰𝔱 𝞂𝞽һ𝓮𝓇ƽ հ𝖺𝖉 ⅾ𝛐𝝅ⅇ 𝝰πԁ 𝔂ᴑᴜ 𝓉ﮨ၀𝚔 " + -// "т𝒽𝑒 𝗇𝕖ⅹ𝚝 𝔰𝒕е𝓅. 𝘠ⲟ𝖚 𝖉ⅰԁ𝝕'τ 𝙚𝚊r𝞹 𝘵Ꮒ𝖾 𝝒𝐧هwl𝑒𝖉ƍ𝙚 𝓯૦r 𝔂𝞼𝒖𝕣𝑠𝕖l𝙫𝖊𝓼, 𐑈о y𝘰𝒖 ⅆە𝗇't 𝜏α𝒌𝕖 𝛂𝟉ℽ " + -// "𝐫ⅇ𝗌ⲣ๐ϖ𝖘ꙇᖯ𝓲l𝓲𝒕𝘆 𝐟𝞼𝘳 𝚤𝑡. 𝛶𝛔𝔲 s𝕥σσ𝐝 ﮩ𝕟 𝒕𝗁𝔢 𝘴𝐡𝜎ᴜlⅾ𝓮𝔯𝚜 𝛐𝙛 ᶃ𝚎ᴨᎥս𝚜𝘦𝓈 𝓽𝞸 a𝒄𝚌𝞸mρl𝛊ꜱ𝐡 𝓈𝚘m𝚎𝞃𝔥⍳𝞹𝔤 𝐚𝗌 𝖋a𝐬𝒕 " + -// "αs γ𝛐𝕦 𝔠ﻫ𝛖lԁ, 𝚊π𝑑 Ь𝑒𝙛૦𝓇𝘦 𝓎٥𝖚 ⅇvℯ𝝅 𝜅ո𝒆w w𝗵𝒂𝘁 ᶌ੦𝗎 h𝐚𝗱, 𝜸ﮨ𝒖 𝓹𝝰𝔱𝖾𝗇𝓽𝔢ⅆ і𝕥, 𝚊𝜛𝓭 𝓹𝖺ⅽϰ𝘢ℊеᏧ 𝑖𝞃, " + -// "𝐚𝛑ꓒ 𝙨l𝔞р𝘱𝔢𝓭 ɩ𝗍 ہ𝛑 𝕒 pl𝛂ѕᴛ𝗂𝐜 l𝞄ℼ𝔠𝒽𝑏ﮪ⨯, 𝔞ϖ𝒹 n𝛔w 𝛾𝐨𝞄'𝗿𝔢 ꜱ℮ll𝙞nɡ ɩ𝘁, 𝙮𝕠𝛖 w𝑎ℼ𝚗𝛂 𝕤𝓮ll 𝙞𝓉." -// ) -// -// */ -// //assertEquals( -// // "[size=1496 text=Սm, I'll 𝓽𝖾ll ᶌօ𝘂 ᴛℎ℮ 𝜚𝕣०bl𝖾m wі𝕥𝒽 𝘵𝘩𝐞 𝓼𝙘𝐢𝔢𝓷𝗍𝜄𝚏𝑖c 𝛠𝝾w𝚎𝑟 𝕥h⍺𝞃 " + -// // "𝛄𝓸𝘂…]", -// // factory.encodeUtf8(war).toString(), -// //) -// TODO() -// } -// -// @Test -// fun toStringOnTextWithNewlines() { -// // Instead of emitting a literal newline in the toString(), these are escaped as "\n". -// //assertEquals( -// // "[text=a\\r\\nb\\nc\\rd\\\\e]", -// // factory.encodeUtf8("a\r\nb\nc\rd\\e").toString(), -// // ) -// TODO() -// } -// -// @Test -// fun toStringOnData() { -// /* -// val byteString = factory.decodeHex( -// "" + -// "60b420bb3851d9d47acb933dbe70399bf6c92da33af01d4fb770e98c0325f41d3ebaf8986da712c82bcd4d55" + -// "4bf0b54023c29b624de9ef9c2f931efc580f9afb", -// ) -// assertEquals( -// "[hex=" + -// "60b420bb3851d9d47acb933dbe70399bf6c92da33af01d4fb770e98c0325f41d3ebaf8986da712c82bcd4d55" + -// "4bf0b54023c29b624de9ef9c2f931efc580f9afb]", -// byteString.toString(), -// ) -// */ -// TODO() -// } -// -// @Test -// fun toStringOnLongDataIsTruncated() { -// /* -// val byteString = factory.decodeHex( -// "" + -// "60b420bb3851d9d47acb933dbe70399bf6c92da33af01d4fb770e98c0325f41d3ebaf8986da712c82bcd4d55" + -// "4bf0b54023c29b624de9ef9c2f931efc580f9afba1", -// ) -// assertEquals( -// "[size=65 hex=" + -// "60b420bb3851d9d47acb933dbe70399bf6c92da33af01d4fb770e98c0325f41d3ebaf8986da712c82bcd4d55" + -// "4bf0b54023c29b624de9ef9c2f931efc580f9afb…]", -// byteString.toString(), -// ) -// */ -// TODO() -// } -// -// @Test -// fun compareToSingleBytes() { -// val originalByteStrings = listOf( -// "00".decodeHex(), -// "01".decodeHex(), -// "7e".decodeHex(), -// "7f".decodeHex(), -// "80".decodeHex(), -// "81".decodeHex(), -// "fe".decodeHex(), -// "ff".decodeHex(), -// ) -// -// val sortedByteStrings = originalByteStrings.toMutableList() -// sortedByteStrings.shuffle(Random(0)) -// assertNotEquals(originalByteStrings, sortedByteStrings) -// -// sortedByteStrings.sort() -// assertEquals(originalByteStrings, sortedByteStrings) -// } -// -// @Test -// fun compareToMultipleBytes() { -// val originalByteStrings = listOf( -// "".decodeHex(), -// "00".decodeHex(), -// "0000".decodeHex(), -// "000000".decodeHex(), -// "00000000".decodeHex(), -// "0000000000".decodeHex(), -// "0000000001".decodeHex(), -// "000001".decodeHex(), -// "00007f".decodeHex(), -// "0000ff".decodeHex(), -// "000100".decodeHex(), -// "000101".decodeHex(), -// "007f00".decodeHex(), -// "00ff00".decodeHex(), -// "010000".decodeHex(), -// "010001".decodeHex(), -// "01007f".decodeHex(), -// "0100ff".decodeHex(), -// "010100".decodeHex(), -// "01010000".decodeHex(), -// "0101000000".decodeHex(), -// "0101000001".decodeHex(), -// "010101".decodeHex(), -// "7f0000".decodeHex(), -// "7f0000ffff".decodeHex(), -// "ffffff".decodeHex(), -// ) -// -// val sortedByteStrings = originalByteStrings.toMutableList() -// sortedByteStrings.shuffle(Random(0)) -// assertNotEquals(originalByteStrings, sortedByteStrings) -// -// sortedByteStrings.sort() -// assertEquals(originalByteStrings, sortedByteStrings) -// } -// -// /* -// @Test fun testHash() = with(factory.encodeUtf8("Kevin")) { -// assertEquals("e043899daa0c7add37bc99792b2c045d6abbc6dc", sha1().hex()) -// assertEquals("f1cd318e412b5f7226e5f377a9544ff7", md5().hex()) -// assertEquals("0e4dd66217fc8d2e298b78c8cd9392870dcd065d0ff675d0edff5bcd227837e9", sha256().hex()) -// assertEquals("483676b93c4417198b465083d196ec6a9fab8d004515874b8ff47e041f5f56303cc08179625030b8b5b721c09149a18f0f59e64e7ae099518cea78d3d83167e1", sha512().hex()) -// } -//*/ -// -// @Test -// fun copyInto() { -// val byteString = ByteString("abcdefgh".encodeToByteArray()) -// val byteArray = "WwwwXxxxYyyyZzzz".encodeToByteArray() -// byteString.copyInto(byteArray, 0, 0, 5) -// assertEquals("abcdexxxYyyyZzzz", byteArray.decodeToString()) -// } -// -// @Test -// fun copyIntoFullRange() { -// val byteString = ByteString("abcdefghijklmnop".encodeToByteArray()) -// val byteArray = "WwwwXxxxYyyyZzzz".encodeToByteArray() -// byteString.copyInto(byteArray, 0, 0, 16) -// assertEquals("abcdefghijklmnop", byteArray.decodeToString()) -// } -// -// @Test -// fun copyIntoWithTargetOffset() { -// val byteString = ByteString("abcdefgh".encodeToByteArray()) -// val byteArray = "WwwwXxxxYyyyZzzz".encodeToByteArray() -// byteString.copyInto(byteArray, 11, 0, 5) -// assertEquals("WwwwXxxxYyyabcde", byteArray.decodeToString()) -// } -// -// @Test -// fun copyIntoWithSourceOffset() { -// val byteString = ByteString("abcdefgh".encodeToByteArray()) -// val byteArray = "WwwwXxxxYyyyZzzz".encodeToByteArray() -// byteString.copyInto(byteArray, 0, 3, 5) -// assertEquals("defghxxxYyyyZzzz", byteArray.decodeToString()) -// } -// -// /* -// @Test fun copyIntoWithAllParameters() { -// val byteString = factory.encodeUtf8("abcdefgh") -// val byteArray = "WwwwXxxxYyyyZzzz".encodeToByteArray() -// byteString.copyInto(offset = 3, target = byteArray, targetOffset = 11, byteCount = 5) -// assertEquals("WwwwXxxxYyydefgh", byteArray.decodeToString()) -// } -// -// */ -// -// /* -// TODO -// @Test fun copyIntoBoundsChecks() { -// val byteString = ByteString("abcdefgh".encodeToByteArray()) -// val byteArray = "WwwwXxxxYyyyZzzz".encodeToByteArray() -// assertFailsWith { -// byteString.copyInto(offset = -1, target = byteArray, targetOffset = 1, byteCount = 1) -// } -// assertFailsWith { -// byteString.copyInto(offset = 9, target = byteArray, targetOffset = 0, byteCount = 0) -// } -// assertFailsWith { -// byteString.copyInto(offset = 1, target = byteArray, targetOffset = -1, byteCount = 1) -// } -// assertFailsWith { -// byteString.copyInto(offset = 1, target = byteArray, targetOffset = 17, byteCount = 1) -// } -// assertFailsWith { -// byteString.copyInto(offset = 7, target = byteArray, targetOffset = 1, byteCount = 2) -// } -// assertFailsWith { -// byteString.copyInto(offset = 1, target = byteArray, targetOffset = 15, byteCount = 2) -// } -// } -// -// @Test fun copyEmptyAtBounds() { -// val byteString = ByteString("abcdefgh".encodeToByteArray()) -// val byteArray = "WwwwXxxxYyyyZzzz".encodeToByteArray() -// byteString.copyInto(offset = 0, target = byteArray, targetOffset = 0, byteCount = 0) -// assertEquals("WwwwXxxxYyyyZzzz", byteArray.decodeToString()) -// byteString.copyInto(offset = 0, target = byteArray, targetOffset = 16, byteCount = 0) -// assertEquals("WwwwXxxxYyyyZzzz", byteArray.decodeToString()) -// byteString.copyInto(offset = 8, target = byteArray, targetOffset = 0, byteCount = 0) -// assertEquals("WwwwXxxxYyyyZzzz", byteArray.decodeToString()) -// } -// */ } \ No newline at end of file From e09f1886837930c6fdf444623dae2132ce97b67b Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Thu, 22 Jun 2023 10:42:39 +0200 Subject: [PATCH 07/32] Removed debugging code --- core/common/src/Buffer.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/common/src/Buffer.kt b/core/common/src/Buffer.kt index 39f5bbfba..12724a16c 100644 --- a/core/common/src/Buffer.kt +++ b/core/common/src/Buffer.kt @@ -320,9 +320,8 @@ public class Buffer : Source, Sink { override fun readAtMostTo(sink: ByteArray, startIndex: Int, endIndex: Int): Int { checkBounds(sink.size, startIndex, endIndex) - val s = head ?: return -1 - val toCopy = minOf(endIndex - startIndex, s.limit - s.pos) - println("${s.limit} ${s.pos}") + val s = head ?: return -1 + val toCopy = minOf(endIndex - startIndex, s.limit - s.pos) s.data.copyInto( destination = sink, destinationOffset = startIndex, startIndex = s.pos, endIndex = s.pos + toCopy ) From 09e8676ca537b6b3f7097c470815c86fec9275f2 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Fri, 23 Jun 2023 12:26:13 +0200 Subject: [PATCH 08/32] Change ByteString::toString to always return full string representation --- .../src/commonMain/kotlin/ByteString.kt | 37 ++++++------------- .../kotlinx/io/bytestring/ByteStringTest.kt | 25 +++---------- 2 files changed, 17 insertions(+), 45 deletions(-) diff --git a/bytestring/src/commonMain/kotlin/ByteString.kt b/bytestring/src/commonMain/kotlin/ByteString.kt index 5633a5991..8e1c17f2a 100644 --- a/bytestring/src/commonMain/kotlin/ByteString.kt +++ b/bytestring/src/commonMain/kotlin/ByteString.kt @@ -209,47 +209,34 @@ public class ByteString private constructor( /** * Returns a string representation of this byte string. A string representation consists of [size] and - * prefix of the byte sequence wrapped by this byte string encoded to hexadecimal string. + * a hexadecimal-encoded string of a byte sequence wrapped by this byte string. * - * By default, for short byte string, the string representation looks like `[size=3 hex=ABCDEF]`, - * for empty strings it's always `[size=0]` and for long string only a prefix of data will be included: - * `[size=128 hex=0123456789…]`. - * This behavior can be changed by setting [full] to `true`, in that case a string representation will include - * hexadecimal string encoding full byte string's content. + * The string representation has the following format `ByteString(size=3 hex=ABCDEF)`, + * for empty strings it's always `ByteString(size=0)`. * - * @param full the flag indicating whether the resulting string will include the whole byte string or only - * its prefix. + * Note that a string representation includes the whole byte string content encoded. + * Due to limitations of the maxi */ - public fun toString(full: Boolean = false): String { + override fun toString(): String { if (isEmpty()) { - return "[size=0]" + return "ByteString(size=0)" } - // format: "[size=XXX hex=YYYY]" + // format: "ByteString(size=XXX hex=YYYY)" val sizeStr = size.toString() - val hexLen = if (full) size else min(64, size) - val len = 12 + sizeStr.length + hexLen * 2 + val len = 22 + sizeStr.length + size * 2 return with(StringBuilder(len)) { - append("[size=") + append("ByteString(size=") append(sizeStr) append(" hex=") - for (i in 0 until hexLen) { + for (i in 0 until size) { val b = this@ByteString[i].toInt() append(HEX_DIGITS[(b ushr 4) and 0xf]) append(HEX_DIGITS[b and 0xf]) } - if (!full && size > 64) { - append('…') - } - append(']') + append(')') }.toString() } - /** - * Returns a string representation of this byte string. - * This call is equivalent to `toString(false)`. - */ - override fun toString(): String = toString(false) - /** * Returns a reference to the underlying array. * diff --git a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt index afa700236..643db2fe4 100644 --- a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt +++ b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt @@ -369,31 +369,16 @@ class ByteStringTest { @Test fun testToString() { - assertEquals("[size=0]", ByteString.EMPTY.toString()) - assertEquals("[size=1 hex=00]", ByteString(0).toString()) + assertEquals("ByteString(size=0)", ByteString.EMPTY.toString()) + assertEquals("ByteString(size=1 hex=00)", ByteString(0).toString()) assertEquals( - "[size=16 hex=000102030405060708090A0B0C0D0E0F]", + "ByteString(size=16 hex=000102030405060708090A0B0C0D0E0F)", ByteString(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15).toString() ) assertEquals( - "[size=64 hex=0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000]", + "ByteString(size=64 hex=0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000)", ByteString(ByteArray(64)).toString() ) - assertEquals( - "[size=65 hex=0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000…]", - ByteString(ByteArray(65)).toString() - ) - assertEquals( - "[size=65 hex=0000000000000000000000000000000000000000000000000000000000000000" + - "000000000000000000000000000000000000000000000000000000000000000000]", - ByteString(ByteArray(65)).toString(true) - ) - assertEquals( - "[size=65 hex=0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000…]", - ByteString(ByteArray(65)).toString(false) - ) } } \ No newline at end of file From 9c8d00981ab853c434072083bbb754a4fffa4f70 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Fri, 23 Jun 2023 14:18:25 +0200 Subject: [PATCH 09/32] Restructure unsafe API, move utf-8 conversion to base module --- bytestring/api/kotlinx-io-bytestring.api | 16 +++++-- .../src/commonMain/kotlin/Annotations.kt | 21 +++++++++ .../src/commonMain/kotlin/ByteString.kt | 43 ++++++++---------- .../commonMain/kotlin/ByteStringBuilder.kt | 6 +-- .../kotlin/UnsafeByteStringOperations.kt | 38 ++++++++++++++++ .../kotlinx/io/bytestring/ByteStringTest.kt | 10 +++++ .../src/jvmMain/kotlin/ByteStringJvmExt.kt | 24 ++++++++++ .../src/jvmTest/kotlin/ByteStringJvmTest.kt | 28 ++++++++++++ core/api/kotlinx-io-core.api | 2 - core/common/src/ByteStringExt.kt | 26 ++--------- core/common/test/AbstractSourceTest.kt | 5 ++- core/common/test/ByteStringExtensionsTest.kt | 45 ------------------- core/common/test/util.kt | 3 +- 13 files changed, 161 insertions(+), 106 deletions(-) create mode 100644 bytestring/src/commonMain/kotlin/Annotations.kt create mode 100644 bytestring/src/commonMain/kotlin/UnsafeByteStringOperations.kt create mode 100644 bytestring/src/jvmMain/kotlin/ByteStringJvmExt.kt create mode 100644 bytestring/src/jvmTest/kotlin/ByteStringJvmTest.kt delete mode 100644 core/common/test/ByteStringExtensionsTest.kt diff --git a/bytestring/api/kotlinx-io-bytestring.api b/bytestring/api/kotlinx-io-bytestring.api index dc091ac1a..411f7081f 100644 --- a/bytestring/api/kotlinx-io-bytestring.api +++ b/bytestring/api/kotlinx-io-bytestring.api @@ -10,7 +10,6 @@ public final class kotlinx/io/bytestring/ByteString : java/lang/Comparable { public static synthetic fun copyInto$default (Lkotlinx/io/bytestring/ByteString;[BIIIILjava/lang/Object;)V public fun equals (Ljava/lang/Object;)Z public final fun get (I)B - public final fun getByteArrayUnsafe ()[B public final fun getSize ()I public fun hashCode ()I public final fun substring (II)Lkotlinx/io/bytestring/ByteString; @@ -18,13 +17,10 @@ public final class kotlinx/io/bytestring/ByteString : java/lang/Comparable { public final fun toByteArray (II)[B public static synthetic fun toByteArray$default (Lkotlinx/io/bytestring/ByteString;IIILjava/lang/Object;)[B public fun toString ()Ljava/lang/String; - public final fun toString (Z)Ljava/lang/String; - public static synthetic fun toString$default (Lkotlinx/io/bytestring/ByteString;ZILjava/lang/Object;)Ljava/lang/String; } public final class kotlinx/io/bytestring/ByteString$Companion { public final fun getEMPTY ()Lkotlinx/io/bytestring/ByteString; - public final fun wrapUnsafe ([B)Lkotlinx/io/bytestring/ByteString; } public final class kotlinx/io/bytestring/ByteStringBuilder { @@ -48,6 +44,7 @@ public final class kotlinx/io/bytestring/ByteStringKt { public static final fun ByteString ()Lkotlinx/io/bytestring/ByteString; public static final fun endsWith (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;)Z public static final fun endsWith (Lkotlinx/io/bytestring/ByteString;[B)Z + public static final fun fromUtf8 (Lkotlinx/io/bytestring/ByteString$Companion;Ljava/lang/String;)Lkotlinx/io/bytestring/ByteString; public static final fun indexOf (Lkotlinx/io/bytestring/ByteString;BI)I public static final fun indexOf (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;I)I public static final fun indexOf (Lkotlinx/io/bytestring/ByteString;[BI)I @@ -64,8 +61,19 @@ public final class kotlinx/io/bytestring/ByteStringKt { public static synthetic fun lastIndexOf$default (Lkotlinx/io/bytestring/ByteString;[BIILjava/lang/Object;)I public static final fun startsWith (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;)Z public static final fun startsWith (Lkotlinx/io/bytestring/ByteString;[B)Z + public static final fun toUtf8 (Lkotlinx/io/bytestring/ByteString;)Ljava/lang/String; } public abstract interface annotation class kotlinx/io/bytestring/UnsafeByteStringApi : java/lang/annotation/Annotation { } +public final class kotlinx/io/bytestring/unsafe/UnsafeByteStringOperations { + public static final field Companion Lkotlinx/io/bytestring/unsafe/UnsafeByteStringOperations$Companion; + public fun ()V +} + +public final class kotlinx/io/bytestring/unsafe/UnsafeByteStringOperations$Companion { + public final fun getByteArrayUnsafe (Lkotlinx/io/bytestring/ByteString;)[B + public final fun wrapUnsafe ([B)Lkotlinx/io/bytestring/ByteString; +} + diff --git a/bytestring/src/commonMain/kotlin/Annotations.kt b/bytestring/src/commonMain/kotlin/Annotations.kt new file mode 100644 index 000000000..d35c7e4c1 --- /dev/null +++ b/bytestring/src/commonMain/kotlin/Annotations.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. + */ + +package kotlinx.io.bytestring + +/** + * Marks declarations whose usage may brake some ByteString invariants. + * + * Consider using other APIs instead when possible. + * Otherwise, make sure to read documentation describing an unsafe API. + */ +@MustBeDocumented +@Retention(AnnotationRetention.BINARY) +@RequiresOptIn( + level = RequiresOptIn.Level.ERROR, + message = "This is a unsafe API and its use may corrupt the data stored in a byte string. " + + "Make sure you fully read and understand documentation of the declaration that is marked as an unsafe API." +) +public annotation class UnsafeByteStringApi \ No newline at end of file diff --git a/bytestring/src/commonMain/kotlin/ByteString.kt b/bytestring/src/commonMain/kotlin/ByteString.kt index 8e1c17f2a..9a9b42374 100644 --- a/bytestring/src/commonMain/kotlin/ByteString.kt +++ b/bytestring/src/commonMain/kotlin/ByteString.kt @@ -26,21 +26,6 @@ import kotlin.math.min private val HEX_DIGITS = "0123456789ABCDEF".toCharArray() -/** - * Marks declarations whose usage may brake some ByteString invariants. - * - * Consider using other APIs instead when possible. - * Otherwise, make sure to read documentation describing an unsafe API. - */ -@MustBeDocumented -@Retention(AnnotationRetention.BINARY) -@RequiresOptIn( - level = RequiresOptIn.Level.ERROR, - message = "This is a delicate API and its use requires care. " + - "Make sure you fully read and understand documentation of the declaration that is marked as a delicate API." -) -public annotation class UnsafeByteStringApi - /** * Constructs empty byte string. */ @@ -61,14 +46,7 @@ public class ByteString private constructor( */ public val EMPTY: ByteString = ByteString(ByteArray(0), null) - /** - * Creates a new byte string by wrapping [byteArray] without copying it. - * Make sure that the wrapped array won't be modified during the lifespan of the returned byte string. - * - * @param byteArray the array to wrap into the byte string. - */ - @UnsafeByteStringApi - public fun wrapUnsafe(byteArray: ByteArray): ByteString = ByteString(byteArray, null) + internal fun wrap(byteArray: ByteArray): ByteString = ByteString(byteArray, null) } /** @@ -243,8 +221,7 @@ public class ByteString private constructor( * These methods return reference to the underlying array, not to its copy. * Consider using [toByteArray] if it's impossible to guarantee that the array won't be modified. */ - @UnsafeByteStringApi - public fun getByteArrayUnsafe(): ByteArray = data + internal fun getBackingArrayReference(): ByteArray = data } /** @@ -442,3 +419,19 @@ private fun ByteString.rangeEquals( * Returns `true` if this byte string is empty. */ public fun ByteString.isEmpty(): Boolean = size == 0 + +/** + * Decodes content of a byte string into a string using UTF-8 encoding. + */ +public fun ByteString.toUtf8String(): String { + return getBackingArrayReference().decodeToString() +} + +/** + * Encodes a string into a byte sequence using UTF8-encoding and wraps it into a byte string. + * + * @param string the string to be encoded. + */ +public fun ByteString.Companion.fromUtf8String(string: String): ByteString { + return wrap(string.encodeToByteArray()) +} \ No newline at end of file diff --git a/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt b/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt index b3e994f7c..5ca3e44f3 100644 --- a/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt +++ b/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt @@ -43,13 +43,12 @@ public class ByteStringBuilder(initialCapacity: Int = 0) { * * There will be no additional allocations or copying of data when `size == capacity`. */ - @OptIn(UnsafeByteStringApi::class) public fun toByteString(): ByteString { if (size == 0) { return ByteString.EMPTY } if (buffer.size == size) { - return ByteString.wrapUnsafe(buffer) + return ByteString.wrap(buffer) } return ByteString(buffer, 0, size) } @@ -106,7 +105,6 @@ public fun ByteStringBuilder.append(byte: UByte): Unit = append(byte.toByte()) /** * Appends a byte string to this builder. */ -@OptIn(UnsafeByteStringApi::class) public fun ByteStringBuilder.append(byteString: ByteString) { - append(byteString.getByteArrayUnsafe()) + append(byteString.getBackingArrayReference()) } \ No newline at end of file diff --git a/bytestring/src/commonMain/kotlin/UnsafeByteStringOperations.kt b/bytestring/src/commonMain/kotlin/UnsafeByteStringOperations.kt new file mode 100644 index 000000000..04a80666b --- /dev/null +++ b/bytestring/src/commonMain/kotlin/UnsafeByteStringOperations.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. + */ + +package kotlinx.io.bytestring.unsafe + +import kotlinx.io.bytestring.ByteString +import kotlinx.io.bytestring.UnsafeByteStringApi + +/** + * Collection of helper functions providing unsafe access to the [ByteString]'s underlying byte sequence or allowing + * to wrap byte arrays into [ByteString] without copying the array. + * + * These functions are provided for performance sensitive cases where it is known that the data accessed + * in an unsafe manner won't be modified. Modification of the data backing byte strings may lead to unpredicted + * consequences in the code using the byte string and should be avoided at all costs. + */ +@UnsafeByteStringApi +public class UnsafeByteStringOperations { + public companion object { + /** + * Creates a new byte string by wrapping [array] without copying it. + * Make sure that the wrapped array won't be modified during the lifespan of the returned byte string. + * + * @param array the array to wrap into the byte string. + */ + public fun wrapUnsafe(array: ByteArray): ByteString = ByteString.wrap(array) + + /** + * Returns a reference to the underlying array. + * + * These methods return reference to the underlying array, not to its copy. + * Consider using [ByteString.toByteArray] if it's impossible to guarantee that the array won't be modified. + */ + public fun getByteArrayUnsafe(byteString: ByteString): ByteArray = byteString.getBackingArrayReference() + } +} \ No newline at end of file diff --git a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt index 643db2fe4..279d3c544 100644 --- a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt +++ b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt @@ -381,4 +381,14 @@ class ByteStringTest { ByteString(ByteArray(64)).toString() ) } + + private val bronzeHorseman = "На берегу пустынных волн" + + @Test + fun utf8() { + val byteString = ByteString.fromUtf8String(bronzeHorseman) + assertEquals(byteString.toByteArray().toList(), bronzeHorseman.encodeToByteArray().toList()) + assertEquals(byteString, ByteString(*bronzeHorseman.encodeToByteArray())) + assertEquals(byteString.toUtf8String(), bronzeHorseman) + } } \ No newline at end of file diff --git a/bytestring/src/jvmMain/kotlin/ByteStringJvmExt.kt b/bytestring/src/jvmMain/kotlin/ByteStringJvmExt.kt new file mode 100644 index 000000000..b78c603bc --- /dev/null +++ b/bytestring/src/jvmMain/kotlin/ByteStringJvmExt.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. + */ + +package kotlinx.io.bytestring + +import java.nio.charset.Charset + +/** + * Decodes the content of a byte string to a string using given [charset]. + * + * @param charset the charset to decode data into a string. + */ +public fun ByteString.toString(charset: Charset): String = getBackingArrayReference().toString(charset) + +/** + * Constructs a new byte string containing [string] encoded into bytes using [charset]. + * + * @param string string to encode. + * @param charset the encoding. + */ +public fun ByteString.Companion.fromString(string: String, charset: Charset): ByteString = + wrap(string.toByteArray(charset)) \ No newline at end of file diff --git a/bytestring/src/jvmTest/kotlin/ByteStringJvmTest.kt b/bytestring/src/jvmTest/kotlin/ByteStringJvmTest.kt new file mode 100644 index 000000000..f1dbc5f5b --- /dev/null +++ b/bytestring/src/jvmTest/kotlin/ByteStringJvmTest.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. + */ + +package kotlinx.io.bytestring + +import kotlin.test.Test +import kotlin.test.assertEquals + +class ByteStringJvmTest { + @Test + fun createFromString() { + val str = "hello" + + assertEquals(ByteString(byteArrayOf(0x68, 0x65, 0x6c, 0x6c, 0x6f)), ByteString.fromString(str, Charsets.UTF_8)) + assertEquals(ByteString(byteArrayOf(0, 0, 0, 0x68, 0, 0, 0, 0x65, 0, 0, 0, 0x6c, + 0, 0, 0, 0x6c, 0, 0, 0, 0x6f)), ByteString.fromString(str, Charsets.UTF_32)) + } + + @Test + fun decodeToString() { + assertEquals("Ϭ", + ByteString(0xfeU.toByte(), 0xffU.toByte(), 0x03, 0xecU.toByte()).toString(Charsets.UTF_16)) + + assertEquals("123", ByteString("123".encodeToByteArray()).toString(Charsets.UTF_8)) + } +} \ No newline at end of file diff --git a/core/api/kotlinx-io-core.api b/core/api/kotlinx-io-core.api index f38d9d028..e79ca86fb 100644 --- a/core/api/kotlinx-io-core.api +++ b/core/api/kotlinx-io-core.api @@ -52,12 +52,10 @@ public final class kotlinx/io/BufferExtKt { } public final class kotlinx/io/ByteStringExtKt { - public static final fun fromUtf8 (Lkotlinx/io/bytestring/ByteString$Companion;Ljava/lang/String;)Lkotlinx/io/bytestring/ByteString; public static final fun indexOf (Lkotlinx/io/Source;Lkotlinx/io/bytestring/ByteString;J)J public static synthetic fun indexOf$default (Lkotlinx/io/Source;Lkotlinx/io/bytestring/ByteString;JILjava/lang/Object;)J public static final fun readByteString (Lkotlinx/io/Source;)Lkotlinx/io/bytestring/ByteString; public static final fun readByteString (Lkotlinx/io/Source;I)Lkotlinx/io/bytestring/ByteString; - public static final fun toUtf8 (Lkotlinx/io/bytestring/ByteString;)Ljava/lang/String; public static final fun write (Lkotlinx/io/Sink;Lkotlinx/io/bytestring/ByteString;II)V public static synthetic fun write$default (Lkotlinx/io/Sink;Lkotlinx/io/bytestring/ByteString;IIILjava/lang/Object;)V } diff --git a/core/common/src/ByteStringExt.kt b/core/common/src/ByteStringExt.kt index c612ec9d6..8218b9fad 100644 --- a/core/common/src/ByteStringExt.kt +++ b/core/common/src/ByteStringExt.kt @@ -9,29 +9,9 @@ import kotlinx.io.bytestring.ByteString import kotlinx.io.bytestring.UnsafeByteStringApi import kotlinx.io.bytestring.indices import kotlinx.io.bytestring.isEmpty -import kotlinx.io.internal.commonAsUtf8ToByteArray -import kotlinx.io.internal.commonToUtf8String +import kotlinx.io.bytestring.unsafe.UnsafeByteStringOperations import kotlin.math.min - -/** - * Decodes content of a byte string into a string using UTF-8 encoding. - */ -@OptIn(UnsafeByteStringApi::class) -public fun ByteString.toUtf8(): String { - return getByteArrayUnsafe().commonToUtf8String() -} - -/** - * Encodes a string into a byte sequence using UTF8-encoding and wraps it into a byte string. - * - * @param string the string to be encoded. - */ -@OptIn(UnsafeByteStringApi::class) -public fun ByteString.Companion.fromUtf8(string: String): ByteString { - return wrapUnsafe(string.commonAsUtf8ToByteArray()) -} - /** * Writes subsequence of data from [byteString] starting at [startIndex] and ending at [endIndex] into a sink. * @@ -67,7 +47,7 @@ public fun Sink.write(byteString: ByteString, startIndex: Int = 0, endIndex: Int */ @OptIn(UnsafeByteStringApi::class) public fun Source.readByteString(): ByteString { - return ByteString.wrapUnsafe(readByteArray()) + return UnsafeByteStringOperations.wrapUnsafe(readByteArray()) } /** @@ -81,7 +61,7 @@ public fun Source.readByteString(): ByteString { */ @OptIn(UnsafeByteStringApi::class) public fun Source.readByteString(byteCount: Int): ByteString { - return ByteString.wrapUnsafe(readByteArray(byteCount)) + return UnsafeByteStringOperations.wrapUnsafe(readByteArray(byteCount)) } /** diff --git a/core/common/test/AbstractSourceTest.kt b/core/common/test/AbstractSourceTest.kt index 7f5d7fb4a..535414e53 100644 --- a/core/common/test/AbstractSourceTest.kt +++ b/core/common/test/AbstractSourceTest.kt @@ -22,6 +22,7 @@ package kotlinx.io import kotlinx.io.bytestring.ByteString +import kotlinx.io.bytestring.toUtf8String import kotlin.test.* private const val SEGMENT_SIZE = Segment.SIZE @@ -1543,7 +1544,7 @@ abstract class AbstractBufferedSourceTest internal constructor( writeUtf8("e".repeat(Segment.SIZE)) emit() } - assertEquals("abcd" + "e".repeat(Segment.SIZE), source.readByteString().toUtf8()) + assertEquals("abcd" + "e".repeat(Segment.SIZE), source.readByteString().toUtf8String()) } @Test fun readByteStringPartial() { @@ -1552,7 +1553,7 @@ abstract class AbstractBufferedSourceTest internal constructor( writeUtf8("e".repeat(Segment.SIZE)) emit() } - assertEquals("abc", source.readByteString(3).toUtf8()) + assertEquals("abc", source.readByteString(3).toUtf8String()) assertEquals("d", source.readUtf8(1)) } diff --git a/core/common/test/ByteStringExtensionsTest.kt b/core/common/test/ByteStringExtensionsTest.kt deleted file mode 100644 index dfee62603..000000000 --- a/core/common/test/ByteStringExtensionsTest.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -/* - * Copyright (C) 2018 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 kotlinx.io - -import kotlinx.io.bytestring.ByteString -import kotlinx.io.internal.commonAsUtf8ToByteArray -import kotlin.test.Test -import kotlin.test.assertEquals - -class ByteStringExtensionsTest { - private val bronzeHorseman = "На берегу пустынных волн" - - @Test - fun utf8() { - val byteString = ByteString.fromUtf8(bronzeHorseman) - assertEquals(byteString.toByteArray().toList(), bronzeHorseman.commonAsUtf8ToByteArray().toList()) - assertEquals(byteString, ByteString(*bronzeHorseman.commonAsUtf8ToByteArray())) - assertEquals( - byteString, - ByteString( - "d09dd0b020d0b1d0b5d180d0b5d0b3d18320d0bfd183d181d182d18bd0bdd0bdd18bd18520d0b2d0bed0bbd0bd".decodeHex() - ) - ) - assertEquals(byteString.toUtf8(), bronzeHorseman) - } -} \ No newline at end of file diff --git a/core/common/test/util.kt b/core/common/test/util.kt index fd21ef562..80dc15337 100644 --- a/core/common/test/util.kt +++ b/core/common/test/util.kt @@ -21,6 +21,7 @@ package kotlinx.io import kotlinx.io.bytestring.ByteString +import kotlinx.io.bytestring.fromUtf8String import kotlin.test.assertEquals fun segmentSizes(buffer: Buffer): List { @@ -69,4 +70,4 @@ fun assertArrayEquals(a: ByteArray, b: ByteArray) { assertEquals(a.contentToString(), b.contentToString()) } -fun String.encodeUtf8(): ByteString = ByteString.fromUtf8(this) +fun String.encodeUtf8(): ByteString = ByteString.fromUtf8String(this) From 4f3190911102ebe9a59272ae2913a1588ce075a2 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Fri, 23 Jun 2023 14:40:22 +0200 Subject: [PATCH 10/32] Hide ByteString.EMPTY --- bytestring/api/kotlinx-io-bytestring.api | 10 ++- .../src/commonMain/kotlin/ByteString.kt | 4 +- .../commonMain/kotlin/ByteStringBuilder.kt | 2 +- .../kotlinx/io/bytestring/ByteStringTest.kt | 72 +++++++++---------- core/common/src/BufferExt.kt | 2 +- core/common/test/AbstractSourceTest.kt | 2 +- 6 files changed, 48 insertions(+), 44 deletions(-) diff --git a/bytestring/api/kotlinx-io-bytestring.api b/bytestring/api/kotlinx-io-bytestring.api index 411f7081f..2fb567620 100644 --- a/bytestring/api/kotlinx-io-bytestring.api +++ b/bytestring/api/kotlinx-io-bytestring.api @@ -20,7 +20,6 @@ public final class kotlinx/io/bytestring/ByteString : java/lang/Comparable { } public final class kotlinx/io/bytestring/ByteString$Companion { - public final fun getEMPTY ()Lkotlinx/io/bytestring/ByteString; } public final class kotlinx/io/bytestring/ByteStringBuilder { @@ -40,11 +39,16 @@ public final class kotlinx/io/bytestring/ByteStringBuilderKt { public static final fun append-EK-6454 (Lkotlinx/io/bytestring/ByteStringBuilder;B)V } +public final class kotlinx/io/bytestring/ByteStringJvmExtKt { + public static final fun fromString (Lkotlinx/io/bytestring/ByteString$Companion;Ljava/lang/String;Ljava/nio/charset/Charset;)Lkotlinx/io/bytestring/ByteString; + public static final fun toString (Lkotlinx/io/bytestring/ByteString;Ljava/nio/charset/Charset;)Ljava/lang/String; +} + public final class kotlinx/io/bytestring/ByteStringKt { public static final fun ByteString ()Lkotlinx/io/bytestring/ByteString; public static final fun endsWith (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;)Z public static final fun endsWith (Lkotlinx/io/bytestring/ByteString;[B)Z - public static final fun fromUtf8 (Lkotlinx/io/bytestring/ByteString$Companion;Ljava/lang/String;)Lkotlinx/io/bytestring/ByteString; + public static final fun fromUtf8String (Lkotlinx/io/bytestring/ByteString$Companion;Ljava/lang/String;)Lkotlinx/io/bytestring/ByteString; public static final fun indexOf (Lkotlinx/io/bytestring/ByteString;BI)I public static final fun indexOf (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;I)I public static final fun indexOf (Lkotlinx/io/bytestring/ByteString;[BI)I @@ -61,7 +65,7 @@ public final class kotlinx/io/bytestring/ByteStringKt { public static synthetic fun lastIndexOf$default (Lkotlinx/io/bytestring/ByteString;[BIILjava/lang/Object;)I public static final fun startsWith (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;)Z public static final fun startsWith (Lkotlinx/io/bytestring/ByteString;[B)Z - public static final fun toUtf8 (Lkotlinx/io/bytestring/ByteString;)Ljava/lang/String; + public static final fun toUtf8String (Lkotlinx/io/bytestring/ByteString;)Ljava/lang/String; } public abstract interface annotation class kotlinx/io/bytestring/UnsafeByteStringApi : java/lang/annotation/Annotation { diff --git a/bytestring/src/commonMain/kotlin/ByteString.kt b/bytestring/src/commonMain/kotlin/ByteString.kt index 9a9b42374..68bb99145 100644 --- a/bytestring/src/commonMain/kotlin/ByteString.kt +++ b/bytestring/src/commonMain/kotlin/ByteString.kt @@ -27,7 +27,7 @@ import kotlin.math.min private val HEX_DIGITS = "0123456789ABCDEF".toCharArray() /** - * Constructs empty byte string. + * Constructs an empty byte string. */ public fun ByteString(): ByteString = ByteString.EMPTY @@ -44,7 +44,7 @@ public class ByteString private constructor( /** * An empty ByteString. */ - public val EMPTY: ByteString = ByteString(ByteArray(0), null) + internal val EMPTY: ByteString = ByteString(ByteArray(0), null) internal fun wrap(byteArray: ByteArray): ByteString = ByteString(byteArray, null) } diff --git a/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt b/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt index 5ca3e44f3..afc978a05 100644 --- a/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt +++ b/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt @@ -45,7 +45,7 @@ public class ByteStringBuilder(initialCapacity: Int = 0) { */ public fun toByteString(): ByteString { if (size == 0) { - return ByteString.EMPTY + return ByteString() } if (buffer.size == size) { return ByteString.wrap(buffer) diff --git a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt index 279d3c544..9b72c90ca 100644 --- a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt +++ b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt @@ -27,7 +27,7 @@ class ByteStringTest { @Test fun equalsAndHashCode() { with(ByteString(1, 2, 3)) { checkEqualsAndHashCodeAreSame(this, this) } - checkEqualsAndHashCodeAreSame(ByteString.EMPTY, ByteString(byteArrayOf())) + checkEqualsAndHashCodeAreSame(ByteString(), ByteString(byteArrayOf())) checkEqualsAndHashCodeAreSame(ByteString(1, 2, 3), ByteString(1, 2, 3)) assertNotEquals(ByteString(1, 2, 3), ByteString(3, 2, 1)) @@ -118,7 +118,7 @@ class ByteStringTest { fun substring() { val str = ByteString(1, 2, 3, 4, 5) - assertEquals(ByteString.EMPTY, str.substring(0, 0)) + assertEquals(ByteString(), str.substring(0, 0)) assertEquals(ByteString(1, 2, 3), str.substring(startIndex = 0, endIndex = 3)) assertEquals(ByteString(3, 4, 5), str.substring(startIndex = 2)) assertEquals(ByteString(2, 3, 4), str.substring(startIndex = 1, endIndex = 4)) @@ -136,7 +136,7 @@ class ByteStringTest { @Test fun compareTo() { - assertEquals(0, ByteString.EMPTY.compareTo(ByteString.EMPTY)) + assertEquals(0, ByteString().compareTo(ByteString())) assertEquals(0, ByteString(1, 2, 3).compareTo(ByteString(1, 2, 3))) assertEquals(-1, ByteString(1, 2).compareTo(ByteString(1, 2, 3))) assertEquals(-1, ByteString(0, 1, 2).compareTo(ByteString(0, 1, 3))) @@ -148,7 +148,7 @@ class ByteStringTest { @Test fun size() { - assertEquals(0, ByteString.EMPTY.size) + assertEquals(0, ByteString().size) assertEquals(1, ByteString(0).size) assertEquals(12345, ByteString(ByteArray(12345)).size) } @@ -156,12 +156,12 @@ class ByteStringTest { @Test fun indices() { assertEquals(0 until 10, ByteString(ByteArray(10)).indices()) - assertTrue(ByteString.EMPTY.indices().isEmpty()) + assertTrue(ByteString().indices().isEmpty()) } @Test fun isEmpty() { - assertTrue(ByteString.EMPTY.isEmpty()) + assertTrue(ByteString().isEmpty()) assertTrue(ByteString(byteArrayOf()).isEmpty()) assertFalse(ByteString(byteArrayOf(0)).isEmpty()) } @@ -179,8 +179,8 @@ class ByteStringTest { assertEquals(0, str.indexOf(1, -10)) assertEquals(1, ByteString(0, 1, 1, 1).indexOf(1)) - assertEquals(-1, ByteString.EMPTY.indexOf(0)) - assertEquals(-1, ByteString.EMPTY.indexOf(0, 100500)) + assertEquals(-1, ByteString().indexOf(0)) + assertEquals(-1, ByteString().indexOf(0, 100500)) assertEquals(-1, str.indexOf(1, 100500)) } @@ -205,11 +205,11 @@ class ByteStringTest { assertEquals(1, ByteString(0, 1, 0, 1, 0, 1).indexOf(byteArrayOf(1, 0))) - assertEquals(0, ByteString.EMPTY.indexOf(byteArrayOf())) - assertEquals(0, ByteString.EMPTY.indexOf(byteArrayOf(), -100500)) - assertEquals(0, ByteString.EMPTY.indexOf(byteArrayOf(), 100500)) + assertEquals(0, ByteString().indexOf(byteArrayOf())) + assertEquals(0, ByteString().indexOf(byteArrayOf(), -100500)) + assertEquals(0, ByteString().indexOf(byteArrayOf(), 100500)) assertEquals(-1, str.indexOf(byteArrayOf(1, 2, 3), 100500)) - assertEquals(-1, ByteString.EMPTY.indexOf(byteArrayOf(1, 2, 3, 4, 5))) + assertEquals(-1, ByteString().indexOf(byteArrayOf(1, 2, 3, 4, 5))) assertEquals(-1, str.indexOf(byteArrayOf(2, 3, 5))) } @@ -222,18 +222,18 @@ class ByteStringTest { assertEquals(0, str.indexOf(ByteString(1))) assertEquals(2, str.indexOf(ByteString(3, 4, 5))) assertEquals(-1, str.indexOf(ByteString(3, 4, 5, 6))) - assertEquals(0, str.indexOf(ByteString.EMPTY)) + assertEquals(0, str.indexOf(ByteString())) assertEquals(-1, str.indexOf(ByteString(-1))) assertEquals(-1, str.indexOf(ByteString(1, 2, 3, 4, 5), 1)) assertEquals(3, str.indexOf(ByteString(4, 5), 3)) assertEquals(0, str.indexOf(ByteString(1, 2, 3), -1000)) assertEquals(1, str.indexOf(ByteString(2, 3), -1)) assertEquals(1, ByteString(0, 1, 0, 1, 0, 1).indexOf(ByteString(1, 0))) - assertEquals(0, ByteString.EMPTY.indexOf(ByteString.EMPTY)) - assertEquals(0, ByteString.EMPTY.indexOf(ByteString.EMPTY, -100500)) - assertEquals(0, ByteString.EMPTY.indexOf(ByteString.EMPTY, 100500)) + assertEquals(0, ByteString().indexOf(ByteString())) + assertEquals(0, ByteString().indexOf(ByteString(), -100500)) + assertEquals(0, ByteString().indexOf(ByteString(), 100500)) assertEquals(-1, str.indexOf(ByteString(1, 2, 3), 100500)) - assertEquals(-1, ByteString.EMPTY.indexOf(ByteString(1, 2, 3, 4, 5))) + assertEquals(-1, ByteString().indexOf(ByteString(1, 2, 3, 4, 5))) assertEquals(-1, str.indexOf(ByteString(2, 3, 5))) } @@ -250,8 +250,8 @@ class ByteStringTest { assertEquals(0, str.lastIndexOf(1, -10)) assertEquals(3, ByteString(0, 1, 1, 1, 0).lastIndexOf(1)) - assertEquals(-1, ByteString.EMPTY.lastIndexOf(0)) - assertEquals(-1, ByteString.EMPTY.lastIndexOf(0, 100500)) + assertEquals(-1, ByteString().lastIndexOf(0)) + assertEquals(-1, ByteString().lastIndexOf(0, 100500)) assertEquals(-1, str.lastIndexOf(1, 1005000)) } @@ -271,11 +271,11 @@ class ByteStringTest { assertEquals(0, str.lastIndexOf(byteArrayOf(1), -1)) assertEquals(4, ByteString(1, 1, 1, 1, 1).lastIndexOf(byteArrayOf(1))) assertEquals(3, ByteString(0, 1, 0, 1, 0).lastIndexOf(byteArrayOf(1, 0))) - assertEquals(0, ByteString.EMPTY.lastIndexOf(byteArrayOf())) - assertEquals(0, ByteString.EMPTY.lastIndexOf(byteArrayOf(), -100500)) - assertEquals(0, ByteString.EMPTY.lastIndexOf(byteArrayOf(), 100500)) + assertEquals(0, ByteString().lastIndexOf(byteArrayOf())) + assertEquals(0, ByteString().lastIndexOf(byteArrayOf(), -100500)) + assertEquals(0, ByteString().lastIndexOf(byteArrayOf(), 100500)) assertEquals(-1, str.lastIndexOf(byteArrayOf(1, 2, 3), 100500)) - assertEquals(-1, ByteString.EMPTY.lastIndexOf(byteArrayOf(1, 2, 3))) + assertEquals(-1, ByteString().lastIndexOf(byteArrayOf(1, 2, 3))) assertEquals(-1, str.lastIndexOf(byteArrayOf(2, 3, 5))) } @@ -289,17 +289,17 @@ class ByteStringTest { assertEquals(2, str.lastIndexOf(ByteString(3, 4, 5))) assertEquals(-1, str.lastIndexOf(ByteString(1, 2, 3), 1)) assertEquals(1, str.lastIndexOf(ByteString(2, 3, 4), 1)) - assertEquals(str.size, str.lastIndexOf(ByteString.EMPTY)) - assertEquals(str.size, str.lastIndexOf(ByteString.EMPTY)) + assertEquals(str.size, str.lastIndexOf(ByteString())) + assertEquals(str.size, str.lastIndexOf(ByteString())) assertEquals(2, str.lastIndexOf(ByteString(3, 4), -1000)) assertEquals(0, str.lastIndexOf(ByteString(1), -1)) assertEquals(4, ByteString(1, 1, 1, 1, 1).lastIndexOf(ByteString(1))) assertEquals(3, ByteString(0, 1, 0, 1, 0).lastIndexOf(ByteString(1, 0))) - assertEquals(0, ByteString.EMPTY.lastIndexOf(ByteString.EMPTY)) - assertEquals(0, ByteString.EMPTY.lastIndexOf(ByteString.EMPTY, -100500)) - assertEquals(0, ByteString.EMPTY.lastIndexOf(ByteString.EMPTY, 100500)) + assertEquals(0, ByteString().lastIndexOf(ByteString())) + assertEquals(0, ByteString().lastIndexOf(ByteString(), -100500)) + assertEquals(0, ByteString().lastIndexOf(ByteString(), 100500)) assertEquals(-1, str.lastIndexOf(ByteString(1, 2, 3), 100500)) - assertEquals(-1, ByteString.EMPTY.lastIndexOf(ByteString(1, 2, 3))) + assertEquals(-1, ByteString().lastIndexOf(ByteString(1, 2, 3))) assertEquals(-1, str.lastIndexOf(ByteString(2, 3, 5))) } @@ -316,7 +316,7 @@ class ByteStringTest { assertFalse(str.startsWith(byteArrayOf(2, 3, 4))) assertFalse(str.startsWith(byteArrayOf(1, 2, 3, 4, 5, 6))) - assertTrue(ByteString.EMPTY.startsWith(byteArrayOf())) + assertTrue(ByteString().startsWith(byteArrayOf())) } @Test @@ -326,13 +326,13 @@ class ByteStringTest { assertTrue(str.startsWith(ByteString(1, 2, 3, 4, 5))) assertTrue(str.startsWith(ByteString(1, 2, 3))) - assertTrue(str.startsWith(ByteString.EMPTY)) + assertTrue(str.startsWith(ByteString())) assertFalse(str.startsWith(ByteString(0, 1, 2, 3))) assertFalse(str.startsWith(ByteString(2, 3, 4))) assertFalse(str.startsWith(ByteString(1, 2, 3, 4, 5, 6))) - assertTrue(ByteString.EMPTY.startsWith(ByteString.EMPTY)) + assertTrue(ByteString().startsWith(ByteString())) } @Test @@ -348,7 +348,7 @@ class ByteStringTest { assertFalse(str.endsWith(byteArrayOf(0, 1, 2, 3, 4, 5))) assertFalse(str.endsWith(byteArrayOf(2, 3, 4))) - assertTrue(ByteString.EMPTY.endsWith(byteArrayOf())) + assertTrue(ByteString().endsWith(byteArrayOf())) } @Test @@ -358,18 +358,18 @@ class ByteStringTest { assertTrue(str.endsWith(ByteString(1, 2, 3, 4, 5))) assertTrue(str.endsWith(ByteString(3, 4, 5))) - assertTrue(str.endsWith(ByteString.EMPTY)) + assertTrue(str.endsWith(ByteString())) assertFalse(str.endsWith(ByteString(3, 4, 5, 6))) assertFalse(str.endsWith(ByteString(0, 1, 2, 3, 4, 5))) assertFalse(str.endsWith(ByteString(2, 3, 4))) - assertTrue(ByteString.EMPTY.endsWith(ByteString.EMPTY)) + assertTrue(ByteString().endsWith(ByteString())) } @Test fun testToString() { - assertEquals("ByteString(size=0)", ByteString.EMPTY.toString()) + assertEquals("ByteString(size=0)", ByteString().toString()) assertEquals("ByteString(size=1 hex=00)", ByteString(0).toString()) assertEquals( "ByteString(size=16 hex=000102030405060708090A0B0C0D0E0F)", diff --git a/core/common/src/BufferExt.kt b/core/common/src/BufferExt.kt index cd963a3ef..4ccfab563 100644 --- a/core/common/src/BufferExt.kt +++ b/core/common/src/BufferExt.kt @@ -14,7 +14,7 @@ import kotlinx.io.bytestring.ByteStringBuilder * This call doesn't consume data from the buffer, but instead copies it. */ public fun Buffer.snapshot(): ByteString { - if (size == 0L) return ByteString.EMPTY + if (size == 0L) return ByteString() val bufferSize = this@snapshot.size return with (ByteStringBuilder(bufferSize.toInt())) { diff --git a/core/common/test/AbstractSourceTest.kt b/core/common/test/AbstractSourceTest.kt index 535414e53..c4b356aa0 100644 --- a/core/common/test/AbstractSourceTest.kt +++ b/core/common/test/AbstractSourceTest.kt @@ -1659,7 +1659,7 @@ abstract class AbstractBufferedSourceTest internal constructor( } @Test fun indexOfEmptyByteString() { - assertEquals(0, source.indexOf(ByteString.EMPTY)) + assertEquals(0, source.indexOf(ByteString())) } @Test fun indexOfByteStringInvalidArgumentsThrows() { From 5a1b06f24e2d33645472a3e9c66b77e8a4b0d3df Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Fri, 23 Jun 2023 15:18:44 +0200 Subject: [PATCH 11/32] Cleanup --- .../src/commonMain/kotlin/ByteString.kt | 2 +- .../io/bytestring/ByteStringBuilderTest.kt | 20 ++++++++-------- .../kotlinx/io/bytestring/ByteStringTest.kt | 2 +- .../src/jvmMain/kotlin/ByteStringJvmExt.kt | 2 +- .../src/jvmTest/kotlin/ByteStringJvmTest.kt | 18 +++++++++++---- core/common/src/ByteStringExt.kt | 2 +- core/jvm/test/md5.kt | 23 ------------------- settings.gradle.kts | 2 +- 8 files changed, 28 insertions(+), 43 deletions(-) delete mode 100644 core/jvm/test/md5.kt diff --git a/bytestring/src/commonMain/kotlin/ByteString.kt b/bytestring/src/commonMain/kotlin/ByteString.kt index 68bb99145..a608eb741 100644 --- a/bytestring/src/commonMain/kotlin/ByteString.kt +++ b/bytestring/src/commonMain/kotlin/ByteString.kt @@ -434,4 +434,4 @@ public fun ByteString.toUtf8String(): String { */ public fun ByteString.Companion.fromUtf8String(string: String): ByteString { return wrap(string.encodeToByteArray()) -} \ No newline at end of file +} diff --git a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringBuilderTest.kt b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringBuilderTest.kt index f8e4257d8..0497872f5 100644 --- a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringBuilderTest.kt +++ b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringBuilderTest.kt @@ -20,7 +20,7 @@ class ByteStringBuilderTest { @Test fun appendByte() { val builder = ByteStringBuilder() - with (builder) { + with(builder) { append(1) append(2) append(3) @@ -31,7 +31,7 @@ class ByteStringBuilderTest { @Test fun appendUByte() { val builder = ByteStringBuilder() - with (builder) { + with(builder) { append(0x80U) append(0x81U) append(0x82U) @@ -41,27 +41,27 @@ class ByteStringBuilderTest { @Test fun appendArray() { - with (ByteStringBuilder()) { + with(ByteStringBuilder()) { append(byteArrayOf(1, 2, 3, 4)) assertEquals(ByteString(1, 2, 3, 4), toByteString()) } - with (ByteStringBuilder()) { + with(ByteStringBuilder()) { append(byteArrayOf(1, 2, 3, 4), startIndex = 2) assertEquals(ByteString(3, 4), toByteString()) } - with (ByteStringBuilder()) { + with(ByteStringBuilder()) { append(byteArrayOf(1, 2, 3, 4), endIndex = 2) assertEquals(ByteString(1, 2), toByteString()) } - with (ByteStringBuilder()) { + with(ByteStringBuilder()) { append(byteArrayOf(1, 2, 3, 4), startIndex = 1, endIndex = 3) assertEquals(ByteString(2, 3), toByteString()) } - with (ByteStringBuilder()) { + with(ByteStringBuilder()) { append(byteArrayOf(1, 2, 3, 4), startIndex = 1, endIndex = 1) assertEquals(ByteString(), toByteString()) } @@ -86,7 +86,7 @@ class ByteStringBuilderTest { @Test fun appendMultipleValues() { - val string = with (ByteStringBuilder()) { + val string = with(ByteStringBuilder()) { append(42) append(ByteArray(10) { it.toByte() }) append(42) @@ -123,7 +123,7 @@ class ByteStringBuilderTest { assertEquals(0, ByteStringBuilder().capacity) assertEquals(10, ByteStringBuilder(10).capacity) - with (ByteStringBuilder()) { + with(ByteStringBuilder()) { append(1) assertTrue(capacity >= 1) append(ByteArray(1024)) @@ -142,4 +142,4 @@ class ByteStringBuilderTest { assertEquals(ByteString(1, 2), builder.toByteString()) assertEquals(ByteString(1), str0) } -} \ No newline at end of file +} diff --git a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt index 9b72c90ca..9d8249e18 100644 --- a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt +++ b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt @@ -391,4 +391,4 @@ class ByteStringTest { assertEquals(byteString, ByteString(*bronzeHorseman.encodeToByteArray())) assertEquals(byteString.toUtf8String(), bronzeHorseman) } -} \ No newline at end of file +} diff --git a/bytestring/src/jvmMain/kotlin/ByteStringJvmExt.kt b/bytestring/src/jvmMain/kotlin/ByteStringJvmExt.kt index b78c603bc..344af05d7 100644 --- a/bytestring/src/jvmMain/kotlin/ByteStringJvmExt.kt +++ b/bytestring/src/jvmMain/kotlin/ByteStringJvmExt.kt @@ -21,4 +21,4 @@ public fun ByteString.toString(charset: Charset): String = getBackingArrayRefere * @param charset the encoding. */ public fun ByteString.Companion.fromString(string: String, charset: Charset): ByteString = - wrap(string.toByteArray(charset)) \ No newline at end of file + wrap(string.toByteArray(charset)) diff --git a/bytestring/src/jvmTest/kotlin/ByteStringJvmTest.kt b/bytestring/src/jvmTest/kotlin/ByteStringJvmTest.kt index f1dbc5f5b..734a259f7 100644 --- a/bytestring/src/jvmTest/kotlin/ByteStringJvmTest.kt +++ b/bytestring/src/jvmTest/kotlin/ByteStringJvmTest.kt @@ -14,15 +14,23 @@ class ByteStringJvmTest { val str = "hello" assertEquals(ByteString(byteArrayOf(0x68, 0x65, 0x6c, 0x6c, 0x6f)), ByteString.fromString(str, Charsets.UTF_8)) - assertEquals(ByteString(byteArrayOf(0, 0, 0, 0x68, 0, 0, 0, 0x65, 0, 0, 0, 0x6c, - 0, 0, 0, 0x6c, 0, 0, 0, 0x6f)), ByteString.fromString(str, Charsets.UTF_32)) + assertEquals( + ByteString( + byteArrayOf( + 0, 0, 0, 0x68, 0, 0, 0, 0x65, 0, 0, 0, 0x6c, + 0, 0, 0, 0x6c, 0, 0, 0, 0x6f + ) + ), ByteString.fromString(str, Charsets.UTF_32) + ) } @Test fun decodeToString() { - assertEquals("Ϭ", - ByteString(0xfeU.toByte(), 0xffU.toByte(), 0x03, 0xecU.toByte()).toString(Charsets.UTF_16)) + assertEquals( + "Ϭ", + ByteString(0xfeU.toByte(), 0xffU.toByte(), 0x03, 0xecU.toByte()).toString(Charsets.UTF_16) + ) assertEquals("123", ByteString("123".encodeToByteArray()).toString(Charsets.UTF_8)) } -} \ No newline at end of file +} diff --git a/core/common/src/ByteStringExt.kt b/core/common/src/ByteStringExt.kt index 8218b9fad..bd6a3bf48 100644 --- a/core/common/src/ByteStringExt.kt +++ b/core/common/src/ByteStringExt.kt @@ -114,4 +114,4 @@ public fun Source.indexOf(byteString: ByteString, startIndex: Long = 0): Long { } } return -1L -} \ No newline at end of file +} diff --git a/core/jvm/test/md5.kt b/core/jvm/test/md5.kt deleted file mode 100644 index 1f99d9adb..000000000 --- a/core/jvm/test/md5.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io - -import java.nio.ByteBuffer -import java.security.MessageDigest -import kotlin.test.Test - - -class T { - @Test - fun test() { - val md = MessageDigest.getInstance("MD5") - var bb = ByteBuffer.allocate(1024) - bb.put(ByteArray(1024)) - bb = bb.asReadOnlyBuffer() - bb.position(0) - md.update(bb) - } -} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 5e0a8c5d0..912ac632c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,4 +18,4 @@ include(":kotlinx-io-benchmarks") include(":kotlinx-io-bytestring") project(":kotlinx-io-core").projectDir = file("./core") project(":kotlinx-io-benchmarks").projectDir = file("./benchmarks") -project(":kotlinx-io-bytestring").projectDir = file("./bytestring") \ No newline at end of file +project(":kotlinx-io-bytestring").projectDir = file("./bytestring") From 9eff7c8c2e6b76398b2cb09757aef857d9097c6f Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Fri, 23 Jun 2023 15:40:56 +0200 Subject: [PATCH 12/32] Added module description --- bytestring/Module.md | 3 +++ bytestring/build.gradle.kts | 5 +++++ 2 files changed, 8 insertions(+) create mode 100644 bytestring/Module.md diff --git a/bytestring/Module.md b/bytestring/Module.md new file mode 100644 index 000000000..9a3193763 --- /dev/null +++ b/bytestring/Module.md @@ -0,0 +1,3 @@ +# Module kotlinx-io-bytestring + +The module provides the [ByteString] - an immutable sequence of bytes, and extensions facilitating work with it. diff --git a/bytestring/build.gradle.kts b/bytestring/build.gradle.kts index 904cc80a3..87deeb384 100644 --- a/bytestring/build.gradle.kts +++ b/bytestring/build.gradle.kts @@ -53,5 +53,10 @@ fun KotlinSourceSet.configureSourceSet() { tasks.withType().configureEach { dokkaSourceSets.configureEach { includes.from("Module.md") + + perPackageOption { + suppress.set(true) + matchingRegex.set(".*unsafe.*") + } } } \ No newline at end of file From 7d16db1d6a728d0a4032ea7df061af48ec9d24b3 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Fri, 23 Jun 2023 15:41:08 +0200 Subject: [PATCH 13/32] Move Unsafe Api annotation to unsafe package --- bytestring/api/kotlinx-io-bytestring.api | 2 +- bytestring/src/commonMain/kotlin/Annotations.kt | 2 +- bytestring/src/commonMain/kotlin/UnsafeByteStringOperations.kt | 1 - core/common/src/ByteStringExt.kt | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/bytestring/api/kotlinx-io-bytestring.api b/bytestring/api/kotlinx-io-bytestring.api index 2fb567620..c9eab718b 100644 --- a/bytestring/api/kotlinx-io-bytestring.api +++ b/bytestring/api/kotlinx-io-bytestring.api @@ -68,7 +68,7 @@ public final class kotlinx/io/bytestring/ByteStringKt { public static final fun toUtf8String (Lkotlinx/io/bytestring/ByteString;)Ljava/lang/String; } -public abstract interface annotation class kotlinx/io/bytestring/UnsafeByteStringApi : java/lang/annotation/Annotation { +public abstract interface annotation class kotlinx/io/bytestring/unsafe/UnsafeByteStringApi : java/lang/annotation/Annotation { } public final class kotlinx/io/bytestring/unsafe/UnsafeByteStringOperations { diff --git a/bytestring/src/commonMain/kotlin/Annotations.kt b/bytestring/src/commonMain/kotlin/Annotations.kt index d35c7e4c1..2e0361b0c 100644 --- a/bytestring/src/commonMain/kotlin/Annotations.kt +++ b/bytestring/src/commonMain/kotlin/Annotations.kt @@ -3,7 +3,7 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. */ -package kotlinx.io.bytestring +package kotlinx.io.bytestring.unsafe /** * Marks declarations whose usage may brake some ByteString invariants. diff --git a/bytestring/src/commonMain/kotlin/UnsafeByteStringOperations.kt b/bytestring/src/commonMain/kotlin/UnsafeByteStringOperations.kt index 04a80666b..0d63defd8 100644 --- a/bytestring/src/commonMain/kotlin/UnsafeByteStringOperations.kt +++ b/bytestring/src/commonMain/kotlin/UnsafeByteStringOperations.kt @@ -6,7 +6,6 @@ package kotlinx.io.bytestring.unsafe import kotlinx.io.bytestring.ByteString -import kotlinx.io.bytestring.UnsafeByteStringApi /** * Collection of helper functions providing unsafe access to the [ByteString]'s underlying byte sequence or allowing diff --git a/core/common/src/ByteStringExt.kt b/core/common/src/ByteStringExt.kt index bd6a3bf48..79fd50fe6 100644 --- a/core/common/src/ByteStringExt.kt +++ b/core/common/src/ByteStringExt.kt @@ -6,9 +6,9 @@ package kotlinx.io import kotlinx.io.bytestring.ByteString -import kotlinx.io.bytestring.UnsafeByteStringApi import kotlinx.io.bytestring.indices import kotlinx.io.bytestring.isEmpty +import kotlinx.io.bytestring.unsafe.UnsafeByteStringApi import kotlinx.io.bytestring.unsafe.UnsafeByteStringOperations import kotlin.math.min From 9519b83423387e1b440669becfbb5a8dfadf28f6 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Fri, 23 Jun 2023 15:56:09 +0200 Subject: [PATCH 14/32] Implement contentEquals --- bytestring/api/kotlinx-io-bytestring.api | 1 + bytestring/src/commonMain/kotlin/ByteString.kt | 9 +++++++++ .../kotlin/kotlinx/io/bytestring/ByteStringTest.kt | 12 ++++++++++++ 3 files changed, 22 insertions(+) diff --git a/bytestring/api/kotlinx-io-bytestring.api b/bytestring/api/kotlinx-io-bytestring.api index c9eab718b..439a9f774 100644 --- a/bytestring/api/kotlinx-io-bytestring.api +++ b/bytestring/api/kotlinx-io-bytestring.api @@ -46,6 +46,7 @@ public final class kotlinx/io/bytestring/ByteStringJvmExtKt { public final class kotlinx/io/bytestring/ByteStringKt { public static final fun ByteString ()Lkotlinx/io/bytestring/ByteString; + public static final fun contentEquals (Lkotlinx/io/bytestring/ByteString;[B)Z public static final fun endsWith (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;)Z public static final fun endsWith (Lkotlinx/io/bytestring/ByteString;[B)Z public static final fun fromUtf8String (Lkotlinx/io/bytestring/ByteString$Companion;Ljava/lang/String;)Lkotlinx/io/bytestring/ByteString; diff --git a/bytestring/src/commonMain/kotlin/ByteString.kt b/bytestring/src/commonMain/kotlin/ByteString.kt index a608eb741..26a3a57ba 100644 --- a/bytestring/src/commonMain/kotlin/ByteString.kt +++ b/bytestring/src/commonMain/kotlin/ByteString.kt @@ -435,3 +435,12 @@ public fun ByteString.toUtf8String(): String { public fun ByteString.Companion.fromUtf8String(string: String): ByteString { return wrap(string.encodeToByteArray()) } + +/** + * Returns `true` if the content of this byte string equals to the [array]. + * + * @param array the array to test this byte string's content against. + */ +public fun ByteString.contentEquals(array: ByteArray): Boolean { + return getBackingArrayReference().contentEquals(array) +} diff --git a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt index 9d8249e18..60ca6e2a4 100644 --- a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt +++ b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt @@ -391,4 +391,16 @@ class ByteStringTest { assertEquals(byteString, ByteString(*bronzeHorseman.encodeToByteArray())) assertEquals(byteString.toUtf8String(), bronzeHorseman) } + + @Test + fun contentEquals() { + assertTrue(ByteString().contentEquals(byteArrayOf())) + assertFalse(ByteString(1, 2, 3).contentEquals(byteArrayOf())) + assertFalse(ByteString().contentEquals(byteArrayOf(1, 2, 3))) + + assertTrue(ByteString(1, 2, 3, 4, 5).contentEquals(byteArrayOf(1, 2, 3, 4, 5))) + assertFalse(ByteString(1, 2, 3, 4, 5).contentEquals(byteArrayOf(1, 2, 3, 4, 4))) + assertFalse(ByteString(1, 2, 3, 4, 5).contentEquals(byteArrayOf(1, 2, 3, 4, 5, 6))) + assertFalse(ByteString(1, 2, 3, 4, 5, 6).contentEquals(byteArrayOf(1, 2, 3, 4, 5))) + } } From c05ac7a5606412412d3e864252d9143513f5c71d Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Fri, 23 Jun 2023 16:19:40 +0200 Subject: [PATCH 15/32] Cleanup --- bytestring/api/kotlinx-io-bytestring.api | 6 +--- .../kotlin/UnsafeByteStringOperations.kt | 34 +++++++++---------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/bytestring/api/kotlinx-io-bytestring.api b/bytestring/api/kotlinx-io-bytestring.api index 439a9f774..83348ecb3 100644 --- a/bytestring/api/kotlinx-io-bytestring.api +++ b/bytestring/api/kotlinx-io-bytestring.api @@ -73,11 +73,7 @@ public abstract interface annotation class kotlinx/io/bytestring/unsafe/UnsafeBy } public final class kotlinx/io/bytestring/unsafe/UnsafeByteStringOperations { - public static final field Companion Lkotlinx/io/bytestring/unsafe/UnsafeByteStringOperations$Companion; - public fun ()V -} - -public final class kotlinx/io/bytestring/unsafe/UnsafeByteStringOperations$Companion { + public static final field INSTANCE Lkotlinx/io/bytestring/unsafe/UnsafeByteStringOperations; public final fun getByteArrayUnsafe (Lkotlinx/io/bytestring/ByteString;)[B public final fun wrapUnsafe ([B)Lkotlinx/io/bytestring/ByteString; } diff --git a/bytestring/src/commonMain/kotlin/UnsafeByteStringOperations.kt b/bytestring/src/commonMain/kotlin/UnsafeByteStringOperations.kt index 0d63defd8..a7c87e2db 100644 --- a/bytestring/src/commonMain/kotlin/UnsafeByteStringOperations.kt +++ b/bytestring/src/commonMain/kotlin/UnsafeByteStringOperations.kt @@ -16,22 +16,20 @@ import kotlinx.io.bytestring.ByteString * consequences in the code using the byte string and should be avoided at all costs. */ @UnsafeByteStringApi -public class UnsafeByteStringOperations { - public companion object { - /** - * Creates a new byte string by wrapping [array] without copying it. - * Make sure that the wrapped array won't be modified during the lifespan of the returned byte string. - * - * @param array the array to wrap into the byte string. - */ - public fun wrapUnsafe(array: ByteArray): ByteString = ByteString.wrap(array) +public object UnsafeByteStringOperations { + /** + * Creates a new byte string by wrapping [array] without copying it. + * Make sure that the wrapped array won't be modified during the lifespan of the returned byte string. + * + * @param array the array to wrap into the byte string. + */ + public fun wrapUnsafe(array: ByteArray): ByteString = ByteString.wrap(array) - /** - * Returns a reference to the underlying array. - * - * These methods return reference to the underlying array, not to its copy. - * Consider using [ByteString.toByteArray] if it's impossible to guarantee that the array won't be modified. - */ - public fun getByteArrayUnsafe(byteString: ByteString): ByteArray = byteString.getBackingArrayReference() - } -} \ No newline at end of file + /** + * Returns a reference to the underlying array. + * + * These methods return reference to the underlying array, not to its copy. + * Consider using [ByteString.toByteArray] if it's impossible to guarantee that the array won't be modified. + */ + public fun getByteArrayUnsafe(byteString: ByteString): ByteArray = byteString.getBackingArrayReference() +} From 18620a377ad3cd61512bd46ec89c449599c6d68d Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Fri, 23 Jun 2023 16:40:44 +0200 Subject: [PATCH 16/32] Ended the sentence --- bytestring/src/commonMain/kotlin/ByteString.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bytestring/src/commonMain/kotlin/ByteString.kt b/bytestring/src/commonMain/kotlin/ByteString.kt index 26a3a57ba..a8d41f297 100644 --- a/bytestring/src/commonMain/kotlin/ByteString.kt +++ b/bytestring/src/commonMain/kotlin/ByteString.kt @@ -193,7 +193,8 @@ public class ByteString private constructor( * for empty strings it's always `ByteString(size=0)`. * * Note that a string representation includes the whole byte string content encoded. - * Due to limitations of the maxi + * Due to limitations exposed for the maximum string length, an attempt to return a string representation + * of too long byte string may fail. */ override fun toString(): String { if (isEmpty()) { From 045cbe1a3d93562d81c69aef7094920b3f1a6023 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Mon, 26 Jun 2023 16:29:18 +0200 Subject: [PATCH 17/32] Bump up dependencies version --- bytestring/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bytestring/build.gradle.kts b/bytestring/build.gradle.kts index 87deeb384..14108bbfc 100644 --- a/bytestring/build.gradle.kts +++ b/bytestring/build.gradle.kts @@ -3,8 +3,8 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet plugins { kotlin("multiplatform") - id("org.jetbrains.kotlinx.kover") version "0.7.0" - id("org.jetbrains.dokka") version "1.8.10" + id("org.jetbrains.kotlinx.kover") version "0.7.1" + id("org.jetbrains.dokka") version "1.8.20" } kotlin { From f2d820a2bda4eec5593f34f354f0fc408eb8e064 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Mon, 26 Jun 2023 16:34:01 +0200 Subject: [PATCH 18/32] Add isNotEmpty extension --- bytestring/src/commonMain/kotlin/ByteString.kt | 5 +++++ .../kotlin/kotlinx/io/bytestring/ByteStringTest.kt | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/bytestring/src/commonMain/kotlin/ByteString.kt b/bytestring/src/commonMain/kotlin/ByteString.kt index a8d41f297..33e19d9fb 100644 --- a/bytestring/src/commonMain/kotlin/ByteString.kt +++ b/bytestring/src/commonMain/kotlin/ByteString.kt @@ -421,6 +421,11 @@ private fun ByteString.rangeEquals( */ public fun ByteString.isEmpty(): Boolean = size == 0 +/** + * Returns `true` if this byte string is not empty. + */ +public fun ByteString.isNotEmpty(): Boolean = !isEmpty() + /** * Decodes content of a byte string into a string using UTF-8 encoding. */ diff --git a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt index 60ca6e2a4..acf77c2d7 100644 --- a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt +++ b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt @@ -166,6 +166,13 @@ class ByteStringTest { assertFalse(ByteString(byteArrayOf(0)).isEmpty()) } + @Test + fun isNotEmpty() { + assertFalse(ByteString().isNotEmpty()) + assertFalse(ByteString(byteArrayOf()).isNotEmpty()) + assertTrue(ByteString(byteArrayOf(0)).isNotEmpty()) + } + @Test fun indexOfByte() { val str = ByteString(1, 2, 3, 4) From 09af0acd7d10b3a7d7936541538b87a0a982d507 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Mon, 26 Jun 2023 16:39:28 +0200 Subject: [PATCH 19/32] Add buildByteString functions --- .../src/commonMain/kotlin/ByteStringBuilder.kt | 18 +++++++++++++++++- core/common/src/BufferExt.kt | 8 ++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt b/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt index afc978a05..40b2aace4 100644 --- a/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt +++ b/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt @@ -107,4 +107,20 @@ public fun ByteStringBuilder.append(byte: UByte): Unit = append(byte.toByte()) */ public fun ByteStringBuilder.append(byteString: ByteString) { append(byteString.getBackingArrayReference()) -} \ No newline at end of file +} + +/** + * Builds new string by populating newly created [ByteStringBuilder] using provided [builderAction] + * and then converting it to [ByteString]. + */ +public inline fun buildByteString(builderAction: ByteStringBuilder.() -> Unit): ByteString { + return ByteStringBuilder().apply(builderAction).toByteString() +} + +/** + * Builds new byte string by populating newly created [ByteStringBuilder] initialized with the given [capacity] + * using provided [builderAction] and then converting it to [ByteString]. + */ +public inline fun buildByteString(capacity: Int, builderAction: ByteStringBuilder.() -> Unit): ByteString { + return ByteStringBuilder(capacity).apply(builderAction).toByteString() +} diff --git a/core/common/src/BufferExt.kt b/core/common/src/BufferExt.kt index 4ccfab563..07fd950d5 100644 --- a/core/common/src/BufferExt.kt +++ b/core/common/src/BufferExt.kt @@ -6,7 +6,7 @@ package kotlinx.io import kotlinx.io.bytestring.ByteString -import kotlinx.io.bytestring.ByteStringBuilder +import kotlinx.io.bytestring.buildByteString /** * Creates a byte string containing a copy of all the data from this buffer. @@ -16,14 +16,14 @@ import kotlinx.io.bytestring.ByteStringBuilder public fun Buffer.snapshot(): ByteString { if (size == 0L) return ByteString() - val bufferSize = this@snapshot.size - return with (ByteStringBuilder(bufferSize.toInt())) { + check(size <= Int.MAX_VALUE) { "Buffer is too long ($size) to be converted into a byte string." } + + return buildByteString(size.toInt()) { var curr = head do { check(curr != null) { "Current segment is null" } append(curr.data, curr.pos, curr.limit) curr = curr.next } while (curr !== head) - toByteString() } } \ No newline at end of file From fc853c72cb3b1f05b47c6a6e2c870e863350c27e Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Mon, 26 Jun 2023 16:42:45 +0200 Subject: [PATCH 20/32] Add append(vararg Byte) extension to the builder --- bytestring/src/commonMain/kotlin/ByteStringBuilder.kt | 5 +++++ .../kotlin/kotlinx/io/bytestring/ByteStringBuilderTest.kt | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt b/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt index 40b2aace4..cf99da3ee 100644 --- a/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt +++ b/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt @@ -109,6 +109,11 @@ public fun ByteStringBuilder.append(byteString: ByteString) { append(byteString.getBackingArrayReference()) } +/** + * Appends bytes to this builder. + */ +public fun ByteStringBuilder.append(vararg bytes: Byte): Unit = append(bytes) + /** * Builds new string by populating newly created [ByteStringBuilder] using provided [builderAction] * and then converting it to [ByteString]. diff --git a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringBuilderTest.kt b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringBuilderTest.kt index 0497872f5..78d35a4a5 100644 --- a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringBuilderTest.kt +++ b/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringBuilderTest.kt @@ -28,6 +28,11 @@ class ByteStringBuilderTest { assertEquals(ByteString(1, 2, 3), builder.toByteString()) } + @Test + fun appendBytes() { + assertEquals(ByteString(1, 2, 3), buildByteString { append(1, 2, 3) }) + } + @Test fun appendUByte() { val builder = ByteStringBuilder() From 74fcebec7aa7ede51c6d91fac4f7d22558786549 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Mon, 26 Jun 2023 17:09:53 +0200 Subject: [PATCH 21/32] Update API dump --- bytestring/api/kotlinx-io-bytestring.api | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bytestring/api/kotlinx-io-bytestring.api b/bytestring/api/kotlinx-io-bytestring.api index 83348ecb3..84949c69b 100644 --- a/bytestring/api/kotlinx-io-bytestring.api +++ b/bytestring/api/kotlinx-io-bytestring.api @@ -36,7 +36,10 @@ public final class kotlinx/io/bytestring/ByteStringBuilder { public final class kotlinx/io/bytestring/ByteStringBuilderKt { public static final fun append (Lkotlinx/io/bytestring/ByteStringBuilder;Lkotlinx/io/bytestring/ByteString;)V + public static final fun append (Lkotlinx/io/bytestring/ByteStringBuilder;[B)V public static final fun append-EK-6454 (Lkotlinx/io/bytestring/ByteStringBuilder;B)V + public static final fun buildByteString (ILkotlin/jvm/functions/Function1;)Lkotlinx/io/bytestring/ByteString; + public static final fun buildByteString (Lkotlin/jvm/functions/Function1;)Lkotlinx/io/bytestring/ByteString; } public final class kotlinx/io/bytestring/ByteStringJvmExtKt { @@ -58,6 +61,7 @@ public final class kotlinx/io/bytestring/ByteStringKt { public static synthetic fun indexOf$default (Lkotlinx/io/bytestring/ByteString;[BIILjava/lang/Object;)I public static final fun indices (Lkotlinx/io/bytestring/ByteString;)Lkotlin/ranges/IntRange; public static final fun isEmpty (Lkotlinx/io/bytestring/ByteString;)Z + public static final fun isNotEmpty (Lkotlinx/io/bytestring/ByteString;)Z public static final fun lastIndexOf (Lkotlinx/io/bytestring/ByteString;BI)I public static final fun lastIndexOf (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;I)I public static final fun lastIndexOf (Lkotlinx/io/bytestring/ByteString;[BI)I From e2f6d76fa926fcd05f61d7c7f2a61c74871ade90 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Mon, 26 Jun 2023 17:19:27 +0200 Subject: [PATCH 22/32] Restructure project layout --- bytestring/{src/commonMain/kotlin => common/src}/ByteString.kt | 0 .../{src/commonMain/kotlin => common/src}/ByteStringBuilder.kt | 0 .../{src/commonMain/kotlin => common/src/unsafe}/Annotations.kt | 0 .../kotlin => common/src/unsafe}/UnsafeByteStringOperations.kt | 0 .../io/bytestring => common/test}/ByteStringBuilderTest.kt | 0 .../kotlinx/io/bytestring => common/test}/ByteStringTest.kt | 0 bytestring/{src/jvmMain/kotlin => jvm/src}/ByteStringJvmExt.kt | 0 bytestring/{src/jvmTest/kotlin => jvm/test}/ByteStringJvmTest.kt | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename bytestring/{src/commonMain/kotlin => common/src}/ByteString.kt (100%) rename bytestring/{src/commonMain/kotlin => common/src}/ByteStringBuilder.kt (100%) rename bytestring/{src/commonMain/kotlin => common/src/unsafe}/Annotations.kt (100%) rename bytestring/{src/commonMain/kotlin => common/src/unsafe}/UnsafeByteStringOperations.kt (100%) rename bytestring/{src/commonTest/kotlin/kotlinx/io/bytestring => common/test}/ByteStringBuilderTest.kt (100%) rename bytestring/{src/commonTest/kotlin/kotlinx/io/bytestring => common/test}/ByteStringTest.kt (100%) rename bytestring/{src/jvmMain/kotlin => jvm/src}/ByteStringJvmExt.kt (100%) rename bytestring/{src/jvmTest/kotlin => jvm/test}/ByteStringJvmTest.kt (100%) diff --git a/bytestring/src/commonMain/kotlin/ByteString.kt b/bytestring/common/src/ByteString.kt similarity index 100% rename from bytestring/src/commonMain/kotlin/ByteString.kt rename to bytestring/common/src/ByteString.kt diff --git a/bytestring/src/commonMain/kotlin/ByteStringBuilder.kt b/bytestring/common/src/ByteStringBuilder.kt similarity index 100% rename from bytestring/src/commonMain/kotlin/ByteStringBuilder.kt rename to bytestring/common/src/ByteStringBuilder.kt diff --git a/bytestring/src/commonMain/kotlin/Annotations.kt b/bytestring/common/src/unsafe/Annotations.kt similarity index 100% rename from bytestring/src/commonMain/kotlin/Annotations.kt rename to bytestring/common/src/unsafe/Annotations.kt diff --git a/bytestring/src/commonMain/kotlin/UnsafeByteStringOperations.kt b/bytestring/common/src/unsafe/UnsafeByteStringOperations.kt similarity index 100% rename from bytestring/src/commonMain/kotlin/UnsafeByteStringOperations.kt rename to bytestring/common/src/unsafe/UnsafeByteStringOperations.kt diff --git a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringBuilderTest.kt b/bytestring/common/test/ByteStringBuilderTest.kt similarity index 100% rename from bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringBuilderTest.kt rename to bytestring/common/test/ByteStringBuilderTest.kt diff --git a/bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt b/bytestring/common/test/ByteStringTest.kt similarity index 100% rename from bytestring/src/commonTest/kotlin/kotlinx/io/bytestring/ByteStringTest.kt rename to bytestring/common/test/ByteStringTest.kt diff --git a/bytestring/src/jvmMain/kotlin/ByteStringJvmExt.kt b/bytestring/jvm/src/ByteStringJvmExt.kt similarity index 100% rename from bytestring/src/jvmMain/kotlin/ByteStringJvmExt.kt rename to bytestring/jvm/src/ByteStringJvmExt.kt diff --git a/bytestring/src/jvmTest/kotlin/ByteStringJvmTest.kt b/bytestring/jvm/test/ByteStringJvmTest.kt similarity index 100% rename from bytestring/src/jvmTest/kotlin/ByteStringJvmTest.kt rename to bytestring/jvm/test/ByteStringJvmTest.kt From d83827c86702b9db5c6f3af74e6859bddeccf5fe Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Tue, 27 Jun 2023 11:31:19 +0200 Subject: [PATCH 23/32] Minor API changes --- bytestring/api/kotlinx-io-bytestring.api | 6 +++--- bytestring/common/src/ByteString.kt | 3 ++- bytestring/common/src/ByteStringBuilder.kt | 9 +-------- .../common/src/unsafe/UnsafeByteStringOperations.kt | 8 +++++--- bytestring/common/test/ByteStringTest.kt | 8 ++++---- core/common/src/ByteStringExt.kt | 2 +- 6 files changed, 16 insertions(+), 20 deletions(-) diff --git a/bytestring/api/kotlinx-io-bytestring.api b/bytestring/api/kotlinx-io-bytestring.api index 84949c69b..1ca5266cb 100644 --- a/bytestring/api/kotlinx-io-bytestring.api +++ b/bytestring/api/kotlinx-io-bytestring.api @@ -39,7 +39,7 @@ public final class kotlinx/io/bytestring/ByteStringBuilderKt { public static final fun append (Lkotlinx/io/bytestring/ByteStringBuilder;[B)V public static final fun append-EK-6454 (Lkotlinx/io/bytestring/ByteStringBuilder;B)V public static final fun buildByteString (ILkotlin/jvm/functions/Function1;)Lkotlinx/io/bytestring/ByteString; - public static final fun buildByteString (Lkotlin/jvm/functions/Function1;)Lkotlinx/io/bytestring/ByteString; + public static synthetic fun buildByteString$default (ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/io/bytestring/ByteString; } public final class kotlinx/io/bytestring/ByteStringJvmExtKt { @@ -53,13 +53,13 @@ public final class kotlinx/io/bytestring/ByteStringKt { public static final fun endsWith (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;)Z public static final fun endsWith (Lkotlinx/io/bytestring/ByteString;[B)Z public static final fun fromUtf8String (Lkotlinx/io/bytestring/ByteString$Companion;Ljava/lang/String;)Lkotlinx/io/bytestring/ByteString; + public static final fun getIndices (Lkotlinx/io/bytestring/ByteString;)Lkotlin/ranges/IntRange; public static final fun indexOf (Lkotlinx/io/bytestring/ByteString;BI)I public static final fun indexOf (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;I)I public static final fun indexOf (Lkotlinx/io/bytestring/ByteString;[BI)I public static synthetic fun indexOf$default (Lkotlinx/io/bytestring/ByteString;BIILjava/lang/Object;)I public static synthetic fun indexOf$default (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;IILjava/lang/Object;)I public static synthetic fun indexOf$default (Lkotlinx/io/bytestring/ByteString;[BIILjava/lang/Object;)I - public static final fun indices (Lkotlinx/io/bytestring/ByteString;)Lkotlin/ranges/IntRange; public static final fun isEmpty (Lkotlinx/io/bytestring/ByteString;)Z public static final fun isNotEmpty (Lkotlinx/io/bytestring/ByteString;)Z public static final fun lastIndexOf (Lkotlinx/io/bytestring/ByteString;BI)I @@ -78,7 +78,7 @@ public abstract interface annotation class kotlinx/io/bytestring/unsafe/UnsafeBy public final class kotlinx/io/bytestring/unsafe/UnsafeByteStringOperations { public static final field INSTANCE Lkotlinx/io/bytestring/unsafe/UnsafeByteStringOperations; - public final fun getByteArrayUnsafe (Lkotlinx/io/bytestring/ByteString;)[B + public final fun withByteArrayUnsafe (Lkotlinx/io/bytestring/ByteString;Lkotlin/jvm/functions/Function1;)V public final fun wrapUnsafe ([B)Lkotlinx/io/bytestring/ByteString; } diff --git a/bytestring/common/src/ByteString.kt b/bytestring/common/src/ByteString.kt index 33e19d9fb..e4db129b1 100644 --- a/bytestring/common/src/ByteString.kt +++ b/bytestring/common/src/ByteString.kt @@ -228,7 +228,8 @@ public class ByteString private constructor( /** * Returns the range of valid byte indices for this byte string. */ -public fun ByteString.indices(): IntRange = 0 until size +public val ByteString.indices: IntRange + get() = 0 until size /** * Returns the index within this byte string of the first occurrence of the specified [byte], diff --git a/bytestring/common/src/ByteStringBuilder.kt b/bytestring/common/src/ByteStringBuilder.kt index cf99da3ee..5152fb4f5 100644 --- a/bytestring/common/src/ByteStringBuilder.kt +++ b/bytestring/common/src/ByteStringBuilder.kt @@ -114,18 +114,11 @@ public fun ByteStringBuilder.append(byteString: ByteString) { */ public fun ByteStringBuilder.append(vararg bytes: Byte): Unit = append(bytes) -/** - * Builds new string by populating newly created [ByteStringBuilder] using provided [builderAction] - * and then converting it to [ByteString]. - */ -public inline fun buildByteString(builderAction: ByteStringBuilder.() -> Unit): ByteString { - return ByteStringBuilder().apply(builderAction).toByteString() -} /** * Builds new byte string by populating newly created [ByteStringBuilder] initialized with the given [capacity] * using provided [builderAction] and then converting it to [ByteString]. */ -public inline fun buildByteString(capacity: Int, builderAction: ByteStringBuilder.() -> Unit): ByteString { +public inline fun buildByteString(capacity: Int = 0, builderAction: ByteStringBuilder.() -> Unit): ByteString { return ByteStringBuilder(capacity).apply(builderAction).toByteString() } diff --git a/bytestring/common/src/unsafe/UnsafeByteStringOperations.kt b/bytestring/common/src/unsafe/UnsafeByteStringOperations.kt index a7c87e2db..daaeaac72 100644 --- a/bytestring/common/src/unsafe/UnsafeByteStringOperations.kt +++ b/bytestring/common/src/unsafe/UnsafeByteStringOperations.kt @@ -26,10 +26,12 @@ public object UnsafeByteStringOperations { public fun wrapUnsafe(array: ByteArray): ByteString = ByteString.wrap(array) /** - * Returns a reference to the underlying array. + * Applies [block] to a reference to the underlying array. * - * These methods return reference to the underlying array, not to its copy. + * This method invokes [block] on a reference to the underlying array, not to its copy. * Consider using [ByteString.toByteArray] if it's impossible to guarantee that the array won't be modified. */ - public fun getByteArrayUnsafe(byteString: ByteString): ByteArray = byteString.getBackingArrayReference() + public fun withByteArrayUnsafe(byteString: ByteString, block: (ByteArray) -> Unit) { + block(byteString.getBackingArrayReference()) + } } diff --git a/bytestring/common/test/ByteStringTest.kt b/bytestring/common/test/ByteStringTest.kt index acf77c2d7..13350d309 100644 --- a/bytestring/common/test/ByteStringTest.kt +++ b/bytestring/common/test/ByteStringTest.kt @@ -155,8 +155,8 @@ class ByteStringTest { @Test fun indices() { - assertEquals(0 until 10, ByteString(ByteArray(10)).indices()) - assertTrue(ByteString().indices().isEmpty()) + assertEquals(0 until 10, ByteString(ByteArray(10)).indices) + assertTrue(ByteString().indices.isEmpty()) } @Test @@ -176,7 +176,7 @@ class ByteStringTest { @Test fun indexOfByte() { val str = ByteString(1, 2, 3, 4) - for (idx in str.indices()) { + for (idx in str.indices) { assertEquals(idx, str.indexOf(str[idx])) } @@ -247,7 +247,7 @@ class ByteStringTest { @Test fun lastIndexOfByte() { val str = ByteString(1, 2, 3, 4) - for (idx in str.indices()) { + for (idx in str.indices) { assertEquals(idx, str.lastIndexOf(str[idx])) } diff --git a/core/common/src/ByteStringExt.kt b/core/common/src/ByteStringExt.kt index 79fd50fe6..aea7f58d5 100644 --- a/core/common/src/ByteStringExt.kt +++ b/core/common/src/ByteStringExt.kt @@ -101,7 +101,7 @@ public fun Source.indexOf(byteString: ByteString, startIndex: Long = 0): Long { } var matches = true - for (idx in byteString.indices()) { + for (idx in byteString.indices) { if (byteString[idx] != peek.buffer[idx.toLong()]) { matches = false offset++ From 1ffce7ac9d46f154019e3af0b86d88e9ed76c97c Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Tue, 27 Jun 2023 17:13:11 +0200 Subject: [PATCH 24/32] Enable JS target in bytestring module --- bytestring/api/kotlinx-io-bytestring.api | 3 +-- bytestring/build.gradle.kts | 18 ++++++++++++++++++ bytestring/common/src/ByteString.kt | 23 +++++++++++++---------- core/build.gradle.kts | 4 ++-- 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/bytestring/api/kotlinx-io-bytestring.api b/bytestring/api/kotlinx-io-bytestring.api index 1ca5266cb..cd9830ccf 100644 --- a/bytestring/api/kotlinx-io-bytestring.api +++ b/bytestring/api/kotlinx-io-bytestring.api @@ -1,6 +1,5 @@ public final class kotlinx/io/bytestring/ByteString : java/lang/Comparable { public static final field Companion Lkotlinx/io/bytestring/ByteString$Companion; - public fun ([B)V public fun ([BII)V public synthetic fun ([BIIILkotlin/jvm/internal/DefaultConstructorMarker;)V public synthetic fun ([BLjava/lang/Object;Lkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -48,7 +47,7 @@ public final class kotlinx/io/bytestring/ByteStringJvmExtKt { } public final class kotlinx/io/bytestring/ByteStringKt { - public static final fun ByteString ()Lkotlinx/io/bytestring/ByteString; + public static final fun ByteString ([B)Lkotlinx/io/bytestring/ByteString; public static final fun contentEquals (Lkotlinx/io/bytestring/ByteString;[B)Z public static final fun endsWith (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;)Z public static final fun endsWith (Lkotlinx/io/bytestring/ByteString;[B)Z diff --git a/bytestring/build.gradle.kts b/bytestring/build.gradle.kts index 14108bbfc..4e1ef8a51 100644 --- a/bytestring/build.gradle.kts +++ b/bytestring/build.gradle.kts @@ -15,6 +15,24 @@ kotlin { } } + js(IR) { + nodejs { + testTask { + useMocha { + timeout = "30s" + } + } + } + browser { + testTask { + filter.setExcludePatterns("*SmokeFileTest*") + useMocha { + timeout = "30s" + } + } + } + } + configureNativePlatforms() sourceSets { val commonMain by getting diff --git a/bytestring/common/src/ByteString.kt b/bytestring/common/src/ByteString.kt index e4db129b1..07f6295df 100644 --- a/bytestring/common/src/ByteString.kt +++ b/bytestring/common/src/ByteString.kt @@ -27,9 +27,15 @@ import kotlin.math.min private val HEX_DIGITS = "0123456789ABCDEF".toCharArray() /** - * Constructs an empty byte string. + * Wraps given [bytes] into a byte string. + * + * @param bytes a sequence of bytes to be wrapped. */ -public fun ByteString(): ByteString = ByteString.EMPTY +public fun ByteString(vararg bytes: Byte): ByteString = if (bytes.isEmpty()) { + ByteString.EMPTY +} else { + ByteString.wrap(bytes) +} /** * An immutable wrapper around a byte sequence providing [String] like functionality. @@ -68,13 +74,6 @@ public class ByteString private constructor( public constructor(data: ByteArray, startIndex: Int = 0, endIndex: Int = data.size) : this(data.copyOfRange(startIndex, endIndex), null) - /** - * Wraps given [bytes] into a byte string. - * - * @param bytes a sequence of bytes to be wrapped. - */ - public constructor(vararg bytes: Byte) : this(bytes, null) - /** * Returns `true` if [other] is a byte string containing exactly the same byte sequence. * @@ -110,7 +109,11 @@ public class ByteString private constructor( * * @throws IndexOutOfBoundsException when [index] is negative or greater or equal to the [size]. */ - public operator fun get(index: Int): Byte = data[index] + public operator fun get(index: Int): Byte { + if (index < 0 || index >= size) throw IndexOutOfBoundsException( + "index ($index) is out of byte string bounds: [0..$size)") + return data[index] + } /** * Returns a copy of subsequence starting at [startIndex] and ending at [endIndex] of a byte sequence diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 1bde4e349..841c85722 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -24,7 +24,7 @@ kotlin { nodejs { testTask { useMocha { - timeout = "30s" + timeout = "120s" } } } @@ -32,7 +32,7 @@ kotlin { testTask { filter.setExcludePatterns("*SmokeFileTest*") useMocha { - timeout = "30s" + timeout = "120s" } } } From a21ec1f9b78ada64949fd9601025cf87a46a230e Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Wed, 28 Jun 2023 10:23:24 +0200 Subject: [PATCH 25/32] Fixed formatting for byte-string related tests --- core/common/test/AbstractSinkTest.kt | 24 +- core/common/test/AbstractSourceTest.kt | 313 +++++++++++++------------ core/common/test/CommonBufferTest.kt | 37 +-- 3 files changed, 196 insertions(+), 178 deletions(-) diff --git a/core/common/test/AbstractSinkTest.kt b/core/common/test/AbstractSinkTest.kt index 10714ed66..6ae32b7d1 100644 --- a/core/common/test/AbstractSinkTest.kt +++ b/core/common/test/AbstractSinkTest.kt @@ -448,15 +448,17 @@ abstract class AbstractSinkTest internal constructor( assertEquals("Buffer(size=8 hex=efcdab9078563412)", data.toString()) } - @Test fun writeByteString() { - sink.write("təˈranəˌsôr".encodeUtf8()) - sink.flush() - assertEquals(ByteString("74c999cb8872616ec999cb8c73c3b472".decodeHex()), data.readByteString()) - } - - @Test fun writeByteStringOffset() { - sink.write("təˈranəˌsôr".encodeUtf8(), 5, 10) - sink.flush() - assertEquals(ByteString("72616ec999".decodeHex()), data.readByteString()) - } + @Test + fun writeByteString() { + sink.write("təˈranəˌsôr".encodeUtf8()) + sink.flush() + assertEquals(ByteString("74c999cb8872616ec999cb8c73c3b472".decodeHex()), data.readByteString()) + } + + @Test + fun writeByteStringOffset() { + sink.write("təˈranəˌsôr".encodeUtf8(), 5, 10) + sink.flush() + assertEquals(ByteString("72616ec999".decodeHex()), data.readByteString()) + } } diff --git a/core/common/test/AbstractSourceTest.kt b/core/common/test/AbstractSourceTest.kt index c4b356aa0..f6e9d3775 100644 --- a/core/common/test/AbstractSourceTest.kt +++ b/core/common/test/AbstractSourceTest.kt @@ -1538,155 +1538,166 @@ abstract class AbstractBufferedSourceTest internal constructor( } assertTrue(source.request(7)) } - @Test fun readByteString() { - with (sink) { - writeUtf8("abcd") - writeUtf8("e".repeat(Segment.SIZE)) - emit() - } - assertEquals("abcd" + "e".repeat(Segment.SIZE), source.readByteString().toUtf8String()) - } - - @Test fun readByteStringPartial() { - with (sink) { - writeUtf8("abcd") - writeUtf8("e".repeat(Segment.SIZE)) - emit() - } - assertEquals("abc", source.readByteString(3).toUtf8String()) - assertEquals("d", source.readUtf8(1)) - } - - @Test fun readByteStringTooShortThrows() { - sink.writeUtf8("abc") - sink.emit() - assertFailsWith { source.readByteString(4) } - - assertEquals("abc", source.readUtf8()) // The read shouldn't consume any data. - } - - @Test fun indexOfByteString() { - assertEquals(-1, source.indexOf("flop".encodeUtf8())) - - sink.writeUtf8("flip flop") - sink.emit() - assertEquals(5, source.indexOf("flop".encodeUtf8())) - source.readUtf8() // Clear stream. - - // Make sure we backtrack and resume searching after partial match. - sink.writeUtf8("hi hi hi hey") - sink.emit() - assertEquals(3, source.indexOf("hi hi hey".encodeUtf8())) - } - - @Test fun indexOfByteStringAtSegmentBoundary() { - sink.writeUtf8("a".repeat(Segment.SIZE - 1)) - sink.writeUtf8("bcd") - sink.emit() - assertEquals( - (Segment.SIZE - 3).toLong(), - source.indexOf("aabc".encodeUtf8(), (Segment.SIZE - 4).toLong()), - ) - assertEquals( - (Segment.SIZE - 3).toLong(), - source.indexOf("aabc".encodeUtf8(), (Segment.SIZE - 3).toLong()), - ) - assertEquals( - (Segment.SIZE - 2).toLong(), - source.indexOf("abcd".encodeUtf8(), (Segment.SIZE - 2).toLong()), - ) - assertEquals( - (Segment.SIZE - 2).toLong(), - source.indexOf("abc".encodeUtf8(), (Segment.SIZE - 2).toLong()), - ) - assertEquals( - (Segment.SIZE - 2).toLong(), - source.indexOf("abc".encodeUtf8(), (Segment.SIZE - 2).toLong()), - ) - assertEquals( - (Segment.SIZE - 2).toLong(), - source.indexOf("ab".encodeUtf8(), (Segment.SIZE - 2).toLong()), - ) - assertEquals( - (Segment.SIZE - 2).toLong(), - source.indexOf("a".encodeUtf8(), (Segment.SIZE - 2).toLong()), - ) - assertEquals( - (Segment.SIZE - 1).toLong(), - source.indexOf("bc".encodeUtf8(), (Segment.SIZE - 2).toLong()), - ) - assertEquals( - (Segment.SIZE - 1).toLong(), - source.indexOf("b".encodeUtf8(), (Segment.SIZE - 2).toLong()), - ) - assertEquals( - Segment.SIZE.toLong(), - source.indexOf("c".encodeUtf8(), (Segment.SIZE - 2).toLong()), - ) - assertEquals( - Segment.SIZE.toLong(), - source.indexOf("c".encodeUtf8(), Segment.SIZE.toLong()), - ) - assertEquals( - (Segment.SIZE + 1).toLong(), - source.indexOf("d".encodeUtf8(), (Segment.SIZE - 2).toLong()), - ) - assertEquals( - (Segment.SIZE + 1).toLong(), - source.indexOf("d".encodeUtf8(), (Segment.SIZE + 1).toLong()), - ) - } - - @Test fun indexOfDoesNotWrapAround() { - sink.writeUtf8("a".repeat(Segment.SIZE - 1)) - sink.writeUtf8("bcd") - sink.emit() - assertEquals(-1, source.indexOf("abcda".encodeUtf8(), (Segment.SIZE - 3).toLong())) - } - - @Test fun indexOfByteStringWithOffset() { - assertEquals(-1, source.indexOf("flop".encodeUtf8(), 1)) - - sink.writeUtf8("flop flip flop") - sink.emit() - assertEquals(10, source.indexOf("flop".encodeUtf8(), 1)) - source.readUtf8() // Clear stream - - // Make sure we backtrack and resume searching after partial match. - sink.writeUtf8("hi hi hi hi hey") - sink.emit() - assertEquals(6, source.indexOf("hi hi hey".encodeUtf8(), 1)) - } - - @Test fun indexOfEmptyByteString() { - assertEquals(0, source.indexOf(ByteString())) - } - - @Test fun indexOfByteStringInvalidArgumentsThrows() { - assertFailsWith { - source.indexOf("hi".encodeUtf8(), -1) - } - } - - /** - * With [BufferedSourceFactory.ONE_BYTE_AT_A_TIME_BUFFERED_SOURCE], this code was extremely slow. - * https://github.com/square/okio/issues/171 - */ - @Test fun indexOfByteStringAcrossSegmentBoundaries() { - sink.writeUtf8("a".repeat(Segment.SIZE * 2 - 3)) - sink.writeUtf8("bcdefg") - sink.emit() - assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("ab".encodeUtf8())) - assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abc".encodeUtf8())) - assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcd".encodeUtf8())) - assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcde".encodeUtf8())) - assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcdef".encodeUtf8())) - assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcdefg".encodeUtf8())) - assertEquals((Segment.SIZE * 2 - 3).toLong(), source.indexOf("bcdefg".encodeUtf8())) - assertEquals((Segment.SIZE * 2 - 2).toLong(), source.indexOf("cdefg".encodeUtf8())) - assertEquals((Segment.SIZE * 2 - 1).toLong(), source.indexOf("defg".encodeUtf8())) - assertEquals((Segment.SIZE * 2).toLong(), source.indexOf("efg".encodeUtf8())) - assertEquals((Segment.SIZE * 2 + 1).toLong(), source.indexOf("fg".encodeUtf8())) - assertEquals((Segment.SIZE * 2 + 2).toLong(), source.indexOf("g".encodeUtf8())) - } + + @Test + fun readByteString() { + with(sink) { + writeUtf8("abcd") + writeUtf8("e".repeat(Segment.SIZE)) + emit() + } + assertEquals("abcd" + "e".repeat(Segment.SIZE), source.readByteString().toUtf8String()) + } + + @Test + fun readByteStringPartial() { + with(sink) { + writeUtf8("abcd") + writeUtf8("e".repeat(Segment.SIZE)) + emit() + } + assertEquals("abc", source.readByteString(3).toUtf8String()) + assertEquals("d", source.readUtf8(1)) + } + + @Test + fun readByteStringTooShortThrows() { + sink.writeUtf8("abc") + sink.emit() + assertFailsWith { source.readByteString(4) } + + assertEquals("abc", source.readUtf8()) // The read shouldn't consume any data. + } + + @Test + fun indexOfByteString() { + assertEquals(-1, source.indexOf("flop".encodeUtf8())) + + sink.writeUtf8("flip flop") + sink.emit() + assertEquals(5, source.indexOf("flop".encodeUtf8())) + source.readUtf8() // Clear stream. + + // Make sure we backtrack and resume searching after partial match. + sink.writeUtf8("hi hi hi hey") + sink.emit() + assertEquals(3, source.indexOf("hi hi hey".encodeUtf8())) + } + + @Test + fun indexOfByteStringAtSegmentBoundary() { + sink.writeUtf8("a".repeat(Segment.SIZE - 1)) + sink.writeUtf8("bcd") + sink.emit() + assertEquals( + (Segment.SIZE - 3).toLong(), + source.indexOf("aabc".encodeUtf8(), (Segment.SIZE - 4).toLong()), + ) + assertEquals( + (Segment.SIZE - 3).toLong(), + source.indexOf("aabc".encodeUtf8(), (Segment.SIZE - 3).toLong()), + ) + assertEquals( + (Segment.SIZE - 2).toLong(), + source.indexOf("abcd".encodeUtf8(), (Segment.SIZE - 2).toLong()), + ) + assertEquals( + (Segment.SIZE - 2).toLong(), + source.indexOf("abc".encodeUtf8(), (Segment.SIZE - 2).toLong()), + ) + assertEquals( + (Segment.SIZE - 2).toLong(), + source.indexOf("abc".encodeUtf8(), (Segment.SIZE - 2).toLong()), + ) + assertEquals( + (Segment.SIZE - 2).toLong(), + source.indexOf("ab".encodeUtf8(), (Segment.SIZE - 2).toLong()), + ) + assertEquals( + (Segment.SIZE - 2).toLong(), + source.indexOf("a".encodeUtf8(), (Segment.SIZE - 2).toLong()), + ) + assertEquals( + (Segment.SIZE - 1).toLong(), + source.indexOf("bc".encodeUtf8(), (Segment.SIZE - 2).toLong()), + ) + assertEquals( + (Segment.SIZE - 1).toLong(), + source.indexOf("b".encodeUtf8(), (Segment.SIZE - 2).toLong()), + ) + assertEquals( + Segment.SIZE.toLong(), + source.indexOf("c".encodeUtf8(), (Segment.SIZE - 2).toLong()), + ) + assertEquals( + Segment.SIZE.toLong(), + source.indexOf("c".encodeUtf8(), Segment.SIZE.toLong()), + ) + assertEquals( + (Segment.SIZE + 1).toLong(), + source.indexOf("d".encodeUtf8(), (Segment.SIZE - 2).toLong()), + ) + assertEquals( + (Segment.SIZE + 1).toLong(), + source.indexOf("d".encodeUtf8(), (Segment.SIZE + 1).toLong()), + ) + } + + @Test + fun indexOfDoesNotWrapAround() { + sink.writeUtf8("a".repeat(Segment.SIZE - 1)) + sink.writeUtf8("bcd") + sink.emit() + assertEquals(-1, source.indexOf("abcda".encodeUtf8(), (Segment.SIZE - 3).toLong())) + } + + @Test + fun indexOfByteStringWithOffset() { + assertEquals(-1, source.indexOf("flop".encodeUtf8(), 1)) + + sink.writeUtf8("flop flip flop") + sink.emit() + assertEquals(10, source.indexOf("flop".encodeUtf8(), 1)) + source.readUtf8() // Clear stream + + // Make sure we backtrack and resume searching after partial match. + sink.writeUtf8("hi hi hi hi hey") + sink.emit() + assertEquals(6, source.indexOf("hi hi hey".encodeUtf8(), 1)) + } + + @Test + fun indexOfEmptyByteString() { + assertEquals(0, source.indexOf(ByteString())) + } + + @Test + fun indexOfByteStringInvalidArgumentsThrows() { + assertFailsWith { + source.indexOf("hi".encodeUtf8(), -1) + } + } + + /** + * With [BufferedSourceFactory.ONE_BYTE_AT_A_TIME_BUFFERED_SOURCE], this code was extremely slow. + * https://github.com/square/okio/issues/171 + */ + @Test + fun indexOfByteStringAcrossSegmentBoundaries() { + sink.writeUtf8("a".repeat(Segment.SIZE * 2 - 3)) + sink.writeUtf8("bcdefg") + sink.emit() + assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("ab".encodeUtf8())) + assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abc".encodeUtf8())) + assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcd".encodeUtf8())) + assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcde".encodeUtf8())) + assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcdef".encodeUtf8())) + assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcdefg".encodeUtf8())) + assertEquals((Segment.SIZE * 2 - 3).toLong(), source.indexOf("bcdefg".encodeUtf8())) + assertEquals((Segment.SIZE * 2 - 2).toLong(), source.indexOf("cdefg".encodeUtf8())) + assertEquals((Segment.SIZE * 2 - 1).toLong(), source.indexOf("defg".encodeUtf8())) + assertEquals((Segment.SIZE * 2).toLong(), source.indexOf("efg".encodeUtf8())) + assertEquals((Segment.SIZE * 2 + 1).toLong(), source.indexOf("fg".encodeUtf8())) + assertEquals((Segment.SIZE * 2 + 2).toLong(), source.indexOf("g".encodeUtf8())) + } } diff --git a/core/common/test/CommonBufferTest.kt b/core/common/test/CommonBufferTest.kt index 85b00c99d..325c0d564 100644 --- a/core/common/test/CommonBufferTest.kt +++ b/core/common/test/CommonBufferTest.kt @@ -53,25 +53,30 @@ class CommonBufferTest { fun bufferToString() { assertEquals("Buffer(size=0)", Buffer().toString()) - assertEquals("Buffer(size=10 hex=610d0a620a630d645c65)", + assertEquals( + "Buffer(size=10 hex=610d0a620a630d645c65)", Buffer().also { it.writeUtf8("a\r\nb\nc\rd\\e") }.toString() ) - assertEquals("Buffer(size=11 hex=547972616e6e6f73617572)", + assertEquals( + "Buffer(size=11 hex=547972616e6e6f73617572)", Buffer().also { it.writeUtf8("Tyrannosaur") }.toString() ) - assertEquals("Buffer(size=16 hex=74c999cb8872616ec999cb8c73c3b472)", + assertEquals( + "Buffer(size=16 hex=74c999cb8872616ec999cb8c73c3b472)", Buffer().also { it.write("74c999cb8872616ec999cb8c73c3b472".decodeHex()) }.toString() ) - assertEquals("Buffer(size=64 hex=00000000000000000000000000000000000000000000000000000000000000000000000" + - "000000000000000000000000000000000000000000000000000000000)", + assertEquals( + "Buffer(size=64 hex=00000000000000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000)", Buffer().also { it.write(ByteArray(64)) }.toString() ) - assertEquals("Buffer(size=66 hex=000000000000000000000000000000000000000000000000000000000000" + - "00000000000000000000000000000000000000000000000000000000000000000000…)", + assertEquals( + "Buffer(size=66 hex=000000000000000000000000000000000000000000000000000000000000" + + "00000000000000000000000000000000000000000000000000000000000000000000…)", Buffer().also { it.write(ByteArray(66)) }.toString() ) } @@ -586,13 +591,13 @@ class CommonBufferTest { assertArrayEquals(byteArrayOf(42, 42), buffer.readByteArray()) } - @Test - fun snapshot() { - val buffer = Buffer() - assertEquals(ByteString(), buffer.snapshot()) - buffer.writeUtf8("hello") - assertEquals("hello".encodeUtf8(), buffer.snapshot()) - buffer.clear() - assertEquals(ByteString(), buffer.snapshot()) - } + @Test + fun snapshot() { + val buffer = Buffer() + assertEquals(ByteString(), buffer.snapshot()) + buffer.writeUtf8("hello") + assertEquals("hello".encodeUtf8(), buffer.snapshot()) + buffer.clear() + assertEquals(ByteString(), buffer.snapshot()) + } } From 5e0fb14b5c150796c0b80fcfaae669eae031cda0 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Wed, 28 Jun 2023 11:08:39 +0200 Subject: [PATCH 26/32] Updated tests, docs and made the code BCE-friendly --- bytestring/common/src/ByteString.kt | 31 +++++++--- bytestring/common/src/ByteStringBuilder.kt | 5 +- .../common/test/ByteStringBuilderTest.kt | 2 +- bytestring/jvm/test/ByteStringJvmTest.kt | 3 + core/common/src/ByteStringExt.kt | 60 ++++++++++++------- core/common/test/AbstractSourceTest.kt | 4 ++ 6 files changed, 70 insertions(+), 35 deletions(-) diff --git a/bytestring/common/src/ByteString.kt b/bytestring/common/src/ByteString.kt index 07f6295df..b0a9066ed 100644 --- a/bytestring/common/src/ByteString.kt +++ b/bytestring/common/src/ByteString.kt @@ -175,13 +175,16 @@ public class ByteString private constructor( * in lexicographical order. * Byte values are compared as unsigned integers. * + * The behavior is similar to [String.compareTo]. + * * @param other the byte string to compare this string to. */ override fun compareTo(other: ByteString): Int { if (other === this) return 0 - + val localData = data + val otherData = other.data for (i in 0 until min(size, other.size)) { - val cmp = data[i].toUByte().compareTo(other[i].toUByte()) + val cmp = localData[i].toUByte().compareTo(otherData[i].toUByte()) if (cmp != 0) return cmp } @@ -210,8 +213,9 @@ public class ByteString private constructor( append("ByteString(size=") append(sizeStr) append(" hex=") + val localData = data for (i in 0 until size) { - val b = this@ByteString[i].toInt() + val b = localData[i].toInt() append(HEX_DIGITS[(b ushr 4) and 0xf]) append(HEX_DIGITS[b and 0xf]) } @@ -244,8 +248,9 @@ public val ByteString.indices: IntRange * @param startIndex the index (inclusive) starting from which the [byte] should be searched. */ public fun ByteString.indexOf(byte: Byte, startIndex: Int = 0): Int { + val localData = getBackingArrayReference() for (i in max(startIndex, 0) until size) { - if (this[i] == byte) { + if (localData[i] == byte) { return i } } @@ -263,8 +268,10 @@ public fun ByteString.indexOf(byte: Byte, startIndex: Int = 0): Int { */ public fun ByteString.indexOf(byteString: ByteString, startIndex: Int = 0): Int { if (byteString.isEmpty()) return max(min(startIndex, size), 0) + val localData = getBackingArrayReference() + val firstByte = byteString[0] for (i in max(startIndex, 0)..size - byteString.size) { - if (this[i] == byteString[0] && rangeEquals(i, byteString)) { + if (localData[i] == firstByte && rangeEquals(i, byteString)) { return i } } @@ -282,8 +289,10 @@ public fun ByteString.indexOf(byteString: ByteString, startIndex: Int = 0): Int */ public fun ByteString.indexOf(byteArray: ByteArray, startIndex: Int = 0): Int { if (byteArray.isEmpty()) return max(min(startIndex, size), 0) + val localData = getBackingArrayReference() + val firstByte = byteArray[0] for (i in max(0, startIndex)..size - byteArray.size) { - if (this[i] == byteArray[0] && rangeEquals(i, byteArray)) { + if (localData[i] == firstByte && rangeEquals(i, byteArray)) { return i } } @@ -300,8 +309,9 @@ public fun ByteString.indexOf(byteArray: ByteArray, startIndex: Int = 0): Int { * @param startIndex the index (inclusive) starting from which the [byte] should be searched. */ public fun ByteString.lastIndexOf(byte: Byte, startIndex: Int = 0): Int { + val localData = getBackingArrayReference() for (i in size - 1 downTo max(0, startIndex)) { - if (this[i] == byte) { + if (localData[i] == byte) { return i } } @@ -400,8 +410,10 @@ private fun ByteString.rangeEquals( offset: Int, other: ByteString, otherOffset: Int = 0, byteCount: Int = other.size - otherOffset ): Boolean { + val localData = getBackingArrayReference() + val otherData = other.getBackingArrayReference() for (i in 0 until byteCount) { - if (this[offset + i] != other[otherOffset + i]) { + if (localData[offset + i] != otherData[otherOffset + i]) { return false } } @@ -412,8 +424,9 @@ private fun ByteString.rangeEquals( offset: Int, other: ByteArray, otherOffset: Int = 0, byteCount: Int = other.size - otherOffset ): Boolean { + val localData = getBackingArrayReference() for (i in 0 until byteCount) { - if (this[offset + i] != other[otherOffset + i]) { + if (localData[offset + i] != other[otherOffset + i]) { return false } } diff --git a/bytestring/common/src/ByteStringBuilder.kt b/bytestring/common/src/ByteStringBuilder.kt index 5152fb4f5..3d62964e8 100644 --- a/bytestring/common/src/ByteStringBuilder.kt +++ b/bytestring/common/src/ByteStringBuilder.kt @@ -74,9 +74,10 @@ public class ByteStringBuilder(initialCapacity: Int = 0) { * @throws IllegalArgumentException when `startIndex > endIndex`. */ public fun append(array: ByteArray, startIndex: Int = 0, endIndex: Int = array.size) { - require(startIndex <= endIndex) { "startIndex: $startIndex, endIndex: $endIndex" } + require(startIndex <= endIndex) { "startIndex ($startIndex) > endIndex ($endIndex)" } if (startIndex < 0 || endIndex > array.size) { - throw IndexOutOfBoundsException("startIndex: $startIndex, endIndex: $endIndex") + throw IndexOutOfBoundsException("startIndex ($startIndex) and endIndex ($endIndex) represents " + + "an interval out of array's bounds [0..${array.size}).") } ensureCapacity(offset + endIndex - startIndex) diff --git a/bytestring/common/test/ByteStringBuilderTest.kt b/bytestring/common/test/ByteStringBuilderTest.kt index 78d35a4a5..2fbb05408 100644 --- a/bytestring/common/test/ByteStringBuilderTest.kt +++ b/bytestring/common/test/ByteStringBuilderTest.kt @@ -94,7 +94,7 @@ class ByteStringBuilderTest { val string = with(ByteStringBuilder()) { append(42) append(ByteArray(10) { it.toByte() }) - append(42) + append(42U) append(ByteString(10, 5, 57)) toByteString() } diff --git a/bytestring/jvm/test/ByteStringJvmTest.kt b/bytestring/jvm/test/ByteStringJvmTest.kt index 734a259f7..69b4cce1f 100644 --- a/bytestring/jvm/test/ByteStringJvmTest.kt +++ b/bytestring/jvm/test/ByteStringJvmTest.kt @@ -8,6 +8,9 @@ package kotlinx.io.bytestring import kotlin.test.Test import kotlin.test.assertEquals +/** + * Set of tests covering JVM-specific [ByteString] extensions. + */ class ByteStringJvmTest { @Test fun createFromString() { diff --git a/core/common/src/ByteStringExt.kt b/core/common/src/ByteStringExt.kt index aea7f58d5..b5f0f6034 100644 --- a/core/common/src/ByteStringExt.kt +++ b/core/common/src/ByteStringExt.kt @@ -6,7 +6,6 @@ package kotlinx.io import kotlinx.io.bytestring.ByteString -import kotlinx.io.bytestring.indices import kotlinx.io.bytestring.isEmpty import kotlinx.io.bytestring.unsafe.UnsafeByteStringApi import kotlinx.io.bytestring.unsafe.UnsafeByteStringOperations @@ -26,9 +25,20 @@ import kotlin.math.min @OptIn(DelicateIoApi::class) public fun Sink.write(byteString: ByteString, startIndex: Int = 0, endIndex: Int = byteString.size) { checkBounds(byteString.size, startIndex, endIndex) + if (endIndex == startIndex) { + return + } writeToInternalBuffer { buffer -> var offset = startIndex + val tail = buffer.head?.prev + if (tail != null) { + val bytesToWrite = min(tail.data.size - tail.limit, endIndex - offset) + byteString.copyInto(tail.data, tail.limit, offset, offset + bytesToWrite) + offset += bytesToWrite + tail.limit += bytesToWrite + buffer.size += bytesToWrite + } while (offset < endIndex) { val bytesToWrite = min(endIndex - offset, Segment.SIZE) val seg = buffer.writableSegment(bytesToWrite) @@ -75,7 +85,7 @@ public fun Source.readByteString(byteCount: Int): ByteString { * @throws IllegalArgumentException if [startIndex] is negative. * @throws IllegalStateException if the source is closed. */ -@OptIn(InternalIoApi::class) +@OptIn(InternalIoApi::class, UnsafeByteStringApi::class) public fun Source.indexOf(byteString: ByteString, startIndex: Long = 0): Long { require(startIndex >= 0) { "startIndex: $startIndex" } @@ -89,29 +99,33 @@ public fun Source.indexOf(byteString: ByteString, startIndex: Long = 0): Long { return -1L } peek.skip(offset) - while (!peek.exhausted()) { - val index = peek.indexOf(byteString[0]) - if (index == -1L) { - return -1L - } - offset += index - peek.skip(index) - if (!peek.request(byteString.size.toLong())) { - return -1L - } + var resultingIndex = -1L + UnsafeByteStringOperations.withByteArrayUnsafe(byteString) { data -> + while (!peek.exhausted()) { + val index = peek.indexOf(data[0]) + if (index == -1L) { + return@withByteArrayUnsafe + } + offset += index + peek.skip(index) + if (!peek.request(byteString.size.toLong())) { + return@withByteArrayUnsafe + } - var matches = true - for (idx in byteString.indices) { - if (byteString[idx] != peek.buffer[idx.toLong()]) { - matches = false - offset++ - peek.skip(1) - break + var matches = true + for (idx in data.indices) { + if (data[idx] != peek.buffer[idx.toLong()]) { + matches = false + offset++ + peek.skip(1) + break + } + } + if (matches) { + resultingIndex = offset + return@withByteArrayUnsafe } - } - if (matches) { - return offset } } - return -1L + return resultingIndex } diff --git a/core/common/test/AbstractSourceTest.kt b/core/common/test/AbstractSourceTest.kt index f6e9d3775..4a628afa1 100644 --- a/core/common/test/AbstractSourceTest.kt +++ b/core/common/test/AbstractSourceTest.kt @@ -1669,6 +1669,10 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun indexOfEmptyByteString() { assertEquals(0, source.indexOf(ByteString())) + + sink.writeUtf8("blablabla") + sink.emit() + assertEquals(0, source.indexOf(ByteString())) } @Test From 7207c085409e22c6f71efc64269155080d1ccaa2 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Wed, 28 Jun 2023 11:35:56 +0200 Subject: [PATCH 27/32] Rename string conversion routines --- bytestring/api/kotlinx-io-bytestring.api | 8 +-- bytestring/build.gradle.kts | 2 +- bytestring/common/src/ByteString.kt | 8 +-- bytestring/common/test/ByteStringTest.kt | 4 +- bytestring/jvm/src/ByteStringJvmExt.kt | 8 +-- bytestring/jvm/test/ByteStringJvmTest.kt | 8 +-- core/common/test/AbstractSinkTest.kt | 5 +- core/common/test/AbstractSourceTest.kt | 73 ++++++++++++------------ core/common/test/CommonBufferTest.kt | 3 +- core/common/test/util.kt | 4 -- 10 files changed, 59 insertions(+), 64 deletions(-) diff --git a/bytestring/api/kotlinx-io-bytestring.api b/bytestring/api/kotlinx-io-bytestring.api index cd9830ccf..e56c61ab7 100644 --- a/bytestring/api/kotlinx-io-bytestring.api +++ b/bytestring/api/kotlinx-io-bytestring.api @@ -42,16 +42,17 @@ public final class kotlinx/io/bytestring/ByteStringBuilderKt { } public final class kotlinx/io/bytestring/ByteStringJvmExtKt { - public static final fun fromString (Lkotlinx/io/bytestring/ByteString$Companion;Ljava/lang/String;Ljava/nio/charset/Charset;)Lkotlinx/io/bytestring/ByteString; - public static final fun toString (Lkotlinx/io/bytestring/ByteString;Ljava/nio/charset/Charset;)Ljava/lang/String; + public static final fun decodeToString (Lkotlinx/io/bytestring/ByteString;Ljava/nio/charset/Charset;)Ljava/lang/String; + public static final fun encodeToByteString (Ljava/lang/String;Ljava/nio/charset/Charset;)Lkotlinx/io/bytestring/ByteString; } public final class kotlinx/io/bytestring/ByteStringKt { public static final fun ByteString ([B)Lkotlinx/io/bytestring/ByteString; public static final fun contentEquals (Lkotlinx/io/bytestring/ByteString;[B)Z + public static final fun decodeToString (Lkotlinx/io/bytestring/ByteString;)Ljava/lang/String; + public static final fun encodeToByteString (Ljava/lang/String;)Lkotlinx/io/bytestring/ByteString; public static final fun endsWith (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;)Z public static final fun endsWith (Lkotlinx/io/bytestring/ByteString;[B)Z - public static final fun fromUtf8String (Lkotlinx/io/bytestring/ByteString$Companion;Ljava/lang/String;)Lkotlinx/io/bytestring/ByteString; public static final fun getIndices (Lkotlinx/io/bytestring/ByteString;)Lkotlin/ranges/IntRange; public static final fun indexOf (Lkotlinx/io/bytestring/ByteString;BI)I public static final fun indexOf (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;I)I @@ -69,7 +70,6 @@ public final class kotlinx/io/bytestring/ByteStringKt { public static synthetic fun lastIndexOf$default (Lkotlinx/io/bytestring/ByteString;[BIILjava/lang/Object;)I public static final fun startsWith (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;)Z public static final fun startsWith (Lkotlinx/io/bytestring/ByteString;[B)Z - public static final fun toUtf8String (Lkotlinx/io/bytestring/ByteString;)Ljava/lang/String; } public abstract interface annotation class kotlinx/io/bytestring/unsafe/UnsafeByteStringApi : java/lang/annotation/Annotation { diff --git a/bytestring/build.gradle.kts b/bytestring/build.gradle.kts index 4e1ef8a51..f81792b08 100644 --- a/bytestring/build.gradle.kts +++ b/bytestring/build.gradle.kts @@ -77,4 +77,4 @@ tasks.withType().configureEach { matchingRegex.set(".*unsafe.*") } } -} \ No newline at end of file +} diff --git a/bytestring/common/src/ByteString.kt b/bytestring/common/src/ByteString.kt index b0a9066ed..e12e69920 100644 --- a/bytestring/common/src/ByteString.kt +++ b/bytestring/common/src/ByteString.kt @@ -446,17 +446,15 @@ public fun ByteString.isNotEmpty(): Boolean = !isEmpty() /** * Decodes content of a byte string into a string using UTF-8 encoding. */ -public fun ByteString.toUtf8String(): String { +public fun ByteString.decodeToString(): String { return getBackingArrayReference().decodeToString() } /** * Encodes a string into a byte sequence using UTF8-encoding and wraps it into a byte string. - * - * @param string the string to be encoded. */ -public fun ByteString.Companion.fromUtf8String(string: String): ByteString { - return wrap(string.encodeToByteArray()) +public fun String.encodeToByteString(): ByteString { + return ByteString.wrap(encodeToByteArray()) } /** diff --git a/bytestring/common/test/ByteStringTest.kt b/bytestring/common/test/ByteStringTest.kt index 13350d309..6048d7419 100644 --- a/bytestring/common/test/ByteStringTest.kt +++ b/bytestring/common/test/ByteStringTest.kt @@ -393,10 +393,10 @@ class ByteStringTest { @Test fun utf8() { - val byteString = ByteString.fromUtf8String(bronzeHorseman) + val byteString = bronzeHorseman.encodeToByteString() assertEquals(byteString.toByteArray().toList(), bronzeHorseman.encodeToByteArray().toList()) assertEquals(byteString, ByteString(*bronzeHorseman.encodeToByteArray())) - assertEquals(byteString.toUtf8String(), bronzeHorseman) + assertEquals(byteString.decodeToString(), bronzeHorseman) } @Test diff --git a/bytestring/jvm/src/ByteStringJvmExt.kt b/bytestring/jvm/src/ByteStringJvmExt.kt index 344af05d7..555e65f86 100644 --- a/bytestring/jvm/src/ByteStringJvmExt.kt +++ b/bytestring/jvm/src/ByteStringJvmExt.kt @@ -12,13 +12,11 @@ import java.nio.charset.Charset * * @param charset the charset to decode data into a string. */ -public fun ByteString.toString(charset: Charset): String = getBackingArrayReference().toString(charset) +public fun ByteString.decodeToString(charset: Charset): String = getBackingArrayReference().toString(charset) /** - * Constructs a new byte string containing [string] encoded into bytes using [charset]. + * Encodes a string into a byte string using [charset]. * - * @param string string to encode. * @param charset the encoding. */ -public fun ByteString.Companion.fromString(string: String, charset: Charset): ByteString = - wrap(string.toByteArray(charset)) +public fun String.encodeToByteString(charset: Charset): ByteString = ByteString.wrap(toByteArray(charset)) diff --git a/bytestring/jvm/test/ByteStringJvmTest.kt b/bytestring/jvm/test/ByteStringJvmTest.kt index 69b4cce1f..37f9b86ae 100644 --- a/bytestring/jvm/test/ByteStringJvmTest.kt +++ b/bytestring/jvm/test/ByteStringJvmTest.kt @@ -16,14 +16,14 @@ class ByteStringJvmTest { fun createFromString() { val str = "hello" - assertEquals(ByteString(byteArrayOf(0x68, 0x65, 0x6c, 0x6c, 0x6f)), ByteString.fromString(str, Charsets.UTF_8)) + assertEquals(ByteString(byteArrayOf(0x68, 0x65, 0x6c, 0x6c, 0x6f)), str.encodeToByteString(Charsets.UTF_8)) assertEquals( ByteString( byteArrayOf( 0, 0, 0, 0x68, 0, 0, 0, 0x65, 0, 0, 0, 0x6c, 0, 0, 0, 0x6c, 0, 0, 0, 0x6f ) - ), ByteString.fromString(str, Charsets.UTF_32) + ), str.encodeToByteString(Charsets.UTF_32) ) } @@ -31,9 +31,9 @@ class ByteStringJvmTest { fun decodeToString() { assertEquals( "Ϭ", - ByteString(0xfeU.toByte(), 0xffU.toByte(), 0x03, 0xecU.toByte()).toString(Charsets.UTF_16) + ByteString(0xfeU.toByte(), 0xffU.toByte(), 0x03, 0xecU.toByte()).decodeToString(Charsets.UTF_16) ) - assertEquals("123", ByteString("123".encodeToByteArray()).toString(Charsets.UTF_8)) + assertEquals("123", ByteString("123".encodeToByteArray()).decodeToString(Charsets.UTF_8)) } } diff --git a/core/common/test/AbstractSinkTest.kt b/core/common/test/AbstractSinkTest.kt index 6ae32b7d1..6ffb8d3de 100644 --- a/core/common/test/AbstractSinkTest.kt +++ b/core/common/test/AbstractSinkTest.kt @@ -22,6 +22,7 @@ package kotlinx.io import kotlinx.io.bytestring.ByteString +import kotlinx.io.bytestring.encodeToByteString import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -450,14 +451,14 @@ abstract class AbstractSinkTest internal constructor( @Test fun writeByteString() { - sink.write("təˈranəˌsôr".encodeUtf8()) + sink.write("təˈranəˌsôr".encodeToByteString()) sink.flush() assertEquals(ByteString("74c999cb8872616ec999cb8c73c3b472".decodeHex()), data.readByteString()) } @Test fun writeByteStringOffset() { - sink.write("təˈranəˌsôr".encodeUtf8(), 5, 10) + sink.write("təˈranəˌsôr".encodeToByteString(), 5, 10) sink.flush() assertEquals(ByteString("72616ec999".decodeHex()), data.readByteString()) } diff --git a/core/common/test/AbstractSourceTest.kt b/core/common/test/AbstractSourceTest.kt index 4a628afa1..fbb934bf7 100644 --- a/core/common/test/AbstractSourceTest.kt +++ b/core/common/test/AbstractSourceTest.kt @@ -22,7 +22,8 @@ package kotlinx.io import kotlinx.io.bytestring.ByteString -import kotlinx.io.bytestring.toUtf8String +import kotlinx.io.bytestring.decodeToString +import kotlinx.io.bytestring.encodeToByteString import kotlin.test.* private const val SEGMENT_SIZE = Segment.SIZE @@ -1546,7 +1547,7 @@ abstract class AbstractBufferedSourceTest internal constructor( writeUtf8("e".repeat(Segment.SIZE)) emit() } - assertEquals("abcd" + "e".repeat(Segment.SIZE), source.readByteString().toUtf8String()) + assertEquals("abcd" + "e".repeat(Segment.SIZE), source.readByteString().decodeToString()) } @Test @@ -1556,7 +1557,7 @@ abstract class AbstractBufferedSourceTest internal constructor( writeUtf8("e".repeat(Segment.SIZE)) emit() } - assertEquals("abc", source.readByteString(3).toUtf8String()) + assertEquals("abc", source.readByteString(3).decodeToString()) assertEquals("d", source.readUtf8(1)) } @@ -1571,17 +1572,17 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun indexOfByteString() { - assertEquals(-1, source.indexOf("flop".encodeUtf8())) + assertEquals(-1, source.indexOf("flop".encodeToByteString())) sink.writeUtf8("flip flop") sink.emit() - assertEquals(5, source.indexOf("flop".encodeUtf8())) + assertEquals(5, source.indexOf("flop".encodeToByteString())) source.readUtf8() // Clear stream. // Make sure we backtrack and resume searching after partial match. sink.writeUtf8("hi hi hi hey") sink.emit() - assertEquals(3, source.indexOf("hi hi hey".encodeUtf8())) + assertEquals(3, source.indexOf("hi hi hey".encodeToByteString())) } @Test @@ -1591,55 +1592,55 @@ abstract class AbstractBufferedSourceTest internal constructor( sink.emit() assertEquals( (Segment.SIZE - 3).toLong(), - source.indexOf("aabc".encodeUtf8(), (Segment.SIZE - 4).toLong()), + source.indexOf("aabc".encodeToByteString(), (Segment.SIZE - 4).toLong()), ) assertEquals( (Segment.SIZE - 3).toLong(), - source.indexOf("aabc".encodeUtf8(), (Segment.SIZE - 3).toLong()), + source.indexOf("aabc".encodeToByteString(), (Segment.SIZE - 3).toLong()), ) assertEquals( (Segment.SIZE - 2).toLong(), - source.indexOf("abcd".encodeUtf8(), (Segment.SIZE - 2).toLong()), + source.indexOf("abcd".encodeToByteString(), (Segment.SIZE - 2).toLong()), ) assertEquals( (Segment.SIZE - 2).toLong(), - source.indexOf("abc".encodeUtf8(), (Segment.SIZE - 2).toLong()), + source.indexOf("abc".encodeToByteString(), (Segment.SIZE - 2).toLong()), ) assertEquals( (Segment.SIZE - 2).toLong(), - source.indexOf("abc".encodeUtf8(), (Segment.SIZE - 2).toLong()), + source.indexOf("abc".encodeToByteString(), (Segment.SIZE - 2).toLong()), ) assertEquals( (Segment.SIZE - 2).toLong(), - source.indexOf("ab".encodeUtf8(), (Segment.SIZE - 2).toLong()), + source.indexOf("ab".encodeToByteString(), (Segment.SIZE - 2).toLong()), ) assertEquals( (Segment.SIZE - 2).toLong(), - source.indexOf("a".encodeUtf8(), (Segment.SIZE - 2).toLong()), + source.indexOf("a".encodeToByteString(), (Segment.SIZE - 2).toLong()), ) assertEquals( (Segment.SIZE - 1).toLong(), - source.indexOf("bc".encodeUtf8(), (Segment.SIZE - 2).toLong()), + source.indexOf("bc".encodeToByteString(), (Segment.SIZE - 2).toLong()), ) assertEquals( (Segment.SIZE - 1).toLong(), - source.indexOf("b".encodeUtf8(), (Segment.SIZE - 2).toLong()), + source.indexOf("b".encodeToByteString(), (Segment.SIZE - 2).toLong()), ) assertEquals( Segment.SIZE.toLong(), - source.indexOf("c".encodeUtf8(), (Segment.SIZE - 2).toLong()), + source.indexOf("c".encodeToByteString(), (Segment.SIZE - 2).toLong()), ) assertEquals( Segment.SIZE.toLong(), - source.indexOf("c".encodeUtf8(), Segment.SIZE.toLong()), + source.indexOf("c".encodeToByteString(), Segment.SIZE.toLong()), ) assertEquals( (Segment.SIZE + 1).toLong(), - source.indexOf("d".encodeUtf8(), (Segment.SIZE - 2).toLong()), + source.indexOf("d".encodeToByteString(), (Segment.SIZE - 2).toLong()), ) assertEquals( (Segment.SIZE + 1).toLong(), - source.indexOf("d".encodeUtf8(), (Segment.SIZE + 1).toLong()), + source.indexOf("d".encodeToByteString(), (Segment.SIZE + 1).toLong()), ) } @@ -1648,22 +1649,22 @@ abstract class AbstractBufferedSourceTest internal constructor( sink.writeUtf8("a".repeat(Segment.SIZE - 1)) sink.writeUtf8("bcd") sink.emit() - assertEquals(-1, source.indexOf("abcda".encodeUtf8(), (Segment.SIZE - 3).toLong())) + assertEquals(-1, source.indexOf("abcda".encodeToByteString(), (Segment.SIZE - 3).toLong())) } @Test fun indexOfByteStringWithOffset() { - assertEquals(-1, source.indexOf("flop".encodeUtf8(), 1)) + assertEquals(-1, source.indexOf("flop".encodeToByteString(), 1)) sink.writeUtf8("flop flip flop") sink.emit() - assertEquals(10, source.indexOf("flop".encodeUtf8(), 1)) + assertEquals(10, source.indexOf("flop".encodeToByteString(), 1)) source.readUtf8() // Clear stream // Make sure we backtrack and resume searching after partial match. sink.writeUtf8("hi hi hi hi hey") sink.emit() - assertEquals(6, source.indexOf("hi hi hey".encodeUtf8(), 1)) + assertEquals(6, source.indexOf("hi hi hey".encodeToByteString(), 1)) } @Test @@ -1678,7 +1679,7 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun indexOfByteStringInvalidArgumentsThrows() { assertFailsWith { - source.indexOf("hi".encodeUtf8(), -1) + source.indexOf("hi".encodeToByteString(), -1) } } @@ -1691,17 +1692,17 @@ abstract class AbstractBufferedSourceTest internal constructor( sink.writeUtf8("a".repeat(Segment.SIZE * 2 - 3)) sink.writeUtf8("bcdefg") sink.emit() - assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("ab".encodeUtf8())) - assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abc".encodeUtf8())) - assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcd".encodeUtf8())) - assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcde".encodeUtf8())) - assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcdef".encodeUtf8())) - assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcdefg".encodeUtf8())) - assertEquals((Segment.SIZE * 2 - 3).toLong(), source.indexOf("bcdefg".encodeUtf8())) - assertEquals((Segment.SIZE * 2 - 2).toLong(), source.indexOf("cdefg".encodeUtf8())) - assertEquals((Segment.SIZE * 2 - 1).toLong(), source.indexOf("defg".encodeUtf8())) - assertEquals((Segment.SIZE * 2).toLong(), source.indexOf("efg".encodeUtf8())) - assertEquals((Segment.SIZE * 2 + 1).toLong(), source.indexOf("fg".encodeUtf8())) - assertEquals((Segment.SIZE * 2 + 2).toLong(), source.indexOf("g".encodeUtf8())) + assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("ab".encodeToByteString())) + assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abc".encodeToByteString())) + assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcd".encodeToByteString())) + assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcde".encodeToByteString())) + assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcdef".encodeToByteString())) + assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcdefg".encodeToByteString())) + assertEquals((Segment.SIZE * 2 - 3).toLong(), source.indexOf("bcdefg".encodeToByteString())) + assertEquals((Segment.SIZE * 2 - 2).toLong(), source.indexOf("cdefg".encodeToByteString())) + assertEquals((Segment.SIZE * 2 - 1).toLong(), source.indexOf("defg".encodeToByteString())) + assertEquals((Segment.SIZE * 2).toLong(), source.indexOf("efg".encodeToByteString())) + assertEquals((Segment.SIZE * 2 + 1).toLong(), source.indexOf("fg".encodeToByteString())) + assertEquals((Segment.SIZE * 2 + 2).toLong(), source.indexOf("g".encodeToByteString())) } } diff --git a/core/common/test/CommonBufferTest.kt b/core/common/test/CommonBufferTest.kt index 325c0d564..2df80fb97 100644 --- a/core/common/test/CommonBufferTest.kt +++ b/core/common/test/CommonBufferTest.kt @@ -21,6 +21,7 @@ package kotlinx.io import kotlinx.io.bytestring.ByteString +import kotlinx.io.bytestring.encodeToByteString import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -596,7 +597,7 @@ class CommonBufferTest { val buffer = Buffer() assertEquals(ByteString(), buffer.snapshot()) buffer.writeUtf8("hello") - assertEquals("hello".encodeUtf8(), buffer.snapshot()) + assertEquals("hello".encodeToByteString(), buffer.snapshot()) buffer.clear() assertEquals(ByteString(), buffer.snapshot()) } diff --git a/core/common/test/util.kt b/core/common/test/util.kt index 80dc15337..c621b3c17 100644 --- a/core/common/test/util.kt +++ b/core/common/test/util.kt @@ -20,8 +20,6 @@ */ package kotlinx.io -import kotlinx.io.bytestring.ByteString -import kotlinx.io.bytestring.fromUtf8String import kotlin.test.assertEquals fun segmentSizes(buffer: Buffer): List { @@ -69,5 +67,3 @@ fun Char.repeat(count: Int): String { fun assertArrayEquals(a: ByteArray, b: ByteArray) { assertEquals(a.contentToString(), b.contentToString()) } - -fun String.encodeUtf8(): ByteString = ByteString.fromUtf8String(this) From b3612bd1bdf7f34fd526ba27dc158b24f4aa2f47 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Wed, 28 Jun 2023 14:20:29 +0200 Subject: [PATCH 28/32] Increase timeout of JS tests --- core/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 841c85722..02cb605bb 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -24,7 +24,7 @@ kotlin { nodejs { testTask { useMocha { - timeout = "120s" + timeout = "300s" } } } @@ -32,7 +32,7 @@ kotlin { testTask { filter.setExcludePatterns("*SmokeFileTest*") useMocha { - timeout = "120s" + timeout = "300s" } } } From 9006255e05c593125e6778cdc548593fd8596262 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Wed, 28 Jun 2023 20:13:59 +0200 Subject: [PATCH 29/32] Cleaned up the code, updated docs --- bytestring/common/src/ByteString.kt | 37 +++++++++++-------- bytestring/common/src/unsafe/Annotations.kt | 2 +- .../src/unsafe/UnsafeByteStringOperations.kt | 2 +- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/bytestring/common/src/ByteString.kt b/bytestring/common/src/ByteString.kt index e12e69920..d1ab0a512 100644 --- a/bytestring/common/src/ByteString.kt +++ b/bytestring/common/src/ByteString.kt @@ -24,8 +24,6 @@ package kotlinx.io.bytestring import kotlin.math.max import kotlin.math.min -private val HEX_DIGITS = "0123456789ABCDEF".toCharArray() - /** * Wraps given [bytes] into a byte string. * @@ -44,6 +42,19 @@ public class ByteString private constructor( private val data: ByteArray, @Suppress("UNUSED_PARAMETER") dummy: Any? ) : Comparable { + /** + * Wraps a copy of [data] subarray starting at [startIndex] and ending at [endIndex] into a byte string. + * + * @param data the array whose subarray should be copied and wrapped into a byte string. + * @param startIndex the start index (inclusive) of a subarray to copy, `0` by default. + * @param endIndex the end index (exclusive) of a subarray to copy, `data.size` be default. + * + * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [data] array indices. + * @throws IllegalArgumentException when `startIndex > endIndex`. + */ + public constructor(data: ByteArray, startIndex: Int = 0, endIndex: Int = data.size) : + this(data.copyOfRange(startIndex, endIndex), null) + private var hashCode: Int = 0 public companion object { @@ -53,6 +64,8 @@ public class ByteString private constructor( internal val EMPTY: ByteString = ByteString(ByteArray(0), null) internal fun wrap(byteArray: ByteArray): ByteString = ByteString(byteArray, null) + + private val HEX_DIGITS = "0123456789ABCDEF".toCharArray() } /** @@ -61,19 +74,6 @@ public class ByteString private constructor( public val size: Int get(): Int = data.size - /** - * Wraps a copy of [data] subarray starting at [startIndex] and ending at [endIndex] into a byte string. - * - * @param data the array whose subarray should be copied and wrapped into a byte string. - * @param startIndex the start index (inclusive) of a subarray to copy, `0` by default. - * @param endIndex the end index (exclusive) of a subarray to copy, `data.size` be default. - * - * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [data] array indices. - * @throws IllegalArgumentException when `startIndex > endIndex`. - */ - public constructor(data: ByteArray, startIndex: Int = 0, endIndex: Int = data.size) : - this(data.copyOfRange(startIndex, endIndex), null) - /** * Returns `true` if [other] is a byte string containing exactly the same byte sequence. * @@ -229,6 +229,7 @@ public class ByteString private constructor( * These methods return reference to the underlying array, not to its copy. * Consider using [toByteArray] if it's impossible to guarantee that the array won't be modified. */ + @PublishedApi internal fun getBackingArrayReference(): ByteArray = data } @@ -241,6 +242,7 @@ public val ByteString.indices: IntRange /** * Returns the index within this byte string of the first occurrence of the specified [byte], * starting from the specified [startIndex]. + * If the [byte] not found, `-1` is returned. * * Behavior of this method is compatible with [CharSequence.indexOf]. * @@ -260,6 +262,7 @@ public fun ByteString.indexOf(byte: Byte, startIndex: Int = 0): Int { /** * Returns the index within this byte string of the first occurrence of the specified [byteString], * starting from the specified [startIndex]. + * If the [byteString] not found, `-1` is returned. * * Behavior of this method is compatible with [CharSequence.indexOf]. * @@ -281,6 +284,7 @@ public fun ByteString.indexOf(byteString: ByteString, startIndex: Int = 0): Int /** * Returns the index within this byte string of the first occurrence of the specified [byteArray], * starting from the specified [startIndex]. + * If the [byteArray] not found, `-1` is returned. * * Behavior of this method is compatible with [CharSequence.indexOf]. * @@ -302,6 +306,7 @@ public fun ByteString.indexOf(byteArray: ByteArray, startIndex: Int = 0): Int { /** * Returns the index within this char sequence of the last occurrence of the specified [byte], * starting from the specified [startIndex]. + * If the [byte] not found, `-1` is returned. * * Behavior of this method is compatible with [CharSequence.lastIndexOf]. * @@ -321,6 +326,7 @@ public fun ByteString.lastIndexOf(byte: Byte, startIndex: Int = 0): Int { /** * Returns the index within this char sequence of the last occurrence of the specified [byteString], * starting from the specified [startIndex]. + * If the [byteString] not found, `-1` is returned. * * Behavior of this method is compatible with [CharSequence.lastIndexOf]. * @@ -340,6 +346,7 @@ public fun ByteString.lastIndexOf(byteString: ByteString, startIndex: Int = 0): /** * Returns the index within this char sequence of the last occurrence of the specified [byteArray], * starting from the specified [startIndex]. + * If the [byteArray] not found, `-1` is returned. * * Behavior of this method is compatible with [CharSequence.lastIndexOf]. * diff --git a/bytestring/common/src/unsafe/Annotations.kt b/bytestring/common/src/unsafe/Annotations.kt index 2e0361b0c..41ece7620 100644 --- a/bytestring/common/src/unsafe/Annotations.kt +++ b/bytestring/common/src/unsafe/Annotations.kt @@ -18,4 +18,4 @@ package kotlinx.io.bytestring.unsafe message = "This is a unsafe API and its use may corrupt the data stored in a byte string. " + "Make sure you fully read and understand documentation of the declaration that is marked as an unsafe API." ) -public annotation class UnsafeByteStringApi \ No newline at end of file +public annotation class UnsafeByteStringApi diff --git a/bytestring/common/src/unsafe/UnsafeByteStringOperations.kt b/bytestring/common/src/unsafe/UnsafeByteStringOperations.kt index daaeaac72..fdc89e3b2 100644 --- a/bytestring/common/src/unsafe/UnsafeByteStringOperations.kt +++ b/bytestring/common/src/unsafe/UnsafeByteStringOperations.kt @@ -31,7 +31,7 @@ public object UnsafeByteStringOperations { * This method invokes [block] on a reference to the underlying array, not to its copy. * Consider using [ByteString.toByteArray] if it's impossible to guarantee that the array won't be modified. */ - public fun withByteArrayUnsafe(byteString: ByteString, block: (ByteArray) -> Unit) { + public inline fun withByteArrayUnsafe(byteString: ByteString, block: (ByteArray) -> Unit) { block(byteString.getBackingArrayReference()) } } From 2c1a78519481999b646e8fa85f46ccf141da5081 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Wed, 28 Jun 2023 21:39:08 +0200 Subject: [PATCH 30/32] Updated API dump --- bytestring/api/kotlinx-io-bytestring.api | 1 + 1 file changed, 1 insertion(+) diff --git a/bytestring/api/kotlinx-io-bytestring.api b/bytestring/api/kotlinx-io-bytestring.api index e56c61ab7..8c054de27 100644 --- a/bytestring/api/kotlinx-io-bytestring.api +++ b/bytestring/api/kotlinx-io-bytestring.api @@ -9,6 +9,7 @@ public final class kotlinx/io/bytestring/ByteString : java/lang/Comparable { public static synthetic fun copyInto$default (Lkotlinx/io/bytestring/ByteString;[BIIIILjava/lang/Object;)V public fun equals (Ljava/lang/Object;)Z public final fun get (I)B + public final fun getBackingArrayReference ()[B public final fun getSize ()I public fun hashCode ()I public final fun substring (II)Lkotlinx/io/bytestring/ByteString; From 61332f6780ffe6f94a4d49be79ea18cece52f18f Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Thu, 29 Jun 2023 10:23:36 +0200 Subject: [PATCH 31/32] Updated exception messages --- bytestring/common/src/ByteString.kt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/bytestring/common/src/ByteString.kt b/bytestring/common/src/ByteString.kt index d1ab0a512..155ee4fa8 100644 --- a/bytestring/common/src/ByteString.kt +++ b/bytestring/common/src/ByteString.kt @@ -111,7 +111,8 @@ public class ByteString private constructor( */ public operator fun get(index: Int): Byte { if (index < 0 || index >= size) throw IndexOutOfBoundsException( - "index ($index) is out of byte string bounds: [0..$size)") + "index ($index) is out of byte string bounds: [0..$size)" + ) return data[index] } @@ -126,7 +127,7 @@ public class ByteString private constructor( * @throws IllegalArgumentException when `startIndex > endIndex`. */ public fun toByteArray(startIndex: Int = 0, endIndex: Int = size): ByteArray { - require(startIndex <= endIndex) { "startIndex: $startIndex, endIndex: $endIndex" } + require(startIndex <= endIndex) { "startIndex ($startIndex) > endIndex ($endIndex)" } return data.copyOfRange(startIndex, endIndex) } @@ -148,9 +149,7 @@ public class ByteString private constructor( destination: ByteArray, destinationOffset: Int = 0, startIndex: Int = 0, endIndex: Int = size ) { - require(startIndex <= endIndex) { - "destinationOffset: $destinationOffset, startIndex: $startIndex, endIndex: $endIndex" - } + require(startIndex <= endIndex) { "startIndex ($startIndex) > endIndex ($endIndex)" } data.copyInto(destination, destinationOffset, startIndex, endIndex) } From 457692a982960356fd32e10b3b934b8a896e2d90 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Thu, 29 Jun 2023 11:58:15 +0200 Subject: [PATCH 32/32] Renamed readUtf8/writeUtf8 to readString/writeString (#156) Currently, we use ByteString.decodeToString and String.encodeToByteString as names for conversion methods between String and ByteString, where non-parameterized functions convert to/from UTF-8, and JVM-specific extensions accept Charset. At the same time, the core module uses different naming for methods reading/writing UTF-8 string and methods reading/writing strings using specific Charset: - Source.readUtf8/Sink.writeUtf8 to work with UTF-8 strings - Source.readString/Sink.writeString to work with strings using the given Charset on JVM. The naming is inconsistent and it seems reasonable to unify read/write methods naming with what we have for ByteString. We can use readString/writeString w/o charset for UTF-8 strings (as these are treated as default in the library) and same-titled JVM-specific extensions accepting a Charset. --- benchmarks/src/commonMain/kotlin/BufferOps.kt | 12 +- core/api/kotlinx-io-core.api | 16 +- core/common/src/SinkExt.kt | 2 +- core/common/src/SourceExt.kt | 4 +- core/common/src/Utf8.kt | 22 +- core/common/test/AbstractSinkTest.kt | 58 +-- core/common/test/AbstractSourceTest.kt | 370 +++++++++--------- core/common/test/CommonBufferTest.kt | 200 +++++----- core/common/test/CommonPlatformTest.kt | 8 +- core/common/test/CommonRealSinkTest.kt | 28 +- core/common/test/CommonRealSourceTest.kt | 34 +- core/common/test/Utf8Test.kt | 2 +- core/common/test/files/SmokeFileTest.kt | 4 +- core/jvm/src/SinkExtJvm.kt | 2 +- core/jvm/test/AbstractSinkTestJVM.kt | 6 +- core/jvm/test/AbstractSourceTestJVM.kt | 18 +- core/jvm/test/BufferTest.kt | 78 ++-- core/jvm/test/JvmPlatformTest.kt | 32 +- core/jvm/test/NioTest.kt | 12 +- 19 files changed, 454 insertions(+), 454 deletions(-) diff --git a/benchmarks/src/commonMain/kotlin/BufferOps.kt b/benchmarks/src/commonMain/kotlin/BufferOps.kt index fc1d6d58b..cffcc673b 100644 --- a/benchmarks/src/commonMain/kotlin/BufferOps.kt +++ b/benchmarks/src/commonMain/kotlin/BufferOps.kt @@ -214,8 +214,8 @@ open class Utf8StringBenchmark : BufferRWBenchmarkBase() { @Benchmark fun benchmark(): String { val s = buffer.size - buffer.writeUtf8(string) - return buffer.readUtf8(buffer.size - s) + buffer.writeString(string) + return buffer.readString(buffer.size - s) } } @@ -257,16 +257,16 @@ open class Utf8LineBenchmarkBase : BufferRWBenchmarkBase() { open class Utf8LineBenchmark : Utf8LineBenchmarkBase() { @Benchmark fun benchmark(): String? { - buffer.writeUtf8(string) - return buffer.readUtf8Line() + buffer.writeString(string) + return buffer.readLine() } } open class Utf8LineStrictBenchmark : Utf8LineBenchmarkBase() { @Benchmark fun benchmark(): String { - buffer.writeUtf8(string) - return buffer.readUtf8LineStrict() + buffer.writeString(string) + return buffer.readLineStrict() } } diff --git a/core/api/kotlinx-io-core.api b/core/api/kotlinx-io-core.api index e79ca86fb..38cb45ff8 100644 --- a/core/api/kotlinx-io-core.api +++ b/core/api/kotlinx-io-core.api @@ -175,14 +175,14 @@ public final class kotlinx/io/SourceExtKt { } public final class kotlinx/io/Utf8Kt { - public static final fun readUtf8 (Lkotlinx/io/Buffer;)Ljava/lang/String; - public static final fun readUtf8 (Lkotlinx/io/Source;)Ljava/lang/String; - public static final fun readUtf8 (Lkotlinx/io/Source;J)Ljava/lang/String; - public static final fun readUtf8Line (Lkotlinx/io/Source;)Ljava/lang/String; - public static final fun readUtf8LineStrict (Lkotlinx/io/Source;J)Ljava/lang/String; - public static synthetic fun readUtf8LineStrict$default (Lkotlinx/io/Source;JILjava/lang/Object;)Ljava/lang/String; - public static final fun writeUtf8 (Lkotlinx/io/Sink;Ljava/lang/String;II)V - public static synthetic fun writeUtf8$default (Lkotlinx/io/Sink;Ljava/lang/String;IIILjava/lang/Object;)V + public static final fun readLine (Lkotlinx/io/Source;)Ljava/lang/String; + public static final fun readLineStrict (Lkotlinx/io/Source;J)Ljava/lang/String; + public static synthetic fun readLineStrict$default (Lkotlinx/io/Source;JILjava/lang/Object;)Ljava/lang/String; + public static final fun readString (Lkotlinx/io/Buffer;)Ljava/lang/String; + public static final fun readString (Lkotlinx/io/Source;)Ljava/lang/String; + public static final fun readString (Lkotlinx/io/Source;J)Ljava/lang/String; + public static final fun writeString (Lkotlinx/io/Sink;Ljava/lang/String;II)V + public static synthetic fun writeString$default (Lkotlinx/io/Sink;Ljava/lang/String;IIILjava/lang/Object;)V } public final class kotlinx/io/files/Path { diff --git a/core/common/src/SinkExt.kt b/core/common/src/SinkExt.kt index 0527b2e17..f0d5385c9 100644 --- a/core/common/src/SinkExt.kt +++ b/core/common/src/SinkExt.kt @@ -62,7 +62,7 @@ public fun Sink.writeDecimalLong(long: Long) { if (v < 0L) { v = -v if (v < 0L) { // Only true for Long.MIN_VALUE. - writeUtf8("-9223372036854775808") + writeString("-9223372036854775808") return } negative = true diff --git a/core/common/src/SourceExt.kt b/core/common/src/SourceExt.kt index 59f20ca8a..811ce44c9 100644 --- a/core/common/src/SourceExt.kt +++ b/core/common/src/SourceExt.kt @@ -87,7 +87,7 @@ public fun Source.readDecimalLong(): Long { writeByte(b) if (!negative) readByte() // Skip negative sign. - throw NumberFormatException("Number too large: ${readUtf8()}") + throw NumberFormatException("Number too large: ${readString()}") } } value = value * 10L + digit @@ -143,7 +143,7 @@ public fun Source.readHexadecimalUnsignedLong(): Long { with(Buffer()) { writeHexadecimalUnsignedLong(result) writeByte(b) - throw NumberFormatException("Number too large: " + readUtf8()) + throw NumberFormatException("Number too large: " + readString()) } } result = result.shl(4) + bDigit diff --git a/core/common/src/Utf8.kt b/core/common/src/Utf8.kt index 8e67c0b88..98f33e019 100644 --- a/core/common/src/Utf8.kt +++ b/core/common/src/Utf8.kt @@ -72,7 +72,7 @@ package kotlinx.io import kotlinx.io.internal.* /** - * Returns the number of bytes used to encode the slice of `string` as UTF-8 when using [Sink.writeUtf8]. + * Returns the number of bytes used to encode the slice of `string` as UTF-8 when using [Sink.writeString]. * * @param startIndex the index (inclusive) of the first character to encode, `0` by default. * @param endIndex the index (exclusive) of the character past the last character to encode, `string.length` by default. @@ -140,7 +140,7 @@ internal fun Sink.writeUtf8CodePoint(codePoint: Int): Unit = * @throws IllegalStateException when the sink is closed. */ @OptIn(DelicateIoApi::class) -public fun Sink.writeUtf8(string: String, startIndex: Int = 0, endIndex: Int = string.length): Unit = +public fun Sink.writeString(string: String, startIndex: Int = 0, endIndex: Int = string.length): Unit = writeToInternalBuffer { it.commonWriteUtf8(string, startIndex, endIndex) } /** @@ -151,7 +151,7 @@ public fun Sink.writeUtf8(string: String, startIndex: Int = 0, endIndex: Int = s * @throws IllegalStateException when the source is closed. */ @OptIn(InternalIoApi::class) -public fun Source.readUtf8(): String { +public fun Source.readString(): String { var req: Long = Segment.SIZE.toLong() while (request(req)) { req *= 2 @@ -164,7 +164,7 @@ public fun Source.readUtf8(): String { * * Returns the empty string if this buffer is empty. */ -public fun Buffer.readUtf8(): String { +public fun Buffer.readString(): String { return commonReadUtf8(size) } @@ -178,7 +178,7 @@ public fun Buffer.readUtf8(): String { * @throws IllegalStateException when the source is closed. */ @OptIn(InternalIoApi::class) -public fun Source.readUtf8(byteCount: Long): String { +public fun Source.readString(byteCount: Long): String { require(byteCount) return buffer.commonReadUtf8(byteCount) } @@ -220,7 +220,7 @@ internal fun Buffer.readUtf8CodePoint(): Int { } /** - * Removes and returns characters up to but not including the next line break. A line break is + * Removes and returns UTF-8 encoded 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 returns null. If the source doesn't end with a line break, then @@ -228,7 +228,7 @@ internal fun Buffer.readUtf8CodePoint(): Int { * * @throws IllegalStateException when the source is closed. */ -public fun Source.readUtf8Line(): String? { +public fun Source.readLine(): String? { if (!request(1)) return null val peekSource = peek() @@ -247,13 +247,13 @@ public fun Source.readUtf8Line(): String? { } offset++ } - val line = readUtf8(offset) + val line = readString(offset) skip(newlineSize) return line } /** - * Removes and returns characters up to but not including the next line break, throwing + * Removes and returns UTF-8 encoded characters up to but not including the next line break, throwing * [EOFException] if a line break was not encountered. A line break is either `"\n"` or `"\r\n"`; * these characters are not included in the result. * @@ -270,7 +270,7 @@ public fun Source.readUtf8Line(): String? { * @throws IllegalStateException when the source is closed. * @throws IllegalArgumentException when [limit] is negative. */ -public fun Source.readUtf8LineStrict(limit: Long = Long.MAX_VALUE): String { +public fun Source.readLineStrict(limit: Long = Long.MAX_VALUE): String { require(limit >= 0) { "limit ($limit) < 0" } require(1) @@ -300,7 +300,7 @@ public fun Source.readUtf8LineStrict(limit: Long = Long.MAX_VALUE): String { } } if (newlineSize == 0L) throw EOFException() - val line = readUtf8(offset) + val line = readString(offset) skip(newlineSize) return line } diff --git a/core/common/test/AbstractSinkTest.kt b/core/common/test/AbstractSinkTest.kt index 6ffb8d3de..22e048d60 100644 --- a/core/common/test/AbstractSinkTest.kt +++ b/core/common/test/AbstractSinkTest.kt @@ -67,7 +67,7 @@ abstract class AbstractSinkTest internal constructor( @Test fun writeNothing() { - sink.writeUtf8("") + sink.writeString("") sink.flush() assertEquals(0, data.size) } @@ -89,12 +89,12 @@ abstract class AbstractSinkTest internal constructor( @Test fun writeLastByteInSegment() { - sink.writeUtf8("a".repeat(Segment.SIZE - 1)) + sink.writeString("a".repeat(Segment.SIZE - 1)) sink.writeByte(0x20) sink.writeByte(0x21) sink.flush() assertEquals(listOf(Segment.SIZE, 1), segmentSizes(data)) - assertEquals("a".repeat(Segment.SIZE - 1), data.readUtf8(Segment.SIZE - 1L)) + assertEquals("a".repeat(Segment.SIZE - 1), data.readString(Segment.SIZE - 1L)) assertEquals("Buffer(size=2 hex=2021)", data.toString()) } @@ -138,23 +138,23 @@ abstract class AbstractSinkTest internal constructor( @Test fun writeLastIntegerInSegment() { - sink.writeUtf8("a".repeat(Segment.SIZE - 4)) + sink.writeString("a".repeat(Segment.SIZE - 4)) sink.writeInt(-0x543210ff) sink.writeInt(-0x789abcdf) sink.flush() assertEquals(listOf(Segment.SIZE, 4), segmentSizes(data)) - assertEquals("a".repeat(Segment.SIZE - 4), data.readUtf8(Segment.SIZE - 4L)) + assertEquals("a".repeat(Segment.SIZE - 4), data.readString(Segment.SIZE - 4L)) assertEquals("Buffer(size=8 hex=abcdef0187654321)", data.toString()) } @Test fun writeIntegerDoesNotQuiteFitInSegment() { - sink.writeUtf8("a".repeat(Segment.SIZE - 3)) + sink.writeString("a".repeat(Segment.SIZE - 3)) sink.writeInt(-0x543210ff) sink.writeInt(-0x789abcdf) sink.flush() assertEquals(listOf(Segment.SIZE - 3, 8), segmentSizes(data)) - assertEquals("a".repeat(Segment.SIZE - 3), data.readUtf8(Segment.SIZE - 3L)) + assertEquals("a".repeat(Segment.SIZE - 3), data.readString(Segment.SIZE - 3L)) assertEquals("Buffer(size=8 hex=abcdef0187654321)", data.toString()) } @@ -192,12 +192,12 @@ abstract class AbstractSinkTest internal constructor( @Test fun writeAll() { val source = Buffer() - source.writeUtf8("abcdef") + source.writeString("abcdef") assertEquals(6, sink.transferFrom(source)) assertEquals(0, source.size) sink.flush() - assertEquals("abcdef", data.readUtf8()) + assertEquals("abcdef", data.readString()) } @Test @@ -210,32 +210,32 @@ abstract class AbstractSinkTest internal constructor( @Test fun writeSource() { val source = Buffer() - source.writeUtf8("abcdef") + source.writeString("abcdef") // Force resolution of the Source method overload. sink.write(source as RawSource, 4) sink.flush() - assertEquals("abcd", data.readUtf8()) - assertEquals("ef", source.readUtf8()) + assertEquals("abcd", data.readString()) + assertEquals("ef", source.readString()) } @Test fun writeSourceReadsFully() { val source = object : RawSource by Buffer() { override fun readAtMostTo(sink: Buffer, byteCount: Long): Long { - sink.writeUtf8("abcd") + sink.writeString("abcd") return 4 } } sink.write(source, 8) sink.flush() - assertEquals("abcdabcd", data.readUtf8()) + assertEquals("abcdabcd", data.readString()) } @Test fun writeSourcePropagatesEof() { - val source: RawSource = Buffer().also { it.writeUtf8("abcd") } + val source: RawSource = Buffer().also { it.writeString("abcd") } assertFailsWith { sink.write(source, 8) @@ -243,20 +243,20 @@ abstract class AbstractSinkTest internal constructor( // Ensure that whatever was available was correctly written. sink.flush() - assertEquals("abcd", data.readUtf8()) + assertEquals("abcd", data.readString()) } @Test fun writeBufferThrowsIAE() { val source = Buffer() - source.writeUtf8("abcd") + source.writeString("abcd") assertFailsWith { sink.write(source, 8) } sink.flush() - assertEquals("", data.readUtf8()) + assertEquals("", data.readString()) } @Test @@ -348,11 +348,11 @@ abstract class AbstractSinkTest internal constructor( private fun assertLongDecimalString(string: String, value: Long) { with(sink) { writeDecimalLong(value) - writeUtf8("zzz") + writeString("zzz") flush() } val expected = "${string}zzz" - val actual = data.readUtf8() + val actual = data.readString() assertEquals(expected, actual, "$value expected $expected but was $actual") } @@ -371,33 +371,33 @@ abstract class AbstractSinkTest internal constructor( private fun assertLongHexString(value: Long) { with(sink) { writeHexadecimalUnsignedLong(value) - writeUtf8("zzz") + writeString("zzz") flush() } val expected = "${value.toHexString()}zzz" - val actual = data.readUtf8() + val actual = data.readString() assertEquals(expected, actual, "$value expected $expected but was $actual") } @Test fun writeUtf8FromIndex() { - sink.writeUtf8("12345", 3) + sink.writeString("12345", 3) sink.emit() - assertEquals("45", data.readUtf8()) + assertEquals("45", data.readString()) } @Test fun writeUtf8FromRange() { - sink.writeUtf8("0123456789", 4, 7) + sink.writeString("0123456789", 4, 7) sink.emit() - assertEquals("456", data.readUtf8()) + assertEquals("456", data.readString()) } @Test fun writeUtf8WithInvalidIndexes() { - assertFailsWith { sink.writeUtf8("hello", startIndex = -1) } - assertFailsWith { sink.writeUtf8("hello", startIndex = 0, endIndex = 6) } - assertFailsWith { sink.writeUtf8("hello", startIndex = 6) } + assertFailsWith { sink.writeString("hello", startIndex = -1) } + assertFailsWith { sink.writeString("hello", startIndex = 0, endIndex = 6) } + assertFailsWith { sink.writeString("hello", startIndex = 6) } } @Test diff --git a/core/common/test/AbstractSourceTest.kt b/core/common/test/AbstractSourceTest.kt index fbb934bf7..c27f665f6 100644 --- a/core/common/test/AbstractSourceTest.kt +++ b/core/common/test/AbstractSourceTest.kt @@ -88,7 +88,7 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun readShortSplitAcrossMultipleSegments() { - sink.writeUtf8("a".repeat(Segment.SIZE - 1)) + sink.writeString("a".repeat(Segment.SIZE - 1)) sink.write(byteArrayOf(0xab.toByte(), 0xcd.toByte())) sink.emit() source.skip((Segment.SIZE - 1).toLong()) @@ -160,7 +160,7 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun readIntSplitAcrossMultipleSegments() { - sink.writeUtf8("a".repeat(Segment.SIZE - 3)) + sink.writeString("a".repeat(Segment.SIZE - 3)) sink.write(byteArrayOf(0xab.toByte(), 0xcd.toByte(), 0xef.toByte(), 0x01.toByte())) sink.emit() source.skip((Segment.SIZE - 3).toLong()) @@ -248,7 +248,7 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun readLongSplitAcrossMultipleSegments() { - sink.writeUtf8("a".repeat(Segment.SIZE - 7)) + sink.writeString("a".repeat(Segment.SIZE - 7)) sink.write( byteArrayOf( 0xab.toByte(), @@ -292,13 +292,13 @@ abstract class AbstractBufferedSourceTest internal constructor( @OptIn(InternalIoApi::class) @Test fun transferTo() { - source.buffer.writeUtf8("abc") - sink.writeUtf8("def") + source.buffer.writeString("abc") + sink.writeString("def") sink.emit() val sink = Buffer() assertEquals(6, source.transferTo(sink)) - assertEquals("abcdef", sink.readUtf8()) + assertEquals("abcdef", sink.readString()) assertTrue(source.exhausted()) } @@ -313,7 +313,7 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun readExhaustedSource() { val sink = Buffer() - sink.writeUtf8("a".repeat(10)) + sink.writeString("a".repeat(10)) assertEquals(-1, source.readAtMostTo(sink, 10)) assertEquals(10, sink.size) assertTrue(source.exhausted()) @@ -322,7 +322,7 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun readZeroBytesFromSource() { val sink = Buffer() - sink.writeUtf8("a".repeat(10)) + sink.writeString("a".repeat(10)) // Either 0 or -1 is reasonable here. For consistency with Android's // ByteArrayInputStream we return 0. @@ -394,17 +394,17 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun readToSink() { - sink.writeUtf8("a".repeat(10000)) + sink.writeString("a".repeat(10000)) sink.emit() val sink = Buffer() source.readTo(sink, 9999) - assertEquals("a".repeat(9999), sink.readUtf8()) - assertEquals("a", source.readUtf8()) + assertEquals("a".repeat(9999), sink.readString()) + assertEquals("a", source.readString()) } @Test fun readToSinkTooShortThrows() { - sink.writeUtf8("Hi") + sink.writeString("Hi") sink.emit() val sink = Buffer() assertFailsWith { @@ -412,7 +412,7 @@ abstract class AbstractBufferedSourceTest internal constructor( } // Verify we read all that we could from the source. - assertEquals("Hi", sink.readUtf8()) + assertEquals("Hi", sink.readString()) assertTrue(source.exhausted()) } @@ -426,19 +426,19 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun readToSinkZeroBytes() { - sink.writeUtf8("test") + sink.writeString("test") sink.flush() val sink = Buffer() source.readTo(sink, 0) assertEquals(0, sink.size) - assertEquals("test", source.readUtf8()) + assertEquals("test", source.readString()) } @Test fun readToByteArray() { val data = Buffer() - data.writeUtf8("Hello") - data.writeUtf8("e".repeat(Segment.SIZE)) + data.writeString("Hello") + data.writeString("e".repeat(Segment.SIZE)) val expected = data.copy().readByteArray() sink.write(data, data.size) @@ -456,13 +456,13 @@ abstract class AbstractBufferedSourceTest internal constructor( val sink = ByteArray(8) - buffer.writeUtf8("hello") + buffer.writeString("hello") source.readTo(sink, 0, 3) assertContentEquals(byteArrayOf('h'.code.toByte(), 'e'.code.toByte(), 'l'.code.toByte(), 0, 0, 0, 0, 0), sink) - assertEquals("lo", source.readUtf8()) + assertEquals("lo", source.readString()) sink.fill(0) - buffer.writeUtf8("hello") + buffer.writeString("hello") source.readTo(sink, 3) assertContentEquals( byteArrayOf( @@ -473,10 +473,10 @@ abstract class AbstractBufferedSourceTest internal constructor( assertTrue(source.exhausted()) sink.fill(0) - buffer.writeUtf8("hello") + buffer.writeString("hello") source.readTo(sink, 3, 4) assertContentEquals(byteArrayOf(0, 0, 0, 'h'.code.toByte(), 0, 0, 0, 0), sink) - assertEquals("ello", source.readUtf8()) + assertEquals("ello", source.readString()) } @Test @@ -492,7 +492,7 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun readToByteArrayTooShortThrows() { - sink.writeUtf8("Hello") + sink.writeString("Hello") sink.emit() val array = ByteArray(6) @@ -516,7 +516,7 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun readAtMostToByteArray() { - sink.writeUtf8("abcd") + sink.writeString("abcd") sink.emit() val sink = ByteArray(3) @@ -534,7 +534,7 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun readAtMostToByteArrayNotEnough() { - sink.writeUtf8("abcd") + sink.writeString("abcd") sink.emit() val sink = ByteArray(5) @@ -553,7 +553,7 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun readAtMostToByteArrayOffsetAndCount() { - sink.writeUtf8("abcd") + sink.writeString("abcd") sink.emit() val sink = ByteArray(7) @@ -573,7 +573,7 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun readAtMostToByteArrayFromOffset() { - sink.writeUtf8("abcd") + sink.writeString("abcd") sink.emit() val sink = ByteArray(7) @@ -613,28 +613,28 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun readByteArray() { val string = "abcd" + "e".repeat(Segment.SIZE) - sink.writeUtf8(string) + sink.writeString(string) sink.emit() assertArrayEquals(string.asUtf8ToByteArray(), source.readByteArray()) } @Test fun readByteArrayPartial() { - sink.writeUtf8("abcd") + sink.writeString("abcd") sink.emit() assertEquals("[97, 98, 99]", source.readByteArray(3).contentToString()) - assertEquals("d", source.readUtf8(1)) + assertEquals("d", source.readString(1)) } @Test fun readByteArrayTooShortThrows() { - sink.writeUtf8("abc") + sink.writeString("abc") sink.emit() assertFailsWith { source.readByteArray(4) } - assertEquals("abc", source.readUtf8()) // The read shouldn't consume any data. + assertEquals("abc", source.readString()) // The read shouldn't consume any data. } @Test @@ -644,49 +644,49 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun readUtf8SpansSegments() { - sink.writeUtf8("a".repeat(Segment.SIZE * 2)) + sink.writeString("a".repeat(Segment.SIZE * 2)) sink.emit() source.skip((Segment.SIZE - 1).toLong()) - assertEquals("aa", source.readUtf8(2)) + assertEquals("aa", source.readString(2)) } @Test fun readUtf8Segment() { - sink.writeUtf8("a".repeat(Segment.SIZE)) + sink.writeString("a".repeat(Segment.SIZE)) sink.emit() - assertEquals("a".repeat(Segment.SIZE), source.readUtf8(Segment.SIZE.toLong())) + assertEquals("a".repeat(Segment.SIZE), source.readString(Segment.SIZE.toLong())) } @Test fun readUtf8PartialBuffer() { - sink.writeUtf8("a".repeat(Segment.SIZE + 20)) + sink.writeString("a".repeat(Segment.SIZE + 20)) sink.emit() - assertEquals("a".repeat(Segment.SIZE + 10), source.readUtf8((Segment.SIZE + 10).toLong())) + assertEquals("a".repeat(Segment.SIZE + 10), source.readString((Segment.SIZE + 10).toLong())) } @Test fun readUtf8EntireBuffer() { - sink.writeUtf8("a".repeat(Segment.SIZE * 2)) + sink.writeString("a".repeat(Segment.SIZE * 2)) sink.emit() - assertEquals("a".repeat(Segment.SIZE * 2), source.readUtf8()) + assertEquals("a".repeat(Segment.SIZE * 2), source.readString()) } @Test fun readUtf8TooShortThrows() { - sink.writeUtf8("abc") + sink.writeString("abc") sink.emit() assertFailsWith { - source.readUtf8(4L) + source.readString(4L) } - assertEquals("abc", source.readUtf8()) // The read shouldn't consume any data. + assertEquals("abc", source.readString()) // The read shouldn't consume any data. } @Test fun skip() { - sink.writeUtf8("a") - sink.writeUtf8("b".repeat(Segment.SIZE)) - sink.writeUtf8("c") + sink.writeString("a") + sink.writeString("b".repeat(Segment.SIZE)) + sink.writeString("c") sink.emit() source.skip(1) assertEquals('b'.code.toLong(), (source.readByte() and 0xff).toLong()) @@ -698,7 +698,7 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun skipInsufficientData() { - sink.writeUtf8("a") + sink.writeString("a") sink.emit() assertFailsWith { source.skip(2) @@ -716,13 +716,13 @@ abstract class AbstractBufferedSourceTest internal constructor( assertEquals(-1, source.indexOf('a'.code.toByte())) // The segment has one value. - sink.writeUtf8("a") // a + sink.writeString("a") // a sink.emit() assertEquals(0, source.indexOf('a'.code.toByte())) assertEquals(-1, source.indexOf('b'.code.toByte())) // The segment has lots of data. - sink.writeUtf8("b".repeat(Segment.SIZE - 2)) // ab...b + sink.writeString("b".repeat(Segment.SIZE - 2)) // ab...b sink.emit() assertEquals(0, source.indexOf('a'.code.toByte())) assertEquals(1, source.indexOf('b'.code.toByte())) @@ -735,7 +735,7 @@ abstract class AbstractBufferedSourceTest internal constructor( assertEquals(-1, source.indexOf('c'.code.toByte())) // The segment is full. - sink.writeUtf8("c") // b...bc + sink.writeString("c") // b...bc sink.emit() assertEquals(-1, source.indexOf('a'.code.toByte())) assertEquals(0, source.indexOf('b'.code.toByte())) @@ -748,7 +748,7 @@ abstract class AbstractBufferedSourceTest internal constructor( assertEquals((Segment.SIZE - 5).toLong(), source.indexOf('c'.code.toByte())) // Two segments. - sink.writeUtf8("d") // b...bcd, d is in the 2nd segment. + sink.writeString("d") // b...bcd, d is in the 2nd segment. sink.emit() assertEquals((Segment.SIZE - 4).toLong(), source.indexOf('d'.code.toByte())) assertEquals(-1, source.indexOf('e'.code.toByte())) @@ -757,9 +757,9 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun indexOfByteWithStartOffset() { with(sink) { - writeUtf8("a") - writeUtf8("b".repeat(Segment.SIZE)) - writeUtf8("c") + writeString("a") + writeString("b".repeat(Segment.SIZE)) + writeString("c") emit() } assertEquals(-1, source.indexOf('a'.code.toByte(), 1)) @@ -817,14 +817,14 @@ abstract class AbstractBufferedSourceTest internal constructor( assertEquals(-1, source.indexOf(c, p.toLong(), p.toLong())) // Reset. - source.readUtf8() + source.readString() bytes[p] = a } } @Test fun indexOfByteInvalidBoundsThrows() { - sink.writeUtf8("abc") + sink.writeString("abc") sink.emit() assertFailsWith("Expected failure: fromIndex < 0") { source.indexOf('a'.code.toByte(), -1) @@ -836,7 +836,7 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun indexOfByteWithFromIndex() { - sink.writeUtf8("aaa") + sink.writeString("aaa") sink.emit() assertEquals(0, source.indexOf('a'.code.toByte())) assertEquals(0, source.indexOf('a'.code.toByte(), 0)) @@ -847,9 +847,9 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun request() { with(sink) { - writeUtf8("a") - writeUtf8("b".repeat(Segment.SIZE)) - writeUtf8("c") + writeString("a") + writeString("b".repeat(Segment.SIZE)) + writeString("c") emit() } assertTrue(source.request((Segment.SIZE + 2).toLong())) @@ -869,9 +869,9 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun require() { with(sink) { - writeUtf8("a") - writeUtf8("b".repeat(Segment.SIZE)) - writeUtf8("c") + writeString("a") + writeString("b".repeat(Segment.SIZE)) + writeString("c") emit() } source.require((Segment.SIZE + 2).toLong()) @@ -914,7 +914,7 @@ abstract class AbstractBufferedSourceTest internal constructor( } private fun assertLongHexString(s: String, expected: Long) { - sink.writeUtf8(s) + sink.writeString(s) sink.emit() val actual = source.readHexadecimalUnsignedLong() assertEquals(expected, actual, "$s --> $expected") @@ -923,8 +923,8 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun longHexStringAcrossSegment() { with(sink) { - writeUtf8("a".repeat(Segment.SIZE - 8)) - writeUtf8("FFFFFFFFFFFFFFFF") + writeString("a".repeat(Segment.SIZE - 8)) + writeString("FFFFFFFFFFFFFFFF") emit() } source.skip((Segment.SIZE - 8).toLong()) @@ -933,17 +933,17 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun longHexTerminatedByNonDigit() { - sink.writeUtf8("abcd,") + sink.writeString("abcd,") sink.emit() assertEquals(0xabcdL, source.readHexadecimalUnsignedLong()) } @Test fun longHexAlphabet() { - sink.writeUtf8("7896543210abcdef") + sink.writeString("7896543210abcdef") sink.emit() assertEquals(0x7896543210abcdefL, source.readHexadecimalUnsignedLong()) - sink.writeUtf8("ABCDEF") + sink.writeString("ABCDEF") sink.emit() assertEquals(0xabcdefL, source.readHexadecimalUnsignedLong()) } @@ -951,31 +951,31 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun longHexStringTooLongThrows() { val value = "fffffffffffffffff" - sink.writeUtf8(value) + sink.writeString(value) sink.emit() val e = assertFailsWith { source.readHexadecimalUnsignedLong() } assertEquals("Number too large: fffffffffffffffff", e.message) - assertEquals(value, source.readUtf8()) + assertEquals(value, source.readString()) } @Test fun longHexStringTooShortThrows() { - sink.writeUtf8(" ") + sink.writeString(" ") sink.emit() val e = assertFailsWith { source.readHexadecimalUnsignedLong() } assertEquals("Expected leading [0-9a-fA-F] character but was 0x20", e.message) - assertEquals(" ", source.readUtf8()) + assertEquals(" ", source.readString()) } @Test fun longHexEmptySourceThrows() { - sink.writeUtf8("") + sink.writeString("") sink.emit() assertFailsWith { source.readHexadecimalUnsignedLong() } } @@ -993,81 +993,81 @@ abstract class AbstractBufferedSourceTest internal constructor( } private fun assertLongDecimalString(s: String, expected: Long) { - sink.writeUtf8(s) - sink.writeUtf8("zzz") + sink.writeString(s) + sink.writeString("zzz") sink.emit() val actual = source.readDecimalLong() assertEquals(expected, actual, "$s --> $expected") - assertEquals("zzz", source.readUtf8()) + assertEquals("zzz", source.readString()) } @Test fun longDecimalStringAcrossSegment() { with(sink) { - writeUtf8("a".repeat(Segment.SIZE - 8)) - writeUtf8("1234567890123456") - writeUtf8("zzz") + writeString("a".repeat(Segment.SIZE - 8)) + writeString("1234567890123456") + writeString("zzz") emit() } source.skip((Segment.SIZE - 8).toLong()) assertEquals(1234567890123456L, source.readDecimalLong()) - assertEquals("zzz", source.readUtf8()) + assertEquals("zzz", source.readString()) } @Test fun longDecimalStringTooLongThrows() { val value = "12345678901234567890" - sink.writeUtf8(value) // Too many digits. + sink.writeString(value) // Too many digits. sink.emit() val e = assertFailsWith { source.readDecimalLong() } assertEquals("Number too large: 12345678901234567890", e.message) - assertEquals(value, source.readUtf8()) + assertEquals(value, source.readString()) } @Test fun longDecimalStringTooHighThrows() { val value = "9223372036854775808" - sink.writeUtf8(value) // Right size but cannot fit. + sink.writeString(value) // Right size but cannot fit. sink.emit() val e = assertFailsWith { source.readDecimalLong() } assertEquals("Number too large: 9223372036854775808", e.message) - assertEquals(value, source.readUtf8()) + assertEquals(value, source.readString()) } @Test fun longDecimalStringTooLowThrows() { val value = "-9223372036854775809" - sink.writeUtf8(value) // Right size but cannot fit. + sink.writeString(value) // Right size but cannot fit. sink.emit() val e = assertFailsWith { source.readDecimalLong() } assertEquals("Number too large: -9223372036854775809", e.message) - assertEquals(value, source.readUtf8()) + assertEquals(value, source.readString()) } @Test fun longDecimalStringTooShortThrows() { - sink.writeUtf8(" ") + sink.writeString(" ") sink.emit() val e = assertFailsWith { source.readDecimalLong() } assertEquals("Expected a digit or '-' but was 0x20", e.message) - assertEquals(" ", source.readUtf8()) + assertEquals(" ", source.readString()) } @Test fun longDecimalEmptyThrows() { - sink.writeUtf8("") + sink.writeString("") sink.emit() assertFailsWith { source.readDecimalLong() @@ -1076,22 +1076,22 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun longDecimalLoneDashThrows() { - sink.writeUtf8("-") + sink.writeString("-") sink.emit() assertFailsWith { source.readDecimalLong() } - assertEquals("-", source.readUtf8()) + assertEquals("-", source.readString()) } @Test fun longDecimalDashFollowedByNonDigitThrows() { - sink.writeUtf8("- ") + sink.writeString("- ") sink.emit() assertFailsWith { source.readDecimalLong() } - assertEquals("- ", source.readUtf8()) + assertEquals("- ", source.readString()) } @Test @@ -1154,39 +1154,39 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun peek() { - sink.writeUtf8("abcdefghi") + sink.writeString("abcdefghi") sink.emit() - assertEquals("abc", source.readUtf8(3)) + assertEquals("abc", source.readString(3)) val peek = source.peek() - assertEquals("def", peek.readUtf8(3)) - assertEquals("ghi", peek.readUtf8(3)) + assertEquals("def", peek.readString(3)) + assertEquals("ghi", peek.readString(3)) assertFalse(peek.request(1)) - assertEquals("def", source.readUtf8(3)) + assertEquals("def", source.readString(3)) } @Test fun peekMultiple() { - sink.writeUtf8("abcdefghi") + sink.writeString("abcdefghi") sink.emit() - assertEquals("abc", source.readUtf8(3)) + assertEquals("abc", source.readString(3)) val peek1 = source.peek() val peek2 = source.peek() - assertEquals("def", peek1.readUtf8(3)) + assertEquals("def", peek1.readString(3)) - assertEquals("def", peek2.readUtf8(3)) - assertEquals("ghi", peek2.readUtf8(3)) + assertEquals("def", peek2.readString(3)) + assertEquals("ghi", peek2.readString(3)) assertFalse(peek2.request(1)) - assertEquals("ghi", peek1.readUtf8(3)) + assertEquals("ghi", peek1.readString(3)) assertFalse(peek1.request(1)) - assertEquals("def", source.readUtf8(3)) + assertEquals("def", source.readString(3)) } @Test @@ -1195,40 +1195,40 @@ abstract class AbstractBufferedSourceTest internal constructor( // When run on CI this causes out-of-memory errors. return } - sink.writeUtf8("abcdef") - sink.writeUtf8("g".repeat(2 * Segment.SIZE)) - sink.writeUtf8("hij") + sink.writeString("abcdef") + sink.writeString("g".repeat(2 * Segment.SIZE)) + sink.writeString("hij") sink.emit() - assertEquals("abc", source.readUtf8(3)) + assertEquals("abc", source.readString(3)) val peek = source.peek() - assertEquals("def", peek.readUtf8(3)) + assertEquals("def", peek.readString(3)) peek.skip((2 * Segment.SIZE).toLong()) - assertEquals("hij", peek.readUtf8(3)) + assertEquals("hij", peek.readString(3)) assertFalse(peek.request(1)) - assertEquals("def", source.readUtf8(3)) + assertEquals("def", source.readString(3)) source.skip((2 * Segment.SIZE).toLong()) - assertEquals("hij", source.readUtf8(3)) + assertEquals("hij", source.readString(3)) } @Test fun peekInvalid() { - sink.writeUtf8("abcdefghi") + sink.writeString("abcdefghi") sink.emit() - assertEquals("abc", source.readUtf8(3)) + assertEquals("abc", source.readString(3)) val peek = source.peek() - assertEquals("def", peek.readUtf8(3)) - assertEquals("ghi", peek.readUtf8(3)) + assertEquals("def", peek.readString(3)) + assertEquals("ghi", peek.readString(3)) assertFalse(peek.request(1)) - assertEquals("def", source.readUtf8(3)) + assertEquals("def", source.readString(3)) val e = assertFailsWith { - peek.readUtf8() + peek.readString() } assertEquals("Peek source is invalid because upstream source was used", e.message) } @@ -1236,15 +1236,15 @@ abstract class AbstractBufferedSourceTest internal constructor( @OptIn(InternalIoApi::class) @Test fun peekSegmentThenInvalid() { - sink.writeUtf8("abc") - sink.writeUtf8("d".repeat(2 * Segment.SIZE)) + sink.writeString("abc") + sink.writeString("d".repeat(2 * Segment.SIZE)) sink.emit() - assertEquals("abc", source.readUtf8(3)) + assertEquals("abc", source.readString(3)) // Peek a little data and skip the rest of the upstream source val peek = source.peek() - assertEquals("ddd", peek.readUtf8(3)) + assertEquals("ddd", peek.readString(3)) source.transferTo(discardingSink()) // Skip the rest of the buffered data @@ -1260,10 +1260,10 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun peekDoesntReadTooMuch() { // 6 bytes in source's buffer plus 3 bytes upstream. - sink.writeUtf8("abcdef") + sink.writeString("abcdef") sink.emit() source.require(6L) - sink.writeUtf8("ghi") + sink.writeString("ghi") sink.emit() val peek = source.peek() @@ -1274,7 +1274,7 @@ abstract class AbstractBufferedSourceTest internal constructor( assertEquals(6, source.buffer.size) assertEquals(6, peek.buffer.size) } - assertEquals("abc", peek.readUtf8(3L)) + assertEquals("abc", peek.readString(3L)) // Read 3 more bytes. This exhausts the buffered data. assertTrue(peek.request(3)) @@ -1282,19 +1282,19 @@ abstract class AbstractBufferedSourceTest internal constructor( assertEquals(6, source.buffer.size) assertEquals(3, peek.buffer.size) } - assertEquals("def", peek.readUtf8(3L)) + assertEquals("def", peek.readString(3L)) // Read 3 more bytes. This draws new bytes. assertTrue(peek.request(3)) assertEquals(9, source.buffer.size) assertEquals(3, peek.buffer.size) - assertEquals("ghi", peek.readUtf8(3L)) + assertEquals("ghi", peek.readString(3L)) } @OptIn(InternalIoApi::class) @Test fun factorySegmentSizes() { - sink.writeUtf8("abc") + sink.writeString("abc") sink.emit() source.require(3) if (factory.isOneByteAtATime) { @@ -1306,73 +1306,73 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun readUtf8Line() { - sink.writeUtf8("first line\nsecond line\n") + sink.writeString("first line\nsecond line\n") sink.flush() - assertEquals("first line", source.readUtf8Line()) - assertEquals("second line\n", source.readUtf8()) - assertEquals(null, source.readUtf8Line()) + assertEquals("first line", source.readLine()) + assertEquals("second line\n", source.readString()) + assertEquals(null, source.readLine()) - sink.writeUtf8("\nnext line\n") + sink.writeString("\nnext line\n") sink.flush() - assertEquals("", source.readUtf8Line()) - assertEquals("next line", source.readUtf8Line()) + assertEquals("", source.readLine()) + assertEquals("next line", source.readLine()) - sink.writeUtf8("There is no newline!") + sink.writeString("There is no newline!") sink.flush() - assertEquals("There is no newline!", source.readUtf8Line()) + assertEquals("There is no newline!", source.readLine()) - sink.writeUtf8("Wot do u call it?\r\nWindows") + sink.writeString("Wot do u call it?\r\nWindows") sink.flush() - assertEquals("Wot do u call it?", source.readUtf8Line()) + assertEquals("Wot do u call it?", source.readLine()) source.transferTo(discardingSink()) - sink.writeUtf8("reo\rde\red\n") + sink.writeString("reo\rde\red\n") sink.flush() - assertEquals("reo\rde\red", source.readUtf8Line()) + assertEquals("reo\rde\red", source.readLine()) } @Test fun readUtf8LineStrict() { - sink.writeUtf8("first line\nsecond line\n") + sink.writeString("first line\nsecond line\n") sink.flush() - assertEquals("first line", source.readUtf8LineStrict()) - assertEquals("second line\n", source.readUtf8()) - assertFailsWith { source.readUtf8LineStrict() } + assertEquals("first line", source.readLineStrict()) + assertEquals("second line\n", source.readString()) + assertFailsWith { source.readLineStrict() } - sink.writeUtf8("\nnext line\n") + sink.writeString("\nnext line\n") sink.flush() - assertEquals("", source.readUtf8LineStrict()) - assertEquals("next line", source.readUtf8LineStrict()) + assertEquals("", source.readLineStrict()) + assertEquals("next line", source.readLineStrict()) - sink.writeUtf8("There is no newline!") + sink.writeString("There is no newline!") sink.flush() - assertFailsWith { source.readUtf8LineStrict() } - assertEquals("There is no newline!", source.readUtf8()) + assertFailsWith { source.readLineStrict() } + assertEquals("There is no newline!", source.readString()) - sink.writeUtf8("Wot do u call it?\r\nWindows") + sink.writeString("Wot do u call it?\r\nWindows") sink.flush() - assertEquals("Wot do u call it?", source.readUtf8LineStrict()) + assertEquals("Wot do u call it?", source.readLineStrict()) source.transferTo(discardingSink()) - sink.writeUtf8("reo\rde\red\n") + sink.writeString("reo\rde\red\n") sink.flush() - assertEquals("reo\rde\red", source.readUtf8LineStrict()) + assertEquals("reo\rde\red", source.readLineStrict()) - sink.writeUtf8("line\n") + sink.writeString("line\n") sink.flush() - assertFailsWith { source.readUtf8LineStrict(3) } - assertEquals("line", source.readUtf8LineStrict(4)) + assertFailsWith { source.readLineStrict(3) } + assertEquals("line", source.readLineStrict(4)) assertTrue(source.exhausted()) - sink.writeUtf8("line\r\n") + sink.writeString("line\r\n") sink.flush() - assertFailsWith { source.readUtf8LineStrict(3) } - assertEquals("line", source.readUtf8LineStrict(4)) + assertFailsWith { source.readLineStrict(3) } + assertEquals("line", source.readLineStrict(4)) assertTrue(source.exhausted()) - sink.writeUtf8("line\n") + sink.writeString("line\n") sink.flush() - assertEquals("line", source.readUtf8LineStrict(5)) + assertEquals("line", source.readLineStrict(5)) assertTrue(source.exhausted()) } @@ -1543,8 +1543,8 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun readByteString() { with(sink) { - writeUtf8("abcd") - writeUtf8("e".repeat(Segment.SIZE)) + writeString("abcd") + writeString("e".repeat(Segment.SIZE)) emit() } assertEquals("abcd" + "e".repeat(Segment.SIZE), source.readByteString().decodeToString()) @@ -1553,42 +1553,42 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun readByteStringPartial() { with(sink) { - writeUtf8("abcd") - writeUtf8("e".repeat(Segment.SIZE)) + writeString("abcd") + writeString("e".repeat(Segment.SIZE)) emit() } assertEquals("abc", source.readByteString(3).decodeToString()) - assertEquals("d", source.readUtf8(1)) + assertEquals("d", source.readString(1)) } @Test fun readByteStringTooShortThrows() { - sink.writeUtf8("abc") + sink.writeString("abc") sink.emit() assertFailsWith { source.readByteString(4) } - assertEquals("abc", source.readUtf8()) // The read shouldn't consume any data. + assertEquals("abc", source.readString()) // The read shouldn't consume any data. } @Test fun indexOfByteString() { assertEquals(-1, source.indexOf("flop".encodeToByteString())) - sink.writeUtf8("flip flop") + sink.writeString("flip flop") sink.emit() assertEquals(5, source.indexOf("flop".encodeToByteString())) - source.readUtf8() // Clear stream. + source.readString() // Clear stream. // Make sure we backtrack and resume searching after partial match. - sink.writeUtf8("hi hi hi hey") + sink.writeString("hi hi hi hey") sink.emit() assertEquals(3, source.indexOf("hi hi hey".encodeToByteString())) } @Test fun indexOfByteStringAtSegmentBoundary() { - sink.writeUtf8("a".repeat(Segment.SIZE - 1)) - sink.writeUtf8("bcd") + sink.writeString("a".repeat(Segment.SIZE - 1)) + sink.writeString("bcd") sink.emit() assertEquals( (Segment.SIZE - 3).toLong(), @@ -1646,8 +1646,8 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun indexOfDoesNotWrapAround() { - sink.writeUtf8("a".repeat(Segment.SIZE - 1)) - sink.writeUtf8("bcd") + sink.writeString("a".repeat(Segment.SIZE - 1)) + sink.writeString("bcd") sink.emit() assertEquals(-1, source.indexOf("abcda".encodeToByteString(), (Segment.SIZE - 3).toLong())) } @@ -1656,13 +1656,13 @@ abstract class AbstractBufferedSourceTest internal constructor( fun indexOfByteStringWithOffset() { assertEquals(-1, source.indexOf("flop".encodeToByteString(), 1)) - sink.writeUtf8("flop flip flop") + sink.writeString("flop flip flop") sink.emit() assertEquals(10, source.indexOf("flop".encodeToByteString(), 1)) - source.readUtf8() // Clear stream + source.readString() // Clear stream // Make sure we backtrack and resume searching after partial match. - sink.writeUtf8("hi hi hi hi hey") + sink.writeString("hi hi hi hi hey") sink.emit() assertEquals(6, source.indexOf("hi hi hey".encodeToByteString(), 1)) } @@ -1671,7 +1671,7 @@ abstract class AbstractBufferedSourceTest internal constructor( fun indexOfEmptyByteString() { assertEquals(0, source.indexOf(ByteString())) - sink.writeUtf8("blablabla") + sink.writeString("blablabla") sink.emit() assertEquals(0, source.indexOf(ByteString())) } @@ -1689,8 +1689,8 @@ abstract class AbstractBufferedSourceTest internal constructor( */ @Test fun indexOfByteStringAcrossSegmentBoundaries() { - sink.writeUtf8("a".repeat(Segment.SIZE * 2 - 3)) - sink.writeUtf8("bcdefg") + sink.writeString("a".repeat(Segment.SIZE * 2 - 3)) + sink.writeString("bcdefg") sink.emit() assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("ab".encodeToByteString())) assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abc".encodeToByteString())) diff --git a/core/common/test/CommonBufferTest.kt b/core/common/test/CommonBufferTest.kt index 2df80fb97..d450557a2 100644 --- a/core/common/test/CommonBufferTest.kt +++ b/core/common/test/CommonBufferTest.kt @@ -37,16 +37,16 @@ class CommonBufferTest { @Test fun readAndWriteUtf8() { val buffer = Buffer() - buffer.writeUtf8("ab") + buffer.writeString("ab") assertEquals(2, buffer.size) - buffer.writeUtf8("cdef") + buffer.writeString("cdef") assertEquals(6, buffer.size) - assertEquals("abcd", buffer.readUtf8(4)) + assertEquals("abcd", buffer.readString(4)) assertEquals(2, buffer.size) - assertEquals("ef", buffer.readUtf8(2)) + assertEquals("ef", buffer.readString(2)) assertEquals(0, buffer.size) assertFailsWith { - buffer.readUtf8(1) + buffer.readString(1) } } @@ -56,12 +56,12 @@ class CommonBufferTest { assertEquals( "Buffer(size=10 hex=610d0a620a630d645c65)", - Buffer().also { it.writeUtf8("a\r\nb\nc\rd\\e") }.toString() + Buffer().also { it.writeString("a\r\nb\nc\rd\\e") }.toString() ) assertEquals( "Buffer(size=11 hex=547972616e6e6f73617572)", - Buffer().also { it.writeUtf8("Tyrannosaur") }.toString() + Buffer().also { it.writeString("Tyrannosaur") }.toString() ) assertEquals( @@ -85,19 +85,19 @@ class CommonBufferTest { @Test fun multipleSegmentBuffers() { val buffer = Buffer() - buffer.writeUtf8('a'.repeat(1000)) - buffer.writeUtf8('b'.repeat(2500)) - buffer.writeUtf8('c'.repeat(5000)) - buffer.writeUtf8('d'.repeat(10000)) - buffer.writeUtf8('e'.repeat(25000)) - buffer.writeUtf8('f'.repeat(50000)) - - assertEquals('a'.repeat(999), buffer.readUtf8(999)) // a...a - assertEquals("a" + 'b'.repeat(2500) + "c", buffer.readUtf8(2502)) // ab...bc - assertEquals('c'.repeat(4998), buffer.readUtf8(4998)) // c...c - assertEquals("c" + 'd'.repeat(10000) + "e", buffer.readUtf8(10002)) // cd...de - assertEquals('e'.repeat(24998), buffer.readUtf8(24998)) // e...e - assertEquals("e" + 'f'.repeat(50000), buffer.readUtf8(50001)) // ef...f + buffer.writeString('a'.repeat(1000)) + buffer.writeString('b'.repeat(2500)) + buffer.writeString('c'.repeat(5000)) + buffer.writeString('d'.repeat(10000)) + buffer.writeString('e'.repeat(25000)) + buffer.writeString('f'.repeat(50000)) + + assertEquals('a'.repeat(999), buffer.readString(999)) // a...a + assertEquals("a" + 'b'.repeat(2500) + "c", buffer.readString(2502)) // ab...bc + assertEquals('c'.repeat(4998), buffer.readString(4998)) // c...c + assertEquals("c" + 'd'.repeat(10000) + "e", buffer.readString(10002)) // cd...de + assertEquals('e'.repeat(24998), buffer.readString(24998)) // e...e + assertEquals("e" + 'f'.repeat(50000), buffer.readString(50001)) // ef...f assertEquals(0, buffer.size) } @@ -159,12 +159,12 @@ class CommonBufferTest { val buffer = Buffer() for (s in contents) { val source = Buffer() - source.writeUtf8(s) + source.writeString(s) buffer.transferFrom(source) expected.append(s) } val segmentSizes = segmentSizes(buffer) - assertEquals(expected.toString(), buffer.readUtf8(expected.length.toLong())) + assertEquals(expected.toString(), buffer.readString(expected.length.toLong())) return segmentSizes } @@ -174,10 +174,10 @@ class CommonBufferTest { val writeSize = Segment.SIZE / 2 + 1 val sink = Buffer() - sink.writeUtf8('b'.repeat(Segment.SIZE - 10)) + sink.writeString('b'.repeat(Segment.SIZE - 10)) val source = Buffer() - source.writeUtf8('a'.repeat(Segment.SIZE * 2)) + source.writeString('a'.repeat(Segment.SIZE * 2)) sink.write(source, writeSize.toLong()) assertEquals(listOf(Segment.SIZE - 10, writeSize), segmentSizes(sink)) @@ -190,10 +190,10 @@ class CommonBufferTest { val writeSize = Segment.SIZE / 2 - 1 val sink = Buffer() - sink.writeUtf8('b'.repeat(Segment.SIZE - 10)) + sink.writeString('b'.repeat(Segment.SIZE - 10)) val source = Buffer() - source.writeUtf8('a'.repeat(Segment.SIZE * 2)) + source.writeString('a'.repeat(Segment.SIZE * 2)) sink.write(source, writeSize.toLong()) assertEquals(listOf(Segment.SIZE - 10, writeSize), segmentSizes(sink)) @@ -203,10 +203,10 @@ class CommonBufferTest { @Test fun writePrefixDoesntSplit() { val sink = Buffer() - sink.writeUtf8('b'.repeat(10)) + sink.writeString('b'.repeat(10)) val source = Buffer() - source.writeUtf8('a'.repeat(Segment.SIZE * 2)) + source.writeString('a'.repeat(Segment.SIZE * 2)) sink.write(source, 20) assertEquals(listOf(30), segmentSizes(sink)) @@ -218,11 +218,11 @@ class CommonBufferTest { @Test fun writePrefixDoesntSplitButRequiresCompact() { val sink = Buffer() - sink.writeUtf8('b'.repeat(Segment.SIZE - 10)) // limit = size - 10 - sink.readUtf8((Segment.SIZE - 20).toLong()) // pos = size = 20 + sink.writeString('b'.repeat(Segment.SIZE - 10)) // limit = size - 10 + sink.readString((Segment.SIZE - 20).toLong()) // pos = size = 20 val source = Buffer() - source.writeUtf8('a'.repeat(Segment.SIZE * 2)) + source.writeString('a'.repeat(Segment.SIZE * 2)) sink.write(source, 20) assertEquals(listOf(30), segmentSizes(sink)) @@ -242,39 +242,39 @@ class CommonBufferTest { @Test fun moveAllRequestedBytesWithRead() { val sink = Buffer() - sink.writeUtf8('a'.repeat(10)) + sink.writeString('a'.repeat(10)) val source = Buffer() - source.writeUtf8('b'.repeat(15)) + source.writeString('b'.repeat(15)) assertEquals(10, source.readAtMostTo(sink, 10)) assertEquals(20, sink.size) assertEquals(5, source.size) - assertEquals('a'.repeat(10) + 'b'.repeat(10), sink.readUtf8(20)) + assertEquals('a'.repeat(10) + 'b'.repeat(10), sink.readString(20)) } @Test fun moveFewerThanRequestedBytesWithRead() { val sink = Buffer() - sink.writeUtf8('a'.repeat(10)) + sink.writeString('a'.repeat(10)) val source = Buffer() - source.writeUtf8('b'.repeat(20)) + source.writeString('b'.repeat(20)) assertEquals(20, source.readAtMostTo(sink, 25)) assertEquals(30, sink.size) assertEquals(0, source.size) - assertEquals('a'.repeat(10) + 'b'.repeat(20), sink.readUtf8(30)) + assertEquals('a'.repeat(10) + 'b'.repeat(20), sink.readString(30)) } @Test fun indexOfWithOffset() { val buffer = Buffer() val halfSegment = Segment.SIZE / 2 - buffer.writeUtf8('a'.repeat(halfSegment)) - buffer.writeUtf8('b'.repeat(halfSegment)) - buffer.writeUtf8('c'.repeat(halfSegment)) - buffer.writeUtf8('d'.repeat(halfSegment)) + buffer.writeString('a'.repeat(halfSegment)) + buffer.writeString('b'.repeat(halfSegment)) + buffer.writeString('c'.repeat(halfSegment)) + buffer.writeString('d'.repeat(halfSegment)) assertEquals(0, buffer.indexOf('a'.code.toByte(), 0)) assertEquals((halfSegment - 1).toLong(), buffer.indexOf('a'.code.toByte(), (halfSegment - 1).toLong())) assertEquals(halfSegment.toLong(), buffer.indexOf('b'.code.toByte(), (halfSegment - 1).toLong())) @@ -288,9 +288,9 @@ class CommonBufferTest { @Test fun byteAt() { val buffer = Buffer() - buffer.writeUtf8("a") - buffer.writeUtf8('b'.repeat(Segment.SIZE)) - buffer.writeUtf8("c") + buffer.writeString("a") + buffer.writeString('b'.repeat(Segment.SIZE)) + buffer.writeString("c") assertEquals('a'.code.toLong(), buffer[0].toLong()) assertEquals('a'.code.toLong(), buffer[0].toLong()) // getByte doesn't mutate! assertEquals('c'.code.toLong(), buffer[buffer.size - 1].toLong()) @@ -318,21 +318,21 @@ class CommonBufferTest { fun writePrefixToEmptyBuffer() { val sink = Buffer() val source = Buffer() - source.writeUtf8("abcd") + source.writeString("abcd") sink.write(source, 2) - assertEquals("ab", sink.readUtf8(2)) + assertEquals("ab", sink.readString(2)) } // Buffer don't override equals and hashCode @Test fun equalsAndHashCode() { - val a = Buffer().also { it.writeUtf8("dog") } + val a = Buffer().also { it.writeString("dog") } assertEquals(a, a) - val b = Buffer().also { it.writeUtf8("hotdog") } + val b = Buffer().also { it.writeString("hotdog") } assertTrue(a != b) - b.readUtf8(3) // Leaves b containing 'dog'. + b.readString(3) // Leaves b containing 'dog'. assertTrue(a != b) } @@ -343,14 +343,14 @@ class CommonBufferTest { @Test fun readAllWritesAllSegmentsAtOnce() { val write1 = Buffer() - write1.writeUtf8( + write1.writeString( 'a'.repeat(Segment.SIZE) + 'b'.repeat(Segment.SIZE) + 'c'.repeat(Segment.SIZE) ) val source = Buffer() - source.writeUtf8( + source.writeString( 'a'.repeat(Segment.SIZE) + 'b'.repeat(Segment.SIZE) + 'c'.repeat(Segment.SIZE) @@ -365,60 +365,60 @@ class CommonBufferTest { @Test fun writeAllMultipleSegments() { - val source = Buffer().also { it.writeUtf8('a'.repeat(Segment.SIZE * 3)) } + val source = Buffer().also { it.writeString('a'.repeat(Segment.SIZE * 3)) } val sink = Buffer() assertEquals((Segment.SIZE * 3).toLong(), sink.transferFrom(source)) assertEquals(0, source.size) - assertEquals('a'.repeat(Segment.SIZE * 3), sink.readUtf8()) + assertEquals('a'.repeat(Segment.SIZE * 3), sink.readString()) } @Test fun copyTo() { val source = Buffer() - source.writeUtf8("party") + source.writeString("party") val target = Buffer() source.copyTo(target, startIndex = 1, endIndex = 4) - assertEquals("art", target.readUtf8()) - assertEquals("party", source.readUtf8()) + assertEquals("art", target.readString()) + assertEquals("party", source.readString()) } @Test fun copyToAll() { val source = Buffer() - source.writeUtf8("hello") + source.writeString("hello") val target = Buffer() source.copyTo(target) - assertEquals("hello", source.readUtf8()) - assertEquals("hello", target.readUtf8()) + assertEquals("hello", source.readString()) + assertEquals("hello", target.readString()) } @Test fun copyToWithOnlyStartIndex() { val source = Buffer() - source.writeUtf8("hello") + source.writeString("hello") val target = Buffer() source.copyTo(target, startIndex = 1) - assertEquals("hello", source.readUtf8()) - assertEquals("ello", target.readUtf8()) + assertEquals("hello", source.readString()) + assertEquals("ello", target.readString()) } @Test fun copyToWithOnlyEndIndex() { val source = Buffer() - source.writeUtf8("hello") + source.writeString("hello") val target = Buffer() source.copyTo(target, endIndex = 1) - assertEquals("hello", source.readUtf8()) - assertEquals("h", target.readUtf8()) + assertEquals("hello", source.readString()) + assertEquals("h", target.readString()) } @Test @@ -429,18 +429,18 @@ class CommonBufferTest { val ds = 'd'.repeat(Segment.SIZE) val source = Buffer() - source.writeUtf8(aStr) - source.writeUtf8(bs) - source.writeUtf8(cs) + source.writeString(aStr) + source.writeString(bs) + source.writeString(cs) val target = Buffer() - target.writeUtf8(ds) + target.writeString(ds) source.copyTo( target, startIndex = aStr.length.toLong(), endIndex = aStr.length.toLong() + (bs.length + cs.length).toLong() ) - assertEquals(ds + bs + cs, target.readUtf8()) + assertEquals(ds + bs + cs, target.readString()) } @Test @@ -451,18 +451,18 @@ class CommonBufferTest { val ds = 'd'.repeat(Segment.SIZE + 8) val source = Buffer() - source.writeUtf8(aStr) - source.writeUtf8(bs) - source.writeUtf8(cs) + source.writeString(aStr) + source.writeString(bs) + source.writeString(cs) val target = Buffer() - target.writeUtf8(ds) + target.writeString(ds) source.copyTo( target, startIndex = aStr.length.toLong(), endIndex = aStr.length.toLong() + (bs.length + cs.length).toLong() ) - assertEquals(ds + bs + cs, target.readUtf8()) + assertEquals(ds + bs + cs, target.readString()) } @Test @@ -471,29 +471,29 @@ class CommonBufferTest { val bs = 'b'.repeat(Segment.SIZE) val source = Buffer() - source.writeUtf8(aStr) - source.writeUtf8(bs) + source.writeString(aStr) + source.writeString(bs) source.copyTo(source, startIndex = 0, endIndex = source.size) - assertEquals(aStr + bs + aStr + bs, source.readUtf8()) + assertEquals(aStr + bs + aStr + bs, source.readString()) } @Test fun copyToEmptySource() { val source = Buffer() - val target = Buffer().also { it.writeUtf8("aaa") } + val target = Buffer().also { it.writeString("aaa") } source.copyTo(target, startIndex = 0L, endIndex = 0L) - assertEquals("", source.readUtf8()) - assertEquals("aaa", target.readUtf8()) + assertEquals("", source.readString()) + assertEquals("aaa", target.readString()) } @Test fun copyToEmptyTarget() { - val source = Buffer().also { it.writeUtf8("aaa") } + val source = Buffer().also { it.writeString("aaa") } val target = Buffer() source.copyTo(target, startIndex = 0L, endIndex = 3L) - assertEquals("aaa", source.readUtf8()) - assertEquals("aaa", target.readUtf8()) + assertEquals("aaa", source.readString()) + assertEquals("aaa", target.readString()) } @Test @@ -505,14 +505,14 @@ class CommonBufferTest { @Test fun completeSegmentByteCountOnBufferWithFullSegments() { val buffer = Buffer() - buffer.writeUtf8("a".repeat(Segment.SIZE * 4)) + buffer.writeString("a".repeat(Segment.SIZE * 4)) assertEquals((Segment.SIZE * 4).toLong(), buffer.completeSegmentByteCount()) } @Test fun completeSegmentByteCountOnBufferWithIncompleteTailSegment() { val buffer = Buffer() - buffer.writeUtf8("a".repeat(Segment.SIZE * 4 - 10)) + buffer.writeString("a".repeat(Segment.SIZE * 4 - 10)) assertEquals((Segment.SIZE * 3).toLong(), buffer.completeSegmentByteCount()) } @@ -520,53 +520,53 @@ class CommonBufferTest { fun cloneDoesNotObserveWritesToOriginal() { val original = Buffer() val clone: Buffer = original.copy() - original.writeUtf8("abc") + original.writeString("abc") assertEquals(0, clone.size) } @Test fun cloneDoesNotObserveReadsFromOriginal() { val original = Buffer() - original.writeUtf8("abc") + original.writeString("abc") val clone: Buffer = original.copy() - assertEquals("abc", original.readUtf8(3)) + assertEquals("abc", original.readString(3)) assertEquals(3, clone.size) - assertEquals("ab", clone.readUtf8(2)) + assertEquals("ab", clone.readString(2)) } @Test fun originalDoesNotObserveWritesToClone() { val original = Buffer() val clone: Buffer = original.copy() - clone.writeUtf8("abc") + clone.writeString("abc") assertEquals(0, original.size) } @Test fun originalDoesNotObserveReadsFromClone() { val original = Buffer() - original.writeUtf8("abc") + original.writeString("abc") val clone: Buffer = original.copy() - assertEquals("abc", clone.readUtf8(3)) + assertEquals("abc", clone.readString(3)) assertEquals(3, original.size) - assertEquals("ab", original.readUtf8(2)) + assertEquals("ab", original.readString(2)) } @Test fun cloneMultipleSegments() { val original = Buffer() - original.writeUtf8("a".repeat(SEGMENT_SIZE * 3)) + original.writeString("a".repeat(SEGMENT_SIZE * 3)) val clone: Buffer = original.copy() - original.writeUtf8("b".repeat(SEGMENT_SIZE * 3)) - clone.writeUtf8("c".repeat(SEGMENT_SIZE * 3)) + original.writeString("b".repeat(SEGMENT_SIZE * 3)) + clone.writeString("c".repeat(SEGMENT_SIZE * 3)) assertEquals( "a".repeat(SEGMENT_SIZE * 3) + "b".repeat(SEGMENT_SIZE * 3), - original.readUtf8((SEGMENT_SIZE * 6).toLong()) + original.readString((SEGMENT_SIZE * 6).toLong()) ) assertEquals( "a".repeat(SEGMENT_SIZE * 3) + "c".repeat(SEGMENT_SIZE * 3), - clone.readUtf8((SEGMENT_SIZE * 6).toLong()) + clone.readString((SEGMENT_SIZE * 6).toLong()) ) } @@ -596,7 +596,7 @@ class CommonBufferTest { fun snapshot() { val buffer = Buffer() assertEquals(ByteString(), buffer.snapshot()) - buffer.writeUtf8("hello") + buffer.writeString("hello") assertEquals("hello".encodeToByteString(), buffer.snapshot()) buffer.clear() assertEquals(ByteString(), buffer.snapshot()) diff --git a/core/common/test/CommonPlatformTest.kt b/core/common/test/CommonPlatformTest.kt index 79d6734d8..2cf8faa20 100644 --- a/core/common/test/CommonPlatformTest.kt +++ b/core/common/test/CommonPlatformTest.kt @@ -27,9 +27,9 @@ import kotlin.test.assertEquals class CommonPlatformTest { @Test fun sourceBuffer() { - val source = Buffer().also { it.writeUtf8("a") } + val source = Buffer().also { it.writeString("a") } val buffered = (source as RawSource).buffered() - assertEquals(buffered.readUtf8(), "a") + assertEquals(buffered.readString(), "a") assertEquals(source.size, 0L) } @@ -37,7 +37,7 @@ class CommonPlatformTest { fun sinkBuffer() { val sink = Buffer() val buffered = (sink as RawSink).buffered() - buffered.writeUtf8("a") + buffered.writeString("a") assertEquals(sink.size, 0L) buffered.flush() assertEquals(sink.size, 1L) @@ -45,6 +45,6 @@ class CommonPlatformTest { @Test fun discardingSinkTest() { - discardingSink().write(Buffer().also { it.writeUtf8("a") }, 1L) + discardingSink().write(Buffer().also { it.writeString("a") }, 1L) } } diff --git a/core/common/test/CommonRealSinkTest.kt b/core/common/test/CommonRealSinkTest.kt index 2ee1a6452..f6b454e97 100644 --- a/core/common/test/CommonRealSinkTest.kt +++ b/core/common/test/CommonRealSinkTest.kt @@ -35,7 +35,7 @@ class CommonRealSinkTest { fun bufferedSinkEmitsTailWhenItIsComplete() { val sink = Buffer() val bufferedSink = (sink as RawSink).buffered() - bufferedSink.writeUtf8("a".repeat(Segment.SIZE - 1)) + bufferedSink.writeString("a".repeat(Segment.SIZE - 1)) assertEquals(0, sink.size) bufferedSink.writeByte(0) assertEquals(Segment.SIZE.toLong(), sink.size) @@ -46,7 +46,7 @@ class CommonRealSinkTest { fun bufferedSinkEmitMultipleSegments() { val sink = Buffer() val bufferedSink = (sink as RawSink).buffered() - bufferedSink.writeUtf8("a".repeat(Segment.SIZE * 4 - 1)) + bufferedSink.writeString("a".repeat(Segment.SIZE * 4 - 1)) assertEquals(Segment.SIZE.toLong() * 3L, sink.size) assertEquals(Segment.SIZE.toLong() - 1L, bufferedSink.buffer.size) } @@ -66,7 +66,7 @@ class CommonRealSinkTest { fun bytesEmittedToSinkWithFlush() { val sink = Buffer() val bufferedSink = (sink as RawSink).buffered() - bufferedSink.writeUtf8("abc") + bufferedSink.writeString("abc") bufferedSink.flush() assertEquals(3, sink.size) } @@ -75,7 +75,7 @@ class CommonRealSinkTest { fun bytesNotEmittedToSinkWithoutFlush() { val sink = Buffer() val bufferedSink = (sink as RawSink).buffered() - bufferedSink.writeUtf8("abc") + bufferedSink.writeString("abc") assertEquals(0, sink.size) } @@ -83,7 +83,7 @@ class CommonRealSinkTest { fun bytesEmittedToSinkWithEmit() { val sink = Buffer() val bufferedSink = (sink as RawSink).buffered() - bufferedSink.writeUtf8("abc") + bufferedSink.writeString("abc") bufferedSink.emit() assertEquals(3, sink.size) } @@ -92,7 +92,7 @@ class CommonRealSinkTest { fun completeSegmentsEmitted() { val sink = Buffer() val bufferedSink = (sink as RawSink).buffered() - bufferedSink.writeUtf8("a".repeat(Segment.SIZE * 3)) + bufferedSink.writeString("a".repeat(Segment.SIZE * 3)) assertEquals(Segment.SIZE.toLong() * 3L, sink.size) } @@ -100,7 +100,7 @@ class CommonRealSinkTest { fun incompleteSegmentsNotEmitted() { val sink = Buffer() val bufferedSink = (sink as RawSink).buffered() - bufferedSink.writeUtf8("a".repeat(Segment.SIZE * 3 - 1)) + bufferedSink.writeString("a".repeat(Segment.SIZE * 3 - 1)) assertEquals(Segment.SIZE.toLong() * 2L, sink.size) } @@ -164,11 +164,11 @@ class CommonRealSinkTest { val mockSink = MockSink() val bufferedSink = mockSink.buffered() - bufferedSink.buffer.writeUtf8("abc") - assertEquals(3, bufferedSink.transferFrom(Buffer().also { it.writeUtf8("def") })) + bufferedSink.buffer.writeString("abc") + assertEquals(3, bufferedSink.transferFrom(Buffer().also { it.writeString("def") })) assertEquals(6, bufferedSink.buffer.size) - assertEquals("abcdef", bufferedSink.buffer.readUtf8(6)) + assertEquals("abcdef", bufferedSink.buffer.readString(6)) mockSink.assertLog() // No writes. } @@ -184,12 +184,12 @@ class CommonRealSinkTest { @Test fun writeAllWritesOneSegmentAtATime() { - val write1 = Buffer().also { it.writeUtf8("a".repeat(Segment.SIZE)) } - val write2 = Buffer().also { it.writeUtf8("b".repeat(Segment.SIZE)) } - val write3 = Buffer().also { it.writeUtf8("c".repeat(Segment.SIZE)) } + val write1 = Buffer().also { it.writeString("a".repeat(Segment.SIZE)) } + val write2 = Buffer().also { it.writeString("b".repeat(Segment.SIZE)) } + val write3 = Buffer().also { it.writeString("c".repeat(Segment.SIZE)) } val source = Buffer() - source.writeUtf8( + source.writeString( "${"a".repeat(Segment.SIZE)}${"b".repeat(Segment.SIZE)}${"c".repeat(Segment.SIZE)}" ) diff --git a/core/common/test/CommonRealSourceTest.kt b/core/common/test/CommonRealSourceTest.kt index 9f056b28e..b345516f2 100644 --- a/core/common/test/CommonRealSourceTest.kt +++ b/core/common/test/CommonRealSourceTest.kt @@ -33,7 +33,7 @@ import kotlin.test.assertFailsWith class CommonRealSourceTest { @Test fun indexOfStopsReadingAtLimit() { - val buffer = Buffer().also { it.writeUtf8("abcdef") } + val buffer = Buffer().also { it.writeString("abcdef") } val bufferedSource = ( object : RawSource by buffer { override fun readAtMostTo(sink: Buffer, byteCount: Long): Long { @@ -50,10 +50,10 @@ class CommonRealSourceTest { @Test fun requireTracksBufferFirst() { val source = Buffer() - source.writeUtf8("bb") + source.writeString("bb") val bufferedSource = (source as RawSource).buffered() - bufferedSource.buffer.writeUtf8("aa") + bufferedSource.buffer.writeString("aa") bufferedSource.require(2) assertEquals(2, bufferedSource.buffer.size) @@ -63,19 +63,19 @@ class CommonRealSourceTest { @Test fun requireIncludesBufferBytes() { val source = Buffer() - source.writeUtf8("b") + source.writeString("b") val bufferedSource = (source as RawSource).buffered() - bufferedSource.buffer.writeUtf8("a") + bufferedSource.buffer.writeString("a") bufferedSource.require(2) - assertEquals("ab", bufferedSource.buffer.readUtf8(2)) + assertEquals("ab", bufferedSource.buffer.readString(2)) } @Test fun requireInsufficientData() { val source = Buffer() - source.writeUtf8("a") + source.writeString("a") val bufferedSource = (source as RawSource).buffered() @@ -87,8 +87,8 @@ class CommonRealSourceTest { @Test fun requireReadsOneSegmentAtATime() { val source = Buffer() - source.writeUtf8("a".repeat(Segment.SIZE)) - source.writeUtf8("b".repeat(Segment.SIZE)) + source.writeString("a".repeat(Segment.SIZE)) + source.writeString("b".repeat(Segment.SIZE)) val bufferedSource = (source as RawSource).buffered() @@ -100,8 +100,8 @@ class CommonRealSourceTest { @Test fun skipReadsOneSegmentAtATime() { val source = Buffer() - source.writeUtf8("a".repeat(Segment.SIZE)) - source.writeUtf8("b".repeat(Segment.SIZE)) + source.writeString("a".repeat(Segment.SIZE)) + source.writeString("b".repeat(Segment.SIZE)) val bufferedSource = (source as RawSource).buffered() bufferedSource.skip(2) assertEquals(Segment.SIZE.toLong(), source.size) @@ -111,10 +111,10 @@ class CommonRealSourceTest { @Test fun skipTracksBufferFirst() { val source = Buffer() - source.writeUtf8("bb") + source.writeString("bb") val bufferedSource = (source as RawSource).buffered() - bufferedSource.buffer.writeUtf8("aa") + bufferedSource.buffer.writeString("aa") bufferedSource.skip(2) assertEquals(0, bufferedSource.buffer.size) @@ -143,12 +143,12 @@ class CommonRealSourceTest { */ @Test fun transferToReadsOneSegmentAtATime() { - val write1 = Buffer().also { it.writeUtf8("a".repeat(Segment.SIZE)) } - val write2 = Buffer().also { it.writeUtf8("b".repeat(Segment.SIZE)) } - val write3 = Buffer().also { it.writeUtf8("c".repeat(Segment.SIZE)) } + val write1 = Buffer().also { it.writeString("a".repeat(Segment.SIZE)) } + val write2 = Buffer().also { it.writeString("b".repeat(Segment.SIZE)) } + val write3 = Buffer().also { it.writeString("c".repeat(Segment.SIZE)) } val source = Buffer() - source.writeUtf8( + source.writeString( "${"a".repeat(Segment.SIZE)}${"b".repeat(Segment.SIZE)}${"c".repeat(Segment.SIZE)}" ) diff --git a/core/common/test/Utf8Test.kt b/core/common/test/Utf8Test.kt index e526dcb9a..d040f8a0f 100644 --- a/core/common/test/Utf8Test.kt +++ b/core/common/test/Utf8Test.kt @@ -332,7 +332,7 @@ class Utf8Test { } private fun Buffer.assertUtf8StringEncoded(expectedHex: String, string: String) { - writeUtf8(string) + writeString(string) assertArrayEquals(expectedHex.decodeHex(), readByteArray()) } diff --git a/core/common/test/files/SmokeFileTest.kt b/core/common/test/files/SmokeFileTest.kt index 573360912..f47a12adf 100644 --- a/core/common/test/files/SmokeFileTest.kt +++ b/core/common/test/files/SmokeFileTest.kt @@ -30,11 +30,11 @@ class SmokeFileTest { val path = Path(tempFile!!) path.sink().use { - it.writeUtf8("example") + it.writeString("example") } path.source().use { - assertEquals("example", it.readUtf8Line()) + assertEquals("example", it.readLine()) } } diff --git a/core/jvm/src/SinkExtJvm.kt b/core/jvm/src/SinkExtJvm.kt index d74bad456..494b5c563 100644 --- a/core/jvm/src/SinkExtJvm.kt +++ b/core/jvm/src/SinkExtJvm.kt @@ -41,7 +41,7 @@ import java.nio.charset.Charset */ public fun Sink.writeString(string: String, charset: Charset, startIndex: Int = 0, endIndex: Int = string.length) { checkBounds(string.length, startIndex, endIndex) - if (charset == Charsets.UTF_8) return writeUtf8(string, startIndex, endIndex) + if (charset == Charsets.UTF_8) return writeString(string, startIndex, endIndex) val data = string.substring(startIndex, endIndex).toByteArray(charset) write(data, 0, data.size) } diff --git a/core/jvm/test/AbstractSinkTestJVM.kt b/core/jvm/test/AbstractSinkTestJVM.kt index d383c141d..c143c151c 100644 --- a/core/jvm/test/AbstractSinkTestJVM.kt +++ b/core/jvm/test/AbstractSinkTestJVM.kt @@ -45,7 +45,7 @@ abstract class AbstractSinkTestJVM internal constructor(factory: SinkFactory) { out.write("b".repeat(9998).toByteArray(UTF_8)) out.write('c'.code) out.flush() - assertEquals(("a" + "b".repeat(9998)) + "c", data.readUtf8()) + assertEquals(("a" + "b".repeat(9998)) + "c", data.readString()) } @Test @@ -90,7 +90,7 @@ abstract class AbstractSinkTestJVM internal constructor(factory: SinkFactory) { assertEquals(expected.length, nioByteBuffer.position()) assertEquals(expected.length, nioByteBuffer.limit()) sink.flush() - assertEquals(expected, data.readUtf8()) + assertEquals(expected, data.readString()) } @Test @@ -104,7 +104,7 @@ abstract class AbstractSinkTestJVM internal constructor(factory: SinkFactory) { assertEquals(expected.length, nioByteBuffer.position()) assertEquals(expected.length, nioByteBuffer.limit()) sink.flush() - assertEquals(expected, data.readUtf8()) + assertEquals(expected, data.readString()) } @Test diff --git a/core/jvm/test/AbstractSourceTestJVM.kt b/core/jvm/test/AbstractSourceTestJVM.kt index 5a3f31ef3..dec531d66 100644 --- a/core/jvm/test/AbstractSourceTestJVM.kt +++ b/core/jvm/test/AbstractSourceTestJVM.kt @@ -50,7 +50,7 @@ abstract class AbstractSourceTestJVM(private val factory: SourceFactory) { @Test fun inputStream() { - sink.writeUtf8("abc") + sink.writeString("abc") sink.emit() val input: InputStream = source.asInputStream() val bytes = byteArrayOf('z'.code.toByte(), 'z'.code.toByte(), 'z'.code.toByte()) @@ -73,7 +73,7 @@ abstract class AbstractSourceTestJVM(private val factory: SourceFactory) { @Test fun inputStreamOffsetCount() { - sink.writeUtf8("abcde") + sink.writeString("abcde") sink.emit() val input: InputStream = source.asInputStream() val bytes = @@ -90,12 +90,12 @@ abstract class AbstractSourceTestJVM(private val factory: SourceFactory) { @Test fun inputStreamSkip() { - sink.writeUtf8("abcde") + sink.writeString("abcde") sink.emit() val input: InputStream = source.asInputStream() assertEquals(4, input.skip(4)) assertEquals('e'.code, input.read()) - sink.writeUtf8("abcde") + sink.writeString("abcde") sink.emit() assertEquals(5, input.skip(10)) // Try to skip too much. assertEquals(0, input.skip(1)) // Try to skip when exhausted. @@ -103,7 +103,7 @@ abstract class AbstractSourceTestJVM(private val factory: SourceFactory) { @Test fun inputStreamCharByChar() { - sink.writeUtf8("abc") + sink.writeString("abc") sink.emit() val input: InputStream = source.asInputStream() assertEquals('a'.code, input.read()) @@ -114,7 +114,7 @@ abstract class AbstractSourceTestJVM(private val factory: SourceFactory) { @Test fun inputStreamBounds() { - sink.writeUtf8("a".repeat(100)) + sink.writeString("a".repeat(100)) sink.emit() val input: InputStream = source.asInputStream() assertFailsWith { @@ -196,7 +196,7 @@ abstract class AbstractSourceTestJVM(private val factory: SourceFactory) { @Test fun readNioBuffer() { val expected = if (factory.isOneByteAtATime) "a" else "abcdefg" - sink.writeUtf8("abcdefg") + sink.writeString("abcdefg") sink.emit() val nioByteBuffer: ByteBuffer = ByteBuffer.allocate(1024) val byteCount: Int = source.readAtMostTo(nioByteBuffer) @@ -213,7 +213,7 @@ abstract class AbstractSourceTestJVM(private val factory: SourceFactory) { @Test fun readLargeNioBufferOnlyReadsOneSegment() { val expected: String = if (factory.isOneByteAtATime) "a" else "a".repeat(SEGMENT_SIZE) - sink.writeUtf8("a".repeat(SEGMENT_SIZE * 4)) + sink.writeString("a".repeat(SEGMENT_SIZE * 4)) sink.emit() val nioByteBuffer: ByteBuffer = ByteBuffer.allocate(SEGMENT_SIZE * 3) val byteCount: Int = source.readAtMostTo(nioByteBuffer) @@ -259,6 +259,6 @@ abstract class AbstractSourceTestJVM(private val factory: SourceFactory) { assertFailsWith { source.readString(4, Charsets.US_ASCII) } - assertEquals("abc", source.readUtf8()) // The read shouldn't consume any data. + assertEquals("abc", source.readString()) // The read shouldn't consume any data. } } diff --git a/core/jvm/test/BufferTest.kt b/core/jvm/test/BufferTest.kt index bab8a2cdf..693cf67ca 100644 --- a/core/jvm/test/BufferTest.kt +++ b/core/jvm/test/BufferTest.kt @@ -36,8 +36,8 @@ class BufferTest { @Test fun copyToSpanningSegments() { val source = Buffer() - source.writeUtf8("a".repeat(SEGMENT_SIZE * 2)) - source.writeUtf8("b".repeat(SEGMENT_SIZE * 2)) + source.writeString("a".repeat(SEGMENT_SIZE * 2)) + source.writeString("b".repeat(SEGMENT_SIZE * 2)) val out = ByteArrayOutputStream() source.copyTo(out, startIndex = 10L, endIndex = 10L + SEGMENT_SIZE * 3L) assertEquals( @@ -46,49 +46,49 @@ class BufferTest { ) assertEquals( "a".repeat(SEGMENT_SIZE * 2) + "b".repeat(SEGMENT_SIZE * 2), - source.readUtf8(SEGMENT_SIZE * 4L) + source.readString(SEGMENT_SIZE * 4L) ) } @Test fun copyToSkippingSegments() { val source = Buffer() - source.writeUtf8("a".repeat(SEGMENT_SIZE * 2)) - source.writeUtf8("b".repeat(SEGMENT_SIZE * 2)) + source.writeString("a".repeat(SEGMENT_SIZE * 2)) + source.writeString("b".repeat(SEGMENT_SIZE * 2)) val out = ByteArrayOutputStream() source.copyTo(out, startIndex = SEGMENT_SIZE * 2 + 1L, endIndex = SEGMENT_SIZE * 2 + 4L) assertEquals("bbb", out.toString()) assertEquals( "a".repeat(SEGMENT_SIZE * 2) + "b".repeat(SEGMENT_SIZE * 2), - source.readUtf8(SEGMENT_SIZE * 4L) + source.readString(SEGMENT_SIZE * 4L) ) } @Test fun copyToStream() { - val buffer = Buffer().also { it.writeUtf8("hello, world!") } + val buffer = Buffer().also { it.writeString("hello, world!") } val out = ByteArrayOutputStream() buffer.copyTo(out) val outString = String(out.toByteArray(), UTF_8) assertEquals("hello, world!", outString) - assertEquals("hello, world!", buffer.readUtf8()) + assertEquals("hello, world!", buffer.readString()) } @Test fun writeToSpanningSegments() { val buffer = Buffer() - buffer.writeUtf8("a".repeat(SEGMENT_SIZE * 2)) - buffer.writeUtf8("b".repeat(SEGMENT_SIZE * 2)) + buffer.writeString("a".repeat(SEGMENT_SIZE * 2)) + buffer.writeString("b".repeat(SEGMENT_SIZE * 2)) val out = ByteArrayOutputStream() buffer.skip(10) buffer.readTo(out, SEGMENT_SIZE * 3L) assertEquals("a".repeat(SEGMENT_SIZE * 2 - 10) + "b".repeat(SEGMENT_SIZE + 10), out.toString()) - assertEquals("b".repeat(SEGMENT_SIZE - 10), buffer.readUtf8(buffer.size)) + assertEquals("b".repeat(SEGMENT_SIZE - 10), buffer.readString(buffer.size)) } @Test fun writeToStream() { - val buffer = Buffer().also { it.writeUtf8("hello, world!") } + val buffer = Buffer().also { it.writeString("hello, world!") } val out = ByteArrayOutputStream() buffer.readTo(out) val outString = String(out.toByteArray(), UTF_8) @@ -101,16 +101,16 @@ class BufferTest { val input: InputStream = ByteArrayInputStream("hello, world!".toByteArray(UTF_8)) val buffer = Buffer() buffer.transferFrom(input) - val out = buffer.readUtf8() + val out = buffer.readString() assertEquals("hello, world!", out) } @Test fun readFromSpanningSegments() { val input: InputStream = ByteArrayInputStream("hello, world!".toByteArray(UTF_8)) - val buffer = Buffer().also { it.writeUtf8("a".repeat(SEGMENT_SIZE - 10)) } + val buffer = Buffer().also { it.writeString("a".repeat(SEGMENT_SIZE - 10)) } buffer.transferFrom(input) - val out = buffer.readUtf8() + val out = buffer.readString() assertEquals("a".repeat(SEGMENT_SIZE - 10) + "hello, world!", out) } @@ -119,7 +119,7 @@ class BufferTest { val input: InputStream = ByteArrayInputStream("hello, world!".toByteArray(UTF_8)) val buffer = Buffer() buffer.write(input, 10) - val out = buffer.readUtf8() + val out = buffer.readString() assertEquals("hello, wor", out) } @@ -149,7 +149,7 @@ class BufferTest { @Test fun bufferInputStreamByteByByte() { val source = Buffer() - source.writeUtf8("abc") + source.writeString("abc") val input: InputStream = source.asInputStream() assertEquals(3, input.available()) assertEquals('a'.code, input.read()) @@ -162,7 +162,7 @@ class BufferTest { @Test fun bufferInputStreamBulkReads() { val source = Buffer() - source.writeUtf8("abc") + source.writeString("abc") val byteArray = ByteArray(4) Arrays.fill(byteArray, (-5).toByte()) val input: InputStream = source.asInputStream() @@ -176,78 +176,78 @@ class BufferTest { @Test fun copyToOutputStream() { val source = Buffer() - source.writeUtf8("party") + source.writeString("party") val target = Buffer() source.copyTo(target.asOutputStream()) - assertEquals("party", target.readUtf8()) - assertEquals("party", source.readUtf8()) + assertEquals("party", target.readString()) + assertEquals("party", source.readString()) } @Test fun copyToOutputStreamWithStartIndex() { val source = Buffer() - source.writeUtf8("party") + source.writeString("party") val target = Buffer() source.copyTo(target.asOutputStream(), startIndex = 2) - assertEquals("rty", target.readUtf8()) - assertEquals("party", source.readUtf8()) + assertEquals("rty", target.readString()) + assertEquals("party", source.readString()) } @Test fun copyToOutputStreamWithEndIndex() { val source = Buffer() - source.writeUtf8("party") + source.writeString("party") val target = Buffer() source.copyTo(target.asOutputStream(), endIndex = 3) - assertEquals("par", target.readUtf8()) - assertEquals("party", source.readUtf8()) + assertEquals("par", target.readString()) + assertEquals("party", source.readString()) } @Test fun copyToOutputStreamWithIndices() { val source = Buffer() - source.writeUtf8("party") + source.writeString("party") val target = Buffer() source.copyTo(target.asOutputStream(), startIndex = 1, endIndex = 4) - assertEquals("art", target.readUtf8()) - assertEquals("party", source.readUtf8()) + assertEquals("art", target.readString()) + assertEquals("party", source.readString()) } @Test fun copyToOutputStreamWithEmptyRange() { val source = Buffer() - source.writeUtf8("hello") + source.writeString("hello") val target = Buffer() source.copyTo(target.asOutputStream(), startIndex = 1, endIndex = 1) - assertEquals("hello", source.readUtf8()) - assertEquals("", target.readUtf8()) + assertEquals("hello", source.readString()) + assertEquals("", target.readString()) } @Test fun readToOutputStream() { val source = Buffer() - source.writeUtf8("party") + source.writeString("party") val target = Buffer() source.readTo(target.asOutputStream()) - assertEquals("party", target.readUtf8()) - assertEquals("", source.readUtf8()) + assertEquals("party", target.readString()) + assertEquals("", source.readString()) } @Test fun readToOutputStreamWithByteCount() { val source = Buffer() - source.writeUtf8("party") + source.writeString("party") val target = Buffer() source.readTo(target.asOutputStream(), byteCount = 3) - assertEquals("par", target.readUtf8()) - assertEquals("ty", source.readUtf8()) + assertEquals("par", target.readString()) + assertEquals("ty", source.readString()) } @Test diff --git a/core/jvm/test/JvmPlatformTest.kt b/core/jvm/test/JvmPlatformTest.kt index c07a62d99..ea6ee688e 100644 --- a/core/jvm/test/JvmPlatformTest.kt +++ b/core/jvm/test/JvmPlatformTest.kt @@ -44,7 +44,7 @@ class JvmPlatformTest { fun outputStreamSink() { val baos = ByteArrayOutputStream() val sink = baos.asSink() - sink.write(Buffer().also { it.writeUtf8("a") }, 1L) + sink.write(Buffer().also { it.writeString("a") }, 1L) assertArrayEquals(baos.toByteArray(), byteArrayOf(0x61)) } @@ -52,7 +52,7 @@ class JvmPlatformTest { fun outputStreamSinkWriteZeroBytes() { val baos = ByteArrayOutputStream() val sink = baos.asSink() - sink.write(Buffer().also { it.writeUtf8("a") }, 0L) + sink.write(Buffer().also { it.writeString("a") }, 0L) assertEquals(0, baos.size()) } @@ -61,7 +61,7 @@ class JvmPlatformTest { val baos = ByteArrayOutputStream() val sink = baos.asSink() assertFailsWith { - sink.write(Buffer().also { it.writeUtf8("a") }, -1) + sink.write(Buffer().also { it.writeString("a") }, -1) } } @@ -69,10 +69,10 @@ class JvmPlatformTest { fun outputStreamSinkWritePartOfTheBuffer() { val baos = ByteArrayOutputStream() val sink = baos.asSink() - val buffer = Buffer().also { it.writeUtf8("hello") } + val buffer = Buffer().also { it.writeString("hello") } sink.write(buffer, 2) assertArrayEquals(baos.toByteArray(), byteArrayOf('h'.code.toByte(), 'e'.code.toByte())) - assertEquals("llo", buffer.readUtf8()) + assertEquals("llo", buffer.readString()) } @Test @@ -81,7 +81,7 @@ class JvmPlatformTest { val source = bais.asSource() val buffer = Buffer() source.readAtMostTo(buffer, 1) - assertEquals(buffer.readUtf8(), "a") + assertEquals(buffer.readString(), "a") } @Test @@ -104,7 +104,7 @@ class JvmPlatformTest { fun fileSink() { val file = File(tempDir, "test") file.outputStream().asSink().use { sink -> - sink.write(Buffer().also { it.writeUtf8("a") }, 1L) + sink.write(Buffer().also { it.writeString("a") }, 1L) } assertEquals(file.readText(), "a") } @@ -114,7 +114,7 @@ class JvmPlatformTest { val file = File(tempDir, "test") file.writeText("a") FileOutputStream(file, true).asSink().use { sink -> - sink.write(Buffer().also { it.writeUtf8("b") }, 1L) + sink.write(Buffer().also { it.writeString("b") }, 1L) } assertEquals(file.readText(), "ab") } @@ -127,14 +127,14 @@ class JvmPlatformTest { file.inputStream().asSource().use { source -> source.readAtMostTo(buffer, 1L) } - assertEquals(buffer.readUtf8(), "a") + assertEquals(buffer.readString(), "a") } @Test fun pathSink() { val file = File(tempDir, "test") file.toPath().outputStream().asSink().use { sink -> - sink.write(Buffer().also { it.writeUtf8("a") }, 1L) + sink.write(Buffer().also { it.writeString("a") }, 1L) } assertEquals(file.readText(), "a") } @@ -144,7 +144,7 @@ class JvmPlatformTest { val file = File(tempDir, "test") file.writeText("a") file.toPath().outputStream(StandardOpenOption.APPEND).asSink().use { sink -> - sink.write(Buffer().also { it.writeUtf8("b") }, 1L) + sink.write(Buffer().also { it.writeString("b") }, 1L) } assertEquals(file.readText(), "ab") } @@ -157,7 +157,7 @@ class JvmPlatformTest { file.toPath().inputStream().asSource().use { source -> source.readAtMostTo(buffer, 1L) } - assertEquals(buffer.readUtf8(), "a") + assertEquals(buffer.readString(), "a") } @Test @@ -173,9 +173,9 @@ class JvmPlatformTest { } assertFailsWith { - link.toPath().inputStream(LinkOption.NOFOLLOW_LINKS).asSource().use { it.buffered().readUtf8Line() } + link.toPath().inputStream(LinkOption.NOFOLLOW_LINKS).asSource().use { it.buffered().readLine() } } - assertNull(link.toPath().inputStream().asSource().use { it.buffered().readUtf8Line() }) + assertNull(link.toPath().inputStream().asSource().use { it.buffered().readLine() }) } @Test @@ -185,7 +185,7 @@ class JvmPlatformTest { override fun getOutputStream() = baos } val sink = socket.outputStream.asSink() - sink.write(Buffer().also { it.writeUtf8("a") }, 1L) + sink.write(Buffer().also { it.writeString("a") }, 1L) assertArrayEquals(baos.toByteArray(), byteArrayOf(0x61)) } @@ -198,6 +198,6 @@ class JvmPlatformTest { val source = socket.inputStream.asSource() val buffer = Buffer() source.readAtMostTo(buffer, 1L) - assertEquals(buffer.readUtf8(), "a") + assertEquals(buffer.readString(), "a") } } diff --git a/core/jvm/test/NioTest.kt b/core/jvm/test/NioTest.kt index 541237da6..96c59629d 100644 --- a/core/jvm/test/NioTest.kt +++ b/core/jvm/test/NioTest.kt @@ -65,7 +65,7 @@ class NioTest { val fileChannel: FileChannel = FileChannel.open(file, StandardOpenOption.WRITE) testWritableByteChannel(false, fileChannel) val emitted: Source = file.inputStream().asSource().buffered() - assertEquals("defghijklmnopqrstuvw", emitted.readUtf8()) + assertEquals("defghijklmnopqrstuvw", emitted.readString()) emitted.close() } @@ -73,7 +73,7 @@ class NioTest { fun writableChannelBuffer() { val buffer = Buffer() testWritableByteChannel(true, buffer.asByteChannel()) - assertEquals("defghijklmnopqrstuvw", buffer.readUtf8()) + assertEquals("defghijklmnopqrstuvw", buffer.readString()) } @Test @@ -81,14 +81,14 @@ class NioTest { val buffer = Buffer() val bufferedSink: Sink = buffer testWritableByteChannel(true, bufferedSink.asByteChannel()) - assertEquals("defghijklmnopqrstuvw", buffer.readUtf8()) + assertEquals("defghijklmnopqrstuvw", buffer.readString()) } @Test fun readableChannelNioFile() { val file: File = Paths.get(temporaryFolder.toString(), "test").toFile() val initialData: Sink = file.outputStream().asSink().buffered() - initialData.writeUtf8("abcdefghijklmnopqrstuvwxyz") + initialData.writeString("abcdefghijklmnopqrstuvwxyz") initialData.close() val fileChannel: FileChannel = FileChannel.open(file.toPath(), StandardOpenOption.READ) testReadableByteChannel(false, fileChannel) @@ -97,7 +97,7 @@ class NioTest { @Test fun readableChannelBuffer() { val buffer = Buffer() - buffer.writeUtf8("abcdefghijklmnopqrstuvwxyz") + buffer.writeString("abcdefghijklmnopqrstuvwxyz") testReadableByteChannel(true, buffer.asByteChannel()) } @@ -105,7 +105,7 @@ class NioTest { fun readableChannelBufferedSource() { val buffer = Buffer() val bufferedSource: Source = buffer - buffer.writeUtf8("abcdefghijklmnopqrstuvwxyz") + buffer.writeString("abcdefghijklmnopqrstuvwxyz") testReadableByteChannel(true, bufferedSource.asByteChannel()) }