From 6c43310276f01097344e91f6ae8f46b59c753404 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Mon, 26 Jun 2023 14:44:36 +0200 Subject: [PATCH 1/7] Added samples for common src-set --- bytestring/build.gradle.kts | 2 + bytestring/common/src/ByteString.kt | 39 + bytestring/common/src/ByteStringBuilder.kt | 3 + bytestring/common/test/samples/samples.kt | 240 +++++++ core/build.gradle.kts | 8 + core/common/src/Buffer.kt | 18 +- core/common/src/ByteStringExt.kt | 8 + core/common/src/RawSink.kt | 3 + core/common/src/RawSource.kt | 5 + core/common/src/Sink.kt | 17 + core/common/src/SinkExt.kt | 24 + core/common/src/Source.kt | 24 + core/common/src/SourceExt.kt | 34 + core/common/src/Utf8.kt | 18 + core/common/test/samples/byteStringSample.kt | 45 ++ .../test/samples/moduleDescriptionSample.kt | 73 ++ core/common/test/samples/rawSinkSample.kt | 76 ++ core/common/test/samples/rawSourceSample.kt | 84 +++ core/common/test/samples/samples.kt | 672 ++++++++++++++++++ 19 files changed, 1388 insertions(+), 5 deletions(-) create mode 100644 bytestring/common/test/samples/samples.kt create mode 100644 core/common/test/samples/byteStringSample.kt create mode 100644 core/common/test/samples/moduleDescriptionSample.kt create mode 100644 core/common/test/samples/rawSinkSample.kt create mode 100644 core/common/test/samples/rawSourceSample.kt create mode 100644 core/common/test/samples/samples.kt diff --git a/bytestring/build.gradle.kts b/bytestring/build.gradle.kts index f81792b08..c0be1e677 100644 --- a/bytestring/build.gradle.kts +++ b/bytestring/build.gradle.kts @@ -76,5 +76,7 @@ tasks.withType().configureEach { suppress.set(true) matchingRegex.set(".*unsafe.*") } + + samples.from("common/test/samples/samples.kt") } } diff --git a/bytestring/common/src/ByteString.kt b/bytestring/common/src/ByteString.kt index 155ee4fa8..180893e60 100644 --- a/bytestring/common/src/ByteString.kt +++ b/bytestring/common/src/ByteString.kt @@ -28,6 +28,8 @@ import kotlin.math.min * Wraps given [bytes] into a byte string. * * @param bytes a sequence of bytes to be wrapped. + * + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.constructionFromBytesSample */ public fun ByteString(vararg bytes: Byte): ByteString = if (bytes.isEmpty()) { ByteString.EMPTY @@ -51,6 +53,8 @@ public class ByteString private constructor( * * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [data] array indices. * @throws IllegalArgumentException when `startIndex > endIndex`. + * + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.constructionSample */ public constructor(data: ByteArray, startIndex: Int = 0, endIndex: Int = data.size) : this(data.copyOfRange(startIndex, endIndex), null) @@ -125,6 +129,9 @@ public class ByteString private constructor( * * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of byte string indices. * @throws IllegalArgumentException when `startIndex > endIndex`. + * + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.toByteArraySample + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.toByteArrayWithIndicesSample */ public fun toByteArray(startIndex: Int = 0, endIndex: Int = size): ByteArray { require(startIndex <= endIndex) { "startIndex ($startIndex) > endIndex ($endIndex)" } @@ -144,6 +151,8 @@ public class ByteString private constructor( * @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`. + * + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.copyToSample */ public fun copyInto( destination: ByteArray, destinationOffset: Int = 0, @@ -162,6 +171,8 @@ public class ByteString private constructor( * * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of byte string indices. * @throws IllegalArgumentException when `startIndex > endIndex`. + * + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.substringSample */ public fun substring(startIndex: Int, endIndex: Int = size): ByteString = if (startIndex == endIndex) { EMPTY @@ -177,6 +188,8 @@ public class ByteString private constructor( * The behavior is similar to [String.compareTo]. * * @param other the byte string to compare this string to. + * + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.compareTo */ override fun compareTo(other: ByteString): Int { if (other === this) return 0 @@ -200,6 +213,8 @@ public class ByteString private constructor( * Note that a string representation includes the whole byte string content encoded. * Due to limitations exposed for the maximum string length, an attempt to return a string representation * of too long byte string may fail. + * + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.toStringSample */ override fun toString(): String { if (isEmpty()) { @@ -247,6 +262,8 @@ public val ByteString.indices: IntRange * * @param byte the value to search for. * @param startIndex the index (inclusive) starting from which the [byte] should be searched. + * + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.indexOfByteSample */ public fun ByteString.indexOf(byte: Byte, startIndex: Int = 0): Int { val localData = getBackingArrayReference() @@ -267,6 +284,8 @@ public fun ByteString.indexOf(byte: Byte, startIndex: Int = 0): Int { * * @param byteString the value to search for. * @param startIndex the index (inclusive) starting from which the [byteString] should be searched. + * + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.indexOfByteStringSample */ public fun ByteString.indexOf(byteString: ByteString, startIndex: Int = 0): Int { if (byteString.isEmpty()) return max(min(startIndex, size), 0) @@ -289,6 +308,8 @@ public fun ByteString.indexOf(byteString: ByteString, startIndex: Int = 0): Int * * @param byteArray the value to search for. * @param startIndex the index (inclusive) starting from which the [byteArray] should be searched. + * + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.indexOfByteArraySample */ public fun ByteString.indexOf(byteArray: ByteArray, startIndex: Int = 0): Int { if (byteArray.isEmpty()) return max(min(startIndex, size), 0) @@ -311,6 +332,8 @@ public fun ByteString.indexOf(byteArray: ByteArray, startIndex: Int = 0): Int { * * @param byte the value to search for. * @param startIndex the index (inclusive) starting from which the [byte] should be searched. + * + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.lastIndexOfByteSample */ public fun ByteString.lastIndexOf(byte: Byte, startIndex: Int = 0): Int { val localData = getBackingArrayReference() @@ -331,6 +354,8 @@ public fun ByteString.lastIndexOf(byte: Byte, startIndex: Int = 0): Int { * * @param byteString the value to search for. * @param startIndex the index (inclusive) starting from which the [byteString] should be searched. + * + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.lastIndexOfByteStringSample */ public fun ByteString.lastIndexOf(byteString: ByteString, startIndex: Int = 0): Int { if (byteString.isEmpty()) return size @@ -351,6 +376,8 @@ public fun ByteString.lastIndexOf(byteString: ByteString, startIndex: Int = 0): * * @param byteArray the value to search for. * @param startIndex the index (inclusive) starting from which the [byteArray] should be searched. + * + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.lastIndexOfByteArraySample */ public fun ByteString.lastIndexOf(byteArray: ByteArray, startIndex: Int = 0): Int { if (byteArray.isEmpty()) return size @@ -368,6 +395,8 @@ public fun ByteString.lastIndexOf(byteArray: ByteArray, startIndex: Int = 0): In * Behavior of this method is compatible with [CharSequence.startsWith]. * * @param byteArray the prefix to check for. + * + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.startsWithByteArraySample */ public fun ByteString.startsWith(byteArray: ByteArray): Boolean = when { byteArray.size > size -> false @@ -380,6 +409,8 @@ public fun ByteString.startsWith(byteArray: ByteArray): Boolean = when { * Behavior of this method is compatible with [CharSequence.startsWith]. * * @param byteString the prefix to check for. + * + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.startsWithByteStringSample */ public fun ByteString.startsWith(byteString: ByteString): Boolean = when { byteString.size > size -> false @@ -393,6 +424,8 @@ public fun ByteString.startsWith(byteString: ByteString): Boolean = when { * Behavior of this method is compatible with [CharSequence.endsWith]. * * @param byteArray the suffix to check for. + * + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.endsWithByteArraySample */ public fun ByteString.endsWith(byteArray: ByteArray): Boolean = when { byteArray.size > size -> false @@ -405,6 +438,8 @@ public fun ByteString.endsWith(byteArray: ByteArray): Boolean = when { * Behavior of this method is compatible with [CharSequence.endsWith]. * * @param byteString the suffix to check for. + * + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.endsWithByteStringSample */ public fun ByteString.endsWith(byteString: ByteString): Boolean = when { byteString.size > size -> false @@ -451,6 +486,8 @@ public fun ByteString.isNotEmpty(): Boolean = !isEmpty() /** * Decodes content of a byte string into a string using UTF-8 encoding. + * + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.encodeAndDecodeUtf8String */ public fun ByteString.decodeToString(): String { return getBackingArrayReference().decodeToString() @@ -458,6 +495,8 @@ public fun ByteString.decodeToString(): String { /** * Encodes a string into a byte sequence using UTF8-encoding and wraps it into a byte string. + * + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.encodeAndDecodeUtf8String */ public fun String.encodeToByteString(): ByteString { return ByteString.wrap(encodeToByteArray()) diff --git a/bytestring/common/src/ByteStringBuilder.kt b/bytestring/common/src/ByteStringBuilder.kt index 3d62964e8..7b0de5d3e 100644 --- a/bytestring/common/src/ByteStringBuilder.kt +++ b/bytestring/common/src/ByteStringBuilder.kt @@ -21,6 +21,9 @@ import kotlin.math.max * will be allocated and previously written data will be copied into it. * * @param initialCapacity the initial size of an underlying byte sequence. + * + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.builderSample + * @sample kotlinx.io.bytestring.samples.ByteStringSamples.builderSampleWithoutAdditionalAllocs */ public class ByteStringBuilder(initialCapacity: Int = 0) { private var buffer = ByteArray(initialCapacity) diff --git a/bytestring/common/test/samples/samples.kt b/bytestring/common/test/samples/samples.kt new file mode 100644 index 000000000..be6c93e72 --- /dev/null +++ b/bytestring/common/test/samples/samples.kt @@ -0,0 +1,240 @@ +/* + * 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.samples + +import kotlinx.io.bytestring.* +import kotlin.test.* + +class ByteStringSamples { + @Test + fun compareTo() { + assertEquals(0, ByteString(1, 2, 3).compareTo(ByteString(1, 2, 3))) + assertEquals(-1, ByteString(1, 2, 3).compareTo(ByteString(1, 3, 2))) + + // If byte strings have different length, their content compared up to the length of the shortest string, + // and if their content was the same, then the shortest string is considered "smaller" + assertEquals(-1, ByteString().compareTo(ByteString(1, 2, 3))) + assertEquals(1, ByteString(1, 2, 3).compareTo(ByteString(1))) + assertEquals(-1, ByteString(1, 2, 3).compareTo(ByteString(1, 3))) + assertEquals(1, ByteString(1, 2, 3).compareTo(ByteString(1, 1, 1, 1))) + } + + @Test + fun toStringSample() { + assertEquals("ByteString(size=0)", ByteString().toString()) + assertEquals("ByteString(size=3 hex=000102)", ByteString(0, 1, 2).toString()) + } + + @Test + fun substringSample() { + val string = ByteString(1, 2, 3, 4, 5) + assertEquals(ByteString(1, 2, 3), string.substring(startIndex = 0, endIndex = 3)) + assertEquals(ByteString(3, 4, 5), string.substring(startIndex = 2)) + assertEquals(ByteString(2, 3, 4), string.substring(startIndex = 1, endIndex = 4)) + } + + @Test + fun toByteArraySample() { + val string = ByteString(1, 2, 3, 4, 5) + val array = string.toByteArray() + + assertContentEquals(byteArrayOf(1, 2, 3, 4, 5), array) + + // Array is a copy of the byte string's content, so its modification won't affect the string. + for (idx in array.indices) { + array[idx] = (array[idx] * 2).toByte() + } + assertEquals(ByteString(1, 2, 3, 4, 5), string) + } + + @Test + fun toByteArrayWithIndicesSample() { + val string = ByteString(1, 2, 3, 4, 5) + + assertContentEquals(byteArrayOf(2, 3, 4), string.toByteArray(startIndex = 1, endIndex = 4)) + assertContentEquals(byteArrayOf(4, 5), string.toByteArray(startIndex = 3)) + } + + @Test + fun copyToSample() { + val string = ByteString(1, 2, 3, 4, 5) + val array = ByteArray(10) + + string.copyInto(array, destinationOffset = 3, startIndex = 1, endIndex = 4) + assertContentEquals(byteArrayOf(0, 0, 0, 2, 3, 4, 0, 0, 0, 0), array) + } + + @Test + fun indexOfByteSample() { + val string = ByteString(1, 2, 3, 2, 1) + + assertEquals(1, string.indexOf(2)) + assertEquals(3, string.indexOf(2, startIndex = 2)) + assertEquals(-1, string.indexOf(0)) + } + + @Test + fun lastIndexOfByteSample() { + val string = ByteString(1, 2, 3, 2, 1) + + assertEquals(3, string.lastIndexOf(2)) + assertEquals(-1, string.lastIndexOf(2, startIndex = 4)) + assertEquals(-1, string.indexOf(0)) + } + + @Test + fun indexOfByteStringSample() { + val string = ByteString(1, 2, 3, 4, 1, 3, 4) + + assertEquals(2, string.indexOf(ByteString(3, 4))) + assertEquals(5, string.indexOf(ByteString(3, 4), startIndex = 3)) + assertEquals(-1, string.indexOf(ByteString(1, 1, 1))) + assertEquals(-1, string.indexOf(ByteString(1, 3, 4, 5))) + assertEquals(0, string.indexOf(ByteString(/* empty byte string */))) + } + + @Test + fun lastIndexOfByteStringSample() { + val string = ByteString(1, 2, 3, 4, 1, 3, 4) + + assertEquals(5, string.lastIndexOf(ByteString(3, 4))) + assertEquals(-1, string.lastIndexOf(ByteString(1, 2), startIndex = 3)) + assertEquals(0, string.lastIndexOf(ByteString(1, 2, 3))) + assertEquals(-1, string.lastIndexOf(ByteString(1, 3, 4, 5))) + assertEquals(string.size, string.lastIndexOf(ByteString(/* empty byte string */))) + } + + @Test + fun indexOfByteArraySample() { + val string = ByteString(1, 2, 3, 4, 1, 3, 4) + + assertEquals(2, string.indexOf(byteArrayOf(3, 4))) + assertEquals(5, string.indexOf(byteArrayOf(3, 4), startIndex = 3)) + assertEquals(-1, string.indexOf(byteArrayOf(1, 1, 1))) + assertEquals(-1, string.indexOf(byteArrayOf(1, 3, 4, 5))) + assertEquals(0, string.indexOf(byteArrayOf(/* empty byte array */))) + } + + @Test + fun lastIndexOfByteArraySample() { + val string = ByteString(1, 2, 3, 4, 1, 3, 4) + + assertEquals(5, string.lastIndexOf(byteArrayOf(3, 4))) + assertEquals(-1, string.lastIndexOf(byteArrayOf(1, 2), startIndex = 3)) + assertEquals(0, string.lastIndexOf(byteArrayOf(1, 2, 3))) + assertEquals(-1, string.lastIndexOf(byteArrayOf(1, 3, 4, 5))) + assertEquals(string.size, string.lastIndexOf(byteArrayOf(/* empty byte array */))) + } + + @Test + fun startsWithByteStringSample() { + val string = ByteString(1, 2, 3, 4, 5) + + assertTrue(string.startsWith(string)) + assertTrue(string.startsWith(ByteString(/* empty byte string */))) + assertTrue(string.startsWith(ByteString(1, 2, 3))) + assertFalse(string.startsWith(ByteString(1, 3, 4))) + assertFalse(string.startsWith(ByteString(1, 2, 3, 4, 5, 6))) + } + + @Test + fun endsWithByteStringSample() { + val string = ByteString(1, 2, 3, 4, 5) + + assertTrue(string.endsWith(string)) + assertTrue(string.endsWith(ByteString(/* empty byte string */))) + assertTrue(string.endsWith(ByteString(3, 4, 5))) + assertFalse(string.endsWith(ByteString(2, 4, 5))) + assertFalse(string.endsWith(ByteString(0, 1, 2, 3, 4, 5))) + } + + @Test + fun startsWithByteArraySample() { + val string = ByteString(1, 2, 3, 4, 5) + + assertTrue(string.startsWith(byteArrayOf(1, 2, 3, 4, 5))) + assertTrue(string.startsWith(byteArrayOf(/* empty byte array */))) + assertTrue(string.startsWith(byteArrayOf(1, 2, 3))) + assertFalse(string.startsWith(byteArrayOf(1, 3, 4))) + assertFalse(string.startsWith(byteArrayOf(1, 2, 3, 4, 5, 6))) + } + + @Test + fun endsWithByteArraySample() { + val string = ByteString(1, 2, 3, 4, 5) + + assertTrue(string.endsWith(byteArrayOf(1, 2, 3, 4, 5))) + assertTrue(string.endsWith(byteArrayOf(/* empty byte array */))) + assertTrue(string.endsWith(byteArrayOf(3, 4, 5))) + assertFalse(string.endsWith(byteArrayOf(2, 4, 5))) + assertFalse(string.endsWith(byteArrayOf(0, 1, 2, 3, 4, 5))) + } + + @Test + fun constructionSample() { + val array = byteArrayOf(1, 2, 3) + val byteStringFromArray = ByteString(array) + array[1] = -1 + // The modification of the source array won't affect the content of the string. + assertContentEquals(byteArrayOf(1, 2, 3), byteStringFromArray.toByteArray()) + + val largeArray = byteArrayOf(1, 2, 3, 4 /*, ... */) + val byteStringFromSubarray = ByteString(largeArray, startIndex = 1, endIndex = 3) + assertContentEquals(byteArrayOf(2, 3), byteStringFromSubarray.toByteArray()) + } + + @Test + fun constructionFromBytesSample() { + val emptyByteString = ByteString() + assertTrue(emptyByteString.isEmpty()) + assertEquals(0, emptyByteString.size) + + val byteStringFromBytes = ByteString(1, 2, 3) + assertFalse(byteStringFromBytes.isEmpty()) + assertEquals(3, byteStringFromBytes.size) + } + + @Test + fun encodeAndDecodeUtf8String() { + val helloAsByteString = "hello".encodeToByteString() + assertEquals( + ByteString( + 'h'.code.toByte(), + 'e'.code.toByte(), + 'l'.code.toByte(), + 'l'.code.toByte(), + 'o'.code.toByte() + ), helloAsByteString + ) + assertEquals("hello", helloAsByteString.decodeToString()) + } + + @Test + fun builderSample() { + val byteString = buildByteString { + append("hello".encodeToByteArray()) + append(' '.code.toByte()) + append("world".encodeToByteArray()) + } + + assertEquals("hello world".encodeToByteString(), byteString) + } + + @Test + fun builderSampleWithoutAdditionalAllocs() { + val array = byteArrayOf(1, 2, 3, 4, 5, 6, 7) + + val byteString = buildByteString(4) { + append(array, startIndex = 2, endIndex = 6) + + // When the capacity (4 in this case) matches the number of bytes appended, + // then a ByteString will wrap builder's backing array without copying it. + assertEquals(capacity, size) + } + + assertEquals(ByteString(3, 4, 5, 6), byteString) + } +} \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 02cb605bb..850447788 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -80,5 +80,13 @@ fun KotlinSourceSet.configureSourceSet() { tasks.withType().configureEach { dokkaSourceSets.configureEach { includes.from("Module.md") + + samples.from( + "common/test/samples/rawSinkSample.kt", + "common/test/samples/rawSourceSample.kt", + "common/test/samples/moduleDescriptionSample.kt", + "common/test/samples/samples.kt", + "common/test/samples/byteStringSample.kt" + ) } } \ No newline at end of file diff --git a/core/common/src/Buffer.kt b/core/common/src/Buffer.kt index 12724a16c..6212436ab 100644 --- a/core/common/src/Buffer.kt +++ b/core/common/src/Buffer.kt @@ -213,6 +213,8 @@ public class Buffer : Source, Sink { * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of this buffer bounds * (`[0..buffer.size)`). * @throws IllegalArgumentException when `startIndex > endIndex`. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.bufferCopy */ public fun copyTo( out: Buffer, @@ -277,6 +279,8 @@ public class Buffer : Source, Sink { * for sequential access to a range of bytes within the buffer. * * @throws IndexOutOfBoundsException when [position] is negative or greater or equal to [Buffer.size]. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.bufferGetByte */ public operator fun get(position: Long): Byte { if (position < 0 || position >= size) { @@ -291,6 +295,8 @@ public class Buffer : Source, Sink { * Discards all bytes in this buffer. * * Call to this method is equivalent to [skip] with `byteCount = size`. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.bufferClear */ public fun clear(): Unit = skip(size) @@ -320,11 +326,11 @@ 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) - s.data.copyInto( - destination = sink, destinationOffset = startIndex, startIndex = s.pos, endIndex = s.pos + toCopy - ) + 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 + ) s.pos += toCopy size -= toCopy.toLong() @@ -603,6 +609,8 @@ public class Buffer : Source, Sink { * few bytes, this is a string like `Buffer(size=4 hex=0000ffff)`. However, if the buffer is too large, * a string will contain its size and only a prefix of data, like `Buffer(size=1024 hex=01234…)`. * Thus, the string could not be used to compare buffers or verify buffer's content. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.bufferToString */ override fun toString(): String { if (size == 0L) return "Buffer(size=0)" diff --git a/core/common/src/ByteStringExt.kt b/core/common/src/ByteStringExt.kt index b5f0f6034..9634d5ea1 100644 --- a/core/common/src/ByteStringExt.kt +++ b/core/common/src/ByteStringExt.kt @@ -21,6 +21,8 @@ import kotlin.math.min * @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. + * + * @sample kotlinx.io.samples.ByteStringSamples.writeByteString */ @OptIn(DelicateIoApi::class) public fun Sink.write(byteString: ByteString, startIndex: Int = 0, endIndex: Int = byteString.size) { @@ -54,6 +56,8 @@ public fun Sink.write(byteString: ByteString, startIndex: Int = 0, endIndex: Int * Consumes all bytes from this source and wraps it into a byte string. * * @throws IllegalStateException if the source is closed. + * + * @sample kotlinx.io.samples.ByteStringSamples.readByteString */ @OptIn(UnsafeByteStringApi::class) public fun Source.readByteString(): ByteString { @@ -68,6 +72,8 @@ public fun Source.readByteString(): ByteString { * @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. + * + * @sample kotlinx.io.samples.ByteStringSamples.readByteString */ @OptIn(UnsafeByteStringApi::class) public fun Source.readByteString(byteCount: Int): ByteString { @@ -84,6 +90,8 @@ public fun Source.readByteString(byteCount: Int): ByteString { * * @throws IllegalArgumentException if [startIndex] is negative. * @throws IllegalStateException if the source is closed. + * + * @sample kotlinx.io.samples.ByteStringSamples.indexOfByteString */ @OptIn(InternalIoApi::class, UnsafeByteStringApi::class) public fun Source.indexOf(byteString: ByteString, startIndex: Long = 0): Long { diff --git a/core/common/src/RawSink.kt b/core/common/src/RawSink.kt index 04a0f41b4..80b88a212 100644 --- a/core/common/src/RawSink.kt +++ b/core/common/src/RawSink.kt @@ -31,6 +31,9 @@ package kotlinx.io * is both more efficient and more convenient. Use [buffered] to wrap any raw sink with a buffer. * * Implementors should abstain from throwing exceptions other than those that are documented for RawSink methods. + * + * @sample kotlinx.io.samples.CRC32Sink + * @sample kotlinx.io.samples.Crc32Sample.crc32 */ @OptIn(ExperimentalStdlibApi::class) public expect interface RawSink : AutoCloseableAlias { diff --git a/core/common/src/RawSource.kt b/core/common/src/RawSource.kt index 440cbcb63..89a86ad6d 100644 --- a/core/common/src/RawSource.kt +++ b/core/common/src/RawSource.kt @@ -31,6 +31,9 @@ package kotlinx.io * is both more efficient and more convenient. Use [buffered] to wrap any raw source with a buffer. * * Implementors should abstain from throwing exceptions other than those that are documented for RawSource methods. + * + * @sample kotlinx.io.samples.RC4DecryptingSource + * @sample kotlinx.io.samples.RC4SourceSample.rc4 */ @OptIn(ExperimentalStdlibApi::class) public interface RawSource : AutoCloseableAlias { @@ -43,6 +46,8 @@ public interface RawSource : AutoCloseableAlias { * * @throws IllegalArgumentException when [byteCount] is negative. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readAtMostToSink */ public fun readAtMostTo(sink: Buffer, byteCount: Long): Long diff --git a/core/common/src/Sink.kt b/core/common/src/Sink.kt index 9f1c18924..4d887de1d 100644 --- a/core/common/src/Sink.kt +++ b/core/common/src/Sink.kt @@ -76,6 +76,8 @@ public sealed interface Sink : RawSink { * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] array indices. * @throws IllegalArgumentException when `startIndex > endIndex`. * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeByteArrayToSink */ public fun write(source: ByteArray, startIndex: Int = 0, endIndex: Int = source.size) @@ -87,6 +89,7 @@ public sealed interface Sink : RawSink { * * @throws IllegalStateException when the sink or [source] is closed. * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.transferFrom */ public fun transferFrom(source: RawSource): Long @@ -101,6 +104,8 @@ public sealed interface Sink : RawSink { * * @throws IllegalArgumentException when [byteCount] is negative. * @throws IllegalStateException when the sink or [source] is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeSourceToSink */ public fun write(source: RawSource, byteCount: Long) @@ -110,6 +115,8 @@ public sealed interface Sink : RawSink { * @param byte the byte to be written. * * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeByte */ public fun writeByte(byte: Byte) @@ -119,6 +126,8 @@ public sealed interface Sink : RawSink { * @param short the short integer to be written. * * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeShort */ public fun writeShort(short: Short) @@ -128,6 +137,8 @@ public sealed interface Sink : RawSink { * @param int the integer to be written. * * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeInt */ public fun writeInt(int: Int) @@ -137,6 +148,8 @@ public sealed interface Sink : RawSink { * @param long the long integer to be written. * * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeLong */ public fun writeLong(long: Long) @@ -145,6 +158,8 @@ public sealed interface Sink : RawSink { * Then the underlying sink is explicitly flushed. * * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.flush */ override fun flush() @@ -156,6 +171,8 @@ public sealed interface Sink : RawSink { * Call this method before a buffered sink goes out of scope so that its data can reach its destination. * * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.emit */ public fun emit() diff --git a/core/common/src/SinkExt.kt b/core/common/src/SinkExt.kt index f0d5385c9..de9387752 100644 --- a/core/common/src/SinkExt.kt +++ b/core/common/src/SinkExt.kt @@ -13,6 +13,8 @@ internal val HEX_DIGIT_BYTES = "0123456789abcdef".asUtf8ToByteArray() * @param short the short integer to be written. * * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeShortLe */ public fun Sink.writeShortLe(short: Short) { this.writeShort(short.reverseBytes()) @@ -24,6 +26,8 @@ public fun Sink.writeShortLe(short: Short) { * @param int the integer to be written. * * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeIntLe */ public fun Sink.writeIntLe(int: Int) { this.writeInt(int.reverseBytes()) @@ -35,6 +39,8 @@ public fun Sink.writeIntLe(int: Int) { * @param long the long integer to be written. * * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeLongLe */ public fun Sink.writeLongLe(long: Long) { this.writeLong(long.reverseBytes()) @@ -48,6 +54,8 @@ public fun Sink.writeLongLe(long: Long) { * @param long the long to be written. * * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeDecimalLong */ @OptIn(DelicateIoApi::class) public fun Sink.writeDecimalLong(long: Long) { @@ -127,6 +135,8 @@ public fun Sink.writeDecimalLong(long: Long) { * @param long the long to be written. * * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeHexLong */ @OptIn(DelicateIoApi::class) public fun Sink.writeHexadecimalUnsignedLong(long: Long) { @@ -180,6 +190,8 @@ public fun Sink.writeHexadecimalUnsignedLong(long: Long) { * @param byte the byte to be written. * * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeUByte */ public fun Sink.writeUByte(byte: UByte) { writeByte(byte.toByte()) @@ -191,6 +203,8 @@ public fun Sink.writeUByte(byte: UByte) { * @param short the unsigned short integer to be written. * * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeUShort */ public fun Sink.writeUShort(short: UShort) { writeShort(short.toShort()) @@ -202,6 +216,8 @@ public fun Sink.writeUShort(short: UShort) { * @param int the unsigned integer to be written. * * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeUInt */ public fun Sink.writeUInt(int: UInt) { writeInt(int.toInt()) @@ -213,6 +229,8 @@ public fun Sink.writeUInt(int: UInt) { * @param long the unsigned long integer to be written. * * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeULong */ public fun Sink.writeULong(long: ULong) { writeLong(long.toLong()) @@ -224,6 +242,8 @@ public fun Sink.writeULong(long: ULong) { * @param short the unsigned short integer to be written. * * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeUShortLe */ public fun Sink.writeUShortLe(short: UShort) { writeShortLe(short.toShort()) @@ -235,6 +255,8 @@ public fun Sink.writeUShortLe(short: UShort) { * @param int the unsigned integer to be written. * * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeUIntLe */ public fun Sink.writeUIntLe(int: UInt) { writeIntLe(int.toInt()) @@ -246,6 +268,8 @@ public fun Sink.writeUIntLe(int: UInt) { * @param long the unsigned long integer to be written. * * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeULongLe */ public fun Sink.writeULongLe(long: ULong) { writeLongLe(long.toLong()) diff --git a/core/common/src/Source.kt b/core/common/src/Source.kt index d33b7b799..73a764eab 100644 --- a/core/common/src/Source.kt +++ b/core/common/src/Source.kt @@ -77,6 +77,8 @@ public sealed interface Source : RawSource { * The call of this method will block until there are bytes to read or the source is definitely exhausted. * * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.exhausted */ public fun exhausted(): Boolean @@ -92,6 +94,8 @@ public sealed interface Source : RawSource { * @throws EOFException when the source is exhausted before the required bytes count could be read. * @throws IllegalStateException when the source is closed. * @throws IllegalArgumentException when [byteCount] is negative. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.require */ public fun require(byteCount: Long) @@ -106,6 +110,8 @@ public sealed interface Source : RawSource { * * @throws IllegalArgumentException when [byteCount] is negative. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.request */ public fun request(byteCount: Long): Boolean @@ -114,6 +120,8 @@ public sealed interface Source : RawSource { * * @throws EOFException when there are no more bytes to read. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readByte */ public fun readByte(): Byte @@ -122,6 +130,8 @@ public sealed interface Source : RawSource { * * @throws EOFException when there are not enough data to read a short value. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readShort */ public fun readShort(): Short @@ -130,6 +140,8 @@ public sealed interface Source : RawSource { * * @throws EOFException when there are not enough data to read an int value. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readInt */ public fun readInt(): Int @@ -138,6 +150,8 @@ public sealed interface Source : RawSource { * * @throws EOFException when there are not enough data to read a long value. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readLong */ public fun readLong(): Long @@ -149,6 +163,8 @@ public sealed interface Source : RawSource { * @throws EOFException when the source is exhausted before the requested number of bytes can be skipped. * @throws IllegalArgumentException when [byteCount] is negative. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.skip */ public fun skip(byteCount: Long) @@ -163,6 +179,8 @@ public sealed interface Source : RawSource { * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [sink] array indices. * @throws IllegalArgumentException when `startIndex > endIndex`. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readAtMostToByteArray */ public fun readAtMostTo(sink: ByteArray, startIndex: Int = 0, endIndex: Int = sink.size): Int @@ -175,6 +193,8 @@ public sealed interface Source : RawSource { * @throws IllegalArgumentException when [byteCount] is negative. * @throws EOFException when the requested number of bytes cannot be read. * @throws IllegalStateException when the source or [sink] is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readSourceToSink */ public fun readTo(sink: RawSink, byteCount: Long) @@ -187,6 +207,8 @@ public sealed interface Source : RawSource { * @param sink the sink to which data will be written from this source. * * @throws IllegalStateException when the source or [sink] is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.transferTo */ public fun transferTo(sink: RawSink): Long @@ -197,6 +219,8 @@ public sealed interface Source : RawSource { * Peek could be used to lookahead and read the same data multiple times. * * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.peekSample */ public fun peek(): Source } diff --git a/core/common/src/SourceExt.kt b/core/common/src/SourceExt.kt index 811ce44c9..76b5df53a 100644 --- a/core/common/src/SourceExt.kt +++ b/core/common/src/SourceExt.kt @@ -10,6 +10,8 @@ package kotlinx.io * * @throws EOFException when there are not enough data to read a short value. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readShortLe */ public fun Source.readShortLe(): Short { return readShort().reverseBytes() @@ -20,6 +22,8 @@ public fun Source.readShortLe(): Short { * * @throws EOFException when there are not enough data to read an int value. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readIntLe */ public fun Source.readIntLe(): Int { return readInt().reverseBytes() @@ -30,6 +34,8 @@ public fun Source.readIntLe(): Int { * * @throws EOFException when there are not enough data to read a long value. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readLongLe */ public fun Source.readLongLe(): Long { return readLong().reverseBytes() @@ -49,6 +55,8 @@ internal const val OVERFLOW_DIGIT_START = Long.MIN_VALUE % 10L + 1 * number was not present. * @throws EOFException if the source is exhausted before a call of this method. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readDecimalLong */ @OptIn(InternalIoApi::class) public fun Source.readDecimalLong(): Long { @@ -118,6 +126,8 @@ public fun Source.readDecimalLong(): Long { * hexadecimal was not found. * @throws EOFException if the source is exhausted before a call of this method. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readHexLong */ @OptIn(InternalIoApi::class) public fun Source.readHexadecimalUnsignedLong(): Long { @@ -168,6 +178,8 @@ public fun Source.readHexadecimalUnsignedLong(): Long { * * @throws IllegalStateException when the source is closed. * @throws IllegalArgumentException when `startIndex > endIndex` or either of indices is negative. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.indexOfByteSample */ public fun Source.indexOf(byte: Byte, startIndex: Long = 0L, endIndex: Long = Long.MAX_VALUE): Long { require(startIndex in 0..endIndex) { @@ -197,6 +209,8 @@ public fun Source.indexOf(byte: Byte, startIndex: Long = 0L, endIndex: Long = Lo * Removes all bytes from this source and returns them as a byte array. * * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readToArraySample */ public fun Source.readByteArray(): ByteArray { return readByteArrayImpl(-1) @@ -210,6 +224,8 @@ public fun Source.readByteArray(): ByteArray { * @throws IllegalArgumentException when [byteCount] is negative. * @throws EOFException when the underlying source is exhausted before [byteCount] bytes of data could be read. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readToArraySample */ public fun Source.readByteArray(byteCount: Int): ByteArray { checkByteCount(byteCount.toLong()) @@ -247,6 +263,8 @@ private fun Source.readByteArrayImpl(size: Int): ByteArray { * @throws IllegalStateException when the source is closed. * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [sink] array indices. * @throws IllegalArgumentException when `startIndex > endIndex`. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readToArraySample */ public fun Source.readTo(sink: ByteArray, startIndex: Int = 0, endIndex: Int = sink.size) { checkBounds(sink.size, startIndex, endIndex) @@ -268,6 +286,8 @@ public fun Source.readTo(sink: ByteArray, startIndex: Int = 0, endIndex: Int = s * * @throws EOFException when there are no more bytes to read. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readUByte */ public fun Source.readUByte(): UByte = readByte().toUByte() @@ -277,6 +297,8 @@ public fun Source.readUByte(): UByte = readByte().toUByte() * * @throws EOFException when there are not enough data to read an unsigned short value. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readUShort */ public fun Source.readUShort(): UShort = readShort().toUShort() @@ -286,6 +308,8 @@ public fun Source.readUShort(): UShort = readShort().toUShort() * * @throws EOFException when there are not enough data to read an unsigned int value. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readUInt */ public fun Source.readUInt(): UInt = readInt().toUInt() @@ -295,6 +319,8 @@ public fun Source.readUInt(): UInt = readInt().toUInt() * * @throws EOFException when there are not enough data to read an unsigned long value. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readULong */ public fun Source.readULong(): ULong = readLong().toULong() @@ -304,6 +330,8 @@ public fun Source.readULong(): ULong = readLong().toULong() * * @throws EOFException when there are not enough data to read an unsigned short value. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readUShortLe */ public fun Source.readUShortLe(): UShort = readShortLe().toUShort() @@ -313,6 +341,8 @@ public fun Source.readUShortLe(): UShort = readShortLe().toUShort() * * @throws EOFException when there are not enough data to read an unsigned int value. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readUIntLe */ public fun Source.readUIntLe(): UInt = readIntLe().toUInt() @@ -322,6 +352,8 @@ public fun Source.readUIntLe(): UInt = readIntLe().toUInt() * * @throws EOFException when there are not enough data to read an unsigned long value. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readULongLe */ public fun Source.readULongLe(): ULong = readLongLe().toULong() @@ -332,6 +364,8 @@ public fun Source.readULongLe(): ULong = readLongLe().toULong() * If there is no buffered data, this call will result in a fetch from the underlying source. * * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.startsWithSample */ @OptIn(InternalIoApi::class) public fun Source.startsWith(byte: Byte): Boolean = request(1) && buffer[0] == byte diff --git a/core/common/src/Utf8.kt b/core/common/src/Utf8.kt index 98f33e019..9bed3bd88 100644 --- a/core/common/src/Utf8.kt +++ b/core/common/src/Utf8.kt @@ -79,6 +79,8 @@ import kotlinx.io.internal.* * * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of string indices. * @throws IllegalArgumentException when `startIndex > endIndex`. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.utf8SizeSample */ internal fun String.utf8Size(startIndex: Int = 0, endIndex: Int = length): Long { checkBounds(length, startIndex, endIndex) @@ -123,6 +125,8 @@ internal fun String.utf8Size(startIndex: Int = 0, endIndex: Int = length): Long * @param codePoint the codePoint to be written. * * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.utf8CodePointSample */ @OptIn(DelicateIoApi::class) internal fun Sink.writeUtf8CodePoint(codePoint: Int): Unit = @@ -138,6 +142,8 @@ internal fun Sink.writeUtf8CodePoint(codePoint: Int): Unit = * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [string] indices. * @throws IllegalArgumentException when `startIndex > endIndex`. * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeUtf8Sample */ @OptIn(DelicateIoApi::class) public fun Sink.writeString(string: String, startIndex: Int = 0, endIndex: Int = string.length): Unit = @@ -149,6 +155,8 @@ public fun Sink.writeString(string: String, startIndex: Int = 0, endIndex: Int = * Returns the empty string if this source is empty. * * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readUtf8 */ @OptIn(InternalIoApi::class) public fun Source.readString(): String { @@ -163,6 +171,8 @@ public fun Source.readString(): String { * Removes all bytes from this buffer, decodes them as UTF-8, and returns the string. * * Returns the empty string if this buffer is empty. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readUtf8 */ public fun Buffer.readString(): String { return commonReadUtf8(size) @@ -176,6 +186,8 @@ public fun Buffer.readString(): String { * @throws IllegalArgumentException when [byteCount] is negative. * @throws EOFException when the source is exhausted before reading [byteCount] bytes from it. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readUtf8 */ @OptIn(InternalIoApi::class) public fun Source.readString(byteCount: Long): String { @@ -197,6 +209,8 @@ public fun Source.readString(byteCount: Long): String { * * @throws EOFException when the source is exhausted before a complete code point can be read. * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readUtf8CodePointSample */ @OptIn(InternalIoApi::class) internal fun Source.readUtf8CodePoint(): Int { @@ -227,6 +241,8 @@ internal fun Buffer.readUtf8CodePoint(): Int { * an implicit line break is assumed. Null is returned once the source is exhausted. * * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readLinesSample */ public fun Source.readLine(): String? { if (!request(1)) return null @@ -269,6 +285,8 @@ public fun Source.readLine(): String? { * line break characters. * @throws IllegalStateException when the source is closed. * @throws IllegalArgumentException when [limit] is negative. + * + * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readLinesSample */ public fun Source.readLineStrict(limit: Long = Long.MAX_VALUE): String { require(limit >= 0) { "limit ($limit) < 0" } diff --git a/core/common/test/samples/byteStringSample.kt b/core/common/test/samples/byteStringSample.kt new file mode 100644 index 000000000..f366ea38c --- /dev/null +++ b/core/common/test/samples/byteStringSample.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. + */ + +package kotlinx.io.samples + +import kotlinx.io.* +import kotlinx.io.bytestring.ByteString +import kotlinx.io.bytestring.encodeToByteString +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class ByteStringSamples { + @Test + fun writeByteString() { + val buffer = Buffer() + + buffer.write(ByteString(1, 2, 3, 4)) + assertEquals(4, buffer.size) + } + + @Test + fun readByteString() { + val buffer = Buffer().also { it.write(byteArrayOf(1, 2, 3, 4, 5)) } + + assertEquals(ByteString(1, 2), buffer.readByteString(2)) // reads only two bytes + assertEquals(ByteString(3, 4, 5), buffer.readByteString()) // reads until exhaustion + assertTrue(buffer.exhausted()) + } + + @Test + fun indexOfByteString() { + val buffer = Buffer() + + assertEquals(-1, buffer.indexOf(ByteString(1, 2, 3, 4))) + assertEquals(0, buffer.indexOf(ByteString(/* empty */))) + + buffer.writeUtf8("Content-Type: text/plain\nContent-Length: 12\n\nhello world!") + + assertEquals(43, buffer.indexOf("\n\n".encodeToByteString())) + assertEquals(-1, buffer.indexOf("application/json".encodeToByteString())) + } +} \ No newline at end of file diff --git a/core/common/test/samples/moduleDescriptionSample.kt b/core/common/test/samples/moduleDescriptionSample.kt new file mode 100644 index 000000000..adc06608c --- /dev/null +++ b/core/common/test/samples/moduleDescriptionSample.kt @@ -0,0 +1,73 @@ +/* + * 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.samples + +import kotlinx.io.* +import kotlin.test.Test +import kotlin.test.assertEquals + +data class Message(val timestamp: Long, val text: String) { + companion object +} + +fun Message.toBson(sink: Sink) { + val buffer = Buffer() + with (buffer) { + writeByte(0x9) // UTC-timestamp field + writeUtf8("timestamp") // field name + writeByte(0) + writeLongLe(timestamp) // field value + writeByte(0x2) // string field + writeUtf8("text") // field name + writeByte(0) + writeIntLe(text.utf8Size().toInt() + 1) // field value: length followed by the string + writeUtf8(text) + writeByte(0) + writeByte(0) // end of BSON document + } + + // Write document length and then its body + sink.writeIntLe(buffer.size.toInt() + 4) + buffer.transferTo(sink) + sink.flush() +} + +fun Message.Companion.fromBson(source: Source): Message { + source.require(4) // check if the source contains length + val length = source.readIntLe() - 4L + source.require(length) // check if the source contains the whole message + + fun readFieldName(source: Source): String { + val delimiterOffset = source.indexOf(0) // find offset of the 0-byte terminating the name + check(delimiterOffset >= 0) // indexOf return -1 if value not found + val fieldName = source.readUtf8(delimiterOffset) // read the string until terminator + source.skip(1) // skip the terminator + return fieldName + } + + // for simplicity, let's assume that the order of fields matches serialization order + var tag = source.readByte().toInt() // read the field type + check(tag == 0x9 && readFieldName(source) == "timestamp") + val timestamp = source.readLongLe() // read long value + tag = source.readByte().toInt() + check(tag == 0x2 && readFieldName(source) == "text") + val textLen = source.readIntLe() - 1L // read string length (it includes the terminator) + val text = source.readUtf8(textLen) // read value + source.skip(1) // skip terminator + source.skip(1) // skip end of the document + return Message(timestamp, text) +} + +class ModuleDescriptionSampleTest { + @Test + fun sample() { + val message = Message(1687531969000L, "Time is now") + val buffer = Buffer() + message.toBson(buffer) + + assertEquals(message, Message.fromBson(buffer)) + } +} \ No newline at end of file diff --git a/core/common/test/samples/rawSinkSample.kt b/core/common/test/samples/rawSinkSample.kt new file mode 100644 index 000000000..883f10c91 --- /dev/null +++ b/core/common/test/samples/rawSinkSample.kt @@ -0,0 +1,76 @@ +/* + * 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.samples + +import kotlinx.io.* +import kotlin.test.Test +import kotlin.test.assertEquals + +@OptIn(ExperimentalUnsignedTypes::class) +private fun generateCrc32Table(): UIntArray { + val table = UIntArray(256) + + for (idx in table.indices) { + table[idx] = idx.toUInt() + for (bit in 8 downTo 1) { + table[idx] = if (table[idx] % 2U == 0U) { + table[idx].shr(1) + } else { + table[idx].shr(1).xor(0xEDB88320U) + } + } + } + + return table +} + +/** + * Sink calculating CRC-32 code for all the data written to it and sending this data to the upstream afterward. + * The CRC-32 value could be obtained using [crc32] method. + * + * See https://en.wikipedia.org/wiki/Cyclic_redundancy_check for more information about CRC-32. + */ +@OptIn(ExperimentalUnsignedTypes::class) +class CRC32Sink(private val upstream: RawSink): RawSink { + private val tempBuffer = Buffer() + private val crc32Table = generateCrc32Table() + private var crc32: UInt = 0xffffffffU + + private fun update(value: Byte) { + val index = value.xor(crc32.toByte()).toUByte() + crc32 = crc32Table[index.toInt()].xor(crc32.shr(8)) + } + + fun crc32(): UInt = crc32.xor(0xffffffffU) + + override fun write(source: Buffer, byteCount: Long) { + source.copyTo(tempBuffer, 0, byteCount) + + while (!tempBuffer.exhausted()) { + update(tempBuffer.readByte()) + } + + upstream.write(source, byteCount) + } + + override fun flush() = upstream.flush() + + override fun close() = upstream.close() +} + +class Crc32Sample { + @OptIn(ExperimentalStdlibApi::class) + @Test + fun crc32() { + val crc32Sink = CRC32Sink(discardingSink()) + + crc32Sink.buffered().use { + it.writeUtf8("hello crc32") + } + + assertEquals(0x9896d398U, crc32Sink.crc32()) + } +} \ No newline at end of file diff --git a/core/common/test/samples/rawSourceSample.kt b/core/common/test/samples/rawSourceSample.kt new file mode 100644 index 000000000..807c18fc3 --- /dev/null +++ b/core/common/test/samples/rawSourceSample.kt @@ -0,0 +1,84 @@ +/* + * 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.samples + +import kotlinx.io.* +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * Source decrypting all the data read from the downstream using RC4 algorithm. + * + * See https://en.wikipedia.org/wiki/RC4 for more information about the cypher. + * + * Implementation of RC4 stream cypher based on http://cypherpunks.venona.com/archive/1994/09/msg00304.html + */ +@OptIn(ExperimentalUnsignedTypes::class) +class RC4DecryptingSource(private val downstream: RawSource, key: String): RawSource { + private val buffer = Buffer() + private val key = RC4Key(key) + + override fun readAtMostTo(sink: Buffer, byteCount: Long): Long { + val bytesRead = downstream.readAtMostTo(buffer, byteCount) + if (bytesRead == -1L) { + return -1L + } + + while (!buffer.exhausted()) { + val byte = buffer.readByte() + sink.writeByte(byte.xor(key.nextByte())) + } + + return bytesRead + } + + override fun close() = downstream.close() + + private class RC4Key(key: String) { + private var keyState: UByteArray + private var keyX: Int = 0 + private var keyY: Int = 0 + + init { + require(key.isNotEmpty()) { "Key could not be empty" } + val keyBytes = key.encodeToByteArray() + keyState = UByteArray(256) { it.toUByte() } + var index1 = 0 + var index2 = 0 + + for (idx in keyState.indices) { + index2 = (keyBytes[index1] + keyState[idx].toInt() + index2) % 256 + swapStateBytes(idx, index2) + index1 = (index1 + 1) % keyBytes.size + } + } + + fun nextByte(): Byte { + keyX = (keyX + 1) % 256 + keyY = (keyState[keyX].toInt() + keyY) % 256 + swapStateBytes(keyX, keyY) + val idx = (keyState[keyX] + keyState[keyY]) % 256U + return keyState[idx.toInt()].toByte() + } + + private fun swapStateBytes(x: Int, y: Int) { + val tmp = keyState[x] + keyState[x] = keyState[y] + keyState[y] = tmp + } + } +} + +class RC4SourceSample { + @Test + fun rc4() { + val key = "key" + val source = Buffer().also { it.write(byteArrayOf(0x58, 0x09, 0x57, 0x9fU.toByte(), 0x41, 0xfbU.toByte())) } + val rc4Source = RC4DecryptingSource(source, key).buffered() + + assertEquals("Secret", rc4Source.readUtf8()) + } +} \ No newline at end of file diff --git a/core/common/test/samples/samples.kt b/core/common/test/samples/samples.kt new file mode 100644 index 000000000..2efebc0dd --- /dev/null +++ b/core/common/test/samples/samples.kt @@ -0,0 +1,672 @@ +/* + * 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.samples + +import kotlinx.io.* +import kotlin.test.* + +class KotlinxIoCoreCommonSamples { + @Test + fun bufferToString() { + val buffer = Buffer() + assertEquals("Buffer(size=0)", buffer.toString()) + + buffer.writeInt(0x12345678) + assertEquals("Buffer(size=4 hex=12345678)", buffer.toString()) + + buffer.skip(1) + assertEquals("Buffer(size=3 hex=345678)", buffer.toString()) + } + + @Test + fun bufferClear() { + val buffer = Buffer() + buffer.write(ByteArray(1024)) + buffer.clear() + assertTrue(buffer.exhausted()) + } + + @Test + fun bufferGetByte() { + val buffer = Buffer() + buffer.writeUtf8("Hello World!") + + assertEquals('H'.code, buffer[0].toInt()) + assertEquals('W'.code, buffer[buffer.indexOf('W'.code.toByte())].toInt()) + } + + @Test + fun bufferCopy() { + val buffer = Buffer() + buffer.writeUtf8("some string") + + val copy = Buffer() + copy.writeUtf8("sub") + buffer.copyTo(copy, startIndex = 5) + + assertEquals("some string", buffer.readUtf8()) + assertEquals("substring", copy.readUtf8()) + } + + @Test + fun transferFrom() { + val src: Source = Buffer().also { it.writeUtf8("Some data to transfer") } + val dst = Buffer().also { it.writeUtf8("Transferred: ") } + + dst.transferFrom(src) + + assertTrue(src.exhausted()) + assertEquals("Transferred: Some data to transfer", dst.readUtf8()) + } + + @Test + fun transferTo() { + val src: Source = Buffer().also { it.writeUtf8("Some data to transfer") } + val dst = Buffer().also { it.writeUtf8("Transferred: ") } + + src.transferTo(dst) + + assertTrue(src.exhausted()) + assertEquals("Transferred: Some data to transfer", dst.readUtf8()) + } + + @Test + fun peekSample() { + val source: Source = Buffer().also { it.writeUtf8("hello world") } + + val peek = source.peek().buffered() + assertEquals("hello", peek.readUtf8(5)) + peek.skip(1) + assertEquals("world", peek.readUtf8(5)) + assertTrue(peek.exhausted()) + + assertEquals("hello world", source.readUtf8()) + } + + @Test + fun utf8SizeSample() { + assertEquals("yes".length, "yes".utf8Size().toInt()) + + assertNotEquals("არა".length, "არა".utf8Size().toInt()) + assertEquals(9, "არა".utf8Size().toInt()) + assertEquals("არა".encodeToByteArray().size, "არა".utf8Size().toInt()) + } + + @Test + fun writeUtf8CodePointSample() { + val buffer = Buffer() + + buffer.writeInt('Δ'.code) // writes integer value as is + assertContentEquals(byteArrayOf(0, 0, 0x3, 0x94.toByte()), buffer.readByteArray()) + + buffer.writeUtf8CodePoint('Δ'.code) // encodes code point using UTF-8 encoding + assertContentEquals(byteArrayOf(0xce.toByte(), 0x94.toByte()), buffer.readByteArray()) + } + + @Test + fun readUtf8CodePointSample() { + val buffer = Buffer() + + buffer.writeUShort(0xce94U) + assertEquals(0x394, buffer.readUtf8CodePoint()) // decodes single UTF-8 encoded code point + } + + @Test + fun readLinesSample() { + val buffer = Buffer() + buffer.writeUtf8("No new line here.") + + assertFailsWith { buffer.readUtf8LineStrict() } + assertEquals("No new line here.", buffer.readUtf8Line()) + + buffer.writeUtf8("Line1\n\nLine2") + assertEquals("Line1", buffer.readUtf8LineStrict()) + assertEquals("\nLine2", buffer.readUtf8()) + } + + @Test + fun readShortLe() { + val buffer = Buffer() + buffer.writeShort(0x1234) + assertEquals(0x3412, buffer.readShortLe()) + } + + @Test + fun readIntLe() { + val buffer = Buffer() + buffer.writeInt(0x12345678) + assertEquals(0x78563412, buffer.readIntLe()) + } + + @Test + fun readLongLe() { + val buffer = Buffer() + buffer.writeLong(0x123456789ABCDEF0) + assertEquals(0xF0DEBC9A78563412U.toLong(), buffer.readLongLe()) + } + + @Test + fun writeShortLe() { + val buffer = Buffer() + buffer.writeShortLe(0x1234) + assertEquals(0x3412, buffer.readShort()) + } + + @Test + fun writeIntLe() { + val buffer = Buffer() + buffer.writeIntLe(0x12345678) + assertEquals(0x78563412, buffer.readInt()) + } + + @Test + fun writeLongLe() { + val buffer = Buffer() + buffer.writeLongLe(0x123456789ABCDEF0) + assertEquals(0xF0DEBC9A78563412U.toLong(), buffer.readLong()) + } + + @Test + fun readDecimalLong() { + val buffer = Buffer() + buffer.writeUtf8("42 -1 1234567!") + + assertEquals(42L, buffer.readDecimalLong()) + buffer.skip(1) // skip space + assertEquals(-1L, buffer.readDecimalLong()) + buffer.skip(1) // skip space + assertEquals(1234567L, buffer.readDecimalLong()) + buffer.skip(1) // skip ! + } + + @Test + fun writeDecimalLong() { + val buffer = Buffer() + + buffer.writeDecimalLong(1024) + buffer.writeUtf8(", ") + buffer.writeDecimalLong(-24) + + assertEquals("1024, -24", buffer.readUtf8()) + } + + @Test + fun writeHexLong() { + val buffer = Buffer() + + buffer.writeHexadecimalUnsignedLong(10) + assertEquals("a", buffer.readUtf8()) + + buffer.writeHexadecimalUnsignedLong(-10) + assertEquals("fffffffffffffff6", buffer.readUtf8()) + } + + @Test + fun readHexLong() { + val buffer = Buffer() + + buffer.writeUtf8("0000a") + assertEquals(10L, buffer.readHexadecimalUnsignedLong()) + + buffer.writeUtf8("fffFffFffFfffff6") + assertEquals(-10L, buffer.readHexadecimalUnsignedLong()) + + buffer.writeUtf8("dear friend!") + assertEquals(0xdea, buffer.readHexadecimalUnsignedLong()) + assertEquals("r friend!", buffer.readUtf8()) + } + + @Test + fun startsWithSample() { + val buffer = Buffer() + buffer.write(byteArrayOf(1, 2, 3, 4, 5)) + + assertTrue(buffer.startsWith(1)) + assertFalse(buffer.startsWith(0)) + + buffer.clear() + assertFalse(buffer.startsWith(1)) + } + + @Test + fun indexOfByteSample() { + val buffer = Buffer() + + assertEquals(-1, buffer.indexOf('\n'.code.toByte())) + + buffer.writeUtf8("Hello\nThis is line 2\nAnd this one is third.") + assertEquals(5, buffer.indexOf('\n'.code.toByte())) + assertEquals(20, buffer.indexOf('\n'.code.toByte(), startIndex = 6)) + assertEquals(-1, buffer.indexOf('\n'.code.toByte(), startIndex = 21)) + } + + @Test + fun readToArraySample() { + val buffer = Buffer() + buffer.write(byteArrayOf(1, 2, 3, 4, 5, 6, 7)) + + val out = ByteArray(10) + buffer.readTo(out, startIndex = 1, endIndex = 4) + assertContentEquals(byteArrayOf(0, 1, 2, 3, 0, 0, 0, 0, 0, 0), out) + assertContentEquals(byteArrayOf(4, 5, 6, 7), buffer.readByteArray()) + } + + @Test + fun writeUtf8Sample() { + val buffer = Buffer() + + buffer.writeUtf8("hello", startIndex = 1, endIndex = 4) + assertContentEquals( + byteArrayOf( + 'e'.code.toByte(), + 'l'.code.toByte(), + 'l'.code.toByte() + ), buffer.readByteArray() + ) + + buffer.writeUtf8("Δ") + assertContentEquals(byteArrayOf(0xce.toByte(), 0x94.toByte()), buffer.readByteArray()) + } + + @Test + fun readUtf8() { + val buffer = Buffer() + + buffer.write("hello world".encodeToByteArray()) + assertEquals("hello", buffer.readUtf8(5)) + assertEquals(" world", buffer.readUtf8()) + + buffer.write(byteArrayOf(0xce.toByte(), 0x94.toByte())) + assertEquals("Δ", buffer.readUtf8()) + } + + @Test + fun readByteArraySample() { + val buffer = Buffer() + buffer.writeInt(0x12345678) + buffer.writeShort(0) + + assertContentEquals(byteArrayOf(0x12, 0x34), buffer.readByteArray(2)) + assertContentEquals(byteArrayOf(0x56, 0x78, 0, 0), buffer.readByteArray()) + } + + @Test + fun readByte() { + val buffer = Buffer() + buffer.write(byteArrayOf(1, 2, 3, 4)) + + assertEquals(1, buffer.readByte()) + assertEquals(3, buffer.size) + } + + @Test + fun readShort() { + val buffer = Buffer() + buffer.write(byteArrayOf(1, 2, 3, 4)) + + assertEquals(0x0102, buffer.readShort()) + assertEquals(2, buffer.size) + } + + @Test + fun readInt() { + val buffer = Buffer() + buffer.write(byteArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) + + assertEquals(0x01020304, buffer.readInt()) + assertEquals(6, buffer.size) + } + + @Test + fun readLong() { + val buffer = Buffer() + buffer.write(byteArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) + + assertEquals(0x0102030405060708L, buffer.readLong()) + assertEquals(2, buffer.size) + } + + @Test + fun skip() { + val buffer = Buffer() + buffer.write(byteArrayOf(1, 2, 3, 4, 5, 6, 7, 8)) + + buffer.skip(3) + assertContentEquals(byteArrayOf(4, 5, 6, 7, 8), buffer.readByteArray()) + } + + @Test + fun request() { + val singleByteSource = object : RawSource { + private var exhausted = false + override fun readAtMostTo(sink: Buffer, byteCount: Long): Long = when { + byteCount == 0L -> 0L + exhausted -> -1L + else -> { + exhausted = true + sink.writeByte(42) + 1L + } + } + + override fun close() = Unit + } + + val source = singleByteSource.buffered() + + assertTrue(source.request(1)) + // The request call already soaked all the data from the source + assertEquals(-1, singleByteSource.readAtMostTo(Buffer(), 1)) + // There is only one byte, so we can't request more + assertFalse(source.request(2)) + // But we didn't consume single byte yet, so request(1) will succeed + assertTrue(source.request(1)) + assertEquals(42, source.readByte()) + assertFalse(source.request(1)) + } + + @Test + fun require() { + val singleByteSource = object : RawSource { + private var exhausted = false + override fun readAtMostTo(sink: Buffer, byteCount: Long): Long = when { + byteCount == 0L -> 0L + exhausted -> -1L + else -> { + exhausted = true + sink.writeByte(42) + 1L + } + } + + override fun close() = Unit + } + + val source = singleByteSource.buffered() + + source.require(1) + // The require call already soaked all the data from the source + assertEquals(-1, singleByteSource.readAtMostTo(Buffer(), 1)) + // There is only one byte, so we can't request more + assertFailsWith { source.require(2) } + // But we didn't consume single byte yet, so require(1) will succeed + source.require(1) + assertEquals(42, source.readByte()) + assertFailsWith { source.require(1) } + } + + @Test + fun exhausted() { + val singleByteSource = object : RawSource { + private var exhausted = false + override fun readAtMostTo(sink: Buffer, byteCount: Long): Long = when { + byteCount == 0L -> 0L + exhausted -> -1L + else -> { + exhausted = true + sink.writeByte(42) + 1L + } + } + + override fun close() = Unit + } + + val source = singleByteSource.buffered() + + assertFalse(source.exhausted()) + assertContentEquals(byteArrayOf(42), source.readByteArray()) + assertTrue(source.exhausted()) + } + + @Test + fun writeByte() { + val buffer = Buffer() + + buffer.writeByte(42) + assertContentEquals(byteArrayOf(42), buffer.readByteArray()) + } + + @Test + fun writeShort() { + val buffer = Buffer() + + buffer.writeShort(42) + assertContentEquals(byteArrayOf(0, 42), buffer.readByteArray()) + } + + @Test + fun writeInt() { + val buffer = Buffer() + + buffer.writeInt(42) + assertContentEquals(byteArrayOf(0, 0, 0, 42), buffer.readByteArray()) + } + + + @Test + fun writeLong() { + val buffer = Buffer() + + buffer.writeLong(42) + assertContentEquals(byteArrayOf(0, 0, 0, 0, 0, 0, 0, 42), buffer.readByteArray()) + } + + @Test + fun readUByte() { + val buffer = Buffer() + buffer.write(byteArrayOf(-1)) + + assertEquals(255U, buffer.readUByte()) + } + + @Test + fun readUShort() { + val buffer = Buffer() + buffer.write(byteArrayOf(-1, -1)) + + assertEquals(65535U, buffer.readUShort()) + } + + @Test + fun readUInt() { + val buffer = Buffer() + buffer.write(byteArrayOf(-1, -1, -1, -1)) + + assertEquals(4294967295U, buffer.readUInt()) + } + + @Test + fun readULong() { + val buffer = Buffer() + buffer.write(byteArrayOf(-1, -1, -1, -1, -1, -1, -1, -1)) + + assertEquals(18446744073709551615UL, buffer.readULong()) + } + + @Test + fun writeUByte() { + val buffer = Buffer() + buffer.writeUByte(255U) + + assertContentEquals(byteArrayOf(-1), buffer.readByteArray()) + } + + @Test + fun writeUShort() { + val buffer = Buffer() + buffer.writeUShort(65535U) + + assertContentEquals(byteArrayOf(-1, -1), buffer.readByteArray()) + } + + @Test + fun writeUInt() { + val buffer = Buffer() + buffer.writeUInt(4294967295U) + + assertContentEquals(byteArrayOf(-1, -1, -1, -1), buffer.readByteArray()) + } + + @Test + fun writeULong() { + val buffer = Buffer() + buffer.writeULong(18446744073709551615UL) + + assertContentEquals(byteArrayOf(-1, -1, -1, -1, -1, -1, -1, -1), buffer.readByteArray()) + } + + @Test + fun flush() { + val rawSink = object : RawSink { + val buffer = Buffer() + var flushed = false + override fun write(source: Buffer, byteCount: Long) { + source.readTo(buffer, byteCount) + } + + override fun flush() { + flushed = true + } + + override fun close() = Unit + } + + val buffered = rawSink.buffered() + + buffered.writeShort(0x1000) + // Not data were sent to the underlying sink and it was not flushed yet + assertFalse(rawSink.flushed) + assertEquals(0, rawSink.buffer.size) + + buffered.flush() + // The sink was filled with buffered data and then flushed + assertTrue(rawSink.flushed) + assertContentEquals(byteArrayOf(0x10, 0), rawSink.buffer.readByteArray()) + } + + @Test + fun emit() { + val rawSink = object : RawSink { + val buffer = Buffer() + var flushed = false + override fun write(source: Buffer, byteCount: Long) { + source.readTo(buffer, byteCount) + } + + override fun flush() { + flushed = true + } + + override fun close() = Unit + } + + val buffered = rawSink.buffered() + + buffered.writeShort(0x1000) + // Not data were sent to the underlying sink and it was not flushed yet + assertFalse(rawSink.flushed) + assertEquals(0, rawSink.buffer.size) + + buffered.emit() + // The sink was filled with buffered data, but it was not flushed + assertFalse(rawSink.flushed) + assertContentEquals(byteArrayOf(0x10, 0), rawSink.buffer.readByteArray()) + } + + @Test + fun writeSourceToSink() { + val sink = Buffer() + val source = Buffer().also { it.writeInt(0x01020304) } + + sink.write(source, 3) + assertContentEquals(byteArrayOf(1, 2, 3), sink.readByteArray()) + assertContentEquals(byteArrayOf(4), source.readByteArray()) + } + + @Test + fun writeByteArrayToSink() { + val sink = Buffer() + + sink.write(byteArrayOf(1, 2, 3, 4)) + assertContentEquals(byteArrayOf(1, 2, 3, 4), sink.readByteArray()) + + sink.write(byteArrayOf(1, 2, 3, 4), startIndex = 1, endIndex = 3) + assertContentEquals(byteArrayOf(2, 3), sink.readByteArray()) + } + + @Test + fun readSourceToSink() { + val sink = Buffer() + val source = Buffer().also { it.writeInt(0x01020304) } + + source.readTo(sink, 3) + assertContentEquals(byteArrayOf(1, 2, 3), sink.readByteArray()) + assertContentEquals(byteArrayOf(4), source.readByteArray()) + } + + @Test + fun readAtMostToByteArray() { + val source = Buffer().also { it.write(byteArrayOf(1, 2, 3, 4, 5, 6)) } + val sink = ByteArray(10) + + val bytesRead = source.readAtMostTo(sink) // read at most 10 bytes + assertEquals(6, bytesRead) + assertContentEquals(byteArrayOf(1, 2, 3, 4, 5, 6, 0, 0, 0, 0), sink) + } + + @Test + fun readAtMostToSink() { + val source = Buffer().also { it.write(byteArrayOf(1, 2, 3, 4, 5, 6)) } + val sink = Buffer() + + val bytesRead = source.readAtMostTo(sink, 10) // read at most 10 bytes + assertEquals(6, bytesRead) + assertContentEquals(byteArrayOf(1, 2, 3, 4, 5, 6), sink.readByteArray()) + } + + @Test + fun readUShortLe() { + val buffer = Buffer() + buffer.writeUShort(0x1234U) + assertEquals(0x3412U, buffer.readUShortLe()) + } + + @Test + fun readUIntLe() { + val buffer = Buffer() + buffer.writeUInt(0x12345678U) + assertEquals(0x78563412U, buffer.readUIntLe()) + } + + @Test + fun readULongLe() { + val buffer = Buffer() + buffer.writeULong(0x123456789ABCDEF0U) + assertEquals(0xF0DEBC9A78563412U, buffer.readULongLe()) + } + + @Test + fun writeUShortLe() { + val buffer = Buffer() + buffer.writeUShortLe(0x1234U) + assertEquals(0x3412U, buffer.readUShort()) + } + + @Test + fun writeUIntLe() { + val buffer = Buffer() + buffer.writeUIntLe(0x12345678U) + assertEquals(0x78563412U, buffer.readUInt()) + } + + @Test + fun writeULongLe() { + val buffer = Buffer() + buffer.writeULongLe(0x123456789ABCDEF0U) + assertEquals(0xF0DEBC9A78563412U, buffer.readULong()) + } +} \ No newline at end of file From 98c10cc0004a4447c13100787d51f3ecf39aa78a Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Wed, 28 Jun 2023 16:34:56 +0200 Subject: [PATCH 2/7] Cleanup --- bytestring/common/test/samples/samples.kt | 2 +- core/build.gradle.kts | 2 +- core/common/test/samples/byteStringSample.kt | 2 +- core/common/test/samples/moduleDescriptionSample.kt | 2 +- core/common/test/samples/rawSinkSample.kt | 2 +- core/common/test/samples/rawSourceSample.kt | 2 +- core/common/test/samples/samples.kt | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bytestring/common/test/samples/samples.kt b/bytestring/common/test/samples/samples.kt index be6c93e72..d31a4a2b8 100644 --- a/bytestring/common/test/samples/samples.kt +++ b/bytestring/common/test/samples/samples.kt @@ -237,4 +237,4 @@ class ByteStringSamples { assertEquals(ByteString(3, 4, 5, 6), byteString) } -} \ No newline at end of file +} diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 850447788..b0e2cf22b 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -89,4 +89,4 @@ tasks.withType().configureEach { "common/test/samples/byteStringSample.kt" ) } -} \ No newline at end of file +} diff --git a/core/common/test/samples/byteStringSample.kt b/core/common/test/samples/byteStringSample.kt index f366ea38c..288a27d41 100644 --- a/core/common/test/samples/byteStringSample.kt +++ b/core/common/test/samples/byteStringSample.kt @@ -42,4 +42,4 @@ class ByteStringSamples { assertEquals(43, buffer.indexOf("\n\n".encodeToByteString())) assertEquals(-1, buffer.indexOf("application/json".encodeToByteString())) } -} \ No newline at end of file +} diff --git a/core/common/test/samples/moduleDescriptionSample.kt b/core/common/test/samples/moduleDescriptionSample.kt index adc06608c..305c91f3b 100644 --- a/core/common/test/samples/moduleDescriptionSample.kt +++ b/core/common/test/samples/moduleDescriptionSample.kt @@ -70,4 +70,4 @@ class ModuleDescriptionSampleTest { assertEquals(message, Message.fromBson(buffer)) } -} \ No newline at end of file +} diff --git a/core/common/test/samples/rawSinkSample.kt b/core/common/test/samples/rawSinkSample.kt index 883f10c91..186a54f16 100644 --- a/core/common/test/samples/rawSinkSample.kt +++ b/core/common/test/samples/rawSinkSample.kt @@ -73,4 +73,4 @@ class Crc32Sample { assertEquals(0x9896d398U, crc32Sink.crc32()) } -} \ No newline at end of file +} diff --git a/core/common/test/samples/rawSourceSample.kt b/core/common/test/samples/rawSourceSample.kt index 807c18fc3..7f21b5751 100644 --- a/core/common/test/samples/rawSourceSample.kt +++ b/core/common/test/samples/rawSourceSample.kt @@ -81,4 +81,4 @@ class RC4SourceSample { assertEquals("Secret", rc4Source.readUtf8()) } -} \ No newline at end of file +} diff --git a/core/common/test/samples/samples.kt b/core/common/test/samples/samples.kt index 2efebc0dd..864121396 100644 --- a/core/common/test/samples/samples.kt +++ b/core/common/test/samples/samples.kt @@ -669,4 +669,4 @@ class KotlinxIoCoreCommonSamples { buffer.writeULongLe(0x123456789ABCDEF0U) assertEquals(0xF0DEBC9A78563412U, buffer.readULong()) } -} \ No newline at end of file +} From 5f0afa6656c2aa44c7b8040e917f5fe8e20a7e6d Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Thu, 29 Jun 2023 10:02:59 +0200 Subject: [PATCH 3/7] Added samples for JVM-specific extensions --- core/build.gradle.kts | 3 +- core/jvm/src/BufferExtJvm.kt | 12 +++ core/jvm/src/JvmCore.kt | 4 + core/jvm/src/SinkExtJvm.kt | 6 ++ core/jvm/src/SourceExtJvm.kt | 8 ++ core/jvm/test/samples/samplesJvm.kt | 142 ++++++++++++++++++++++++++++ 6 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 core/jvm/test/samples/samplesJvm.kt diff --git a/core/build.gradle.kts b/core/build.gradle.kts index b0e2cf22b..21604aebf 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -86,7 +86,8 @@ tasks.withType().configureEach { "common/test/samples/rawSourceSample.kt", "common/test/samples/moduleDescriptionSample.kt", "common/test/samples/samples.kt", - "common/test/samples/byteStringSample.kt" + "common/test/samples/byteStringSample.kt", + "jvm/test/samples/samplesJvm.kt" ) } } diff --git a/core/jvm/src/BufferExtJvm.kt b/core/jvm/src/BufferExtJvm.kt index 0015fedee..8d4a1999e 100644 --- a/core/jvm/src/BufferExtJvm.kt +++ b/core/jvm/src/BufferExtJvm.kt @@ -31,6 +31,8 @@ import java.nio.channels.ByteChannel * Read and exhaust bytes from [input] into this buffer. Stops reading data on [input] exhaustion. * * @param input the stream to read data from. + * + * @sample kotlinx.io.samples.KotlinxIoSamplesJvm.bufferTransferToStream */ public fun Buffer.transferFrom(input: InputStream): Buffer { write(input, Long.MAX_VALUE, true) @@ -46,6 +48,8 @@ public fun Buffer.transferFrom(input: InputStream): Buffer { * * @throws IOException when [input] exhausted before reading [byteCount] bytes from it. * @throws IllegalArgumentException when [byteCount] is negative. + * + * @sample kotlinx.io.samples.KotlinxIoSamplesJvm.writeInputStreamToBuffer */ public fun Buffer.write(input: InputStream, byteCount: Long): Buffer { checkByteCount(byteCount) @@ -81,6 +85,8 @@ private fun Buffer.write(input: InputStream, byteCount: Long, forever: Boolean) * @param byteCount the number of bytes to be written, [Buffer.size] by default. * * @throws IllegalArgumentException when [byteCount] is negative or exceeds the buffer size. + * + * @sample kotlinx.io.samples.KotlinxIoSamplesJvm.bufferTransferToStream */ public fun Buffer.readTo(out: OutputStream, byteCount: Long = size) { checkOffsetAndCount(size, 0, byteCount) @@ -114,6 +120,8 @@ public fun Buffer.readTo(out: OutputStream, byteCount: Long = size) { * * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of this buffer bounds (`[0..buffer.size)`). * @throws IllegalArgumentException when `startIndex > endIndex`. + * + * @sample kotlinx.io.samples.KotlinxIoSamplesJvm.copyBufferToOutputStream */ public fun Buffer.copyTo( out: OutputStream, @@ -149,6 +157,8 @@ public fun Buffer.copyTo( * Return the number of bytes written. * * @param sink the sink to write data to. + * + * @sample kotlinx.io.samples.KotlinxIoSamplesJvm.readWriteByteBuffer */ public fun Buffer.readAtMostTo(sink: ByteBuffer): Int { val s = head ?: return -1 @@ -169,6 +179,8 @@ public fun Buffer.readAtMostTo(sink: ByteBuffer): Int { /** * Reads all data from [source] into this buffer. + * + * @sample kotlinx.io.samples.KotlinxIoSamplesJvm.bufferInteropWithNioBuffer */ public fun Buffer.transferFrom(source: ByteBuffer): Buffer { val byteCount = source.remaining() diff --git a/core/jvm/src/JvmCore.kt b/core/jvm/src/JvmCore.kt index 4da0c9761..1506ab1ba 100644 --- a/core/jvm/src/JvmCore.kt +++ b/core/jvm/src/JvmCore.kt @@ -29,6 +29,8 @@ import java.io.OutputStream * Returns [RawSink] that writes to an output stream. * * Use [RawSink.buffered] to create a buffered sink from it. + * + * @sample kotlinx.io.samples.KotlinxIoSamplesJvm.outputStreamAsSink */ public fun OutputStream.asSink(): RawSink = OutputStreamSink(this) @@ -67,6 +69,8 @@ private open class OutputStreamSink( * Returns [RawSource] that reads from an input stream. * * Use [RawSource.buffered] to create a buffered source from it. + * + * @sample kotlinx.io.samples.KotlinxIoSamplesJvm.inputStreamAsSource */ public fun InputStream.asSource(): RawSource = InputStreamSource(this) diff --git a/core/jvm/src/SinkExtJvm.kt b/core/jvm/src/SinkExtJvm.kt index 494b5c563..8c5500349 100644 --- a/core/jvm/src/SinkExtJvm.kt +++ b/core/jvm/src/SinkExtJvm.kt @@ -38,6 +38,8 @@ import java.nio.charset.Charset * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [string] indices. * @throws IllegalArgumentException when `startIndex > endIndex`. * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoSamplesJvm.readWriteStrings */ public fun Sink.writeString(string: String, charset: Charset, startIndex: Int = 0, endIndex: Int = string.length) { checkBounds(string.length, startIndex, endIndex) @@ -48,6 +50,8 @@ public fun Sink.writeString(string: String, charset: Charset, startIndex: Int = /** * Returns an output stream that writes to this sink. Closing the stream will also close this sink. + * + * @sample kotlinx.io.samples.KotlinxIoSamplesJvm.asStream */ @OptIn(DelicateIoApi::class) public fun Sink.asOutputStream(): OutputStream { @@ -88,6 +92,8 @@ public fun Sink.asOutputStream(): OutputStream { * @param source the source to read from. * * @throws IllegalStateException when the sink is closed. + * + * @sample kotlinx.io.samples.KotlinxIoSamplesJvm.readWriteByteBuffer */ @OptIn(InternalIoApi::class) public fun Sink.write(source: ByteBuffer): Int { diff --git a/core/jvm/src/SourceExtJvm.kt b/core/jvm/src/SourceExtJvm.kt index 2385b7637..7c940cdb1 100644 --- a/core/jvm/src/SourceExtJvm.kt +++ b/core/jvm/src/SourceExtJvm.kt @@ -59,6 +59,8 @@ private fun Buffer.readStringImpl(byteCount: Long, charset: Charset): String { * @param charset the [Charset] to use for string decoding. * * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoSamplesJvm.readWriteStrings */ @OptIn(InternalIoApi::class) public fun Source.readString(charset: Charset): String { @@ -78,6 +80,8 @@ public fun Source.readString(charset: Charset): String { * @throws EOFException when the source exhausted before [byteCount] bytes could be read from it. * @throws IllegalStateException when the source is closed. * @throws IllegalArgumentException if [byteCount] is negative or its value is greater than [Int.MAX_VALUE]. + * + * @sample kotlinx.io.samples.KotlinxIoSamplesJvm.readStringBounded */ @OptIn(InternalIoApi::class) public fun Source.readString(byteCount: Long, charset: Charset): String { @@ -87,6 +91,8 @@ public fun Source.readString(byteCount: Long, charset: Charset): String { /** * Returns an input stream that reads from this source. Closing the stream will also close this source. + * + * @sample kotlinx.io.samples.KotlinxIoSamplesJvm.asStream */ @OptIn(InternalIoApi::class) public fun Source.asInputStream(): InputStream { @@ -130,6 +136,8 @@ public fun Source.asInputStream(): InputStream { * @param sink the sink to write the data to. * * @throws IllegalStateException when the source is closed. + * + * @sample kotlinx.io.samples.KotlinxIoSamplesJvm.readWriteByteBuffer */ @OptIn(InternalIoApi::class) public fun Source.readAtMostTo(sink: ByteBuffer): Int { diff --git a/core/jvm/test/samples/samplesJvm.kt b/core/jvm/test/samples/samplesJvm.kt new file mode 100644 index 000000000..cfaedad20 --- /dev/null +++ b/core/jvm/test/samples/samplesJvm.kt @@ -0,0 +1,142 @@ +/* + * 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.samples + +import kotlinx.io.* +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer +import java.util.zip.GZIPInputStream +import java.util.zip.GZIPOutputStream +import kotlin.test.Test +import kotlin.test.assertContentEquals +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class KotlinxIoSamplesJvm { + @Test + fun inputStreamAsSource() { + val data = ByteArray(100) { it.toByte() } + val inputStream = ByteArrayInputStream(data) + + val receivedData = inputStream.asSource().buffered().readByteArray() + assertContentEquals(data, receivedData) + } + + @Test + fun outputStreamAsSink() { + val data = ByteArray(100) { it.toByte() } + val outputStream = ByteArrayOutputStream() + + val sink = outputStream.asSink().buffered() + sink.write(data) + sink.flush() + + assertContentEquals(data, outputStream.toByteArray()) + } + + @Test + fun asStream() { + val buffer = Buffer() + val data = ByteArray(100) { it.toByte() } + + GZIPOutputStream(buffer.asOutputStream()).use { + it.write(data) + } + val decodedData = GZIPInputStream(buffer.asInputStream()).use { + it.readBytes() + } + assertContentEquals(data, decodedData) + } + + @Test + fun readWriteByteBuffer() { + val buffer = Buffer() + val nioByteBuffer = ByteBuffer.allocate(1024) + + buffer.writeUtf8("hello") + val bytesRead = buffer.readAtMostTo(nioByteBuffer) + assertEquals(5, bytesRead) + assertEquals(5, nioByteBuffer.capacity() - nioByteBuffer.remaining()) + + nioByteBuffer.position(0) + nioByteBuffer.limit(5) + + val bytesWrite = buffer.write(nioByteBuffer) + assertEquals(5, bytesWrite) + assertEquals("hello", buffer.readUtf8()) + } + + @Test + fun bufferTransferToStream() { + val buffer = Buffer() + buffer.writeUtf8("hello") + + val outputStream = ByteArrayOutputStream() + buffer.readTo(outputStream) + + assertTrue(buffer.exhausted()) + + val inputStream = ByteArrayInputStream(outputStream.toByteArray()) + buffer.transferFrom(inputStream) + + assertEquals("hello", buffer.readUtf8()) + } + + @Test + fun writeInputStreamToBuffer() { + val inputStream = ByteArrayInputStream("hello!".encodeToByteArray()) + val buffer = Buffer() + + buffer.write(inputStream, 5) + assertEquals("hello", buffer.readUtf8()) + } + + @Test + fun copyBufferToOutputStream() { + val buffer = Buffer() + buffer.writeUtf8("string") + + val outputStream = ByteArrayOutputStream() + buffer.copyTo(outputStream, startIndex = 2, endIndex = 6) + + assertEquals("string", buffer.readUtf8()) + assertEquals("ring", outputStream.toString("UTF-8")) + } + + @Test + fun transferBufferFromByteBuffer() { + val buffer = Buffer() + val nioBuffer = ByteBuffer.allocate(32) + + nioBuffer.put("hello".encodeToByteArray()) + nioBuffer.position(0) + nioBuffer.limit(5) + buffer.transferFrom(nioBuffer) + + assertEquals("hello", buffer.readUtf8()) + assertEquals(5, nioBuffer.position()) + } + + @Test + fun readWriteStrings() { + val buffer = Buffer() + + buffer.write(byteArrayOf(0, 0, 0, 0x68, 0, 0, 0, 0x69)) + assertEquals("hi", buffer.readString(Charsets.UTF_32BE)) + + buffer.writeString("hi", Charsets.UTF_16BE) + assertContentEquals(byteArrayOf(0, 0x68, 0, 0x69), buffer.readByteArray()) + } + + @Test + fun readStringBounded() { + val buffer = Buffer() + + buffer.write(byteArrayOf(0, 0, 0, 0x68, 0, 0, 0, 0x69)) + assertEquals("h", buffer.readString(byteCount = 4, charset = Charsets.UTF_32BE)) + } +} \ No newline at end of file From 3905b340294e7e3dccaad75e832a80c407afc349 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Thu, 29 Jun 2023 10:04:25 +0200 Subject: [PATCH 4/7] Cleanup --- core/jvm/test/samples/samplesJvm.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/jvm/test/samples/samplesJvm.kt b/core/jvm/test/samples/samplesJvm.kt index cfaedad20..aef2a77c0 100644 --- a/core/jvm/test/samples/samplesJvm.kt +++ b/core/jvm/test/samples/samplesJvm.kt @@ -139,4 +139,4 @@ class KotlinxIoSamplesJvm { buffer.write(byteArrayOf(0, 0, 0, 0x68, 0, 0, 0, 0x69)) assertEquals("h", buffer.readString(byteCount = 4, charset = Charsets.UTF_32BE)) } -} \ No newline at end of file +} From 4b18eec756a8f8091d2416fb9bf5d09055a5c3fa Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Thu, 29 Jun 2023 15:57:14 +0200 Subject: [PATCH 5/7] Use comparison operators in compareTo samples --- bytestring/common/test/samples/samples.kt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/bytestring/common/test/samples/samples.kt b/bytestring/common/test/samples/samples.kt index d31a4a2b8..2d0162e12 100644 --- a/bytestring/common/test/samples/samples.kt +++ b/bytestring/common/test/samples/samples.kt @@ -11,15 +11,17 @@ import kotlin.test.* class ByteStringSamples { @Test fun compareTo() { - assertEquals(0, ByteString(1, 2, 3).compareTo(ByteString(1, 2, 3))) - assertEquals(-1, ByteString(1, 2, 3).compareTo(ByteString(1, 3, 2))) + assertTrue(ByteString(1, 2, 3) == ByteString(1, 2, 3)) + assertTrue(ByteString(1, 2, 3) <= ByteString(1, 2, 3)) + assertTrue(ByteString(1, 2, 3) >= ByteString(1, 2, 3)) + assertTrue(ByteString(1, 2, 3) < ByteString(1, 3, 2)) // If byte strings have different length, their content compared up to the length of the shortest string, // and if their content was the same, then the shortest string is considered "smaller" - assertEquals(-1, ByteString().compareTo(ByteString(1, 2, 3))) - assertEquals(1, ByteString(1, 2, 3).compareTo(ByteString(1))) - assertEquals(-1, ByteString(1, 2, 3).compareTo(ByteString(1, 3))) - assertEquals(1, ByteString(1, 2, 3).compareTo(ByteString(1, 1, 1, 1))) + assertTrue(ByteString() < ByteString(1, 2, 3)) + assertTrue(ByteString(1, 2, 3) > ByteString(1)) + assertTrue(ByteString(1, 2, 3) < ByteString(1, 3)) + assertTrue(ByteString(1, 2, 3) > ByteString(1, 1, 1, 1)) } @Test From 74f35bbdf9474206658465996f22b9af5f52680e Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Thu, 29 Jun 2023 18:40:56 +0200 Subject: [PATCH 6/7] Improve bufferClear sample --- core/common/test/samples/samples.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/core/common/test/samples/samples.kt b/core/common/test/samples/samples.kt index 864121396..b9d59ac17 100644 --- a/core/common/test/samples/samples.kt +++ b/core/common/test/samples/samples.kt @@ -25,6 +25,7 @@ class KotlinxIoCoreCommonSamples { fun bufferClear() { val buffer = Buffer() buffer.write(ByteArray(1024)) + assertFalse(buffer.exhausted()) buffer.clear() assertTrue(buffer.exhausted()) } From 8ecb906f0a8210da47f4444079ed731189fa3b99 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Fri, 30 Jun 2023 10:46:46 +0200 Subject: [PATCH 7/7] Renamed utf8-related methods in samples --- core/Module.md | 12 ++-- core/common/test/samples/byteStringSample.kt | 2 +- .../test/samples/moduleDescriptionSample.kt | 10 +-- core/common/test/samples/rawSinkSample.kt | 2 +- core/common/test/samples/rawSourceSample.kt | 2 +- core/common/test/samples/samples.kt | 72 +++++++++---------- core/jvm/test/samples/samplesJvm.kt | 16 ++--- 7 files changed, 58 insertions(+), 58 deletions(-) diff --git a/core/Module.md b/core/Module.md index 71e6dba86..9e2da6d11 100644 --- a/core/Module.md +++ b/core/Module.md @@ -30,14 +30,14 @@ fun Message.toBson(sink: Sink) { val buffer = Buffer() with (buffer) { writeByte(0x9) // UTC-timestamp field - writeUtf8("timestamp") // field name + writeString("timestamp") // field name writeByte(0) writeLongLe(timestamp) // field value writeByte(0x2) // string field - writeUtf8("text") // field name + writeString("text") // field name writeByte(0) writeIntLe(text.utf8Size().toInt() + 1) // field value: length followed by the string - writeUtf8(text) + writeString(text) writeByte(0) writeByte(0) // end of BSON document } @@ -56,7 +56,7 @@ fun Message.Companion.fromBson(source: Source): Message { fun readFieldName(source: Source): String { val delimiterOffset = source.indexOf(0) // find offset of the 0-byte terminating the name check(delimiterOffset >= 0) // indexOf return -1 if value not found - val fieldName = source.readUtf8(delimiterOffset) // read the string until terminator + val fieldName = source.readString(delimiterOffset) // read the string until terminator source.skip(1) // skip the terminator return fieldName } @@ -68,7 +68,7 @@ fun Message.Companion.fromBson(source: Source): Message { tag = source.readByte().toInt() check(tag == 0x2 && readFieldName(source) == "text") val textLen = source.readIntLe() - 1L // read string length (it includes the terminator) - val text = source.readUtf8(textLen) // read value + val text = source.readString(textLen) // read value source.skip(1) // skip terminator source.skip(1) // skip end of the document return Message(timestamp, text) @@ -81,4 +81,4 @@ Core IO primitives. # Package kotlinx.io.files -Basic API for working with files. \ No newline at end of file +Basic API for working with files. diff --git a/core/common/test/samples/byteStringSample.kt b/core/common/test/samples/byteStringSample.kt index 288a27d41..04451342d 100644 --- a/core/common/test/samples/byteStringSample.kt +++ b/core/common/test/samples/byteStringSample.kt @@ -37,7 +37,7 @@ class ByteStringSamples { assertEquals(-1, buffer.indexOf(ByteString(1, 2, 3, 4))) assertEquals(0, buffer.indexOf(ByteString(/* empty */))) - buffer.writeUtf8("Content-Type: text/plain\nContent-Length: 12\n\nhello world!") + buffer.writeString("Content-Type: text/plain\nContent-Length: 12\n\nhello world!") assertEquals(43, buffer.indexOf("\n\n".encodeToByteString())) assertEquals(-1, buffer.indexOf("application/json".encodeToByteString())) diff --git a/core/common/test/samples/moduleDescriptionSample.kt b/core/common/test/samples/moduleDescriptionSample.kt index 305c91f3b..b6d2ef20a 100644 --- a/core/common/test/samples/moduleDescriptionSample.kt +++ b/core/common/test/samples/moduleDescriptionSample.kt @@ -17,14 +17,14 @@ fun Message.toBson(sink: Sink) { val buffer = Buffer() with (buffer) { writeByte(0x9) // UTC-timestamp field - writeUtf8("timestamp") // field name + writeString("timestamp") // field name writeByte(0) writeLongLe(timestamp) // field value writeByte(0x2) // string field - writeUtf8("text") // field name + writeString("text") // field name writeByte(0) writeIntLe(text.utf8Size().toInt() + 1) // field value: length followed by the string - writeUtf8(text) + writeString(text) writeByte(0) writeByte(0) // end of BSON document } @@ -43,7 +43,7 @@ fun Message.Companion.fromBson(source: Source): Message { fun readFieldName(source: Source): String { val delimiterOffset = source.indexOf(0) // find offset of the 0-byte terminating the name check(delimiterOffset >= 0) // indexOf return -1 if value not found - val fieldName = source.readUtf8(delimiterOffset) // read the string until terminator + val fieldName = source.readString(delimiterOffset) // read the string until terminator source.skip(1) // skip the terminator return fieldName } @@ -55,7 +55,7 @@ fun Message.Companion.fromBson(source: Source): Message { tag = source.readByte().toInt() check(tag == 0x2 && readFieldName(source) == "text") val textLen = source.readIntLe() - 1L // read string length (it includes the terminator) - val text = source.readUtf8(textLen) // read value + val text = source.readString(textLen) // read value source.skip(1) // skip terminator source.skip(1) // skip end of the document return Message(timestamp, text) diff --git a/core/common/test/samples/rawSinkSample.kt b/core/common/test/samples/rawSinkSample.kt index 186a54f16..be2cae496 100644 --- a/core/common/test/samples/rawSinkSample.kt +++ b/core/common/test/samples/rawSinkSample.kt @@ -68,7 +68,7 @@ class Crc32Sample { val crc32Sink = CRC32Sink(discardingSink()) crc32Sink.buffered().use { - it.writeUtf8("hello crc32") + it.writeString("hello crc32") } assertEquals(0x9896d398U, crc32Sink.crc32()) diff --git a/core/common/test/samples/rawSourceSample.kt b/core/common/test/samples/rawSourceSample.kt index 7f21b5751..56cddbe2d 100644 --- a/core/common/test/samples/rawSourceSample.kt +++ b/core/common/test/samples/rawSourceSample.kt @@ -79,6 +79,6 @@ class RC4SourceSample { val source = Buffer().also { it.write(byteArrayOf(0x58, 0x09, 0x57, 0x9fU.toByte(), 0x41, 0xfbU.toByte())) } val rc4Source = RC4DecryptingSource(source, key).buffered() - assertEquals("Secret", rc4Source.readUtf8()) + assertEquals("Secret", rc4Source.readString()) } } diff --git a/core/common/test/samples/samples.kt b/core/common/test/samples/samples.kt index b9d59ac17..988670e3e 100644 --- a/core/common/test/samples/samples.kt +++ b/core/common/test/samples/samples.kt @@ -33,7 +33,7 @@ class KotlinxIoCoreCommonSamples { @Test fun bufferGetByte() { val buffer = Buffer() - buffer.writeUtf8("Hello World!") + buffer.writeString("Hello World!") assertEquals('H'.code, buffer[0].toInt()) assertEquals('W'.code, buffer[buffer.indexOf('W'.code.toByte())].toInt()) @@ -42,49 +42,49 @@ class KotlinxIoCoreCommonSamples { @Test fun bufferCopy() { val buffer = Buffer() - buffer.writeUtf8("some string") + buffer.writeString("some string") val copy = Buffer() - copy.writeUtf8("sub") + copy.writeString("sub") buffer.copyTo(copy, startIndex = 5) - assertEquals("some string", buffer.readUtf8()) - assertEquals("substring", copy.readUtf8()) + assertEquals("some string", buffer.readString()) + assertEquals("substring", copy.readString()) } @Test fun transferFrom() { - val src: Source = Buffer().also { it.writeUtf8("Some data to transfer") } - val dst = Buffer().also { it.writeUtf8("Transferred: ") } + val src: Source = Buffer().also { it.writeString("Some data to transfer") } + val dst = Buffer().also { it.writeString("Transferred: ") } dst.transferFrom(src) assertTrue(src.exhausted()) - assertEquals("Transferred: Some data to transfer", dst.readUtf8()) + assertEquals("Transferred: Some data to transfer", dst.readString()) } @Test fun transferTo() { - val src: Source = Buffer().also { it.writeUtf8("Some data to transfer") } - val dst = Buffer().also { it.writeUtf8("Transferred: ") } + val src: Source = Buffer().also { it.writeString("Some data to transfer") } + val dst = Buffer().also { it.writeString("Transferred: ") } src.transferTo(dst) assertTrue(src.exhausted()) - assertEquals("Transferred: Some data to transfer", dst.readUtf8()) + assertEquals("Transferred: Some data to transfer", dst.readString()) } @Test fun peekSample() { - val source: Source = Buffer().also { it.writeUtf8("hello world") } + val source: Source = Buffer().also { it.writeString("hello world") } val peek = source.peek().buffered() - assertEquals("hello", peek.readUtf8(5)) + assertEquals("hello", peek.readString(5)) peek.skip(1) - assertEquals("world", peek.readUtf8(5)) + assertEquals("world", peek.readString(5)) assertTrue(peek.exhausted()) - assertEquals("hello world", source.readUtf8()) + assertEquals("hello world", source.readString()) } @Test @@ -118,14 +118,14 @@ class KotlinxIoCoreCommonSamples { @Test fun readLinesSample() { val buffer = Buffer() - buffer.writeUtf8("No new line here.") + buffer.writeString("No new line here.") - assertFailsWith { buffer.readUtf8LineStrict() } - assertEquals("No new line here.", buffer.readUtf8Line()) + assertFailsWith { buffer.readLineStrict() } + assertEquals("No new line here.", buffer.readLine()) - buffer.writeUtf8("Line1\n\nLine2") - assertEquals("Line1", buffer.readUtf8LineStrict()) - assertEquals("\nLine2", buffer.readUtf8()) + buffer.writeString("Line1\n\nLine2") + assertEquals("Line1", buffer.readLineStrict()) + assertEquals("\nLine2", buffer.readString()) } @Test @@ -173,7 +173,7 @@ class KotlinxIoCoreCommonSamples { @Test fun readDecimalLong() { val buffer = Buffer() - buffer.writeUtf8("42 -1 1234567!") + buffer.writeString("42 -1 1234567!") assertEquals(42L, buffer.readDecimalLong()) buffer.skip(1) // skip space @@ -188,10 +188,10 @@ class KotlinxIoCoreCommonSamples { val buffer = Buffer() buffer.writeDecimalLong(1024) - buffer.writeUtf8(", ") + buffer.writeString(", ") buffer.writeDecimalLong(-24) - assertEquals("1024, -24", buffer.readUtf8()) + assertEquals("1024, -24", buffer.readString()) } @Test @@ -199,25 +199,25 @@ class KotlinxIoCoreCommonSamples { val buffer = Buffer() buffer.writeHexadecimalUnsignedLong(10) - assertEquals("a", buffer.readUtf8()) + assertEquals("a", buffer.readString()) buffer.writeHexadecimalUnsignedLong(-10) - assertEquals("fffffffffffffff6", buffer.readUtf8()) + assertEquals("fffffffffffffff6", buffer.readString()) } @Test fun readHexLong() { val buffer = Buffer() - buffer.writeUtf8("0000a") + buffer.writeString("0000a") assertEquals(10L, buffer.readHexadecimalUnsignedLong()) - buffer.writeUtf8("fffFffFffFfffff6") + buffer.writeString("fffFffFffFfffff6") assertEquals(-10L, buffer.readHexadecimalUnsignedLong()) - buffer.writeUtf8("dear friend!") + buffer.writeString("dear friend!") assertEquals(0xdea, buffer.readHexadecimalUnsignedLong()) - assertEquals("r friend!", buffer.readUtf8()) + assertEquals("r friend!", buffer.readString()) } @Test @@ -238,7 +238,7 @@ class KotlinxIoCoreCommonSamples { assertEquals(-1, buffer.indexOf('\n'.code.toByte())) - buffer.writeUtf8("Hello\nThis is line 2\nAnd this one is third.") + buffer.writeString("Hello\nThis is line 2\nAnd this one is third.") assertEquals(5, buffer.indexOf('\n'.code.toByte())) assertEquals(20, buffer.indexOf('\n'.code.toByte(), startIndex = 6)) assertEquals(-1, buffer.indexOf('\n'.code.toByte(), startIndex = 21)) @@ -259,7 +259,7 @@ class KotlinxIoCoreCommonSamples { fun writeUtf8Sample() { val buffer = Buffer() - buffer.writeUtf8("hello", startIndex = 1, endIndex = 4) + buffer.writeString("hello", startIndex = 1, endIndex = 4) assertContentEquals( byteArrayOf( 'e'.code.toByte(), @@ -268,7 +268,7 @@ class KotlinxIoCoreCommonSamples { ), buffer.readByteArray() ) - buffer.writeUtf8("Δ") + buffer.writeString("Δ") assertContentEquals(byteArrayOf(0xce.toByte(), 0x94.toByte()), buffer.readByteArray()) } @@ -277,11 +277,11 @@ class KotlinxIoCoreCommonSamples { val buffer = Buffer() buffer.write("hello world".encodeToByteArray()) - assertEquals("hello", buffer.readUtf8(5)) - assertEquals(" world", buffer.readUtf8()) + assertEquals("hello", buffer.readString(5)) + assertEquals(" world", buffer.readString()) buffer.write(byteArrayOf(0xce.toByte(), 0x94.toByte())) - assertEquals("Δ", buffer.readUtf8()) + assertEquals("Δ", buffer.readString()) } @Test diff --git a/core/jvm/test/samples/samplesJvm.kt b/core/jvm/test/samples/samplesJvm.kt index aef2a77c0..01297d781 100644 --- a/core/jvm/test/samples/samplesJvm.kt +++ b/core/jvm/test/samples/samplesJvm.kt @@ -57,7 +57,7 @@ class KotlinxIoSamplesJvm { val buffer = Buffer() val nioByteBuffer = ByteBuffer.allocate(1024) - buffer.writeUtf8("hello") + buffer.writeString("hello") val bytesRead = buffer.readAtMostTo(nioByteBuffer) assertEquals(5, bytesRead) assertEquals(5, nioByteBuffer.capacity() - nioByteBuffer.remaining()) @@ -67,13 +67,13 @@ class KotlinxIoSamplesJvm { val bytesWrite = buffer.write(nioByteBuffer) assertEquals(5, bytesWrite) - assertEquals("hello", buffer.readUtf8()) + assertEquals("hello", buffer.readString()) } @Test fun bufferTransferToStream() { val buffer = Buffer() - buffer.writeUtf8("hello") + buffer.writeString("hello") val outputStream = ByteArrayOutputStream() buffer.readTo(outputStream) @@ -83,7 +83,7 @@ class KotlinxIoSamplesJvm { val inputStream = ByteArrayInputStream(outputStream.toByteArray()) buffer.transferFrom(inputStream) - assertEquals("hello", buffer.readUtf8()) + assertEquals("hello", buffer.readString()) } @Test @@ -92,18 +92,18 @@ class KotlinxIoSamplesJvm { val buffer = Buffer() buffer.write(inputStream, 5) - assertEquals("hello", buffer.readUtf8()) + assertEquals("hello", buffer.readString()) } @Test fun copyBufferToOutputStream() { val buffer = Buffer() - buffer.writeUtf8("string") + buffer.writeString("string") val outputStream = ByteArrayOutputStream() buffer.copyTo(outputStream, startIndex = 2, endIndex = 6) - assertEquals("string", buffer.readUtf8()) + assertEquals("string", buffer.readString()) assertEquals("ring", outputStream.toString("UTF-8")) } @@ -117,7 +117,7 @@ class KotlinxIoSamplesJvm { nioBuffer.limit(5) buffer.transferFrom(nioBuffer) - assertEquals("hello", buffer.readUtf8()) + assertEquals("hello", buffer.readString()) assertEquals(5, nioBuffer.position()) }