From 9292e68cb3199f9e90c1f1200ff5b65c5b9d96ae Mon Sep 17 00:00:00 2001 From: zoobestik Date: Thu, 17 Oct 2024 20:37:18 +0200 Subject: [PATCH] Remove files except .html --- .gitignore | 21 - .idea/vcs.xml | 20 - CHANGELOG-0.1.X.md | 65 - CHANGELOG.md | 156 -- CODE_OF_CONDUCT.md | 4 - CONTRIBUTING.md | 52 - LICENSE | 201 -- NOTICE | 8 - README.md | 100 - benchmarks/README.md | 20 - benchmarks/build.gradle.kts | 65 - benchmarks/src/commonMain/kotlin/BufferOps.kt | 482 ----- benchmarks/src/commonMain/kotlin/Constants.kt | 8 - .../src/commonMain/kotlin/PeekBenchmark.kt | 57 - .../commonMain/kotlin/ReadStringBenchmark.kt | 42 - .../commonMain/kotlin/SegmentPoolBenchmark.kt | 22 - .../kotlin/bytestring/ByteStringOps.kt | 266 --- .../jvmMain/kotlin/ByteBufferBenchmarks.kt | 39 - .../jvmMain/kotlin/SegmentPoolBenchmarkMT.kt | 36 - .../src/jvmMain/kotlin/StreamBenchmarks.kt | 73 - build-logic/build.gradle.kts | 22 - build-logic/settings.gradle.kts | 16 - .../kotlinx-io-android-compat.gradle.kts | 19 - .../conventions/kotlinx-io-clean.gradle.kts | 17 - .../conventions/kotlinx-io-dokka.gradle.kts | 36 - .../kotlinx-io-multiplatform.gradle.kts | 152 -- .../conventions/kotlinx-io-publish.gradle.kts | 124 -- build.gradle.kts | 49 - bytestring/Module.md | 3 - bytestring/api/kotlinx-io-bytestring.api | 122 -- bytestring/api/kotlinx-io-bytestring.klib.api | 93 - bytestring/apple/src/ByteStringApple.kt | 47 - bytestring/apple/test/ByteStringAppleTest.kt | 72 - bytestring/apple/test/samples/samplesApple.kt | 30 - bytestring/build.gradle.kts | 26 - bytestring/common/src/-Platform.kt | 14 - bytestring/common/src/Base64.kt | 247 --- bytestring/common/src/ByteString.kt | 544 ----- bytestring/common/src/ByteStringBuilder.kt | 128 -- bytestring/common/src/Hex.kt | 58 - bytestring/common/src/unsafe/Annotations.kt | 21 - .../src/unsafe/UnsafeByteStringOperations.kt | 44 - .../common/test/ByteStringBase64Test.kt | 147 -- .../common/test/ByteStringBuilderTest.kt | 150 -- bytestring/common/test/ByteStringHexTest.kt | 61 - bytestring/common/test/ByteStringTest.kt | 423 ---- bytestring/common/test/samples/samples.kt | 250 --- .../unsafe/UnsafeByteStringOperationsTest.kt | 24 - bytestring/jvm/src/-PlatformJvm.kt | 12 - bytestring/jvm/src/ByteStringJvmExt.kt | 136 -- .../ByteStringByteBufferExtensionsTest.kt | 98 - bytestring/jvm/test/ByteStringJvmTest.kt | 39 - bytestring/jvm/test/samples/samplesJvm.kt | 78 - bytestring/native/src/-PlatformNative.kt | 9 - core/Module.md | 89 - .../src/files/FileSystemAndroid.kt | 47 - core/api/kotlinx-io-core.api | 332 --- core/api/kotlinx-io-core.klib.api | 312 --- core/apple/src/-Util.kt | 21 - core/apple/src/AppleCore.kt | 112 -- core/apple/src/BuffersApple.kt | 72 - core/apple/src/SinksApple.kt | 160 -- core/apple/src/SourcesApple.kt | 179 -- core/apple/src/files/FileSystemApple.kt | 66 - core/apple/test/NSInputStreamSourceTest.kt | 85 - core/apple/test/NSOutputStreamSinkTest.kt | 51 - core/apple/test/SinkNSOutputStreamTest.kt | 191 -- core/apple/test/SourceNSInputStreamTest.kt | 283 --- core/apple/test/samples/samplesApple.kt | 54 - core/apple/test/utilApple.kt | 79 - core/build.gradle.kts | 85 - core/common/src/-CommonPlatform.kt | 40 - core/common/src/-Util.kt | 185 -- core/common/src/Annotations.kt | 55 - core/common/src/Buffer.kt | 677 ------- core/common/src/Buffers.kt | 79 - core/common/src/ByteStrings.kt | 166 -- core/common/src/Core.kt | 45 - core/common/src/PeekSource.kt | 75 - core/common/src/RawSink.kt | 65 - core/common/src/RawSource.kt | 60 - core/common/src/RealSink.kt | 157 -- core/common/src/RealSource.kt | 161 -- core/common/src/Segment.kt | 528 ----- core/common/src/SegmentPool.kt | 48 - core/common/src/Sink.kt | 205 -- core/common/src/Sinks.kt | 376 ---- core/common/src/Source.kt | 237 --- core/common/src/Sources.kt | 464 ----- core/common/src/Utf8.kt | 623 ------ core/common/src/files/FileSystem.kt | 210 -- core/common/src/files/Paths.kt | 176 -- core/common/src/internal/-Utf8.kt | 405 ---- .../src/unsafe/UnsafeBufferOperations.kt | 567 ------ core/common/test/AbstractSinkTest.kt | 514 ----- core/common/test/AbstractSourceTest.kt | 1785 ----------------- .../test/AlwaysSharedCopyTrackerTest.kt | 26 - core/common/test/CommonBufferTest.kt | 621 ------ core/common/test/CommonPlatformTest.kt | 50 - core/common/test/CommonRealSinkTest.kt | 224 --- core/common/test/CommonRealSourceTest.kt | 214 -- core/common/test/DelicateApiTest.kt | 53 - core/common/test/MockSink.kt | 63 - core/common/test/SinkFactory.kt | 41 - core/common/test/SourceFactory.kt | 155 -- core/common/test/Utf8Test.kt | 509 ----- core/common/test/UtilsTest.kt | 42 - core/common/test/files/SmokeFileTest.kt | 472 ----- .../common/test/files/SmokeFileTestWindows.kt | 45 - core/common/test/files/UtilsTest.kt | 55 - core/common/test/samples/byteStringSample.kt | 43 - .../test/samples/moduleDescriptionSample.kt | 72 - core/common/test/samples/rawSinkSample.kt | 73 - core/common/test/samples/rawSourceSample.kt | 83 - core/common/test/samples/samples.kt | 816 -------- .../test/samples/unsafe/unsafeSamples.kt | 327 --- .../UnsafeBufferOperationsForEachTest.kt | 73 - .../UnsafeBufferOperationsIterationTest.kt | 168 -- .../unsafe/UnsafeBufferOperationsMoveTest.kt | 91 - .../unsafe/UnsafeBufferOperationsReadTest.kt | 194 -- .../unsafe/UnsafeBufferOperationsWriteTest.kt | 249 --- core/common/test/util.kt | 167 -- core/js/src/-PlatformJs.kt | 31 - core/js/src/RawSink.kt | 14 - core/js/src/SegmentPool.kt | 19 - core/js/src/node/nodeModulesJs.kt | 39 - core/js/test/utils.kt | 8 - core/jvm/src/-JvmPlatform.kt | 26 - core/jvm/src/BuffersJvm.kt | 206 -- core/jvm/src/JvmCore.kt | 111 - core/jvm/src/RawSink.kt | 31 - core/jvm/src/SegmentPool.kt | 304 --- core/jvm/src/SinksJvm.kt | 132 -- core/jvm/src/SourcesJvm.kt | 176 -- core/jvm/src/files/FileSystemJvm.kt | 117 -- core/jvm/src/files/PathsJvm.kt | 54 - .../src/unsafe/UnsafeBufferOperationsJvm.kt | 185 -- core/jvm/test/AbstractSinkTestJVM.kt | 142 -- core/jvm/test/AbstractSourceTestJVM.kt | 264 --- core/jvm/test/BufferTest.kt | 260 --- core/jvm/test/JvmPlatformTest.kt | 203 -- core/jvm/test/NioTest.kt | 152 -- core/jvm/test/PoolingTest.kt | 155 -- core/jvm/test/RefCounteringCopyTrackerTest.kt | 35 - .../jvm/test/files/SmokeFileTestWindowsJVM.kt | 18 - core/jvm/test/samples/samplesJvm.kt | 139 -- .../test/samples/unsafeAccessSamplesJvm.kt | 124 -- .../UnsafeBufferOperationsJvmReadBulkTest.kt | 222 -- ...safeBufferOperationsJvmReadFromHeadTest.kt | 144 -- ...nsafeBufferOperationsJvmWriteToTailTest.kt | 127 -- core/jvm/test/utilJVM.kt | 28 - core/linux/src/files/FileSystemLinux.kt | 32 - core/mingw/src/files/FileSystemMingw.kt | 62 - .../test/files/SmokeFileTestWindowsMinGW.kt | 17 - core/native/src/-NonJvmPlatform.kt | 37 - core/native/src/RawSink.kt | 29 - core/native/src/SegmentPool.kt | 34 - core/native/src/files/FileSystemNative.kt | 132 -- core/native/src/files/PathsNative.kt | 150 -- core/native/test/util.kt | 28 - .../src/files/FileSystemNativeNonAndroid.kt | 35 - .../src/files/FileSystemNativeNonApple.kt | 32 - .../src/files/FileSystemNodeJs.kt | 131 -- .../src/files/PathsNodeJs.kt | 182 -- core/nodeFilesystemShared/src/node/buffer.kt | 25 - core/nodeFilesystemShared/src/node/fs.kt | 110 - core/nodeFilesystemShared/src/node/os.kt | 21 - core/nodeFilesystemShared/src/node/path.kt | 17 - core/nodeFilesystemShared/src/try.kt | 12 - .../test/files/SmokeFileTestWindowsNodeJs.kt | 20 - core/nodeFilesystemShared/test/files/utils.kt | 25 - core/unix/src/files/FileSystemUnix.kt | 36 - core/wasm/src/-PlatformWasm.kt | 22 - core/wasm/src/RawSink.kt | 14 - core/wasm/src/SegmentPool.kt | 19 - core/wasm/test/utils.kt | 8 - core/wasmJs/src/-PlatformWasmJs.kt | 25 - core/wasmJs/src/node/nodeModulesWasmJs.kt | 47 - core/wasmWasi/src/-WasmUtils.kt | 116 -- core/wasmWasi/src/files/FileSystemWasm.kt | 628 ------ core/wasmWasi/src/files/PathsWasm.kt | 57 - core/wasmWasi/src/wasi/functions.kt | 71 - core/wasmWasi/src/wasi/types.kt | 285 --- core/wasmWasi/test/WasiFsTest.kt | 231 --- core/wasmWasi/test/test-driver.mjs.template | 41 - core/wasmWasi/test/utils.kt | 14 - gradle.properties | 11 - gradle/libs.versions.toml | 25 - gradle/wrapper/gradle-wrapper.jar | Bin 43453 -> 0 bytes gradle/wrapper/gradle-wrapper.properties | 7 - gradlew | 249 --- gradlew.bat | 92 - kotlin-js-store/yarn.lock | 529 ----- settings.gradle.kts | 30 - smoke-tests/README.md | 20 - smoke-tests/build.gradle.kts | 100 - .../src/test/kotlin/GradleProjectsTest.kt | 114 -- .../bytestring-jvm/SmokeTest.kt | 17 - .../bytestring-multiplatform/SmokeTest.kt | 15 - .../gradle-projects/core-jvm/SmokeTest.kt | 33 - .../core-multiplatform/SmokeTest.kt | 42 - .../.mvn/wrapper/maven-wrapper.jar | Bin 62547 -> 0 bytes .../.mvn/wrapper/maven-wrapper.properties | 18 - .../maven-projects/bytestring-jvm/pom.xml | 67 - .../src/test/kotlin/SmokeTest.kt | 17 - .../resources/maven-projects/core-jvm/pom.xml | 65 - .../core-jvm/src/test/kotlin/SmokeTest.kt | 34 - .../src/test/resources/maven-projects/mvnw | 308 --- .../test/resources/maven-projects/mvnw.cmd | 205 -- .../src/test/resources/maven-projects/pom.xml | 52 - .../resources/templates/jvm.build.gradle.kts | 26 - .../resources/templates/kmp.build.gradle.kts | 94 - .../resources/templates/settings.gradle.kts | 12 - 213 files changed, 28488 deletions(-) delete mode 100644 .idea/vcs.xml delete mode 100644 CHANGELOG-0.1.X.md delete mode 100644 CHANGELOG.md delete mode 100644 CODE_OF_CONDUCT.md delete mode 100644 CONTRIBUTING.md delete mode 100644 LICENSE delete mode 100644 NOTICE delete mode 100644 README.md delete mode 100644 benchmarks/README.md delete mode 100644 benchmarks/build.gradle.kts delete mode 100644 benchmarks/src/commonMain/kotlin/BufferOps.kt delete mode 100644 benchmarks/src/commonMain/kotlin/Constants.kt delete mode 100644 benchmarks/src/commonMain/kotlin/PeekBenchmark.kt delete mode 100644 benchmarks/src/commonMain/kotlin/ReadStringBenchmark.kt delete mode 100644 benchmarks/src/commonMain/kotlin/SegmentPoolBenchmark.kt delete mode 100644 benchmarks/src/commonMain/kotlin/bytestring/ByteStringOps.kt delete mode 100644 benchmarks/src/jvmMain/kotlin/ByteBufferBenchmarks.kt delete mode 100644 benchmarks/src/jvmMain/kotlin/SegmentPoolBenchmarkMT.kt delete mode 100644 benchmarks/src/jvmMain/kotlin/StreamBenchmarks.kt delete mode 100644 build-logic/build.gradle.kts delete mode 100644 build-logic/settings.gradle.kts delete mode 100644 build-logic/src/main/kotlin/kotlinx/io/conventions/kotlinx-io-android-compat.gradle.kts delete mode 100644 build-logic/src/main/kotlin/kotlinx/io/conventions/kotlinx-io-clean.gradle.kts delete mode 100644 build-logic/src/main/kotlin/kotlinx/io/conventions/kotlinx-io-dokka.gradle.kts delete mode 100644 build-logic/src/main/kotlin/kotlinx/io/conventions/kotlinx-io-multiplatform.gradle.kts delete mode 100644 build-logic/src/main/kotlin/kotlinx/io/conventions/kotlinx-io-publish.gradle.kts delete mode 100644 build.gradle.kts delete mode 100644 bytestring/Module.md delete mode 100644 bytestring/api/kotlinx-io-bytestring.api delete mode 100644 bytestring/api/kotlinx-io-bytestring.klib.api delete mode 100644 bytestring/apple/src/ByteStringApple.kt delete mode 100644 bytestring/apple/test/ByteStringAppleTest.kt delete mode 100644 bytestring/apple/test/samples/samplesApple.kt delete mode 100644 bytestring/build.gradle.kts delete mode 100644 bytestring/common/src/-Platform.kt delete mode 100644 bytestring/common/src/Base64.kt delete mode 100644 bytestring/common/src/ByteString.kt delete mode 100644 bytestring/common/src/ByteStringBuilder.kt delete mode 100644 bytestring/common/src/Hex.kt delete mode 100644 bytestring/common/src/unsafe/Annotations.kt delete mode 100644 bytestring/common/src/unsafe/UnsafeByteStringOperations.kt delete mode 100644 bytestring/common/test/ByteStringBase64Test.kt delete mode 100644 bytestring/common/test/ByteStringBuilderTest.kt delete mode 100644 bytestring/common/test/ByteStringHexTest.kt delete mode 100644 bytestring/common/test/ByteStringTest.kt delete mode 100644 bytestring/common/test/samples/samples.kt delete mode 100644 bytestring/common/test/unsafe/UnsafeByteStringOperationsTest.kt delete mode 100644 bytestring/jvm/src/-PlatformJvm.kt delete mode 100644 bytestring/jvm/src/ByteStringJvmExt.kt delete mode 100644 bytestring/jvm/test/ByteStringByteBufferExtensionsTest.kt delete mode 100644 bytestring/jvm/test/ByteStringJvmTest.kt delete mode 100644 bytestring/jvm/test/samples/samplesJvm.kt delete mode 100644 bytestring/native/src/-PlatformNative.kt delete mode 100644 core/Module.md delete mode 100644 core/androidNative/src/files/FileSystemAndroid.kt delete mode 100644 core/api/kotlinx-io-core.api delete mode 100644 core/api/kotlinx-io-core.klib.api delete mode 100644 core/apple/src/-Util.kt delete mode 100644 core/apple/src/AppleCore.kt delete mode 100644 core/apple/src/BuffersApple.kt delete mode 100644 core/apple/src/SinksApple.kt delete mode 100644 core/apple/src/SourcesApple.kt delete mode 100644 core/apple/src/files/FileSystemApple.kt delete mode 100644 core/apple/test/NSInputStreamSourceTest.kt delete mode 100644 core/apple/test/NSOutputStreamSinkTest.kt delete mode 100644 core/apple/test/SinkNSOutputStreamTest.kt delete mode 100644 core/apple/test/SourceNSInputStreamTest.kt delete mode 100644 core/apple/test/samples/samplesApple.kt delete mode 100644 core/apple/test/utilApple.kt delete mode 100644 core/build.gradle.kts delete mode 100644 core/common/src/-CommonPlatform.kt delete mode 100644 core/common/src/-Util.kt delete mode 100644 core/common/src/Annotations.kt delete mode 100644 core/common/src/Buffer.kt delete mode 100644 core/common/src/Buffers.kt delete mode 100644 core/common/src/ByteStrings.kt delete mode 100644 core/common/src/Core.kt delete mode 100644 core/common/src/PeekSource.kt delete mode 100644 core/common/src/RawSink.kt delete mode 100644 core/common/src/RawSource.kt delete mode 100644 core/common/src/RealSink.kt delete mode 100644 core/common/src/RealSource.kt delete mode 100644 core/common/src/Segment.kt delete mode 100644 core/common/src/SegmentPool.kt delete mode 100644 core/common/src/Sink.kt delete mode 100644 core/common/src/Sinks.kt delete mode 100644 core/common/src/Source.kt delete mode 100644 core/common/src/Sources.kt delete mode 100644 core/common/src/Utf8.kt delete mode 100644 core/common/src/files/FileSystem.kt delete mode 100644 core/common/src/files/Paths.kt delete mode 100644 core/common/src/internal/-Utf8.kt delete mode 100644 core/common/src/unsafe/UnsafeBufferOperations.kt delete mode 100644 core/common/test/AbstractSinkTest.kt delete mode 100644 core/common/test/AbstractSourceTest.kt delete mode 100644 core/common/test/AlwaysSharedCopyTrackerTest.kt delete mode 100644 core/common/test/CommonBufferTest.kt delete mode 100644 core/common/test/CommonPlatformTest.kt delete mode 100644 core/common/test/CommonRealSinkTest.kt delete mode 100644 core/common/test/CommonRealSourceTest.kt delete mode 100644 core/common/test/DelicateApiTest.kt delete mode 100644 core/common/test/MockSink.kt delete mode 100644 core/common/test/SinkFactory.kt delete mode 100644 core/common/test/SourceFactory.kt delete mode 100644 core/common/test/Utf8Test.kt delete mode 100644 core/common/test/UtilsTest.kt delete mode 100644 core/common/test/files/SmokeFileTest.kt delete mode 100644 core/common/test/files/SmokeFileTestWindows.kt delete mode 100644 core/common/test/files/UtilsTest.kt delete mode 100644 core/common/test/samples/byteStringSample.kt delete mode 100644 core/common/test/samples/moduleDescriptionSample.kt delete mode 100644 core/common/test/samples/rawSinkSample.kt delete mode 100644 core/common/test/samples/rawSourceSample.kt delete mode 100644 core/common/test/samples/samples.kt delete mode 100644 core/common/test/samples/unsafe/unsafeSamples.kt delete mode 100644 core/common/test/unsafe/UnsafeBufferOperationsForEachTest.kt delete mode 100644 core/common/test/unsafe/UnsafeBufferOperationsIterationTest.kt delete mode 100644 core/common/test/unsafe/UnsafeBufferOperationsMoveTest.kt delete mode 100644 core/common/test/unsafe/UnsafeBufferOperationsReadTest.kt delete mode 100644 core/common/test/unsafe/UnsafeBufferOperationsWriteTest.kt delete mode 100644 core/common/test/util.kt delete mode 100644 core/js/src/-PlatformJs.kt delete mode 100644 core/js/src/RawSink.kt delete mode 100644 core/js/src/SegmentPool.kt delete mode 100644 core/js/src/node/nodeModulesJs.kt delete mode 100644 core/js/test/utils.kt delete mode 100644 core/jvm/src/-JvmPlatform.kt delete mode 100644 core/jvm/src/BuffersJvm.kt delete mode 100644 core/jvm/src/JvmCore.kt delete mode 100644 core/jvm/src/RawSink.kt delete mode 100644 core/jvm/src/SegmentPool.kt delete mode 100644 core/jvm/src/SinksJvm.kt delete mode 100644 core/jvm/src/SourcesJvm.kt delete mode 100644 core/jvm/src/files/FileSystemJvm.kt delete mode 100644 core/jvm/src/files/PathsJvm.kt delete mode 100644 core/jvm/src/unsafe/UnsafeBufferOperationsJvm.kt delete mode 100644 core/jvm/test/AbstractSinkTestJVM.kt delete mode 100644 core/jvm/test/AbstractSourceTestJVM.kt delete mode 100644 core/jvm/test/BufferTest.kt delete mode 100644 core/jvm/test/JvmPlatformTest.kt delete mode 100644 core/jvm/test/NioTest.kt delete mode 100644 core/jvm/test/PoolingTest.kt delete mode 100644 core/jvm/test/RefCounteringCopyTrackerTest.kt delete mode 100644 core/jvm/test/files/SmokeFileTestWindowsJVM.kt delete mode 100644 core/jvm/test/samples/samplesJvm.kt delete mode 100644 core/jvm/test/samples/unsafeAccessSamplesJvm.kt delete mode 100644 core/jvm/test/unsafe/UnsafeBufferOperationsJvmReadBulkTest.kt delete mode 100644 core/jvm/test/unsafe/UnsafeBufferOperationsJvmReadFromHeadTest.kt delete mode 100644 core/jvm/test/unsafe/UnsafeBufferOperationsJvmWriteToTailTest.kt delete mode 100644 core/jvm/test/utilJVM.kt delete mode 100644 core/linux/src/files/FileSystemLinux.kt delete mode 100644 core/mingw/src/files/FileSystemMingw.kt delete mode 100644 core/mingw/test/files/SmokeFileTestWindowsMinGW.kt delete mode 100644 core/native/src/-NonJvmPlatform.kt delete mode 100644 core/native/src/RawSink.kt delete mode 100644 core/native/src/SegmentPool.kt delete mode 100644 core/native/src/files/FileSystemNative.kt delete mode 100644 core/native/src/files/PathsNative.kt delete mode 100644 core/native/test/util.kt delete mode 100644 core/nativeNonAndroid/src/files/FileSystemNativeNonAndroid.kt delete mode 100644 core/nativeNonApple/src/files/FileSystemNativeNonApple.kt delete mode 100644 core/nodeFilesystemShared/src/files/FileSystemNodeJs.kt delete mode 100644 core/nodeFilesystemShared/src/files/PathsNodeJs.kt delete mode 100644 core/nodeFilesystemShared/src/node/buffer.kt delete mode 100644 core/nodeFilesystemShared/src/node/fs.kt delete mode 100644 core/nodeFilesystemShared/src/node/os.kt delete mode 100644 core/nodeFilesystemShared/src/node/path.kt delete mode 100644 core/nodeFilesystemShared/src/try.kt delete mode 100644 core/nodeFilesystemShared/test/files/SmokeFileTestWindowsNodeJs.kt delete mode 100644 core/nodeFilesystemShared/test/files/utils.kt delete mode 100644 core/unix/src/files/FileSystemUnix.kt delete mode 100644 core/wasm/src/-PlatformWasm.kt delete mode 100644 core/wasm/src/RawSink.kt delete mode 100644 core/wasm/src/SegmentPool.kt delete mode 100644 core/wasm/test/utils.kt delete mode 100644 core/wasmJs/src/-PlatformWasmJs.kt delete mode 100644 core/wasmJs/src/node/nodeModulesWasmJs.kt delete mode 100644 core/wasmWasi/src/-WasmUtils.kt delete mode 100644 core/wasmWasi/src/files/FileSystemWasm.kt delete mode 100644 core/wasmWasi/src/files/PathsWasm.kt delete mode 100644 core/wasmWasi/src/wasi/functions.kt delete mode 100644 core/wasmWasi/src/wasi/types.kt delete mode 100644 core/wasmWasi/test/WasiFsTest.kt delete mode 100644 core/wasmWasi/test/test-driver.mjs.template delete mode 100644 core/wasmWasi/test/utils.kt delete mode 100644 gradle.properties delete mode 100644 gradle/libs.versions.toml delete mode 100644 gradle/wrapper/gradle-wrapper.jar delete mode 100644 gradle/wrapper/gradle-wrapper.properties delete mode 100755 gradlew delete mode 100644 gradlew.bat delete mode 100644 kotlin-js-store/yarn.lock delete mode 100644 settings.gradle.kts delete mode 100644 smoke-tests/README.md delete mode 100644 smoke-tests/build.gradle.kts delete mode 100644 smoke-tests/src/test/kotlin/GradleProjectsTest.kt delete mode 100644 smoke-tests/src/test/resources/gradle-projects/bytestring-jvm/SmokeTest.kt delete mode 100644 smoke-tests/src/test/resources/gradle-projects/bytestring-multiplatform/SmokeTest.kt delete mode 100644 smoke-tests/src/test/resources/gradle-projects/core-jvm/SmokeTest.kt delete mode 100644 smoke-tests/src/test/resources/gradle-projects/core-multiplatform/SmokeTest.kt delete mode 100644 smoke-tests/src/test/resources/maven-projects/.mvn/wrapper/maven-wrapper.jar delete mode 100644 smoke-tests/src/test/resources/maven-projects/.mvn/wrapper/maven-wrapper.properties delete mode 100644 smoke-tests/src/test/resources/maven-projects/bytestring-jvm/pom.xml delete mode 100644 smoke-tests/src/test/resources/maven-projects/bytestring-jvm/src/test/kotlin/SmokeTest.kt delete mode 100644 smoke-tests/src/test/resources/maven-projects/core-jvm/pom.xml delete mode 100644 smoke-tests/src/test/resources/maven-projects/core-jvm/src/test/kotlin/SmokeTest.kt delete mode 100755 smoke-tests/src/test/resources/maven-projects/mvnw delete mode 100644 smoke-tests/src/test/resources/maven-projects/mvnw.cmd delete mode 100644 smoke-tests/src/test/resources/maven-projects/pom.xml delete mode 100644 smoke-tests/src/test/resources/templates/jvm.build.gradle.kts delete mode 100644 smoke-tests/src/test/resources/templates/kmp.build.gradle.kts delete mode 100644 smoke-tests/src/test/resources/templates/settings.gradle.kts diff --git a/.gitignore b/.gitignore index e3813e39f..78d47efa0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,24 +1,3 @@ -build -bin .idea *.iml -.gradle *.swp -.project - -.kotlin - -kotlinx-io-native/repo/ - -!.idea/vcs.xml -!.idea/dictionaries -!.idea/dictionaries/* -!.idea/codeStyles -!.idea/codeStyles/* - -.gradletasknamecache -.settings -.classpath - -logs - diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index d7e1d1533..000000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/CHANGELOG-0.1.X.md b/CHANGELOG-0.1.X.md deleted file mode 100644 index c5bd635f7..000000000 --- a/CHANGELOG-0.1.X.md +++ /dev/null @@ -1,65 +0,0 @@ -# 0.1.5 -> Published 12 Feb 2019 - -- `AbstractOutput` is implementable, simplified API -- Fixed ISO-8859-1 and other character encodings on native -- Fixed segfault caused by a wrong character encoding specified -- Introduced initial unsigned types support (#28) -- Introduced initial POSIX synchronous support (#34): - - added `Input(fileDescriptor)` and `Output(fileDescriptor)` - - added `read`, `write`, `fread`, `fwrite`, `send`, `receive`, `sendto`, `recvfrom` - with `IoBuffer` parameter -- Introduced initial `PosixException` support -- Strengthened internal API restrictions -- Introduced `Input.copyTo(Output)` utility function -- Introduced multiplatform `IOException` -- Introduced `reverseByteOrder` for primitive numeric types -- Fixed several memory management bugs -- Eliminated accidentally used JDK8+ API (#35) -- Fixed loosing trailing bytes in byte channel on native and JS ( - [ktor/787](https://github.com/ktorio/ktor/issues/787), - [ktor/920](https://github.com/ktorio/ktor/issues/920) - ) -- Improved `readDirect`/`writeDirect` functions on platforms. -- Fixed non-local returns from `use {}` block. -- Kotlin 1.3.21 - -# 0.1.4 -> Published 23 Jan 2019 - -- Fixed byteOrder switch for packets (#30) -- Upgrade to Gradle 4.10 with new metadata -- Kotlin 1.3.20 - -# 0.1.3 -> Published 25 Dec 2018 - -- Fixed wrong pom dependencies - -# 0.1.2 -> Published 24 Dec 2018 - -- Fixed byte channel constructor from an array -- Fixed endGap related errors (#23) -- Introduced suspending consumeEachRemaining (#22) -- Kotlin 1.3.11, kotlinx.coroutines 1.1.0 -- Fixed await returned wrong result in sequential implementation (#24) -- `await` and `awaitAtLeast` contract clarified (#24) -- Fixed blocking I/O adapter to use coroutine's event loop - -# 0.1.1 -> Published 4 Dec 2018 - -- Fixed ability to implement DefaultPool in common -- Fixed error "Unable to stop reading in state Writing" -- Fixed tryPeek implementation to not consume byte -- Introduced peekCharUtf8 -- Added a cpointer constructor to native IoBuffer so that IoBuffer can be used to read AND write on a memory chunk -- Made ByteChannel pass original cause from the owner job -- Fixed reading UTF-8 lines -- Fixed empty chunk view creation -- Utility functions takeWhile* improvements - -# 0.1.0 -> Published 15 Nov 2018 -Initial release, maven central diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index ec7b69e5c..000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,156 +0,0 @@ -# CHANGELOG - -## 0.5.4 -> Published 17 September 2024 - -### Features -- Extended Unsafe API with a function allowing to iterate over all Buffer's segments [#383](https://github.com/Kotlin/kotlinx-io/pull/383) -- Implemented `ByteString` conversion to/from `NSData` on Apple platforms [#384](https://github.com/Kotlin/kotlinx-io/pull/384) -- Implemented extensions to read/write `ByteString` from to/from `ByteBuffer`, as well as representing - `ByteString` as a read-only `ByteBuffer` on JVM [#387](https://github.com/Kotlin/kotlinx-io/pull/387) -- Implemented `ByteString` factory accepting unsigned bytes [#390](https://github.com/Kotlin/kotlinx-io/pull/390) -- Reimplemented various functions using Unsafe API [#337](https://github.com/Kotlin/kotlinx-io/pull/337) -- Minor documentation improvements - -## 0.5.3 -> Published 19 August 2024 - -### Features -- Updated JS `braces` library to `3.0.3` -- More unnecessary backing fields were eliminated [#374](https://github.com/Kotlin/kotlinx-io/pull/374) - -## 0.5.2 -> Published 15 August 2024 - -### Features -- Unsafe API method signatures were updated to return number of written/read bytes [#360](https://github.com/Kotlin/kotlinx-io/issues/360) - - Note that it's a breaking change as return type is a part of method signature. - However, updated methods are all inline, so in general, - projects depending on libraries that were compiled against the previous version - should not experience binary incompatibility issues after adding a dependency on a never `kotlinx-io` version. - -- Added call-in-place contracts to Unsafe API methods [#361](https://github.com/Kotlin/kotlinx-io/issues/361) -- Updated Gradle and some dependencies [#353](https://github.com/Kotlin/kotlinx-io/pull/353) -- Improved build scripts [#371](https://github.com/Kotlin/kotlinx-io/pull/371) -- Removed the `Buffer.buffer` backing field [#366](https://github.com/Kotlin/kotlinx-io/pull/366) - -## 0.5.1 -> Published 15 July 2024 - -### Bugfixes -- Fixed a bug in segment pool implementation affecting a second level pool uses. - -## 0.5.0 -> Published 12 July 2024 - -### Features -- Provided an API allowing direct access to Buffer and Segment internals [#135](https://github.com/Kotlin/kotlinx-io/issues/135), [#166](https://github.com/Kotlin/kotlinx-io/issues/166) - - The API is unsafe, delisted from public docs and requires explicit opt-in. It's recommended to - avoid this API unless you're working on integration with other APIs (like, `java.nio` or - `io_uring`, for example). - -- Improved the way segment pooling is working on JVM [#352](https://github.com/Kotlin/kotlinx-io/pull/352) - - Now sharing a segment won't make an original segment and all its copies recyclable. - Instead, the last remaining copy will be placed back into the pool when recycled. - Segments are no longer allocated or lost when taking or recycling a segment from pool - under a high contention due to concurrent requests. - Size of the segment pool on the JVM could now be statically configured by setting a system property - `kotlinx.io.pool.size.bytes`. - -## 0.4.0 -> Published 6 June 2024 - -### Features -- Updated Kotlin to 2.0 [#327](https://github.com/Kotlin/kotlinx-io/pull/327) - -## 0.3.5 -> Published 17 May 2024 - -### Features -- Provided an extension function to write `CharSequence`s (`Sink.writeString`) [#318](https://github.com/Kotlin/kotlinx-io/pull/318) -- Various minor improvements in build scripts and docs - -## 0.3.4 -> Published 8 May 2024 - -### Features -- Kotlin updated to 1.9.24 [#315](https://github.com/Kotlin/kotlinx-io/pull/315) -- Supported `linuxArm32Hfp` target [#303](https://github.com/Kotlin/kotlinx-io/issues/303) -- Enabled KLib ABI validation -- Provided extension functions to read (`Source.readCodePointValue`) and write (`Sink.writeCodePointValue`) - Unicode code point values [#307](https://github.com/Kotlin/kotlinx-io/issues/307) -- Provided function to list directories (`FileSystem.list`) [#222](https://github.com/Kotlin/kotlinx-io/issues/222) - -## 0.3.3 -> Published 22 Apr 2024 - -### Features -- Reimplemented file metadata gathering without required reasoning APIs on Apple targets [#297](https://github.com/Kotlin/kotlinx-io/pull/297) - -### Bugfixes -- For JS target, Webpack should no longer report missing nodejs modules during compilation [#285](https://github.com/Kotlin/kotlinx-io/pull/285) - -## 0.3.2 -> Published 18 Mar 2024 - -### Features -- Implemented basic filesystem support for Wasm WASI target [#257](https://github.com/Kotlin/kotlinx-io/pull/257) -- Enabled native benchmarks by default [#263](https://github.com/Kotlin/kotlinx-io/pull/263) - -## 0.3.1 -> Published 2 Feb 2024 - -### Features -- Updated Kotlin to `1.9.22` -- Enabled Wasm Wasi target [#236](https://github.com/Kotlin/kotlinx-io/pull/236) (Note that neither of Wasm targets has filesystem support yet) -- Support path resolution [#228](https://github.com/Kotlin/kotlinx-io/pull/228) - -### Bugfixes -- Fixed `Path::parent` behavior on Windows [#227](https://github.com/Kotlin/kotlinx-io/pull/227) -- Aligned behavior of `FileSystem::sink` and `FileSystem::source` across all platforms [#252](https://github.com/Kotlin/kotlinx-io/pull/252) -- Fixed handling of paths containing a whitespace character [#248](https://github.com/Kotlin/kotlinx-io/pull/248) - -## 0.3.0 -> Published 13 Sep 2023 - -### Features -- Enabled Wasm target [#164](https://github.com/Kotlin/kotlinx-io/issues/164) -- Added Sink/Source integration with Apple's NSInputStream and NSOutputStream ([#174](https://github.com/Kotlin/kotlinx-io/pull/174)) -- Added extension functions integrating ByteString with Base64 and HexFormat APIs ([#149](https://github.com/Kotlin/kotlinx-io/issues/149)) -- Added extension functions to read and write floating point numbers ([#167](https://github.com/Kotlin/kotlinx-io/issues/167)) -- Extended filesystems support by adding functions to create and delete files and directories, check their existence, - perform atomic move, and get file size ([#211](https://github.com/Kotlin/kotlinx-io/issues/211), - [#214](https://github.com/Kotlin/kotlinx-io/issues/214)). - Also extended Path's API to request Path's parent and to get file's name - ([#206](https://github.com/Kotlin/kotlinx-io/issues/206), [#212](https://github.com/Kotlin/kotlinx-io/issues/212)). -- Updated Kotlin version to 1.9.10 - -### Bugfixes -- Fixed undefined behavior in the ByteString's hashCode computation on native targets ([#190](https://github.com/Kotlin/kotlinx-io/issues/190)) -- Fixed compatibility issues with Android API 25 and below ([#202](https://github.com/Kotlin/kotlinx-io/issues/202)) - -## 0.2.1 -> Published 11 Jul 2023 - -The release includes a bug fix solving the issue with dependency management. - -### Bugfixes -- Fixed the dependency type for `bytesting` module, - it is no longer required to explicitly specify it when using `kotlinx-io-core` - ([#169](https://github.com/Kotlin/kotlinx-io/issues/169)). - -## 0.2.0 -> Published 3 Jul 2023 - -Initial release of the new `kotlinx-io` version implemented based on `Okio` library. - -### Features -- A trimmed-down and reworked version of the core Okio API - ([#132](https://github.com/Kotlin/kotlinx-io/issues/132), [#137](https://github.com/Kotlin/kotlinx-io/issues/137)) -- ByteString implementation ([#133](https://github.com/Kotlin/kotlinx-io/issues/133)) - ---- -Changelog for previous versions may be found in [CHANGELOG-0.1.X.md](CHANGELOG-0.1.X.md) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 85ed20dba..000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,4 +0,0 @@ -## Code of Conduct - -This project and the corresponding community is governed by the [JetBrains Open Source and Community Code of Conduct](https://confluence.jetbrains.com/display/ALL/JetBrains+Open+Source+and+Community+Code+of+Conduct). Please make sure you read it. - diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 73f83958a..000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,52 +0,0 @@ -# Contributing Guidelines - -One can contribute to the project by reporting issues or submitting changes via pull request. - -## Reporting issues - -Please use [GitHub issues](https://github.com/Kotlin/kotlinx-io/issues) for filing feature requests and bug reports. - -Questions about usage and general inquiries are better suited for StackOverflow or the #io channel in KotlinLang Slack. - -## Submitting changes - -Submit pull requests [here](https://github.com/Kotlin/kotlinx-io/pulls). -However, please keep in mind that maintainers will have to support the resulting code of the project, -so do familiarize yourself with the following guidelines. - -* All development (both new features and bug fixes) is performed in the `develop` branch. - * The `master` branch contains the sources of the most recently released version. - * Base your PRs against the `develop` branch. - * The `develop` branch is pushed to the `master` branch during release. - * Documentation in markdown files can be updated directly in the `master` branch, - unless the documentation is in the source code, and the patch changes line numbers. -* If you make any code changes: - * Follow the [Kotlin Coding Conventions](https://kotlinlang.org/docs/reference/coding-conventions.html). - * Use 4 spaces for indentation. - * Use imports with '*'. - * [Build the project](#building) to make sure it all works and passes the tests. -* If you fix a bug: - * Write the test that reproduces the bug. - * Fixes without tests are accepted only in exceptional circumstances if it can be shown that writing the - corresponding test is too hard or otherwise impractical. - * Follow the style of writing tests that is used in this project: - name test functions as `testXxx`. Don't use backticks in test names. -* Comment on the existing issue if you want to work on it. Ensure that the issue not only describes a problem, but also describes a solution that has received positive feedback. Propose a solution if none has been suggested. - -## Building - -This library is built with Gradle. - -* Run `./gradlew build` to build. It also runs all the tests. -* Run `./gradlew :check` to test the module you are looking at to speed - things up during development. -* Run `./gradlew :jvmTest` to perform only the fast JVM tests of a multiplatform module. - -You can import this project into IDEA, but you have to delegate build actions -to Gradle (in Preferences -> Build, Execution, Deployment -> Build Tools -> Gradle -> Build and run). - -### Updating the public API dump - -* Use the [Binary Compatibility Validator](https://github.com/Kotlin/binary-compatibility-validator/blob/master/README.md) for updates to public API: - * Run `./gradlew apiDump` to update API index files. - * Commit the updated API indexes together with other changes. diff --git a/LICENSE b/LICENSE deleted file mode 100644 index f5208f2f2..000000000 --- a/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/NOTICE b/NOTICE deleted file mode 100644 index 32ae3ac4f..000000000 --- a/NOTICE +++ /dev/null @@ -1,8 +0,0 @@ - ========================================================================= - == NOTICE file corresponding to the section 4 d of == - == the Apache License, Version 2.0, == - == in this case for the kotlinx-io library. == - ========================================================================= - - kotlinx-io library. - Copyright 2017-2023 JetBrains s.r.o and respective authors and developers \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index bca1c2c7a..000000000 --- a/README.md +++ /dev/null @@ -1,100 +0,0 @@ -# kotlinx-io - -[![Kotlin Alpha](https://kotl.in/badges/alpha.svg)](https://kotlinlang.org/docs/components-stability.html) -[![JetBrains incubator project](https://jb.gg/badges/incubator.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) -[![GitHub license](https://img.shields.io/github/license/kotlin/kotlinx-io)](LICENSE) -[![Download](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-io-core)](https://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-io-core/) -[![Kotlin](https://img.shields.io/badge/kotlin-2.0-blue.svg?logo=kotlin)](http://kotlinlang.org) -[![TeamCity build](https://img.shields.io/teamcity/build/s/KotlinTools_KotlinxIo_BuildAggregated.svg?server=http%3A%2F%2Fteamcity.jetbrains.com)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=KotlinTools_KotlinxIo_BuildAggregated&guest=1) -[![KDoc link](https://img.shields.io/badge/API_reference-KDoc-blue)](https://kotlin.github.io/kotlinx-io/) - -A multiplatform Kotlin library providing basic IO primitives. `kotlinx-io` is based on [Okio](https://github.com/square/okio) but does not preserve backward compatibility with it. - -## Overview -**kotlinx-io** is built around `Buffer` - a mutable sequence of bytes. - -`Buffer` works like a queue, allowing to read data from its head or to write data to its tail. -`Buffer` provides functions to read and write data of different built-in types, and to copy data to or from other `Buffer`s. -Depending on the target platform, extension functions allowing data exchange with platform-specific types are also available. - -A `Buffer` consists of segments organized as a linked list: segments allow reducing memory allocations during the buffer's expansion and copy, -with the latter achieved by delegating or sharing the ownership over the underlying buffer's segments with other buffers. - -**kotlinx-io** provides interfaces representing data sources and destinations - `Source` and `Sink`, -and in addition to the *mutable* `Buffer` the library also provides an *immutable* sequence of bytes - `ByteString`. - -An experimental filesystem support is shipped under the `kotlinx.io.files` package, -which includes the `FileSystem` interface and its default implementation - `SystemFileSystem`. - -`FileSystem` provides basic operations for working with files and directories, which are represented by yet another class under the same package - `Path`. - -There are two `kotlinx-io` modules: -- [kotlinx-io-bytestring](./bytestring) - provides `ByteString`. -- [kotlinx-io-core](./core) - provides IO primitives (`Buffer`, `Source`, `Sink`), filesystems support, depends on `kotlinx-io-bytestring`. - -## Using in your projects - -> Note that the library is experimental, and the API is subject to change. - -### Gradle - -Make sure that you have `mavenCentral()` in the list of repositories: -```kotlin -repositories { - mavenCentral() -} -``` - -Add the library to dependencies: -```kotlin -dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-io-core:0.5.4") -} -``` - -In multiplatform projects, add a dependency to the `commonMain` source set dependencies: -```kotlin -kotlin { - sourceSets { - commonMain { - dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-io-core:0.5.4") - } - } - } -} -``` - -### Maven - -Add the library to dependencies: -```xml - - org.jetbrains.kotlinx - kotlinx-io-core-jvm - 0.5.4 - -``` - -### Android - -`kotlinx-io` is not tested on Android on a regular basis, -but the library is compatible with Android 5.0+ (API level 21+). - -## Contributing - -Read the [Contributing Guidelines](CONTRIBUTING.md). - -## Code of Conduct -This project and the corresponding community are governed by the [JetBrains Open Source and Community Code of Conduct](https://confluence.jetbrains.com/display/ALL/JetBrains+Open+Source+and+Community+Code+of+Conduct). Please make sure you read it. - -## License -kotlinx-io is licensed under the [Apache 2.0 License](LICENSE). - -## Credits - -Thanks to everyone involved in the project. - -An honorable mention goes to the developers of [Okio](https://square.github.io/okio/) -that served as the foundation for `kotlinx-io` and to [Jesse Wilson](https://github.com/swankjesse), -for the help with `Okio` adaption, his suggestions, assistance and guidance with `kotlinx-io` development. diff --git a/benchmarks/README.md b/benchmarks/README.md deleted file mode 100644 index dc2917d35..000000000 --- a/benchmarks/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# kotlinx-io benchmarks - -The module consists of benchmarks aimed to track performance of kotlinx-io implementation. - -Currently, the suite includes benchmarks on: -- the core Buffer API usage: read/write primitive types, arrays, UTF8 strings; -- basic `peek` usage; -- segment pooling performance. - -The suite doesn't include benchmarks for more complex APIs inherited from Okio as these APIs are subject to change. -Such benchmarks will be added later along with corresponding changes in the library. - -### Quickstart - -For JVM: -``` -./gradlew :kotlinx-io-benchmarks:jvmJar - -java -jar benchmarks/build/benchmarks/jvm/jars/kotlinx-io-benchmarks-jvm-jmh-0.6.0-SNAPSHOT-JMH.jar ReadStringBenchmark -f 1 -wi 5 -i 5 -tu us -w 1 -r 1 -``` \ No newline at end of file diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts deleted file mode 100644 index e888a74da..000000000 --- a/benchmarks/build.gradle.kts +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -import kotlinx.benchmark.gradle.JvmBenchmarkTarget -import org.jetbrains.kotlin.konan.target.HostManager -import org.jetbrains.kotlin.konan.target.KonanTarget - -plugins { - kotlin("multiplatform") - alias(libs.plugins.kotlinx.benchmark.plugin) - id("kotlinx-io-clean") -} - -kotlin { - jvmToolchain { - languageVersion = JavaLanguageVersion.of(libs.versions.java.get()) - } - - jvm() - - sourceSets { - commonMain { - dependencies { - implementation(project(":kotlinx-io-core")) - implementation(libs.kotlinx.benchmark.runtime) - } - } - - named("jvmMain") { - dependsOn(commonMain.get()) - } - } -} - -val nativeBenchmarksEnabled: String by project.parent!! - -if (nativeBenchmarksEnabled.toBoolean()) { - kotlin { - // TODO: consider supporting non-host native targets. - if (HostManager.host === KonanTarget.MACOS_X64) macosX64("native") - if (HostManager.host === KonanTarget.MACOS_ARM64) macosArm64("native") - if (HostManager.hostIsLinux) linuxX64("native") - if (HostManager.hostIsMingw) mingwX64("native") - - sourceSets { - named("nativeMain") { - dependsOn(commonMain.get()) - } - } - } -} - -benchmark { - targets { - register("jvm") { - this as JvmBenchmarkTarget - jmhVersion = libs.versions.jmh.get() - } - if (nativeBenchmarksEnabled.toBoolean()) { - register("native") - } - } -} diff --git a/benchmarks/src/commonMain/kotlin/BufferOps.kt b/benchmarks/src/commonMain/kotlin/BufferOps.kt deleted file mode 100644 index d835cdfc5..000000000 --- a/benchmarks/src/commonMain/kotlin/BufferOps.kt +++ /dev/null @@ -1,482 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.benchmarks - -import kotlinx.benchmark.* -import kotlinx.io.* -import kotlinx.io.bytestring.ByteString -import kotlin.random.Random - -@State(Scope.Benchmark) -abstract class BufferRWBenchmarkBase { - // Buffers are implemented as a list of segments, as soon as a segment is empty - // it will be unlinked. By reading all previously written data, a segment will be - // cleared and recycled, and the next time we will try to write data, a new segment - // will be requested from the pool. Thus, without having any data in-flight, we will - // benchmark not only read/write ops performance, but also segments allocation/reclamation. - // Specific non-zero minGap's values don't significantly affect overall results, but it is - // left as the parameter to allow fine-tuning in the future. - @Param("128") - var minGap: Int = 0 - - protected val buffer = Buffer() - - protected open fun padding(): ByteArray = ByteArray(minGap) - - @Setup - fun setupBuffers() { - val padding = padding() - buffer.write(padding) - } - - @TearDown - fun clearBuffers() { - buffer.clear() - } -} - -@State(Scope.Benchmark) -open class ByteBenchmark : BufferRWBenchmarkBase() { - @Benchmark - fun benchmark(): Byte { - buffer.writeByte(0x42) - return buffer.readByte() - } -} - -@State(Scope.Benchmark) -open class ShortBenchmark : BufferRWBenchmarkBase() { - @Benchmark - fun benchmark(): Short { - buffer.writeShort(42) - return buffer.readShort() - } -} - -@State(Scope.Benchmark) -open class IntBenchmark : BufferRWBenchmarkBase() { - @Benchmark - fun benchmark(): Int { - buffer.writeInt(42) - return buffer.readInt() - } -} - -@State(Scope.Benchmark) -open class LongBenchmark : BufferRWBenchmarkBase() { - @Benchmark - fun benchmark(): Long { - buffer.writeLong(42) - return buffer.readLong() - } -} - -@State(Scope.Benchmark) -open class ShortLeBenchmark : BufferRWBenchmarkBase() { - @Benchmark - fun benchmark(): Short { - buffer.writeShortLe(42) - return buffer.readShortLe() - } -} - -@State(Scope.Benchmark) -open class IntLeBenchmark : BufferRWBenchmarkBase() { - @Benchmark - fun benchmark(): Int { - buffer.writeIntLe(42) - return buffer.readIntLe() - } -} - -@State(Scope.Benchmark) -open class LongLeBenchmark : BufferRWBenchmarkBase() { - @Benchmark - fun benchmark(): Long { - buffer.writeLongLe(42) - return buffer.readLongLe() - } -} - -@State(Scope.Benchmark) -open class DecimalLongBenchmark : BufferRWBenchmarkBase() { - @Param("-9223372036854775806", "9223372036854775806", "1") - var value = 0L - - override fun padding(): ByteArray { - return with(Buffer()) { - while (size < minGap) { - writeDecimalLong(value) - // use space as a delimiter between consecutive decimal values - writeByte(' '.code.toByte()) - } - readByteArray() - } - } - - @Benchmark - fun benchmark(): Long { - // use space as a delimiter between consecutive decimal values - buffer.writeDecimalLong(value) - buffer.writeByte(' '.code.toByte()) - val l = buffer.readDecimalLong() - buffer.readByte() // consume the delimiter - return l - } -} - -@State(Scope.Benchmark) -open class HexadecimalLongBenchmark : BufferRWBenchmarkBase() { - @Param("9223372036854775806", "1") - var value = 0L - - override fun padding(): ByteArray { - return with(Buffer()) { - while (size < minGap) { - writeHexadecimalUnsignedLong(value) - writeByte(' '.code.toByte()) - } - readByteArray() - } - } - - @Benchmark - fun benchmark(): Long { - buffer.writeHexadecimalUnsignedLong(value) - buffer.writeByte(' '.code.toByte()) - val l = buffer.readHexadecimalUnsignedLong() - buffer.readByte() - return l - } -} - -// This benchmark is based on Okio benchmark: -// https://raw.githubusercontent.com/square/okio/master/okio/jvm/jmh/src/jmh/java/com/squareup/okio/benchmarks/BufferUtf8Benchmark.java -@State(Scope.Benchmark) -open class Utf8StringBenchmark : BufferRWBenchmarkBase() { - private val strings = mapOf( - "ascii" to ("Um, I'll tell you the problem with the scientific power that you're using here, " - + "it didn't require any discipline to attain it. You read what others had done and you " - + "took the next step. You didn't earn the knowledge for yourselves, so you don't take any " - + "responsibility for it. You stood on the shoulders of geniuses to accomplish something " - + "as fast as you could, and before you even knew what you had, you patented it, and " - + "packaged it, and slapped it on a plastic lunchbox, and now you're selling it, you wanna " - + "sell it."), - "utf8" to - ("Սm, I'll 𝓽𝖾ll ᶌօ𝘂 ᴛℎ℮ 𝜚𝕣०bl𝖾m wі𝕥𝒽 𝘵𝘩𝐞 𝓼𝙘𝐢𝔢𝓷𝗍𝜄𝚏𝑖c 𝛠𝝾w𝚎𝑟 𝕥h⍺𝞃 𝛄𝓸𝘂'𝒓𝗲 υ𝖘𝓲𝗇ɡ 𝕙𝚎𝑟e, " - + "𝛊𝓽 ⅆ𝕚𝐝𝝿'𝗍 𝔯𝙚𝙦ᴜ𝜾𝒓𝘦 𝔞𝘯𝐲 ԁ𝜄𝑠𝚌ι𝘱lι𝒏e 𝑡𝜎 𝕒𝚝𝖙𝓪і𝞹 𝔦𝚝. 𝒀ο𝗎 𝔯𝑒⍺𝖉 w𝐡𝝰𝔱 𝞂𝞽һ𝓮𝓇ƽ հ𝖺𝖉 ⅾ𝛐𝝅ⅇ 𝝰πԁ 𝔂ᴑᴜ 𝓉ﮨ၀𝚔 " - + "т𝒽𝑒 𝗇𝕖ⅹ𝚝 𝔰𝒕е𝓅. 𝘠ⲟ𝖚 𝖉ⅰԁ𝝕'τ 𝙚𝚊r𝞹 𝘵Ꮒ𝖾 𝝒𝐧هwl𝑒𝖉ƍ𝙚 𝓯૦r 𝔂𝞼𝒖𝕣𝑠𝕖l𝙫𝖊𝓼, 𐑈о y𝘰𝒖 ⅆە𝗇't 𝜏α𝒌𝕖 𝛂𝟉ℽ " - + "𝐫ⅇ𝗌ⲣ๐ϖ𝖘ꙇᖯ𝓲l𝓲𝒕𝘆 𝐟𝞼𝘳 𝚤𝑡. 𝛶𝛔𝔲 s𝕥σσ𝐝 ﮩ𝕟 𝒕𝗁𝔢 𝘴𝐡𝜎ᴜlⅾ𝓮𝔯𝚜 𝛐𝙛 ᶃ𝚎ᴨᎥս𝚜𝘦𝓈 𝓽𝞸 a𝒄𝚌𝞸mρl𝛊ꜱ𝐡 𝓈𝚘m𝚎𝞃𝔥⍳𝞹𝔤 𝐚𝗌 𝖋a𝐬𝒕 " - + "αs γ𝛐𝕦 𝔠ﻫ𝛖lԁ, 𝚊π𝑑 Ь𝑒𝙛૦𝓇𝘦 𝓎٥𝖚 ⅇvℯ𝝅 𝜅ո𝒆w w𝗵𝒂𝘁 ᶌ੦𝗎 h𝐚𝗱, 𝜸ﮨ𝒖 𝓹𝝰𝔱𝖾𝗇𝓽𝔢ⅆ і𝕥, 𝚊𝜛𝓭 𝓹𝖺ⅽϰ𝘢ℊеᏧ 𝑖𝞃, " - + "𝐚𝛑ꓒ 𝙨l𝔞р𝘱𝔢𝓭 ɩ𝗍 ہ𝛑 𝕒 pl𝛂ѕᴛ𝗂𝐜 l𝞄ℼ𝔠𝒽𝑏ﮪ⨯, 𝔞ϖ𝒹 n𝛔w 𝛾𝐨𝞄'𝗿𝔢 ꜱ℮ll𝙞nɡ ɩ𝘁, 𝙮𝕠𝛖 w𝑎ℼ𝚗𝛂 𝕤𝓮ll 𝙞𝓉."), - // The first 't' is actually a '𝓽' - "sparse" to ("Um, I'll 𝓽ell you the problem with the scientific power that you're using here, " - + "it didn't require any discipline to attain it. You read what others had done and you " - + "took the next step. You didn't earn the knowledge for yourselves, so you don't take any " - + "responsibility for it. You stood on the shoulders of geniuses to accomplish something " - + "as fast as you could, and before you even knew what you had, you patented it, and " - + "packaged it, and slapped it on a plastic lunchbox, and now you're selling it, you wanna " - + "sell it."), - "2bytes" to "\u0080\u07ff", - "3bytes" to "\u0800\ud7ff\ue000\uffff", - "4bytes" to "\ud835\udeca", - // high surrogate, 'a', low surrogate, and 'a' - "bad" to "\ud800\u0061\udc00\u0061" - ) - - @Param("20", "2000", "200000") - var length = 0 - - @Param("ascii", "utf8", "sparse", "2bytes", "3bytes", "4bytes", "bad") - var encoding: String = "ascii" - - private var string: String = "" - - private fun constructString(): String { - val part = strings[encoding] ?: throw IllegalArgumentException("Unsupported encoding: $encoding") - val builder = StringBuilder(length + 1000) - while (builder.length < length) { - builder.append(part) - } - builder.setLength(length) - return builder.toString() - } - - override fun padding(): ByteArray { - val baseString = constructString() - val baseStringByteArray = baseString.encodeToByteArray() - if (baseStringByteArray.size >= minGap) { - return baseStringByteArray - } - val builder = StringBuilder((minGap * 1.5).toInt()) - while (builder.length < minGap) { - builder.append(baseString) - } - return builder.toString().encodeToByteArray() - } - - @Setup - fun setupString() { - string = constructString() - } - - @Benchmark - fun benchmark(): String { - val s = buffer.size - buffer.writeString(string) - return buffer.readString(buffer.size - s) - } -} - -@State(Scope.Benchmark) -open class Utf8LineBenchmarkBase : BufferRWBenchmarkBase() { - @Param("17") - var length: Int = 0 - - @Param("LF", "CRLF") - var separator: String = "" - - protected var string: String = "" - - private fun lineSeparator(): String = when (separator) { - "LF" -> "\n" - "CRLF" -> "\r\n" - else -> throw IllegalArgumentException("Unsupported line separator type: $separator") - } - - private fun constructString(): String = ".".repeat(length) + lineSeparator() - - override fun padding(): ByteArray { - val string = constructString() - if (string.length >= minGap) { - return string.encodeToByteArray() - } - val builder = StringBuilder((minGap * 1.5).toInt()) - while (builder.length < minGap) { - builder.append(string) - } - return builder.toString().encodeToByteArray() - } - - @Setup - fun setupString() { - string = constructString() - } -} - -@State(Scope.Benchmark) -open class Utf8LineBenchmark : Utf8LineBenchmarkBase() { - @Benchmark - fun benchmark(): String? { - buffer.writeString(string) - return buffer.readLine() - } -} - -@State(Scope.Benchmark) -open class Utf8LineStrictBenchmark : Utf8LineBenchmarkBase() { - @Benchmark - fun benchmark(): String { - buffer.writeString(string) - return buffer.readLineStrict() - } -} - -private const val VALUE_TO_FIND: Byte = 1 - -@State(Scope.Benchmark) -open class IndexOfBenchmark { - @Param( - "128:0:-1", // scan a short sequence at the beginning of a segment, target value is not there - "128:0:7", // scan a short sequence at the beginning of a segment, target value in the beginning - "128:0:100", // scan a short sequence at the beginning of a segment, target value at the end - "128:" + (SEGMENT_SIZE_IN_BYTES - 64).toString() + ":100", // scan two consecutive segments - (SEGMENT_SIZE_IN_BYTES * 3).toString() + ":0:-1" // scan multiple segments - ) - var params: String = "0:0:-1"; - - private val buffer = Buffer() - - @Setup - fun setupBuffers() { - val paramsParsed = params.split(':').map { it.toInt() }.toIntArray() - check(paramsParsed.size == 3) { - "Parameters format is: \"dataSize:paddingSize:valueIndex\", " + - "where valueIndex could be -1 if there should be no target value." - } - val dataSize = paramsParsed[0] - val paddingSize = paramsParsed[1] - val valueOffset = paramsParsed[2] - check(paddingSize >= 0 && dataSize >= 0) - check(valueOffset == -1 || valueOffset < dataSize) - - val array = ByteArray(dataSize) - if (valueOffset >= 0) array[valueOffset] = VALUE_TO_FIND - - val padding = ByteArray(paddingSize) - with(buffer) { - write(padding) - write(array) - skip(paddingSize.toLong()) - } - } - - @Benchmark - fun benchmark(): Long = buffer.indexOf(VALUE_TO_FIND) -} - -const val OFFSET_TO_2ND_BYTE_IN_2ND_SEGMENT = (SEGMENT_SIZE_IN_BYTES + 1).toString() - -@State(Scope.Benchmark) -open class BufferGetBenchmark { - private val buffer = Buffer() - - @Param("0", OFFSET_TO_2ND_BYTE_IN_2ND_SEGMENT) - var offset: Long = 0 - - @Setup - fun fillBuffer() { - buffer.write(ByteArray(offset.toInt() + 1)) - } - - @Benchmark - fun get() = buffer[offset] -} - -@State(Scope.Benchmark) -open class BufferReadWriteByteArray : BufferRWBenchmarkBase() { - private var inputArray = ByteArray(0) - private var outputArray = ByteArray(0) - - @Param("1", "1024", (SEGMENT_SIZE_IN_BYTES * 3).toString()) - var size: Int = 0 - - @Setup - fun allocateArrays() { - inputArray = ByteArray(size) - outputArray = ByteArray(size) - } - - @Benchmark - fun benchmark(blackhole: Blackhole) { - buffer.write(inputArray) - buffer.readTo(outputArray) - blackhole.consume(outputArray) - } -} - -@State(Scope.Benchmark) -open class BufferReadNewByteArray : BufferRWBenchmarkBase() { - private var inputArray = ByteArray(0) - - @Param("1", "1024", (SEGMENT_SIZE_IN_BYTES * 3).toString()) - var size: Int = 0 - - @Setup - fun allocateArray() { - inputArray = ByteArray(size) - } - - @Benchmark - fun benchmark(): ByteArray { - buffer.write(inputArray) - return buffer.readByteArray(size) - } -} - -@State(Scope.Benchmark) -open class IndexOfByteString { - @Param("1024:2", "8192:2", "10000:2", "10000:8") - var params: String = ":" - - private var buffer = Buffer() - private var byteString = ByteString() - - @Setup - fun setup() { - val paramsParsed = params.split(':').map { it.toInt() }.toIntArray() - require(paramsParsed.size == 2) - - val bufferSize = paramsParsed[0] - val bsSize = paramsParsed[1] - byteString = ByteString(ByteArray(bsSize) { 0x42 }) - - for (idx in 0 until bufferSize) { - if (idx % bsSize == 0) { - buffer.writeByte(0) - } else { - buffer.writeByte(0x42) - } - } - } - - @Benchmark - fun benchmark() = buffer.indexOf(byteString) -} - -@State(Scope.Benchmark) -open class Utf8CodePointsBenchmark : BufferRWBenchmarkBase() { - private val codePointsCount = 128 - - // Encoding names follow naming from Utf8StringBenchmark - @Param("ascii", "utf8", "sparse", "2bytes", "3bytes", "4bytes", "bad") - var encoding: String = "ascii" - - override fun padding(): ByteArray { - return ByteArray(minGap) { '.'.code.toByte() } - } - - private val codePoints = IntArray(codePointsCount) - private var codePointIdx = 0 - - @Setup - fun fillCodePointsArray() { - fun IntArray.fill(generator: () -> Int) { - for (idx in this.indices) { - this[idx] = generator() - } - } - - when (encoding) { - "ascii" -> codePoints.fill { Random.nextInt(' '.code, '~'.code) } - "utf8" -> codePoints.fill { - var cp: Int - do { - cp = Random.nextInt(0, 0x10ffff) - } while (cp in 0xd800 .. 0xdfff) - cp - } - "sparse" -> { - codePoints.fill { Random.nextInt(' '.code, '~'.code) } - codePoints[42] = '⌛'.code - } - "2bytes" -> codePoints.fill { Random.nextInt(0x80, 0x800) } - "3bytes" -> codePoints.fill { - var cp: Int - do { - cp = Random.nextInt(0x800, 0x10000) - } while (cp in 0xd800 .. 0xdfff) - cp - } - "4bytes" -> codePoints.fill { Random.nextInt(0x10000, 0x10ffff) } - "bad" -> codePoints.fill { Random.nextInt(0xd800, 0xdfff) } - } - } - - - private fun nextCodePoint(): Int { - val idx = codePointIdx - val cp = codePoints[idx] - codePointIdx = (idx + 1) % codePointsCount - return cp - } - - @Benchmark - fun benchmark(): Int { - buffer.writeCodePointValue(nextCodePoint()) - return buffer.readCodePointValue() - } -} diff --git a/benchmarks/src/commonMain/kotlin/Constants.kt b/benchmarks/src/commonMain/kotlin/Constants.kt deleted file mode 100644 index c384fb1c2..000000000 --- a/benchmarks/src/commonMain/kotlin/Constants.kt +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.benchmarks - -const val SEGMENT_SIZE_IN_BYTES: Int = 8192 diff --git a/benchmarks/src/commonMain/kotlin/PeekBenchmark.kt b/benchmarks/src/commonMain/kotlin/PeekBenchmark.kt deleted file mode 100644 index 1041e20c3..000000000 --- a/benchmarks/src/commonMain/kotlin/PeekBenchmark.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.benchmarks - -import kotlinx.benchmark.* -import kotlinx.io.Buffer -import kotlinx.io.Source - -const val OFFSET_TO_LAST_BYTE_IN_SEGMENT = (SEGMENT_SIZE_IN_BYTES - 1).toString() - -@State(Scope.Benchmark) -abstract class PeekBenchmark { - protected val buffer = Buffer() - - // Use OFFSET_TO_LAST_BYTE_IN_SEGMENT to hit a border between - // consecutive segments in benchmarks accessing multibyte values. - @Param("0", OFFSET_TO_LAST_BYTE_IN_SEGMENT) - var offset: Int = 0 - - @Setup - fun fillBuffer() { - buffer.write(ByteArray(offset + 128)) - } - - protected fun peek(): Source { - val peekSource = buffer.peek() - peekSource.skip(offset.toLong()) - return peekSource - } -} - -@State(Scope.Benchmark) -open class PeekByteBenchmark : PeekBenchmark() { - @Benchmark - fun benchmark() = peek().readByte() -} - -@State(Scope.Benchmark) -open class PeekShortBenchmark : PeekBenchmark() { - @Benchmark - fun benchmark() = peek().readShort() -} - -@State(Scope.Benchmark) -open class PeekIntBenchmark : PeekBenchmark() { - @Benchmark - fun benchmark() = peek().readInt() -} - -@State(Scope.Benchmark) -open class PeekLongBenchmark : PeekBenchmark() { - @Benchmark - fun benchmark() = peek().readLong() -} diff --git a/benchmarks/src/commonMain/kotlin/ReadStringBenchmark.kt b/benchmarks/src/commonMain/kotlin/ReadStringBenchmark.kt deleted file mode 100644 index 1b1d15b98..000000000 --- a/benchmarks/src/commonMain/kotlin/ReadStringBenchmark.kt +++ /dev/null @@ -1,42 +0,0 @@ -package kotlinx.io.benchmarks - -import kotlinx.benchmark.Benchmark -import kotlinx.benchmark.Param -import kotlinx.benchmark.Scope -import kotlinx.benchmark.Setup -import kotlinx.benchmark.State -import kotlinx.io.Buffer -import kotlinx.io.Source -import kotlinx.io.readCodePointValue -import kotlinx.io.readString -import kotlinx.io.writeCodePointValue -import kotlinx.io.writeString -import kotlin.random.Random - - -@State(Scope.Benchmark) -open class ReadStringBenchmark() { - - @Param("16", "64", "512") // Fits into a single segment, so the benchmark does not measure segment boundaries crossing - var size: Int = 0 - - val buffer: Buffer = Buffer() - - @Setup - fun setup() { - val string = buildString { repeat(size) { append(('a'..'z').random()) } } - buffer.writeString(string) - } - - - @Benchmark - fun bufferReadString(): String { - return buffer.copy().readString() - } - - @Benchmark - fun sourceReadString(): String { - val source: Source = buffer.copy() - return source.readString() - } -} diff --git a/benchmarks/src/commonMain/kotlin/SegmentPoolBenchmark.kt b/benchmarks/src/commonMain/kotlin/SegmentPoolBenchmark.kt deleted file mode 100644 index a3158d822..000000000 --- a/benchmarks/src/commonMain/kotlin/SegmentPoolBenchmark.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.benchmarks - -import kotlinx.io.* -import kotlinx.benchmark.* - -@State(Scope.Benchmark) -open class SegmentPoolBenchmark { - private val buffer = Buffer() - - @Benchmark - fun acquireReleaseCycle() { - // write will request a new segment - buffer.writeByte(0) - // clear will recycle an old segment - buffer.clear() - } -} diff --git a/benchmarks/src/commonMain/kotlin/bytestring/ByteStringOps.kt b/benchmarks/src/commonMain/kotlin/bytestring/ByteStringOps.kt deleted file mode 100644 index 7279ec2d9..000000000 --- a/benchmarks/src/commonMain/kotlin/bytestring/ByteStringOps.kt +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.bytestring.benchmarks - -import kotlinx.benchmark.* -import kotlinx.io.bytestring.* -import kotlin.math.min - -const val TARGET_BYTE: Byte = 42 - -@State(Scope.Benchmark) -abstract class IndexOfByteBenchmarkBase { - @Param("128:-1", "128:0", "128:127", "128:63") - var params: String = ":" - - protected var byteString = ByteString() - - @Setup - fun setupByteString() { - val paramsParsed = params.split(':').map { it.toInt() }.toIntArray() - require(paramsParsed.size == 2) - val size = paramsParsed[0] - val targetByteIndex = paramsParsed[1] - - - require(targetByteIndex == -1 || targetByteIndex in 0 until size) - val data = ByteArray(size) - if (targetByteIndex >= 0) { - data[targetByteIndex] = TARGET_BYTE - } - byteString = ByteString(data) - } -} - -@State(Scope.Benchmark) -open class IndexOfByteBenchmark : IndexOfByteBenchmarkBase() { - @Benchmark - fun benchmark() = byteString.indexOf(TARGET_BYTE) -} - -@State(Scope.Benchmark) -open class LastIndexOfByteBenchmark : IndexOfByteBenchmarkBase() { - @Benchmark - fun benchmark() = byteString.lastIndexOf(TARGET_BYTE) -} - -@State(Scope.Benchmark) -abstract class IndexOfByteStringBase { - @Param("128:8:-1", "128:129:0", "128:8:0", "128:8:120", "128:8:63") - var params: String = "::" - - protected var byteString = ByteString() - - protected var targetByteString = ByteString() - - @Setup - fun setupByteString() { - val paramsParsed = params.split(':').map { it.toInt() }.toIntArray() - require(paramsParsed.size == 3) - val size = paramsParsed[0] - val patternLength = paramsParsed[1] - val targetValueOffset = paramsParsed[2] - - require(size > 0) - require(targetValueOffset == -1 || targetValueOffset in 0 until size) - - val data = ByteArray(size) - if (targetValueOffset != -1) { - for (idx in targetValueOffset until min(size, targetValueOffset + patternLength)) { - data[idx] = TARGET_BYTE - } - } - byteString = ByteString(data) - - targetByteString = ByteString(ByteArray(patternLength) { TARGET_BYTE }) - } -} - -@State(Scope.Benchmark) -open class IndexOfByteStringBenchmark : IndexOfByteStringBase() { - @Benchmark - fun benchmark() = byteString.indexOf(targetByteString) -} - -@State(Scope.Benchmark) -open class LastIndexOfByteStringBenchmark : IndexOfByteStringBase() { - @Benchmark - fun benchmark() = byteString.lastIndexOf(targetByteString) -} - -@State(Scope.Benchmark) -abstract class IndexOfByteStringWithRepeatedMismatchBase { - @Param("128:8:2", "128:8:7") - var params: String = "::" - - protected var byteString = ByteString() - - protected var targetByteString = ByteString() - - @Setup - fun setup() { - val paramsParsed = params.split(':').map { it.toInt() }.toIntArray() - require(paramsParsed.size == 3) - val size = paramsParsed[0] - val patternLength = paramsParsed[1] - val stride = paramsParsed[2] - require(size > 0) - require(patternLength > 0) - require(stride in 1 until patternLength) - - val data = ByteArray(size) { TARGET_BYTE } - val pattern = ByteArray(patternLength) { TARGET_BYTE } - for (idx in data.indices) { - if (idx % stride == 0) { - data[idx] = 0 - } - } - byteString = ByteString(data) - targetByteString = ByteString(pattern) - } -} - -@State(Scope.Benchmark) -open class IndexOfByteStringWithRepeatedMismatch : IndexOfByteStringWithRepeatedMismatchBase() { - @Benchmark - fun benchmark() = byteString.indexOf(targetByteString) -} - -@State(Scope.Benchmark) -open class LastIndexOfByteStringWithRepeatedMismatch : IndexOfByteStringWithRepeatedMismatchBase() { - @Benchmark - fun benchmark() = byteString.lastIndexOf(targetByteString) -} - -@State(Scope.Benchmark) - -abstract class StartsWithBenchmarkBase { - protected abstract fun getRawParams(): String - - protected var byteString = ByteString() - - protected var targetByteString = ByteString() - - @Setup - fun setup() { - val paramsParsed = getRawParams().split(':').map { it.toInt() }.toIntArray() - require(paramsParsed.size == 3) - val size = paramsParsed[0] - val patternLength = paramsParsed[1] - val mismatchOffset = paramsParsed[2] - require(size > 0) - require(patternLength > 0) - require(mismatchOffset == -1 || mismatchOffset in (0 until size)) - - val data = ByteArray(size) - val prefix = ByteArray(patternLength) - if (mismatchOffset != -1) { - data[mismatchOffset] = TARGET_BYTE - } - byteString = ByteString(data) - targetByteString = ByteString(prefix) - } -} - -@State(Scope.Benchmark) -open class StartsWithBenchmark : StartsWithBenchmarkBase() { - @Param("128:8:-1", "128:8:0", "128:8:7") - var params: String = "::" - - override fun getRawParams(): String = params - - @Benchmark - fun benchmark() = byteString.startsWith(targetByteString) -} - -@State(Scope.Benchmark) -open class EndsWithBenchmark : StartsWithBenchmarkBase() { - @Param("128:8:-1", "128:8:127", "128:8:120") - var params: String = "::" - - override fun getRawParams(): String = params - - @Benchmark - fun benchmark() = byteString.endsWith(targetByteString) -} - -@State(Scope.Benchmark) - -abstract class ByteStringComparisonBenchmarkBase { - @Param("128") - var length: Int = 0 - - @Param("-1", "63") - var mismatchOffset = 0 - - protected var stringA = ByteString() - protected var stringB = ByteString() - - @Setup - fun setup() { - require(length > 0) - require(mismatchOffset == -1 || mismatchOffset in 0 until length) - - stringA = ByteString(ByteArray(length)) - stringB = ByteString(ByteArray(length).apply { - if (mismatchOffset != -1) { - this[mismatchOffset] = TARGET_BYTE - } - }) - } -} - -@State(Scope.Benchmark) -open class CompareBenchmark : ByteStringComparisonBenchmarkBase() { - @Benchmark - fun benchmark() = stringA.compareTo(stringB) -} - -@State(Scope.Benchmark) -open class EqualsBenchmark : ByteStringComparisonBenchmarkBase() { - @Param("true", "false") - var useHashCode: Boolean = false - - @Setup - fun computeHashCodes() { - if (useHashCode) { - stringA.hashCode() - stringB.hashCode() - } - } - - @Benchmark - fun benchmark() = stringA == stringB -} - -@State(Scope.Benchmark) -open class ByteStringHashCode { - @Param("8", "128") - var size: Int = 0 - - @Param("true", "false") - var recomputeOnEveryCall: Boolean = false - - private var byteString = ByteString() - - @Setup - fun setupByteString() { - require(size > 0) { "Invalid byte string size: $size" } - val ba = ByteArray(size) - if (recomputeOnEveryCall) { - ba[0] = -31 - check(ba.contentHashCode() == 0) { "Hash code is non zero" } - } else { - check(ba.contentHashCode() != 0) { "Hash code is zero" } - } - - - byteString = ByteString(ba) - } - - @Benchmark - fun benchmark(): Int = byteString.hashCode() -} diff --git a/benchmarks/src/jvmMain/kotlin/ByteBufferBenchmarks.kt b/benchmarks/src/jvmMain/kotlin/ByteBufferBenchmarks.kt deleted file mode 100644 index 0d1ea310f..000000000 --- a/benchmarks/src/jvmMain/kotlin/ByteBufferBenchmarks.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.benchmarks - -import kotlinx.benchmark.Benchmark -import kotlinx.benchmark.Param -import kotlinx.benchmark.Setup -import kotlinx.io.readAtMostTo -import kotlinx.io.write -import java.nio.ByteBuffer - -open class ByteBufferReadWrite : BufferRWBenchmarkBase() { - private var inputBuffer = ByteBuffer.allocate(0) - private var outputBuffer = ByteBuffer.allocate(0) - - @Param("1", "1024", (SEGMENT_SIZE_IN_BYTES * 3).toString()) - var size: Int = 0 - - @Setup - fun allocateBuffers() { - inputBuffer = ByteBuffer.allocate(size) - inputBuffer.put(ByteArray(size)) - outputBuffer = ByteBuffer.allocate(size) - } - - @Benchmark - fun benchmark(): ByteBuffer { - inputBuffer.rewind() - outputBuffer.clear() - buffer.write(inputBuffer) - while (buffer.readAtMostTo(outputBuffer) > 0) { - // do nothing - } - return outputBuffer - } -} diff --git a/benchmarks/src/jvmMain/kotlin/SegmentPoolBenchmarkMT.kt b/benchmarks/src/jvmMain/kotlin/SegmentPoolBenchmarkMT.kt deleted file mode 100644 index 54141db50..000000000 --- a/benchmarks/src/jvmMain/kotlin/SegmentPoolBenchmarkMT.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.benchmarks - -import kotlinx.benchmark.* -import kotlinx.io.* -import org.openjdk.jmh.annotations.Group -import org.openjdk.jmh.annotations.GroupThreads - -@State(Scope.Benchmark) -open class SegmentPoolBenchmarkMT { - private fun testCycle(): Buffer { - val buffer = Buffer() - buffer.writeByte(0) - buffer.clear() - return buffer - } - - @Benchmark - @Group("ra1") - @GroupThreads(1) - fun acquireReleaseCycle() = testCycle() - - @Benchmark - @Group("ra2") - @GroupThreads(2) - fun acquireReleaseCycle2() = testCycle() - - @Benchmark - @Group("ra4") - @GroupThreads(4) - fun acquireReleaseCycle4() = testCycle() -} diff --git a/benchmarks/src/jvmMain/kotlin/StreamBenchmarks.kt b/benchmarks/src/jvmMain/kotlin/StreamBenchmarks.kt deleted file mode 100644 index 52b90045a..000000000 --- a/benchmarks/src/jvmMain/kotlin/StreamBenchmarks.kt +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.benchmarks - -import kotlinx.benchmark.Benchmark -import kotlinx.benchmark.Blackhole -import kotlinx.benchmark.Param -import kotlinx.benchmark.Setup -import kotlinx.io.asInputStream -import kotlinx.io.asOutputStream -import kotlinx.io.readTo - -open class InputStreamByteRead : BufferRWBenchmarkBase() { - private val stream = buffer.asInputStream() - - @Benchmark - fun benchmark(): Int { - buffer.writeByte(0) - return stream.read() - } -} - -open class OutputStreamByteWrite : BufferRWBenchmarkBase() { - private val stream = buffer.asOutputStream() - - @Benchmark - fun benchmark(): Byte { - stream.write(0) - return buffer.readByte() - } -} - -abstract class StreamByteArrayBenchmarkBase : BufferRWBenchmarkBase() { - protected var inputArray = ByteArray(0) - protected var outputArray = ByteArray(0) - - @Param("1", "128", SEGMENT_SIZE_IN_BYTES.toString()) - var size: Int = 0 - - @Setup - fun setupArray() { - inputArray = ByteArray(size) - outputArray = ByteArray(size) - } -} - -open class InputStreamByteArrayRead : StreamByteArrayBenchmarkBase() { - private val stream = buffer.asInputStream() - - @Benchmark - fun benchmark(blackhole: Blackhole) { - buffer.write(inputArray) - var offset = 0 - while (offset < outputArray.size) { - offset += stream.read(outputArray, offset, outputArray.size - offset) - } - blackhole.consume(outputArray) - } -} - -open class OutputStreamByteArrayWrite : StreamByteArrayBenchmarkBase() { - private val stream = buffer.asOutputStream() - - @Benchmark - fun benchmark(blackhole: Blackhole) { - stream.write(outputArray) - buffer.readTo(inputArray) - blackhole.consume(inputArray) - } -} diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts deleted file mode 100644 index c26e65a27..000000000 --- a/build-logic/build.gradle.kts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -plugins { - `kotlin-dsl` -} - -repositories { - mavenCentral() -} - -dependencies { - implementation(libs.kotlin.gradle.plugin) - implementation(libs.dokka.gradle.plugin) - implementation(libs.animalsniffer.gradle.plugin) -} - -kotlin { - jvmToolchain(JavaLanguageVersion.of(libs.versions.java.get()).asInt()) -} diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts deleted file mode 100644 index 4b401d8b7..000000000 --- a/build-logic/settings.gradle.kts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version ("0.8.0") -} - -dependencyResolutionManagement { - versionCatalogs { - create("libs") { - from(files("../gradle/libs.versions.toml")) - } - } -} diff --git a/build-logic/src/main/kotlin/kotlinx/io/conventions/kotlinx-io-android-compat.gradle.kts b/build-logic/src/main/kotlin/kotlinx/io/conventions/kotlinx-io-android-compat.gradle.kts deleted file mode 100644 index 948857618..000000000 --- a/build-logic/src/main/kotlin/kotlinx/io/conventions/kotlinx-io-android-compat.gradle.kts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -import ru.vyarus.gradle.plugin.animalsniffer.AnimalSnifferExtension - -pluginManager.withPlugin("org.gradle.java-base") { - apply(plugin = "ru.vyarus.animalsniffer") - - configure { - sourceSets = listOf((project.extensions.getByName("sourceSets") as SourceSetContainer).getByName("main")) - } - val signature: Configuration by configurations - dependencies { - // Use the same API level as OkHttp - signature("net.sf.androidscents.signature:android-api-level-21:5.0.1_r2@signature") - } -} diff --git a/build-logic/src/main/kotlin/kotlinx/io/conventions/kotlinx-io-clean.gradle.kts b/build-logic/src/main/kotlin/kotlinx/io/conventions/kotlinx-io-clean.gradle.kts deleted file mode 100644 index 65d319693..000000000 --- a/build-logic/src/main/kotlin/kotlinx/io/conventions/kotlinx-io-clean.gradle.kts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -plugins { - base -} - -tasks.clean { - // Workaround for https://youtrack.jetbrains.com/issue/KT-58303: - // the `clean` task can't delete the expanded.lock file on Windows as it's still held by Gradle, failing the build - setDelete(layout.buildDirectory.asFileTree.matching { - exclude("tmp/.cache/expanded/expanded.lock") - }) -} - diff --git a/build-logic/src/main/kotlin/kotlinx/io/conventions/kotlinx-io-dokka.gradle.kts b/build-logic/src/main/kotlin/kotlinx/io/conventions/kotlinx-io-dokka.gradle.kts deleted file mode 100644 index fc50ede2c..000000000 --- a/build-logic/src/main/kotlin/kotlinx/io/conventions/kotlinx-io-dokka.gradle.kts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -import org.jetbrains.dokka.gradle.DokkaTaskPartial -import java.net.URL - -plugins { - id("org.jetbrains.dokka") -} - -tasks.withType().configureEach { - dokkaSourceSets.configureEach { - includes.from("Module.md") - - sourceLink { - localDirectory = rootDir - remoteUrl = URL("https://github.com/kotlin/kotlinx-io/tree/master") - remoteLineSuffix = "#L" - } - - // we don't want to advertise `unsafe` APIs in documentation - perPackageOption { - suppress = true - matchingRegex = ".*unsafe.*" - } - - // as in kotlinx-io-multiplatform.gradle.kts:configureSourceSet - val platform = name.dropLast(4) - samples.from( - "common/test/samples", - "$platform/test/samples" - ) - } -} diff --git a/build-logic/src/main/kotlin/kotlinx/io/conventions/kotlinx-io-multiplatform.gradle.kts b/build-logic/src/main/kotlin/kotlinx/io/conventions/kotlinx-io-multiplatform.gradle.kts deleted file mode 100644 index 5ac3098b8..000000000 --- a/build-logic/src/main/kotlin/kotlinx/io/conventions/kotlinx-io-multiplatform.gradle.kts +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension -import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet -import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl -import kotlin.jvm.optionals.getOrNull - -plugins { - kotlin("multiplatform") - id("kotlinx-io-clean") -} - -kotlin { - @OptIn(ExperimentalKotlinGradlePluginApi::class) - compilerOptions { - allWarningsAsErrors = true - freeCompilerArgs.add("-Xexpect-actual-classes") - } - - val versionCatalog: VersionCatalog = extensions.getByType().named("libs") - jvmToolchain { - val javaVersion = versionCatalog.findVersion("java").getOrNull()?.requiredVersion - ?: throw GradleException("Version 'java' is not specified in the version catalog") - languageVersion = JavaLanguageVersion.of(javaVersion) - } - - jvm { - withJava() - testRuns["test"].executionTask.configure { - useJUnitPlatform() - } - // can be replaced with just `compilerOptions { }` in Kotlin 2.0 - compilations.configureEach { - compileTaskProvider.configure { - compilerOptions { - freeCompilerArgs.add("-Xjvm-default=all") - } - } - } - } - - js { - browser { - testTask { - filter.setExcludePatterns("*SmokeFileTest*") - } - } - } - - @OptIn(ExperimentalWasmDsl::class) - wasmJs { - nodejs() - // Disabled because we can't exclude some tests: https://youtrack.jetbrains.com/issue/KT-58291 - // browser() - } - - @OptIn(ExperimentalWasmDsl::class) - wasmWasi { - nodejs() - } - - nativeTargets() - - sourceSets { - commonTest.dependencies { - implementation(kotlin("test")) - } - } - - explicitApi() - sourceSets.configureEach { - configureSourceSet() - } - - @OptIn(ExperimentalKotlinGradlePluginApi::class) - applyDefaultHierarchyTemplate { - common { - group("native") { - group("nativeNonApple") { - group("mingw") - group("unix") { - group("linux") - group("androidNative") - } - } - - group("nativeNonAndroid") { - group("apple") - group("mingw") - group("linux") - } - } - group("nodeFilesystemShared") { - withJs() - withWasmJs() - } - group("wasm") { - withWasmJs() - withWasmWasi() - } - } - } -} - -fun KotlinSourceSet.configureSourceSet() { - val srcDir = if (name.endsWith("Main")) "src" else "test" - val platform = name.dropLast(4) - kotlin.srcDir("$platform/$srcDir") - if (name == "jvmMain") { - resources.srcDir("$platform/resources") - } else if (name == "jvmTest") { - resources.srcDir("$platform/test-resources") - } - languageSettings { - progressiveMode = true - } -} - -private fun KotlinMultiplatformExtension.nativeTargets() { - iosX64() - iosArm64() - iosSimulatorArm64() - - tvosX64() - tvosArm64() - tvosSimulatorArm64() - - watchosArm32() - watchosArm64() - watchosX64() - watchosSimulatorArm64() - watchosDeviceArm64() - - androidNativeArm32() - androidNativeArm64() - androidNativeX64() - androidNativeX86() - - linuxX64() - linuxArm64() - @Suppress("DEPRECATION") // https://github.com/Kotlin/kotlinx-io/issues/303 - linuxArm32Hfp() - - macosX64() - macosArm64() - - mingwX64() -} diff --git a/build-logic/src/main/kotlin/kotlinx/io/conventions/kotlinx-io-publish.gradle.kts b/build-logic/src/main/kotlin/kotlinx/io/conventions/kotlinx-io-publish.gradle.kts deleted file mode 100644 index 6f6765dc8..000000000 --- a/build-logic/src/main/kotlin/kotlinx/io/conventions/kotlinx-io-publish.gradle.kts +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -import org.gradle.jvm.tasks.Jar -import java.net.URI - -plugins { - `maven-publish` - signing -} - -publishing { - repositories { - configureMavenPublication(project) - } - - val javadocJar = project.configureEmptyJavadocArtifact() - publications.withType(MavenPublication::class).all { - pom.configureMavenCentralMetadata(project) - signPublicationIfKeyPresent(project, this) - artifact(javadocJar) - } - - tasks.withType().configureEach { - dependsOn(tasks.withType()) - } -} - -fun MavenPom.configureMavenCentralMetadata(project: Project) { - name = project.name - description = "IO support for Kotlin" - url = "https://github.com/Kotlin/kotlinx-io" - - licenses { - license { - name = "The Apache Software License, Version 2.0" - url = "https://www.apache.org/licenses/LICENSE-2.0.txt" - distribution = "repo" - } - } - - developers { - developer { - id = "JetBrains" - name = "JetBrains Team" - organization = "JetBrains" - organizationUrl = "https://www.jetbrains.com" - } - } - - scm { - url = "https://github.com/Kotlin/kotlinx-io" - } -} - -fun MavenPublication.mavenCentralArtifacts(project: Project, sources: SourceDirectorySet) { - val sourcesJar by project.tasks.creating(Jar::class) { - archiveClassifier = "sources" - from(sources) - } - val javadocJar by project.tasks.creating(Jar::class) { - archiveClassifier = "javadoc" - // contents are deliberately left empty - } - artifact(sourcesJar) - artifact(javadocJar) -} - - -fun mavenRepositoryUri(): URI { - val repositoryId: String? = System.getenv("libs.repository.id") - return if (repositoryId == null) { - URI("https://oss.sonatype.org/service/local/staging/deploy/maven2/") - } else { - URI("https://oss.sonatype.org/service/local/staging/deployByRepositoryId/$repositoryId") - } -} - -fun RepositoryHandler.configureMavenPublication( project: Project) { - maven { - url = mavenRepositoryUri() - credentials { - username = project.getSensitiveProperty("libs.sonatype.user") - password = project.getSensitiveProperty("libs.sonatype.password") - } - } - - // Something that's easy to "clean" for development not mavenLocal - maven(project.rootProject.layout.buildDirectory.dir("repo")) { - name = "buildRepo" - } -} - -fun Project.configureEmptyJavadocArtifact(): Jar { - val javadocJar by project.tasks.creating(Jar::class) { - archiveClassifier = "javadoc" - // contents are deliberately left empty - } - return javadocJar -} - -fun signPublicationIfKeyPresent(project: Project, publication: MavenPublication) { - val keyId = project.getSensitiveProperty("libs.sign.key.id") - val signingKey = project.getSensitiveProperty("libs.sign.key.private") - val signingKeyPassphrase = project.getSensitiveProperty("libs.sign.passphrase") - if (!signingKey.isNullOrBlank()) { - project.extensions.configure("signing") { - useInMemoryPgpKeys(keyId, signingKey, signingKeyPassphrase) - sign(publication) - - // Temporary workaround, see https://github.com/gradle/gradle/issues/26091#issuecomment-1722947958 - tasks.withType().configureEach { - val signingTasks = tasks.withType() - mustRunAfter(signingTasks) - } - } - } -} - -fun Project.getSensitiveProperty(name: String): String? { - return project.findProperty(name) as? String ?: System.getenv(name) -} diff --git a/build.gradle.kts b/build.gradle.kts deleted file mode 100644 index 8d99e3da8..000000000 --- a/build.gradle.kts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. - */ - -import kotlinx.kover.gradle.plugin.dsl.CoverageUnit -import kotlinx.validation.ExperimentalBCVApi - -plugins { - id("kotlinx-io-publish") apply false - - alias(libs.plugins.kover) - alias(libs.plugins.bcv) - alias(libs.plugins.dokka) -} - -allprojects { - properties["DeployVersion"]?.let { version = it } - repositories { - mavenCentral() - } -} - -@OptIn(ExperimentalBCVApi::class) -apiValidation { - ignoredProjects.addAll(listOf( - "kotlinx-io-benchmarks", - "kotlinx-io-smoke-tests" - )) - klib.enabled = true -} - -dependencies { - kover(project(":kotlinx-io-core")) - kover(project(":kotlinx-io-bytestring")) -} - -kover { - reports { - verify { - rule { - minBound(95, CoverageUnit.LINE) - - // we allow lower branch coverage, because not all checks in the internal code lead to errors - minBound(80, CoverageUnit.BRANCH) - } - } - } -} diff --git a/bytestring/Module.md b/bytestring/Module.md deleted file mode 100644 index 9a3193763..000000000 --- a/bytestring/Module.md +++ /dev/null @@ -1,3 +0,0 @@ -# Module kotlinx-io-bytestring - -The module provides the [ByteString] - an immutable sequence of bytes, and extensions facilitating work with it. diff --git a/bytestring/api/kotlinx-io-bytestring.api b/bytestring/api/kotlinx-io-bytestring.api deleted file mode 100644 index bf0f9f77b..000000000 --- a/bytestring/api/kotlinx-io-bytestring.api +++ /dev/null @@ -1,122 +0,0 @@ -public final class kotlinx/io/bytestring/Base64Kt { - public static final fun decode (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;II)[B - public static synthetic fun decode$default (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;IIILjava/lang/Object;)[B - public static final fun decodeIntoByteArray (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;[BIII)I - public static synthetic fun decodeIntoByteArray$default (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;[BIIIILjava/lang/Object;)I - public static final fun decodeToByteString (Lkotlin/io/encoding/Base64;Ljava/lang/CharSequence;II)Lkotlinx/io/bytestring/ByteString; - public static final fun decodeToByteString (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;II)Lkotlinx/io/bytestring/ByteString; - public static final fun decodeToByteString (Lkotlin/io/encoding/Base64;[BII)Lkotlinx/io/bytestring/ByteString; - public static synthetic fun decodeToByteString$default (Lkotlin/io/encoding/Base64;Ljava/lang/CharSequence;IIILjava/lang/Object;)Lkotlinx/io/bytestring/ByteString; - public static synthetic fun decodeToByteString$default (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;IIILjava/lang/Object;)Lkotlinx/io/bytestring/ByteString; - public static synthetic fun decodeToByteString$default (Lkotlin/io/encoding/Base64;[BIIILjava/lang/Object;)Lkotlinx/io/bytestring/ByteString; - public static final fun encode (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;II)Ljava/lang/String; - public static synthetic fun encode$default (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;IIILjava/lang/Object;)Ljava/lang/String; - public static final fun encodeIntoByteArray (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;[BIII)I - public static synthetic fun encodeIntoByteArray$default (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;[BIIIILjava/lang/Object;)I - public static final fun encodeToAppendable (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;Ljava/lang/Appendable;II)Ljava/lang/Appendable; - public static synthetic fun encodeToAppendable$default (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;Ljava/lang/Appendable;IIILjava/lang/Object;)Ljava/lang/Appendable; - public static final fun encodeToByteArray (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;II)[B - public static synthetic fun encodeToByteArray$default (Lkotlin/io/encoding/Base64;Lkotlinx/io/bytestring/ByteString;IIILjava/lang/Object;)[B -} - -public final class kotlinx/io/bytestring/ByteString : java/lang/Comparable { - public static final field Companion Lkotlinx/io/bytestring/ByteString$Companion; - public fun ([BII)V - public synthetic fun ([BIIILkotlin/jvm/internal/DefaultConstructorMarker;)V - public synthetic fun ([BLjava/lang/Object;Lkotlin/jvm/internal/DefaultConstructorMarker;)V - public synthetic fun compareTo (Ljava/lang/Object;)I - public fun compareTo (Lkotlinx/io/bytestring/ByteString;)I - public final fun copyInto ([BIII)V - public static synthetic fun copyInto$default (Lkotlinx/io/bytestring/ByteString;[BIIIILjava/lang/Object;)V - public fun equals (Ljava/lang/Object;)Z - public final fun get (I)B - public final fun getBackingArrayReference ()[B - public final fun getSize ()I - public fun hashCode ()I - public final fun substring (II)Lkotlinx/io/bytestring/ByteString; - public static synthetic fun substring$default (Lkotlinx/io/bytestring/ByteString;IIILjava/lang/Object;)Lkotlinx/io/bytestring/ByteString; - public final fun toByteArray (II)[B - public static synthetic fun toByteArray$default (Lkotlinx/io/bytestring/ByteString;IIILjava/lang/Object;)[B - public fun toString ()Ljava/lang/String; -} - -public final class kotlinx/io/bytestring/ByteString$Companion { -} - -public final class kotlinx/io/bytestring/ByteStringBuilder { - public fun ()V - public fun (I)V - public synthetic fun (IILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun append (B)V - public final fun append ([BII)V - public static synthetic fun append$default (Lkotlinx/io/bytestring/ByteStringBuilder;[BIIILjava/lang/Object;)V - public final fun getCapacity ()I - public final fun getSize ()I - public final fun toByteString ()Lkotlinx/io/bytestring/ByteString; -} - -public final class kotlinx/io/bytestring/ByteStringBuilderKt { - public static final fun append (Lkotlinx/io/bytestring/ByteStringBuilder;Lkotlinx/io/bytestring/ByteString;)V - public static final fun append (Lkotlinx/io/bytestring/ByteStringBuilder;[B)V - public static final fun append-EK-6454 (Lkotlinx/io/bytestring/ByteStringBuilder;B)V - public static final fun buildByteString (ILkotlin/jvm/functions/Function1;)Lkotlinx/io/bytestring/ByteString; - public static synthetic fun buildByteString$default (ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/io/bytestring/ByteString; -} - -public final class kotlinx/io/bytestring/ByteStringJvmExtKt { - public static final fun asReadOnlyByteBuffer (Lkotlinx/io/bytestring/ByteString;)Ljava/nio/ByteBuffer; - public static final fun decodeToString (Lkotlinx/io/bytestring/ByteString;Ljava/nio/charset/Charset;)Ljava/lang/String; - public static final fun encodeToByteString (Ljava/lang/String;Ljava/nio/charset/Charset;)Lkotlinx/io/bytestring/ByteString; - public static final fun getByteString (Ljava/nio/ByteBuffer;I)Lkotlinx/io/bytestring/ByteString; - public static final fun getByteString (Ljava/nio/ByteBuffer;II)Lkotlinx/io/bytestring/ByteString; - public static synthetic fun getByteString$default (Ljava/nio/ByteBuffer;IILjava/lang/Object;)Lkotlinx/io/bytestring/ByteString; - public static final fun putByteString (Ljava/nio/ByteBuffer;ILkotlinx/io/bytestring/ByteString;)V - public static final fun putByteString (Ljava/nio/ByteBuffer;Lkotlinx/io/bytestring/ByteString;)V -} - -public final class kotlinx/io/bytestring/ByteStringKt { - public static final fun ByteString ()Lkotlinx/io/bytestring/ByteString; - public static final fun ByteString ([B)Lkotlinx/io/bytestring/ByteString; - public static final fun ByteString-GBYM_sE ([B)Lkotlinx/io/bytestring/ByteString; - public static final fun contentEquals (Lkotlinx/io/bytestring/ByteString;[B)Z - public static final fun decodeToString (Lkotlinx/io/bytestring/ByteString;)Ljava/lang/String; - public static final fun encodeToByteString (Ljava/lang/String;)Lkotlinx/io/bytestring/ByteString; - public static final fun endsWith (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;)Z - public static final fun endsWith (Lkotlinx/io/bytestring/ByteString;[B)Z - public static final fun getIndices (Lkotlinx/io/bytestring/ByteString;)Lkotlin/ranges/IntRange; - public static final fun indexOf (Lkotlinx/io/bytestring/ByteString;BI)I - public static final fun indexOf (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;I)I - public static final fun indexOf (Lkotlinx/io/bytestring/ByteString;[BI)I - public static synthetic fun indexOf$default (Lkotlinx/io/bytestring/ByteString;BIILjava/lang/Object;)I - public static synthetic fun indexOf$default (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;IILjava/lang/Object;)I - public static synthetic fun indexOf$default (Lkotlinx/io/bytestring/ByteString;[BIILjava/lang/Object;)I - public static final fun isEmpty (Lkotlinx/io/bytestring/ByteString;)Z - public static final fun isNotEmpty (Lkotlinx/io/bytestring/ByteString;)Z - public static final fun lastIndexOf (Lkotlinx/io/bytestring/ByteString;BI)I - public static final fun lastIndexOf (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;I)I - public static final fun lastIndexOf (Lkotlinx/io/bytestring/ByteString;[BI)I - public static synthetic fun lastIndexOf$default (Lkotlinx/io/bytestring/ByteString;BIILjava/lang/Object;)I - public static synthetic fun lastIndexOf$default (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;IILjava/lang/Object;)I - public static synthetic fun lastIndexOf$default (Lkotlinx/io/bytestring/ByteString;[BIILjava/lang/Object;)I - public static final fun startsWith (Lkotlinx/io/bytestring/ByteString;Lkotlinx/io/bytestring/ByteString;)Z - public static final fun startsWith (Lkotlinx/io/bytestring/ByteString;[B)Z -} - -public final class kotlinx/io/bytestring/HexKt { - public static final fun hexToByteString (Ljava/lang/String;Lkotlin/text/HexFormat;)Lkotlinx/io/bytestring/ByteString; - public static synthetic fun hexToByteString$default (Ljava/lang/String;Lkotlin/text/HexFormat;ILjava/lang/Object;)Lkotlinx/io/bytestring/ByteString; - public static final fun toHexString (Lkotlinx/io/bytestring/ByteString;IILkotlin/text/HexFormat;)Ljava/lang/String; - public static final fun toHexString (Lkotlinx/io/bytestring/ByteString;Lkotlin/text/HexFormat;)Ljava/lang/String; - public static synthetic fun toHexString$default (Lkotlinx/io/bytestring/ByteString;IILkotlin/text/HexFormat;ILjava/lang/Object;)Ljava/lang/String; - public static synthetic fun toHexString$default (Lkotlinx/io/bytestring/ByteString;Lkotlin/text/HexFormat;ILjava/lang/Object;)Ljava/lang/String; -} - -public abstract interface annotation class kotlinx/io/bytestring/unsafe/UnsafeByteStringApi : java/lang/annotation/Annotation { -} - -public final class kotlinx/io/bytestring/unsafe/UnsafeByteStringOperations { - public static final field INSTANCE Lkotlinx/io/bytestring/unsafe/UnsafeByteStringOperations; - public final fun withByteArrayUnsafe (Lkotlinx/io/bytestring/ByteString;Lkotlin/jvm/functions/Function1;)V - public final fun wrapUnsafe ([B)Lkotlinx/io/bytestring/ByteString; -} - diff --git a/bytestring/api/kotlinx-io-bytestring.klib.api b/bytestring/api/kotlinx-io-bytestring.klib.api deleted file mode 100644 index 0f090e717..000000000 --- a/bytestring/api/kotlinx-io-bytestring.klib.api +++ /dev/null @@ -1,93 +0,0 @@ -// Klib ABI Dump -// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm32Hfp, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, wasmWasi, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] -// Alias: apple => [iosArm64, iosSimulatorArm64, iosX64, macosArm64, macosX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] -// Rendering settings: -// - Signature version: 2 -// - Show manifest properties: true -// - Show declarations: true - -// Library unique name: -open annotation class kotlinx.io.bytestring.unsafe/UnsafeByteStringApi : kotlin/Annotation { // kotlinx.io.bytestring.unsafe/UnsafeByteStringApi|null[0] - constructor () // kotlinx.io.bytestring.unsafe/UnsafeByteStringApi.|(){}[0] -} - -final class kotlinx.io.bytestring/ByteString : kotlin/Comparable { // kotlinx.io.bytestring/ByteString|null[0] - constructor (kotlin/ByteArray, kotlin/Int = ..., kotlin/Int = ...) // kotlinx.io.bytestring/ByteString.|(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] - - final val size // kotlinx.io.bytestring/ByteString.size|{}size[0] - final fun (): kotlin/Int // kotlinx.io.bytestring/ByteString.size.|(){}[0] - - final fun compareTo(kotlinx.io.bytestring/ByteString): kotlin/Int // kotlinx.io.bytestring/ByteString.compareTo|compareTo(kotlinx.io.bytestring.ByteString){}[0] - final fun copyInto(kotlin/ByteArray, kotlin/Int = ..., kotlin/Int = ..., kotlin/Int = ...) // kotlinx.io.bytestring/ByteString.copyInto|copyInto(kotlin.ByteArray;kotlin.Int;kotlin.Int;kotlin.Int){}[0] - final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.io.bytestring/ByteString.equals|equals(kotlin.Any?){}[0] - final fun get(kotlin/Int): kotlin/Byte // kotlinx.io.bytestring/ByteString.get|get(kotlin.Int){}[0] - final fun getBackingArrayReference(): kotlin/ByteArray // kotlinx.io.bytestring/ByteString.getBackingArrayReference|getBackingArrayReference(){}[0] - final fun hashCode(): kotlin/Int // kotlinx.io.bytestring/ByteString.hashCode|hashCode(){}[0] - final fun substring(kotlin/Int, kotlin/Int = ...): kotlinx.io.bytestring/ByteString // kotlinx.io.bytestring/ByteString.substring|substring(kotlin.Int;kotlin.Int){}[0] - final fun toByteArray(kotlin/Int = ..., kotlin/Int = ...): kotlin/ByteArray // kotlinx.io.bytestring/ByteString.toByteArray|toByteArray(kotlin.Int;kotlin.Int){}[0] - final fun toString(): kotlin/String // kotlinx.io.bytestring/ByteString.toString|toString(){}[0] - - final object Companion // kotlinx.io.bytestring/ByteString.Companion|null[0] -} - -final class kotlinx.io.bytestring/ByteStringBuilder { // kotlinx.io.bytestring/ByteStringBuilder|null[0] - constructor (kotlin/Int = ...) // kotlinx.io.bytestring/ByteStringBuilder.|(kotlin.Int){}[0] - - final val capacity // kotlinx.io.bytestring/ByteStringBuilder.capacity|{}capacity[0] - final fun (): kotlin/Int // kotlinx.io.bytestring/ByteStringBuilder.capacity.|(){}[0] - final val size // kotlinx.io.bytestring/ByteStringBuilder.size|{}size[0] - final fun (): kotlin/Int // kotlinx.io.bytestring/ByteStringBuilder.size.|(){}[0] - - final fun append(kotlin/Byte) // kotlinx.io.bytestring/ByteStringBuilder.append|append(kotlin.Byte){}[0] - final fun append(kotlin/ByteArray, kotlin/Int = ..., kotlin/Int = ...) // kotlinx.io.bytestring/ByteStringBuilder.append|append(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] - final fun toByteString(): kotlinx.io.bytestring/ByteString // kotlinx.io.bytestring/ByteStringBuilder.toByteString|toByteString(){}[0] -} - -final object kotlinx.io.bytestring.unsafe/UnsafeByteStringOperations { // kotlinx.io.bytestring.unsafe/UnsafeByteStringOperations|null[0] - final fun wrapUnsafe(kotlin/ByteArray): kotlinx.io.bytestring/ByteString // kotlinx.io.bytestring.unsafe/UnsafeByteStringOperations.wrapUnsafe|wrapUnsafe(kotlin.ByteArray){}[0] - final inline fun withByteArrayUnsafe(kotlinx.io.bytestring/ByteString, kotlin/Function1) // kotlinx.io.bytestring.unsafe/UnsafeByteStringOperations.withByteArrayUnsafe|withByteArrayUnsafe(kotlinx.io.bytestring.ByteString;kotlin.Function1){}[0] -} - -final val kotlinx.io.bytestring/indices // kotlinx.io.bytestring/indices|@kotlinx.io.bytestring.ByteString{}indices[0] - final fun (kotlinx.io.bytestring/ByteString).(): kotlin.ranges/IntRange // kotlinx.io.bytestring/indices.|@kotlinx.io.bytestring.ByteString(){}[0] - -final fun (kotlin.io.encoding/Base64).kotlinx.io.bytestring/decode(kotlinx.io.bytestring/ByteString, kotlin/Int = ..., kotlin/Int = ...): kotlin/ByteArray // kotlinx.io.bytestring/decode|decode@kotlin.io.encoding.Base64(kotlinx.io.bytestring.ByteString;kotlin.Int;kotlin.Int){}[0] -final fun (kotlin.io.encoding/Base64).kotlinx.io.bytestring/decodeIntoByteArray(kotlinx.io.bytestring/ByteString, kotlin/ByteArray, kotlin/Int = ..., kotlin/Int = ..., kotlin/Int = ...): kotlin/Int // kotlinx.io.bytestring/decodeIntoByteArray|decodeIntoByteArray@kotlin.io.encoding.Base64(kotlinx.io.bytestring.ByteString;kotlin.ByteArray;kotlin.Int;kotlin.Int;kotlin.Int){}[0] -final fun (kotlin.io.encoding/Base64).kotlinx.io.bytestring/decodeToByteString(kotlin/ByteArray, kotlin/Int = ..., kotlin/Int = ...): kotlinx.io.bytestring/ByteString // kotlinx.io.bytestring/decodeToByteString|decodeToByteString@kotlin.io.encoding.Base64(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] -final fun (kotlin.io.encoding/Base64).kotlinx.io.bytestring/decodeToByteString(kotlin/CharSequence, kotlin/Int = ..., kotlin/Int = ...): kotlinx.io.bytestring/ByteString // kotlinx.io.bytestring/decodeToByteString|decodeToByteString@kotlin.io.encoding.Base64(kotlin.CharSequence;kotlin.Int;kotlin.Int){}[0] -final fun (kotlin.io.encoding/Base64).kotlinx.io.bytestring/decodeToByteString(kotlinx.io.bytestring/ByteString, kotlin/Int = ..., kotlin/Int = ...): kotlinx.io.bytestring/ByteString // kotlinx.io.bytestring/decodeToByteString|decodeToByteString@kotlin.io.encoding.Base64(kotlinx.io.bytestring.ByteString;kotlin.Int;kotlin.Int){}[0] -final fun (kotlin.io.encoding/Base64).kotlinx.io.bytestring/encode(kotlinx.io.bytestring/ByteString, kotlin/Int = ..., kotlin/Int = ...): kotlin/String // kotlinx.io.bytestring/encode|encode@kotlin.io.encoding.Base64(kotlinx.io.bytestring.ByteString;kotlin.Int;kotlin.Int){}[0] -final fun (kotlin.io.encoding/Base64).kotlinx.io.bytestring/encodeIntoByteArray(kotlinx.io.bytestring/ByteString, kotlin/ByteArray, kotlin/Int = ..., kotlin/Int = ..., kotlin/Int = ...): kotlin/Int // kotlinx.io.bytestring/encodeIntoByteArray|encodeIntoByteArray@kotlin.io.encoding.Base64(kotlinx.io.bytestring.ByteString;kotlin.ByteArray;kotlin.Int;kotlin.Int;kotlin.Int){}[0] -final fun (kotlin.io.encoding/Base64).kotlinx.io.bytestring/encodeToByteArray(kotlinx.io.bytestring/ByteString, kotlin/Int = ..., kotlin/Int = ...): kotlin/ByteArray // kotlinx.io.bytestring/encodeToByteArray|encodeToByteArray@kotlin.io.encoding.Base64(kotlinx.io.bytestring.ByteString;kotlin.Int;kotlin.Int){}[0] -final fun (kotlin/String).kotlinx.io.bytestring/encodeToByteString(): kotlinx.io.bytestring/ByteString // kotlinx.io.bytestring/encodeToByteString|encodeToByteString@kotlin.String(){}[0] -final fun (kotlin/String).kotlinx.io.bytestring/hexToByteString(kotlin.text/HexFormat = ...): kotlinx.io.bytestring/ByteString // kotlinx.io.bytestring/hexToByteString|hexToByteString@kotlin.String(kotlin.text.HexFormat){}[0] -final fun (kotlinx.io.bytestring/ByteString).kotlinx.io.bytestring/contentEquals(kotlin/ByteArray): kotlin/Boolean // kotlinx.io.bytestring/contentEquals|contentEquals@kotlinx.io.bytestring.ByteString(kotlin.ByteArray){}[0] -final fun (kotlinx.io.bytestring/ByteString).kotlinx.io.bytestring/decodeToString(): kotlin/String // kotlinx.io.bytestring/decodeToString|decodeToString@kotlinx.io.bytestring.ByteString(){}[0] -final fun (kotlinx.io.bytestring/ByteString).kotlinx.io.bytestring/endsWith(kotlin/ByteArray): kotlin/Boolean // kotlinx.io.bytestring/endsWith|endsWith@kotlinx.io.bytestring.ByteString(kotlin.ByteArray){}[0] -final fun (kotlinx.io.bytestring/ByteString).kotlinx.io.bytestring/endsWith(kotlinx.io.bytestring/ByteString): kotlin/Boolean // kotlinx.io.bytestring/endsWith|endsWith@kotlinx.io.bytestring.ByteString(kotlinx.io.bytestring.ByteString){}[0] -final fun (kotlinx.io.bytestring/ByteString).kotlinx.io.bytestring/indexOf(kotlin/Byte, kotlin/Int = ...): kotlin/Int // kotlinx.io.bytestring/indexOf|indexOf@kotlinx.io.bytestring.ByteString(kotlin.Byte;kotlin.Int){}[0] -final fun (kotlinx.io.bytestring/ByteString).kotlinx.io.bytestring/indexOf(kotlin/ByteArray, kotlin/Int = ...): kotlin/Int // kotlinx.io.bytestring/indexOf|indexOf@kotlinx.io.bytestring.ByteString(kotlin.ByteArray;kotlin.Int){}[0] -final fun (kotlinx.io.bytestring/ByteString).kotlinx.io.bytestring/indexOf(kotlinx.io.bytestring/ByteString, kotlin/Int = ...): kotlin/Int // kotlinx.io.bytestring/indexOf|indexOf@kotlinx.io.bytestring.ByteString(kotlinx.io.bytestring.ByteString;kotlin.Int){}[0] -final fun (kotlinx.io.bytestring/ByteString).kotlinx.io.bytestring/isEmpty(): kotlin/Boolean // kotlinx.io.bytestring/isEmpty|isEmpty@kotlinx.io.bytestring.ByteString(){}[0] -final fun (kotlinx.io.bytestring/ByteString).kotlinx.io.bytestring/isNotEmpty(): kotlin/Boolean // kotlinx.io.bytestring/isNotEmpty|isNotEmpty@kotlinx.io.bytestring.ByteString(){}[0] -final fun (kotlinx.io.bytestring/ByteString).kotlinx.io.bytestring/lastIndexOf(kotlin/Byte, kotlin/Int = ...): kotlin/Int // kotlinx.io.bytestring/lastIndexOf|lastIndexOf@kotlinx.io.bytestring.ByteString(kotlin.Byte;kotlin.Int){}[0] -final fun (kotlinx.io.bytestring/ByteString).kotlinx.io.bytestring/lastIndexOf(kotlin/ByteArray, kotlin/Int = ...): kotlin/Int // kotlinx.io.bytestring/lastIndexOf|lastIndexOf@kotlinx.io.bytestring.ByteString(kotlin.ByteArray;kotlin.Int){}[0] -final fun (kotlinx.io.bytestring/ByteString).kotlinx.io.bytestring/lastIndexOf(kotlinx.io.bytestring/ByteString, kotlin/Int = ...): kotlin/Int // kotlinx.io.bytestring/lastIndexOf|lastIndexOf@kotlinx.io.bytestring.ByteString(kotlinx.io.bytestring.ByteString;kotlin.Int){}[0] -final fun (kotlinx.io.bytestring/ByteString).kotlinx.io.bytestring/startsWith(kotlin/ByteArray): kotlin/Boolean // kotlinx.io.bytestring/startsWith|startsWith@kotlinx.io.bytestring.ByteString(kotlin.ByteArray){}[0] -final fun (kotlinx.io.bytestring/ByteString).kotlinx.io.bytestring/startsWith(kotlinx.io.bytestring/ByteString): kotlin/Boolean // kotlinx.io.bytestring/startsWith|startsWith@kotlinx.io.bytestring.ByteString(kotlinx.io.bytestring.ByteString){}[0] -final fun (kotlinx.io.bytestring/ByteString).kotlinx.io.bytestring/toHexString(kotlin.text/HexFormat = ...): kotlin/String // kotlinx.io.bytestring/toHexString|toHexString@kotlinx.io.bytestring.ByteString(kotlin.text.HexFormat){}[0] -final fun (kotlinx.io.bytestring/ByteString).kotlinx.io.bytestring/toHexString(kotlin/Int = ..., kotlin/Int = ..., kotlin.text/HexFormat = ...): kotlin/String // kotlinx.io.bytestring/toHexString|toHexString@kotlinx.io.bytestring.ByteString(kotlin.Int;kotlin.Int;kotlin.text.HexFormat){}[0] -final fun (kotlinx.io.bytestring/ByteStringBuilder).kotlinx.io.bytestring/append(kotlin/ByteArray...) // kotlinx.io.bytestring/append|append@kotlinx.io.bytestring.ByteStringBuilder(kotlin.ByteArray...){}[0] -final fun (kotlinx.io.bytestring/ByteStringBuilder).kotlinx.io.bytestring/append(kotlin/UByte) // kotlinx.io.bytestring/append|append@kotlinx.io.bytestring.ByteStringBuilder(kotlin.UByte){}[0] -final fun (kotlinx.io.bytestring/ByteStringBuilder).kotlinx.io.bytestring/append(kotlinx.io.bytestring/ByteString) // kotlinx.io.bytestring/append|append@kotlinx.io.bytestring.ByteStringBuilder(kotlinx.io.bytestring.ByteString){}[0] -final fun <#A: kotlin.text/Appendable> (kotlin.io.encoding/Base64).kotlinx.io.bytestring/encodeToAppendable(kotlinx.io.bytestring/ByteString, #A, kotlin/Int = ..., kotlin/Int = ...): #A // kotlinx.io.bytestring/encodeToAppendable|encodeToAppendable@kotlin.io.encoding.Base64(kotlinx.io.bytestring.ByteString;0:0;kotlin.Int;kotlin.Int){0§}[0] -final fun kotlinx.io.bytestring/ByteString(): kotlinx.io.bytestring/ByteString // kotlinx.io.bytestring/ByteString|ByteString(){}[0] -final fun kotlinx.io.bytestring/ByteString(kotlin/ByteArray...): kotlinx.io.bytestring/ByteString // kotlinx.io.bytestring/ByteString|ByteString(kotlin.ByteArray...){}[0] -final fun kotlinx.io.bytestring/ByteString(kotlin/UByteArray...): kotlinx.io.bytestring/ByteString // kotlinx.io.bytestring/ByteString|ByteString(kotlin.UByteArray...){}[0] -final inline fun kotlinx.io.bytestring/buildByteString(kotlin/Int = ..., kotlin/Function1): kotlinx.io.bytestring/ByteString // kotlinx.io.bytestring/buildByteString|buildByteString(kotlin.Int;kotlin.Function1){}[0] - -// Targets: [apple] -final fun (kotlinx.io.bytestring/ByteString).kotlinx.io.bytestring/toNSData(): platform.Foundation/NSData // kotlinx.io.bytestring/toNSData|toNSData@kotlinx.io.bytestring.ByteString(){}[0] - -// Targets: [apple] -final fun (platform.Foundation/NSData).kotlinx.io.bytestring/toByteString(): kotlinx.io.bytestring/ByteString // kotlinx.io.bytestring/toByteString|toByteString@platform.Foundation.NSData(){}[0] diff --git a/bytestring/apple/src/ByteStringApple.kt b/bytestring/apple/src/ByteStringApple.kt deleted file mode 100644 index 786a4ba8a..000000000 --- a/bytestring/apple/src/ByteStringApple.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -package kotlinx.io.bytestring - -import kotlinx.cinterop.* -import kotlinx.io.bytestring.unsafe.UnsafeByteStringApi -import kotlinx.io.bytestring.unsafe.UnsafeByteStringOperations -import platform.Foundation.NSData -import platform.Foundation.create - -/** - * Returns a new [NSData] instance initialized with bytes copied from [this] ByteString. - * - * @sample kotlinx.io.bytestring.samples.ByteStringSamplesApple.nsDataConversion - */ -@OptIn(UnsafeNumber::class, BetaInteropApi::class, ExperimentalForeignApi::class) -public fun ByteString.toNSData(): NSData { - if (isEmpty()) { - return NSData() - } - val data = getBackingArrayReference() - return data.usePinned { - NSData.create(bytes = it.addressOf(0), length = data.size.convert()) - } -} - -/** - * Returns a new [ByteString] holding data copied from [this] NSData. - * - * @sample kotlinx.io.bytestring.samples.ByteStringSamplesApple.nsDataConversion - */ -@OptIn(ExperimentalForeignApi::class, UnsafeNumber::class, UnsafeByteStringApi::class) -public fun NSData.toByteString(): ByteString { - val l = length.toLong() - if (l == 0L) { - return ByteString.EMPTY - } - if (l > Int.MAX_VALUE) { - throw IllegalArgumentException("NSData content is to long to read as byte array: $l") - } - return UnsafeByteStringOperations.wrapUnsafe( - bytes!!.readBytes(l.toInt()) - ) -} diff --git a/bytestring/apple/test/ByteStringAppleTest.kt b/bytestring/apple/test/ByteStringAppleTest.kt deleted file mode 100644 index 702bbf5a2..000000000 --- a/bytestring/apple/test/ByteStringAppleTest.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -package kotlinx.io.bytestring - -import kotlinx.cinterop.* -import kotlinx.io.bytestring.unsafe.UnsafeByteStringApi -import kotlinx.io.bytestring.unsafe.UnsafeByteStringOperations -import platform.Foundation.NSData -import platform.Foundation.create -import platform.posix.memset -import kotlin.io.encoding.Base64 -import kotlin.io.encoding.ExperimentalEncodingApi -import kotlin.test.* - -@OptIn(UnsafeNumber::class) -class ByteStringAppleTest { - @OptIn(ExperimentalForeignApi::class) - @Test - fun toNSData() { - val emptyData = ByteString().toNSData() - assertEquals(0u, emptyData.length) - - val copy = ByteString(0, 1, 2, 3, 4, 5).toNSData() - assertContentEquals(byteArrayOf(0, 1, 2, 3, 4, 5), copy.bytes!!.readBytes(copy.length.convert())) - } - - @OptIn(BetaInteropApi::class, ExperimentalEncodingApi::class) - @Test - fun fromNSData() { - assertTrue(NSData().toByteString().isEmpty()) - val src = NSData.create( - base64EncodedString = Base64.Default.encode(byteArrayOf(0, 1, 2, 3, 4, 5)), - options = 0u - )!! - val copy = src.toByteString() - assertContentEquals(byteArrayOf(0, 1, 2, 3, 4, 5), copy.toByteArray()) - } - - @OptIn(UnsafeByteStringApi::class, ExperimentalForeignApi::class) - @Test - fun toNSDataDataIntegrity() { - val mutableArray = byteArrayOf(0, 0, 0, 0, 0, 0) - // Don't try that at home, kids! - val cursedString = UnsafeByteStringOperations.wrapUnsafe(mutableArray) - val nsData = cursedString.toNSData() - - mutableArray.fill(42) - // NSData should hold a copy - assertContentEquals(ByteArray(6), nsData.bytes!!.readBytes(6)) - } - - @OptIn(ExperimentalForeignApi::class, BetaInteropApi::class) - @Test - fun fromNSDataIntegrity() = memScoped { - val length = 6 - val data = allocArray(length) - memset(data, 0, length.convert()) - - val cursedData = NSData.create( - bytesNoCopy = data, length = length.convert(), - freeWhenDone = false - ) - - val byteString = cursedData.toByteString() - memset(data, 42, length.convert()) - - assertContentEquals(ByteArray(length), byteString.toByteArray()) - } -} diff --git a/bytestring/apple/test/samples/samplesApple.kt b/bytestring/apple/test/samples/samplesApple.kt deleted file mode 100644 index c2d6432bb..000000000 --- a/bytestring/apple/test/samples/samplesApple.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -package kotlinx.io.bytestring.samples - -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.UnsafeNumber -import kotlinx.io.bytestring.* -import platform.Foundation.* -import kotlin.test.* - -class ByteStringSamplesApple { - @OptIn(UnsafeNumber::class, ExperimentalForeignApi::class, ExperimentalStdlibApi::class) - @Test - fun nsDataConversion() { - val originalByteString: ByteString = "Compress me, please!".encodeToByteString() - - val compressedNSData: NSData = originalByteString.toNSData().compressedDataUsingAlgorithm( - algorithm = NSDataCompressionAlgorithmZlib, - error = null - )!! - - val compressedByteString: ByteString = compressedNSData.toByteString() - assertEquals("73cecf2d284a2d2e56c84dd55128c8494d2c4e550400", compressedByteString.toHexString()) - // If there's no zlib-flate on your path, you can test it using: - // zlib.decompress(binascii.unhexlify("73cecf2d284a2d2e56c84dd55128c8494d2c4e550400"), -15) - } -} diff --git a/bytestring/build.gradle.kts b/bytestring/build.gradle.kts deleted file mode 100644 index 77af1c57c..000000000 --- a/bytestring/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -plugins { - id("kotlinx-io-multiplatform") - id("kotlinx-io-publish") - id("kotlinx-io-dokka") - id("kotlinx-io-android-compat") - alias(libs.plugins.kover) -} - -kotlin { - js { - nodejs { - testTask { - useMocha { - timeout = "30s" - } - } - } - browser { - testTask { - useMocha { - timeout = "30s" - } - } - } - } -} diff --git a/bytestring/common/src/-Platform.kt b/bytestring/common/src/-Platform.kt deleted file mode 100644 index dfaae6840..000000000 --- a/bytestring/common/src/-Platform.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* -* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. -* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. -*/ -package kotlinx.io.bytestring - -/** - * Annotation indicating that the marked property is the subject of benign data race. - * LLVM does not support this notion, so on K/N platforms we alias it into `@Volatile` to prevent potential OoTA. - */ -@OptionalExpectation -@Target(AnnotationTarget.FIELD) -@OptIn(ExperimentalMultiplatform::class) -internal expect annotation class BenignDataRace() diff --git a/bytestring/common/src/Base64.kt b/bytestring/common/src/Base64.kt deleted file mode 100644 index f21ebc4e1..000000000 --- a/bytestring/common/src/Base64.kt +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.bytestring - -import kotlin.io.encoding.Base64 -import kotlin.io.encoding.Base64.Default.encode -import kotlin.io.encoding.Base64.Default.encodeToByteArray -import kotlin.io.encoding.ExperimentalEncodingApi - -/** - * Encodes bytes from the specified [source] byte string or its subrange. - * Returns a [ByteArray] containing the resulting symbols. - * - * If the size of the [source] byte string or its subrange is not an integral multiple of 3, - * the result is padded with `'='` to an integral multiple of 4 symbols. - * - * Each resulting symbol occupies one byte in the returned byte array. - * - * Use [encode] to get the output in string form. - * - * @param source the byte string to encode bytes from. - * @param startIndex the beginning (inclusive) of the subrange to encode, 0 by default. - * @param endIndex the end (exclusive) of the subrange to encode, size of the [source] byte string by default. - * - * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices. - * @throws IllegalArgumentException when `startIndex > endIndex`. - * - * @return a [ByteArray] with the resulting symbols. - */ -@ExperimentalEncodingApi -public fun Base64.encodeToByteArray(source: ByteString, startIndex: Int = 0, endIndex: Int = source.size): ByteArray { - return encodeToByteArray(source.getBackingArrayReference(), startIndex, endIndex) -} - -/** - * Encodes bytes from the specified [source] byte string or its subrange and writes resulting symbols into the [destination] array. - * Returns the number of symbols written. - * - * If the size of the [source] byte string or its subrange is not an integral multiple of 3, - * the result is padded with `'='` to an integral multiple of 4 symbols. - * - * @param source the byte string to encode bytes from. - * @param destination the array to write symbols into. - * @param destinationOffset the starting index in the [destination] array to write symbols to, 0 by default. - * @param startIndex the beginning (inclusive) of the subrange to encode, 0 by default. - * @param endIndex the end (exclusive) of the subrange to encode, size of the [source] byte string by default. - * - * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices. - * @throws IllegalArgumentException when `startIndex > endIndex`. - * @throws IndexOutOfBoundsException when the resulting symbols don't fit into the [destination] array starting at the specified [destinationOffset], - * or when that index is out of the [destination] array indices range. - * - * @return the number of symbols written into [destination] array. - */ -@ExperimentalEncodingApi -public fun Base64.encodeIntoByteArray( - source: ByteString, - destination: ByteArray, - destinationOffset: Int = 0, - startIndex: Int = 0, - endIndex: Int = source.size -): Int { - return encodeIntoByteArray(source.getBackingArrayReference(), destination, destinationOffset, startIndex, endIndex) -} - -/** - * Encodes bytes from the specified [source] byte string or its subrange. - * Returns a string with the resulting symbols. - * - * If the size of the [source] byte string or its subrange is not an integral multiple of 3, - * the result is padded with `'='` to an integral multiple of 4 symbols. - * - * Use [encodeToByteArray] to get the output in [ByteArray] form. - * - * @param source the byte string to encode bytes from. - * @param startIndex the beginning (inclusive) of the subrange to encode, 0 by default. - * @param endIndex the end (exclusive) of the subrange to encode, size of the [source] byte string by default. - * - * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices. - * @throws IllegalArgumentException when `startIndex > endIndex`. - * - * @return a string with the resulting symbols. - */ -@ExperimentalEncodingApi -public fun Base64.encode( - source: ByteString, - startIndex: Int = 0, - endIndex: Int = source.size -): String { - return encode(source.getBackingArrayReference(), startIndex, endIndex) -} - -/** - * Encodes bytes from the specified [source] byte string or its subrange and appends resulting symbols to the [destination] appendable. - * Returns the destination appendable. - * - * If the size of the [source] byte string or its subrange is not an integral multiple of 3, - * the result is padded with `'='` to an integral multiple of 4 symbols. - * - * @param source the byte string to encode bytes from. - * @param destination the appendable to append symbols to. - * @param startIndex the beginning (inclusive) of the subrange to encode, 0 by default. - * @param endIndex the end (exclusive) of the subrange to encode, size of the [source] byte string by default. - * - * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices. - * @throws IllegalArgumentException when `startIndex > endIndex`. - * - * @return the destination appendable. - */ -@ExperimentalEncodingApi -public fun Base64.encodeToAppendable( - source: ByteString, - destination: A, - startIndex: Int = 0, - endIndex: Int = source.size -): A { - return encodeToAppendable(source.getBackingArrayReference(), destination, startIndex, endIndex) -} - - -/** - * Decodes symbols from the specified [source] byte string or its subrange. - * Returns a [ByteArray] containing the resulting bytes. - * - * The symbols for decoding are not required to be padded. - * However, if there is a padding character present, the correct amount of padding character(s) must be present. - * The padding character `'='` is interpreted as the end of the encoded byte data. Subsequent symbols are prohibited. - * - * @param source the byte string to decode symbols from. - * @param startIndex the beginning (inclusive) of the subrange to decode, 0 by default. - * @param endIndex the end (exclusive) of the subrange to decode, size of the [source] byte string by default. - * - * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices. - * @throws IllegalArgumentException when `startIndex > endIndex`. - * @throws IllegalArgumentException when the symbols for decoding are padded incorrectly or there are extra symbols after the padding. - * - * @return a [ByteArray] with the resulting bytes. - */ -@ExperimentalEncodingApi -public fun Base64.decode(source: ByteString, startIndex: Int = 0, endIndex: Int = source.size): ByteArray { - return decode(source.getBackingArrayReference(), startIndex, endIndex) -} - -/** - * Decodes symbols from the specified [source] char sequence or its substring. - * Returns a [ByteString] containing the resulting bytes. - * - * The symbols for decoding are not required to be padded. - * However, if there is a padding character present, the correct amount of padding character(s) must be present. - * The padding character `'='` is interpreted as the end of the encoded byte data. Subsequent symbols are prohibited. - * - * @param source the char sequence to decode symbols from. - * @param startIndex the beginning (inclusive) of the substring to decode, 0 by default. - * @param endIndex the end (exclusive) of the substring to decode, length of the [source] by default. - * - * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] indices. - * @throws IllegalArgumentException when `startIndex > endIndex`. - * @throws IllegalArgumentException when the symbols for decoding are padded incorrectly or there are extra symbols after the padding. - * - * @return a [ByteArray] with the resulting bytes. - */ -@ExperimentalEncodingApi -public fun Base64.decodeToByteString(source: CharSequence, startIndex: Int = 0, endIndex: Int = source.length): ByteString { - return ByteString.wrap(decode(source, startIndex, endIndex)) -} - -/** - * Decodes symbols from the specified [source] byte string or its subrange and writes resulting bytes into the [destination] array. - * Returns the number of bytes written. - * - * The symbols for decoding are not required to be padded. - * However, if there is a padding character present, the correct amount of padding character(s) must be present. - * The padding character `'='` is interpreted as the end of the encoded byte data. Subsequent symbols are prohibited. - * - * @param source the byte string to decode symbols from. - * @param destination the array to write bytes into. - * @param destinationOffset the starting index in the [destination] array to write bytes to, 0 by default. - * @param startIndex the beginning (inclusive) of the subrange to decode, 0 by default. - * @param endIndex the end (exclusive) of the subrange to decode, size of the [source] byte string by default. - * - * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices. - * @throws IllegalArgumentException when `startIndex > endIndex`. - * @throws IndexOutOfBoundsException when the resulting bytes don'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 the symbols for decoding are padded incorrectly or there are extra symbols after the padding. - * - * @return the number of bytes written into [destination] array. - */ -@ExperimentalEncodingApi -public fun Base64.decodeIntoByteArray( - source: ByteString, - destination: ByteArray, - destinationOffset: Int = 0, - startIndex: Int = 0, - endIndex: Int = source.size -): Int { - return decodeIntoByteArray(source.getBackingArrayReference(), destination, destinationOffset, startIndex, endIndex) -} - -/** - * Decodes symbols from the specified [source] byte string or its subrange. - * Returns a [ByteString] containing the resulting bytes. - * - * The symbols for decoding are not required to be padded. - * However, if there is a padding character present, the correct amount of padding character(s) must be present. - * The padding character `'='` is interpreted as the end of the encoded byte data. Subsequent symbols are prohibited. - * - * @param source the byte string to decode symbols from. - * @param startIndex the beginning (inclusive) of the subrange to decode, 0 by default. - * @param endIndex the end (exclusive) of the subrange to decode, size of the [source] byte string by default. - * - * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices. - * @throws IllegalArgumentException when `startIndex > endIndex`. - * @throws IllegalArgumentException when the symbols for decoding are padded incorrectly or there are extra symbols after the padding. - * - * @return a [ByteString] with the resulting bytes. - */ -@ExperimentalEncodingApi -public fun Base64.decodeToByteString(source: ByteArray, startIndex: Int = 0, endIndex: Int = source.size): ByteString { - return ByteString.wrap(decode(source, startIndex, endIndex)) -} - -/** - * Decodes symbols from the specified [source] byte string or its subrange. - * Returns a [ByteString] containing the resulting bytes. - * - * The symbols for decoding are not required to be padded. - * However, if there is a padding character present, the correct amount of padding character(s) must be present. - * The padding character `'='` is interpreted as the end of the encoded byte data. Subsequent symbols are prohibited. - * - * @param source the byte string to decode symbols from. - * @param startIndex the beginning (inclusive) of the subrange to decode, 0 by default. - * @param endIndex the end (exclusive) of the subrange to decode, size of the [source] byte string by default. - * - * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [source] byte string indices. - * @throws IllegalArgumentException when `startIndex > endIndex`. - * @throws IllegalArgumentException when the symbols for decoding are padded incorrectly or there are extra symbols after the padding. - * - * @return a [ByteString] with the resulting bytes. - */ -@ExperimentalEncodingApi -public fun Base64.decodeToByteString(source: ByteString, startIndex: Int = 0, endIndex: Int = source.size): ByteString { - return ByteString.wrap(decode(source.getBackingArrayReference(), startIndex, endIndex)) -} diff --git a/bytestring/common/src/ByteString.kt b/bytestring/common/src/ByteString.kt deleted file mode 100644 index 29820b880..000000000 --- a/bytestring/common/src/ByteString.kt +++ /dev/null @@ -1,544 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -/* - * Copyright (C) 2018 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kotlinx.io.bytestring - -import kotlin.js.JsName -import kotlin.math.max -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 -} else { - ByteString.wrap(bytes) -} - -/** - * Wraps given [bytes] into a byte string. - * - * Internally, [bytes] will be stored as [ByteArray], and accesses to individual - * [ByteString]'s bytes will return a signed [Byte] as if someone call `bytes[i].toByte()`. - * - * @param bytes a sequence of bytes to be wrapped. - * - * @sample kotlinx.io.bytestring.samples.ByteStringSamples.constructionFromUBytesSample - */ -@OptIn(ExperimentalUnsignedTypes::class) -public fun ByteString(vararg bytes: UByte): ByteString = if (bytes.isEmpty()) { - ByteString.EMPTY -} else { - ByteString.wrap(bytes.asByteArray()) -} - -@JsName("EmptyByteString") -public fun ByteString(): ByteString = ByteString.EMPTY - -/** - * An immutable wrapper around a byte sequence providing [String] like functionality. - * - * ByteString allows treating binary data as a value and passing it to other functions - * without worrying about data modification. - * The class facilitates various operations on binary data, like comparison or testing for subsequence inclusion. - * - * ByteString is a good fit for untyped binary data that could not be represented as [String], - * like hashes, payload of network packets, encrypted data, etc. - * - * ByteString copies data on creation as well as on conversion back to [ByteArray], thus guaranteeing that subsequent - * modification of source data or data returned from [toByteArray] won't mutate the string itself. - */ -public class ByteString private constructor( - private val data: ByteArray, - @Suppress("UNUSED_PARAMETER") dummy: Any? -) : Comparable { - /** - * Wraps a copy of [data] subarray starting at [startIndex] and ending at [endIndex] into a byte string. - * - * @param data the array whose subarray should be copied and wrapped into a byte string. - * @param startIndex the start index (inclusive) of a subarray to copy, `0` by default. - * @param endIndex the end index (exclusive) of a subarray to copy, `data.size` be default. - * - * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [data] array indices. - * @throws IllegalArgumentException when `startIndex > endIndex`. - * - * @sample kotlinx.io.bytestring.samples.ByteStringSamples.constructionSample - */ - public constructor(data: ByteArray, startIndex: Int = 0, endIndex: Int = data.size) : - this(data.copyOfRange(startIndex, endIndex), null) - - @BenignDataRace - private var hashCode: Int = 0 - - public companion object { - /** - * An empty ByteString. - */ - internal val EMPTY: ByteString = ByteString(ByteArray(0), null) - - internal fun wrap(byteArray: ByteArray): ByteString = ByteString(byteArray, null) - - private val HEX_DIGITS = "0123456789abcdef".toCharArray() - } - - /** - * Returns size of this ByteString. - */ - public val size: Int - get(): Int = data.size - - /** - * Returns `true` if [other] is a byte string containing exactly the same byte sequence. - * - * @param other the other object to compare this byte string for equality to. - */ - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || this::class != other::class) return false - - other as ByteString - - if (other.data.size != data.size) return false - if (other.hashCode != 0 && hashCode != 0 && other.hashCode != hashCode) return false - return data.contentEquals(other.data) - } - - /** - * Returns a hash code based on the content of this byte string. - */ - override fun hashCode(): Int { - var hc = hashCode - if (hc == 0) { - hc = data.contentHashCode() - hashCode = hc - } - return hc - } - - /** - * Returns a byte at the given index in this byte string. - * - * @param index the index to retrieve the byte at. - * - * @throws IndexOutOfBoundsException when [index] is negative or greater or equal to the [size]. - */ - public operator fun get(index: Int): Byte { - if (index < 0 || index >= size) throw IndexOutOfBoundsException( - "index ($index) is out of byte string bounds: [0..$size)" - ) - return data[index] - } - - /** - * Returns a copy of subsequence starting at [startIndex] and ending at [endIndex] of a byte sequence - * wrapped by this byte string. - * - * @param startIndex the start index (inclusive) of a subsequence to copy, `0` by default. - * @param endIndex the end index (exclusive) of a subsequence to copy, [size] be default. - * - * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of byte string indices. - * @throws IllegalArgumentException when `startIndex > endIndex`. - * - * @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)" } - return data.copyOfRange(startIndex, endIndex) - } - - /** - * Copies a subsequence starting at [startIndex] and ending at [endIndex] of a byte sequence - * wrapped by this byte string and writes it into [destination] array starting at [destinationOffset] offset. - * - * @param destination the array to copy data into. - * @param destinationOffset the offset starting from which data copy should be written to [destination]. - * @param startIndex the start index (inclusive) of a subsequence to copy, `0` by default. - * @param endIndex the end index (exclusive) of a subsequence to copy, [size] be default. - * - * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of byte string indices. - * @throws IndexOutOfBoundsException when the subrange doesn't fit into the [destination] array starting at - * the specified [destinationOffset], or when that index is out of the [destination] array indices range. - * @throws IllegalArgumentException when `startIndex > endIndex`. - * - * @sample kotlinx.io.bytestring.samples.ByteStringSamples.copyToSample - */ - public fun copyInto( - destination: ByteArray, destinationOffset: Int = 0, - startIndex: Int = 0, endIndex: Int = size - ) { - require(startIndex <= endIndex) { "startIndex ($startIndex) > endIndex ($endIndex)" } - data.copyInto(destination, destinationOffset, startIndex, endIndex) - } - - /** - * Returns a new byte string wrapping a subsequence of bytes wrapped by this byte string starting from - * [startIndex] and ending at [endIndex]. - * - * @param startIndex the start index (inclusive) of a subsequence to copy. - * @param endIndex the end index (exclusive) of a subsequence to copy, [size] be default. - * - * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of byte string indices. - * @throws IllegalArgumentException when `startIndex > endIndex`. - * - * @sample kotlinx.io.bytestring.samples.ByteStringSamples.substringSample - */ - public fun substring(startIndex: Int, endIndex: Int = size): ByteString = if (startIndex == endIndex) { - EMPTY - } else { - ByteString(data, startIndex, endIndex) - } - - /** - * Compares a byte sequence wrapped by this byte string to a byte sequence wrapped by [other] - * in lexicographical order. - * Byte values are compared as unsigned integers. - * - * 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 - val localData = data - val otherData = other.data - for (i in 0 until min(size, other.size)) { - val cmp = localData[i].toUByte().compareTo(otherData[i].toUByte()) - if (cmp != 0) return cmp - } - - return size.compareTo(other.size) - } - - /** - * Returns a string representation of this byte string. A string representation consists of [size] and - * a hexadecimal-encoded string of a byte sequence wrapped by this byte string. - * - * The string representation has the following format `ByteString(size=3 hex=ABCDEF)`, - * for empty strings it's always `ByteString(size=0)`. - * - * 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()) { - return "ByteString(size=0)" - } - // format: "ByteString(size=XXX hex=YYYY)" - val sizeStr = size.toString() - val len = 22 + sizeStr.length + size * 2 - return with(StringBuilder(len)) { - append("ByteString(size=") - append(sizeStr) - append(" hex=") - val localData = data - for (i in 0 until size) { - val b = localData[i].toInt() - append(HEX_DIGITS[(b ushr 4) and 0xf]) - append(HEX_DIGITS[b and 0xf]) - } - append(')') - }.toString() - } - - /** - * Returns a reference to the underlying array. - * - * These methods return reference to the underlying array, not to its copy. - * Consider using [toByteArray] if it's impossible to guarantee that the array won't be modified. - */ - @PublishedApi - internal fun getBackingArrayReference(): ByteArray = data -} - -/** - * Returns the range of valid byte indices for this byte string. - */ -public val ByteString.indices: IntRange - get() = 0 until size - -/** - * Returns the index within this byte string of the first occurrence of the specified [byte], - * starting from the specified [startIndex]. - * If the [byte] not found, `-1` is returned. - * - * Behavior of this method is compatible with [CharSequence.indexOf]. - * - * @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() - for (i in max(startIndex, 0) until size) { - if (localData[i] == byte) { - return i - } - } - return -1 -} - -/** - * Returns the index within this byte string of the first occurrence of the specified [byteString], - * starting from the specified [startIndex]. - * If the [byteString] not found, `-1` is returned. - * - * Behavior of this method is compatible with [CharSequence.indexOf]. - * - * @param byteString the value to search for. - * @param startIndex the index (inclusive) starting from which the [byteString] should be searched. - * - * @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) - val localData = getBackingArrayReference() - val firstByte = byteString[0] - for (i in max(startIndex, 0)..size - byteString.size) { - if (localData[i] == firstByte && rangeEquals(i, byteString)) { - return i - } - } - return -1 -} - -/** - * Returns the index within this byte string of the first occurrence of the specified [byteArray], - * starting from the specified [startIndex]. - * If the [byteArray] not found, `-1` is returned. - * - * Behavior of this method is compatible with [CharSequence.indexOf]. - * - * @param byteArray the value to search for. - * @param startIndex the index (inclusive) starting from which the [byteArray] should be searched. - * - * @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) - val localData = getBackingArrayReference() - val firstByte = byteArray[0] - for (i in max(0, startIndex)..size - byteArray.size) { - if (localData[i] == firstByte && rangeEquals(i, byteArray)) { - return i - } - } - return -1 -} - -/** - * Returns the index within this char sequence of the last occurrence of the specified [byte], - * starting from the specified [startIndex]. - * If the [byte] not found, `-1` is returned. - * - * Behavior of this method is compatible with [CharSequence.lastIndexOf]. - * - * @param byte the value to search for. - * @param startIndex the index (inclusive) starting from which the [byte] should be searched. - * - * @sample kotlinx.io.bytestring.samples.ByteStringSamples.lastIndexOfByteSample - */ -public fun ByteString.lastIndexOf(byte: Byte, startIndex: Int = 0): Int { - val localData = getBackingArrayReference() - for (i in size - 1 downTo max(0, startIndex)) { - if (localData[i] == byte) { - return i - } - } - return -1 -} - -/** - * Returns the index within this char sequence of the last occurrence of the specified [byteString], - * starting from the specified [startIndex]. - * If the [byteString] not found, `-1` is returned. - * - * Behavior of this method is compatible with [CharSequence.lastIndexOf]. - * - * @param byteString the value to search for. - * @param startIndex the index (inclusive) starting from which the [byteString] should be searched. - * - * @sample kotlinx.io.bytestring.samples.ByteStringSamples.lastIndexOfByteStringSample - */ -public fun ByteString.lastIndexOf(byteString: ByteString, startIndex: Int = 0): Int { - if (byteString.isEmpty()) return size - for (idx in (size - byteString.size) downTo max(0, startIndex)) { - if (rangeEquals(idx, byteString, 0)) { - return idx - } - } - return -1 -} - -/** - * Returns the index within this char sequence of the last occurrence of the specified [byteArray], - * starting from the specified [startIndex]. - * If the [byteArray] not found, `-1` is returned. - * - * Behavior of this method is compatible with [CharSequence.lastIndexOf]. - * - * @param byteArray the value to search for. - * @param startIndex the index (inclusive) starting from which the [byteArray] should be searched. - * - * @sample kotlinx.io.bytestring.samples.ByteStringSamples.lastIndexOfByteArraySample - */ -public fun ByteString.lastIndexOf(byteArray: ByteArray, startIndex: Int = 0): Int { - if (byteArray.isEmpty()) return size - for (idx in (size - byteArray.size) downTo max(0, startIndex)) { - if (rangeEquals(idx, byteArray, 0)) { - return idx - } - } - return -1 -} - -/** - * Returns true if this byte string starts with the prefix specified by the [byteArray]. - * - * Behavior of this method is compatible with [CharSequence.startsWith]. - * - * @param byteArray the prefix to check for. - * - * @sample kotlinx.io.bytestring.samples.ByteStringSamples.startsWithByteArraySample - */ -public fun ByteString.startsWith(byteArray: ByteArray): Boolean = when { - byteArray.size > size -> false - else -> rangeEquals(0, byteArray) -} - -/** - * Returns true if this byte string starts with the prefix specified by the [byteString]. - * - * Behavior of this method is compatible with [CharSequence.startsWith]. - * - * @param byteString the prefix to check for. - * - * @sample kotlinx.io.bytestring.samples.ByteStringSamples.startsWithByteStringSample - */ -public fun ByteString.startsWith(byteString: ByteString): Boolean = when { - byteString.size > size -> false - byteString.size == size -> equals(byteString) - else -> rangeEquals(0, byteString) -} - -/** - * Returns true if this byte string ends with the suffix specified by the [byteArray]. - * - * Behavior of this method is compatible with [CharSequence.endsWith]. - * - * @param byteArray the suffix to check for. - * - * @sample kotlinx.io.bytestring.samples.ByteStringSamples.endsWithByteArraySample - */ -public fun ByteString.endsWith(byteArray: ByteArray): Boolean = when { - byteArray.size > size -> false - else -> rangeEquals(size - byteArray.size, byteArray) -} - -/** - * Returns true if this byte string ends with the suffix specified by the [byteString]. - * - * Behavior of this method is compatible with [CharSequence.endsWith]. - * - * @param byteString the suffix to check for. - * - * @sample kotlinx.io.bytestring.samples.ByteStringSamples.endsWithByteStringSample - */ -public fun ByteString.endsWith(byteString: ByteString): Boolean = when { - byteString.size > size -> false - byteString.size == size -> equals(byteString) - else -> rangeEquals(size - byteString.size, byteString) -} - -private fun ByteString.rangeEquals( - offset: Int, other: ByteString, otherOffset: Int = 0, - byteCount: Int = other.size - otherOffset -): Boolean { - val localData = getBackingArrayReference() - val otherData = other.getBackingArrayReference() - for (i in 0 until byteCount) { - if (localData[offset + i] != otherData[otherOffset + i]) { - return false - } - } - return true -} - -private fun ByteString.rangeEquals( - offset: Int, other: ByteArray, otherOffset: Int = 0, - byteCount: Int = other.size - otherOffset -): Boolean { - val localData = getBackingArrayReference() - for (i in 0 until byteCount) { - if (localData[offset + i] != other[otherOffset + i]) { - return false - } - } - return true -} - -/** - * Returns `true` if this byte string is empty. - */ -public fun ByteString.isEmpty(): Boolean = size == 0 - -/** - * Returns `true` if this byte string is not empty. - */ -public fun ByteString.isNotEmpty(): Boolean = !isEmpty() - -/** - * Decodes content of a byte string into a string using UTF-8 encoding. - * - * @sample kotlinx.io.bytestring.samples.ByteStringSamples.encodeAndDecodeUtf8String - */ -public fun ByteString.decodeToString(): String { - return getBackingArrayReference().decodeToString() -} - -/** - * Encodes a string into a byte sequence using UTF8-encoding and wraps it into a byte string. - * - * @sample kotlinx.io.bytestring.samples.ByteStringSamples.encodeAndDecodeUtf8String - */ -public fun String.encodeToByteString(): ByteString { - return ByteString.wrap(encodeToByteArray()) -} - -/** - * Returns `true` if the content of this byte string equals to the [array]. - * - * @param array the array to test this byte string's content against. - */ -public fun ByteString.contentEquals(array: ByteArray): Boolean { - return getBackingArrayReference().contentEquals(array) -} diff --git a/bytestring/common/src/ByteStringBuilder.kt b/bytestring/common/src/ByteStringBuilder.kt deleted file mode 100644 index 7b0de5d3e..000000000 --- a/bytestring/common/src/ByteStringBuilder.kt +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.bytestring - -import kotlin.math.max - -/** - * A helper class facilitating [ByteString] construction. - * - * A builder is characterized by the [capacity] - the number of bytes that an instance of builder - * can receive before extending an underlying byte sequence, and [size] - the number of bytes being written - * to the builder. - * - * The builder avoids additional copies and allocations when `size == capacity` when [toByteString] called, - * thus it's recommended to specify expected [ByteString] size as `initialCapacity` when creating a builder. - * - * When a builder runs out of available capacity, a new byte sequence with extended capacity - * will be allocated and previously written data will be copied into it. - * - * @param initialCapacity the initial size of an underlying byte sequence. - * - * @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) - private var offset: Int = 0 - - /** - * The number of bytes being written to this builder. - */ - public val size: Int - get() = offset - - /** - * The number of bytes this builder can store without an extension of an internal buffer. - */ - public val capacity: Int - get() = buffer.size - - /** - * Returns a new [ByteString] wrapping all bytes written to this builder. - * - * There will be no additional allocations or copying of data when `size == capacity`. - */ - public fun toByteString(): ByteString { - if (size == 0) { - return ByteString() - } - if (buffer.size == size) { - return ByteString.wrap(buffer) - } - return ByteString(buffer, 0, size) - } - - /** - * Append a single byte to this builder. - * - * @param byte the byte to append. - */ - public fun append(byte: Byte) { - ensureCapacity(size + 1) - buffer[offset++] = byte - } - - /** - * Appends a subarray of [array] starting at [startIndex] and ending at [endIndex] to this builder. - * - * @param array the array whose subarray should be appended. - * @param startIndex the first index (inclusive) to copy data from the [array]. - * @param endIndex the last index (exclusive) to copy data from the [array] - * - * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [array] array indices. - * @throws IllegalArgumentException when `startIndex > endIndex`. - */ - public fun append(array: ByteArray, startIndex: Int = 0, endIndex: Int = array.size) { - require(startIndex <= endIndex) { "startIndex ($startIndex) > endIndex ($endIndex)" } - if (startIndex < 0 || endIndex > array.size) { - throw IndexOutOfBoundsException("startIndex ($startIndex) and endIndex ($endIndex) represents " + - "an interval out of array's bounds [0..${array.size}).") - } - ensureCapacity(offset + endIndex - startIndex) - - array.copyInto(buffer, offset, startIndex, endIndex) - offset += endIndex - startIndex - } - - private fun ensureCapacity(requiredCapacity: Int) { - if (buffer.size >= requiredCapacity) { - return - } - - var desiredSize = if (buffer.isEmpty()) 16 else (buffer.size * 1.5).toInt() - desiredSize = max(desiredSize, requiredCapacity) - val newBuffer = ByteArray(desiredSize) - buffer.copyInto(newBuffer) - buffer = newBuffer - } -} - -/** - * Appends unsigned byte to this builder. - */ -public fun ByteStringBuilder.append(byte: UByte): Unit = append(byte.toByte()) - -/** - * Appends a byte string to this builder. - */ -public fun ByteStringBuilder.append(byteString: ByteString) { - append(byteString.getBackingArrayReference()) -} - -/** - * Appends bytes to this builder. - */ -public fun ByteStringBuilder.append(vararg bytes: Byte): Unit = append(bytes) - - -/** - * Builds new byte string by populating newly created [ByteStringBuilder] initialized with the given [capacity] - * using provided [builderAction] and then converting it to [ByteString]. - */ -public inline fun buildByteString(capacity: Int = 0, builderAction: ByteStringBuilder.() -> Unit): ByteString { - return ByteStringBuilder(capacity).apply(builderAction).toByteString() -} diff --git a/bytestring/common/src/Hex.kt b/bytestring/common/src/Hex.kt deleted file mode 100644 index 4ecb46b6e..000000000 --- a/bytestring/common/src/Hex.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.bytestring - -/** - * Formats bytes in this byte string using the specified [format]. - * - * Note that only [HexFormat.upperCase] and [HexFormat.BytesHexFormat] affect formatting. - * - * @param format the [HexFormat] to use for formatting, [HexFormat.Default] by default. - * - * @throws IllegalArgumentException if the result length is more than [String] maximum capacity. - */ -@ExperimentalStdlibApi -public fun ByteString.toHexString(format: HexFormat = HexFormat.Default): String { - return getBackingArrayReference().toHexString(0, getBackingArrayReference().size, format) -} - -/** - * Formats bytes in this byte string using the specified [HexFormat]. - * - * Note that only [HexFormat.upperCase] and [HexFormat.BytesHexFormat] affect formatting. - * - * @param startIndex the beginning (inclusive) of the subrange to format, 0 by default. - * @param endIndex the end (exclusive) of the subrange to format, size of this byte string by default. - * @param format the [HexFormat] to use for formatting, [HexFormat.Default] by default. - * - * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of this byte string indices. - * @throws IllegalArgumentException when `startIndex > endIndex`. - * @throws IllegalArgumentException if the result length is more than [String] maximum capacity. - */ -@ExperimentalStdlibApi -public fun ByteString.toHexString( - startIndex: Int = 0, - endIndex: Int = size, - format: HexFormat = HexFormat.Default -): String { - return getBackingArrayReference().toHexString(startIndex, endIndex, format) -} - -/** - * Parses bytes from this string using the specified [HexFormat]. - * - * Note that only [HexFormat.BytesHexFormat] affects parsing, - * and parsing is performed in case-insensitive manner. - * Also, any of the char sequences CRLF, LF and CR is considered a valid line separator. - * - * @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default. - * - * @throws IllegalArgumentException if this string does not comply with the specified [format]. - */ -@ExperimentalStdlibApi -public fun String.hexToByteString(format: HexFormat = HexFormat.Default): ByteString { - return ByteString.wrap(hexToByteArray(format)) -} diff --git a/bytestring/common/src/unsafe/Annotations.kt b/bytestring/common/src/unsafe/Annotations.kt deleted file mode 100644 index 41ece7620..000000000 --- a/bytestring/common/src/unsafe/Annotations.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.bytestring.unsafe - -/** - * Marks declarations whose usage may brake some ByteString invariants. - * - * Consider using other APIs instead when possible. - * Otherwise, make sure to read documentation describing an unsafe API. - */ -@MustBeDocumented -@Retention(AnnotationRetention.BINARY) -@RequiresOptIn( - level = RequiresOptIn.Level.ERROR, - message = "This is a unsafe API and its use may corrupt the data stored in a byte string. " + - "Make sure you fully read and understand documentation of the declaration that is marked as an unsafe API." -) -public annotation class UnsafeByteStringApi diff --git a/bytestring/common/src/unsafe/UnsafeByteStringOperations.kt b/bytestring/common/src/unsafe/UnsafeByteStringOperations.kt deleted file mode 100644 index 9865f48c0..000000000 --- a/bytestring/common/src/unsafe/UnsafeByteStringOperations.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.bytestring.unsafe - -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind.EXACTLY_ONCE -import kotlin.contracts.contract -import kotlinx.io.bytestring.ByteString - -/** - * Collection of helper functions providing unsafe access to the [ByteString]'s underlying byte sequence or allowing - * to wrap byte arrays into [ByteString] without copying the array. - * - * These functions are provided for performance sensitive cases where it is known that the data accessed - * in an unsafe manner won't be modified. Modification of the data backing byte strings may lead to unpredicted - * consequences in the code using the byte string and should be avoided at all costs. - */ -@UnsafeByteStringApi -@OptIn(ExperimentalContracts::class) -public object UnsafeByteStringOperations { - /** - * Creates a new byte string by wrapping [array] without copying it. - * Make sure that the wrapped array won't be modified during the lifespan of the returned byte string. - * - * @param array the array to wrap into the byte string. - */ - public fun wrapUnsafe(array: ByteArray): ByteString = ByteString.wrap(array) - - /** - * Applies [block] to a reference to the underlying array. - * - * This method invokes [block] on a reference to the underlying array, not to its copy. - * Consider using [ByteString.toByteArray] if it's impossible to guarantee that the array won't be modified. - */ - public inline fun withByteArrayUnsafe(byteString: ByteString, block: (ByteArray) -> Unit) { - contract { - callsInPlace(block, EXACTLY_ONCE) - } - block(byteString.getBackingArrayReference()) - } -} diff --git a/bytestring/common/test/ByteStringBase64Test.kt b/bytestring/common/test/ByteStringBase64Test.kt deleted file mode 100644 index 482029096..000000000 --- a/bytestring/common/test/ByteStringBase64Test.kt +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -@file:OptIn(ExperimentalEncodingApi::class) - -package kotlinx.io.bytestring - -import kotlin.io.encoding.Base64 -import kotlin.io.encoding.ExperimentalEncodingApi -import kotlin.test.* - -class ByteStringBase64Test { - private fun bytes(vararg values: Int): ByteArray { - return ByteArray(values.size) { values[it].toByte() } - } - - - private val byteArray: ByteArray = bytes(0b0000_0100, 0b0010_0000, 0b1100_0100, 0b0001_0100, 0b0110_0001, 0b1100_1000) - private val byteString = ByteString.wrap(byteArray) - - private val encodedSymbols = "BCDEFGHI" - private val encodedBytes = encodedSymbols.encodeToByteArray() - private val encodedByteString = ByteString.wrap(encodedBytes) - - - @Test - fun testEncodeToByteArray() { - assertFailsWith { Base64.encodeToByteArray(byteString, startIndex = -1) } - assertFailsWith { Base64.encodeToByteArray(byteString, endIndex = byteString.size + 1) } - assertFailsWith { Base64.encodeToByteArray(byteString, startIndex = byteString.size + 1) } - assertFailsWith { Base64.encodeToByteArray(byteString, startIndex = 3, endIndex = 0) } - - assertTrue(Base64.encodeToByteArray(ByteString.EMPTY).isEmpty()) - assertContentEquals(encodedBytes, Base64.encodeToByteArray(byteString)) - - assertContentEquals(encodedSymbols.encodeToByteArray(0, 4), Base64.encodeToByteArray(byteString, endIndex = 3)) - assertContentEquals(encodedSymbols.encodeToByteArray(4), Base64.encodeToByteArray(byteString, startIndex = 3)) - } - - @Test - fun testEncodeIntoByteArray() { - val destination = ByteArray(encodedBytes.size) - - assertFailsWith { Base64.encodeIntoByteArray(byteString, destination, destinationOffset = -1) } - assertFailsWith { Base64.encodeIntoByteArray(byteString, destination, destinationOffset = destination.size + 1) } - assertFailsWith { Base64.encodeIntoByteArray(byteString, destination, destinationOffset = 1) } - - assertEquals(0, Base64.encodeIntoByteArray(ByteString.EMPTY, destination)) - assertEquals(encodedBytes.size, Base64.encodeIntoByteArray(byteString, destination)) - assertContentEquals(encodedBytes, destination.copyOf(encodedBytes.size)) - - var length = Base64.encodeIntoByteArray(byteString, destination, endIndex = 3) - assertContentEquals(encodedSymbols.encodeToByteArray(0, 4), destination.copyOf(length)) - length += Base64.encodeIntoByteArray(byteString, destination, destinationOffset = length, startIndex = 3) - assertContentEquals(encodedSymbols.encodeToByteArray(), destination) - } - - @Test - fun testEncode() { - assertFailsWith { Base64.encode(byteString, startIndex = -1) } - assertFailsWith { Base64.encode(byteString, endIndex = byteString.size + 1) } - assertFailsWith { Base64.encode(byteString, startIndex = byteString.size + 1) } - assertFailsWith { Base64.encode(byteString, startIndex = 3, endIndex = 0) } - - assertTrue(Base64.encode(ByteArray(0)).isEmpty()) - assertEquals(encodedSymbols, Base64.encode(byteString)) - assertEquals(encodedSymbols.substring(0, 4), Base64.encode(byteString, endIndex = 3)) - assertEquals(encodedSymbols.substring(4), Base64.encode(byteString, startIndex = 3)) - - val destination = StringBuilder() - Base64.encodeToAppendable(byteString, destination, endIndex = 3) - assertEquals(encodedSymbols.substring(0, 4), destination.toString()) - Base64.encodeToAppendable(byteString, destination, startIndex = 3) - assertEquals(encodedSymbols, destination.toString()) - } - - @Test - fun testDecode() { - assertFailsWith { Base64.decode(encodedByteString, startIndex = -1) } - assertFailsWith { Base64.decode(encodedByteString, endIndex = encodedByteString.size + 1) } - assertFailsWith { Base64.decode(encodedByteString, startIndex = encodedByteString.size + 1) } - assertFailsWith { Base64.decode(encodedByteString, startIndex = 4, endIndex = 0) } - - assertEquals(0, Base64.decode(ByteString.EMPTY).size) - assertContentEquals(byteArray, Base64.decode(encodedByteString)) - assertContentEquals(byteArray.copyOfRange(0, 3), Base64.decode(encodedByteString, endIndex = 4)) - assertContentEquals(byteArray.copyOfRange(3, byteString.size), Base64.decode(encodedByteString, startIndex = 4)) - } - - @Test - fun testDecodeIntoByteArray() { - val destination = ByteArray(6) - assertFailsWith { Base64.decodeIntoByteArray(encodedByteString, destination, destinationOffset = -1) } - assertFailsWith { Base64.decodeIntoByteArray(encodedByteString, destination, destinationOffset = destination.size + 1) } - assertFailsWith { Base64.decodeIntoByteArray(encodedByteString, destination, destinationOffset = 1) } - - assertTrue(destination.all { it == 0.toByte() }) - - assertEquals(0, Base64.decodeIntoByteArray(ByteString.EMPTY, destination)) - - var length = Base64.decodeIntoByteArray(encodedByteString, destination, endIndex = 4) - assertContentEquals(byteArray.copyOfRange(0, 3), destination.copyOf(length)) - length += Base64.decodeIntoByteArray(encodedByteString, destination, destinationOffset = length, startIndex = 4) - assertContentEquals(byteArray, destination) - } - - @Test - fun testDecodeToByteString() { - assertFailsWith { Base64.decodeToByteString(encodedSymbols, startIndex = -1) } - assertFailsWith { Base64.decodeToByteString(encodedSymbols, endIndex = encodedByteString.size + 1) } - assertFailsWith { Base64.decodeToByteString(encodedSymbols, startIndex = encodedByteString.size + 1) } - assertFailsWith { Base64.decodeToByteString(encodedSymbols, startIndex = 4, endIndex = 0) } - - assertEquals(0, Base64.decodeToByteString(ByteArray(0)).size) - assertEquals(byteString, Base64.decodeToByteString(encodedSymbols)) - assertEquals(ByteString.wrap(byteArray.copyOfRange(0, 3)), Base64.decodeToByteString(encodedSymbols, endIndex = 4)) - assertEquals(ByteString.wrap(byteArray.copyOfRange(3, byteString.size)), Base64.decodeToByteString(encodedSymbols, startIndex = 4)) - } - - @Test - fun testByteArrayDecodeToByteString() { - assertFailsWith { Base64.decodeToByteString(encodedBytes, startIndex = -1) } - assertFailsWith { Base64.decodeToByteString(encodedBytes, endIndex = encodedByteString.size + 1) } - assertFailsWith { Base64.decodeToByteString(encodedBytes, startIndex = encodedByteString.size + 1) } - assertFailsWith { Base64.decodeToByteString(encodedBytes, startIndex = 4, endIndex = 0) } - - assertEquals(0, Base64.decodeToByteString(ByteArray(0)).size) - assertEquals(byteString, Base64.decodeToByteString(encodedBytes)) - assertEquals(ByteString.wrap(byteArray.copyOfRange(0, 3)), Base64.decodeToByteString(encodedBytes, endIndex = 4)) - assertEquals(ByteString.wrap(byteArray.copyOfRange(3, byteString.size)), Base64.decodeToByteString(encodedBytes, startIndex = 4)) - } - - @Test - fun testByteStringDecodeToByteString() { - assertFailsWith { Base64.decodeToByteString(encodedByteString, startIndex = -1) } - assertFailsWith { Base64.decodeToByteString(encodedByteString, endIndex = encodedByteString.size + 1) } - assertFailsWith { Base64.decodeToByteString(encodedByteString, startIndex = encodedByteString.size + 1) } - assertFailsWith { Base64.decodeToByteString(encodedByteString, startIndex = 4, endIndex = 0) } - - assertEquals(0, Base64.decodeToByteString(ByteString.EMPTY).size) - assertEquals(byteString, Base64.decodeToByteString(encodedByteString)) - assertEquals(ByteString.wrap(byteArray.copyOfRange(0, 3)), Base64.decodeToByteString(encodedByteString, endIndex = 4)) - assertEquals(ByteString.wrap(byteArray.copyOfRange(3, byteString.size)), Base64.decodeToByteString(encodedByteString, startIndex = 4)) - } -} diff --git a/bytestring/common/test/ByteStringBuilderTest.kt b/bytestring/common/test/ByteStringBuilderTest.kt deleted file mode 100644 index 2fbb05408..000000000 --- a/bytestring/common/test/ByteStringBuilderTest.kt +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.bytestring - -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertTrue - -class ByteStringBuilderTest { - @Test - fun emptyString() { - assertTrue(ByteStringBuilder().toByteString().isEmpty()) - assertTrue(ByteStringBuilder(1024).toByteString().isEmpty()) - } - - @Test - fun appendByte() { - val builder = ByteStringBuilder() - with(builder) { - append(1) - append(2) - append(3) - } - assertEquals(ByteString(1, 2, 3), builder.toByteString()) - } - - @Test - fun appendBytes() { - assertEquals(ByteString(1, 2, 3), buildByteString { append(1, 2, 3) }) - } - - @Test - fun appendUByte() { - val builder = ByteStringBuilder() - with(builder) { - append(0x80U) - append(0x81U) - append(0x82U) - } - assertEquals(ByteString(0x80U.toByte(), 0x81U.toByte(), 0x82U.toByte()), builder.toByteString()) - } - - @Test - fun appendArray() { - with(ByteStringBuilder()) { - append(byteArrayOf(1, 2, 3, 4)) - assertEquals(ByteString(1, 2, 3, 4), toByteString()) - } - - with(ByteStringBuilder()) { - append(byteArrayOf(1, 2, 3, 4), startIndex = 2) - assertEquals(ByteString(3, 4), toByteString()) - } - - with(ByteStringBuilder()) { - append(byteArrayOf(1, 2, 3, 4), endIndex = 2) - assertEquals(ByteString(1, 2), toByteString()) - } - - with(ByteStringBuilder()) { - append(byteArrayOf(1, 2, 3, 4), startIndex = 1, endIndex = 3) - assertEquals(ByteString(2, 3), toByteString()) - } - - with(ByteStringBuilder()) { - append(byteArrayOf(1, 2, 3, 4), startIndex = 1, endIndex = 1) - assertEquals(ByteString(), toByteString()) - } - } - - @Test - fun testAppendByteArrayWithInvalidIndices() { - val builder = ByteStringBuilder() - val array = ByteArray(10) - assertFailsWith { builder.append(array, 2, 0) } - assertFailsWith { builder.append(array, -1, 2) } - assertFailsWith { builder.append(array, 0, 1000) } - assertFailsWith { builder.append(array, 1000, 1001) } - } - - @Test - fun appendByteString() { - val builder = ByteStringBuilder() - builder.append(ByteString(1, 2, 3, 4)) - assertEquals(ByteString(1, 2, 3, 4), builder.toByteString()) - } - - @Test - fun appendMultipleValues() { - val string = with(ByteStringBuilder()) { - append(42) - append(ByteArray(10) { it.toByte() }) - append(42U) - append(ByteString(10, 5, 57)) - toByteString() - } - - assertEquals(ByteString(42, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 42, 10, 5, 57), string) - } - - @Test - fun resizeMultipleTimes() { - val builder = ByteStringBuilder() - builder.append(ByteArray(1)) - builder.append(ByteArray(32)) - builder.append(ByteArray(120)) - builder.append(ByteArray(1024)) - - assertEquals(ByteString(ByteArray(1 + 32 + 120 + 1024)), builder.toByteString()) - } - - @Test - fun testSize() { - val builder = ByteStringBuilder() - assertEquals(0, builder.size) - builder.append(1) - assertEquals(1, builder.size) - builder.append(ByteArray(33)) - assertEquals(34, builder.size) - } - - @Test - fun testCapacity() { - assertEquals(0, ByteStringBuilder().capacity) - assertEquals(10, ByteStringBuilder(10).capacity) - - with(ByteStringBuilder()) { - append(1) - assertTrue(capacity >= 1) - append(ByteArray(1024)) - assertTrue(capacity >= 1025) - } - } - - @Test - fun createMultipleByteStrings() { - val builder = ByteStringBuilder() - builder.append(1) - val str0 = builder.toByteString() - assertEquals(ByteString(1), str0) - assertEquals(ByteString(1), builder.toByteString()) - builder.append(2) - assertEquals(ByteString(1, 2), builder.toByteString()) - assertEquals(ByteString(1), str0) - } -} diff --git a/bytestring/common/test/ByteStringHexTest.kt b/bytestring/common/test/ByteStringHexTest.kt deleted file mode 100644 index cd249a050..000000000 --- a/bytestring/common/test/ByteStringHexTest.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -@file:OptIn(ExperimentalStdlibApi::class) - -package kotlinx.io.bytestring - -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFails -import kotlin.test.assertTrue - -class ByteStringHexTest { - private val byteString = ByteString.wrap(byteArrayOf(10, 11, 12, 13, 14, 15)) - - @Test - fun testEmpty() { - assertTrue(ByteString.EMPTY.toHexString().isEmpty()) - assertTrue("".hexToByteString().isEmpty()) - } - - @Test - fun testIndexes() { - assertEquals("0b0c", byteString.toHexString(1, 3)) - assertEquals("0c0d0e0f", byteString.toHexString(2)) - assertEquals("0a0b0c0d", byteString.toHexString(endIndex = 4)) - - assertFails { byteString.toHexString(-1) } - assertFails { byteString.toHexString(endIndex = -1) } - assertFails { byteString.toHexString(3, 2) } - assertFails { byteString.toHexString(10) } - assertFails { byteString.toHexString(endIndex = 11) } - assertFails { byteString.toHexString(10, 11) } - } - - @Test - fun testFormats() { - val format = HexFormat { - bytes { - byteSeparator = "|" - } - } - assertEquals("0a|0b|0c|0d|0e|0f", byteString.toHexString(format)) - assertEquals("0b|0c|0d", byteString.toHexString(1, 4, format)) - - assertEquals(byteString, "0a|0b|0c|0d|0e|0f".hexToByteString(format)) - assertFails { "0a0b0c0d0e0f".hexToByteString(format) } - } - - @Test - fun testDefault() { - assertEquals("0a0b0c0d0e0f", byteString.toHexString()) - assertEquals("0b0c0d", byteString.toHexString(1, 4)) - - assertEquals(byteString, "0a0b0c0d0e0f".hexToByteString()) - assertFails { "0a|0b|0c|0d|0e|0f".hexToByteString() } - } - -} diff --git a/bytestring/common/test/ByteStringTest.kt b/bytestring/common/test/ByteStringTest.kt deleted file mode 100644 index 5aefaf5f5..000000000 --- a/bytestring/common/test/ByteStringTest.kt +++ /dev/null @@ -1,423 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.bytestring - -import kotlin.test.* - -class ByteStringTest { - @Test - fun get() { - val actual = ByteString("abc".encodeToByteArray()) - assertEquals(3, actual.size) - assertEquals(actual[0], 'a'.code.toByte()) - assertEquals(actual[1], 'b'.code.toByte()) - assertEquals(actual[2], 'c'.code.toByte()) - } - - @Test - fun getWithInvalidIndex() { - val str = ByteString(0, 1, 2) - assertFailsWith { str[-1] } - assertFailsWith { str[3] } - } - - @Test - fun equalsAndHashCode() { - with(ByteString(1, 2, 3)) { checkEqualsAndHashCodeAreSame(this, this) } - checkEqualsAndHashCodeAreSame(ByteString(), ByteString(byteArrayOf())) - checkEqualsAndHashCodeAreSame(ByteString(1, 2, 3), ByteString(1, 2, 3)) - - assertNotEquals(ByteString(1, 2, 3), ByteString(3, 2, 1)) - assertNotEquals(ByteString(1, 2, 3).hashCode(), ByteString(3, 2, 1).hashCode()) - - assertNotEquals(ByteString(1, 2, 3, 4), ByteString(1, 2, 3)) - - val str1 = ByteString(1, 2, 3) - val str2 = ByteString(2, 3, 4) - // force hashCode computation - assertNotEquals(str1.hashCode(), str2.hashCode()) - assertNotEquals(str1, str2) - - assertFalse(ByteString().equals(null)) - assertFalse(ByteString().equals(byteArrayOf(1, 2, 3))) - } - - private fun checkEqualsAndHashCodeAreSame(first: ByteString, second: ByteString) { - assertEquals(first, second) - assertEquals(first.hashCode(), second.hashCode()) - } - - @Test - fun toByteArray() { - val str = ByteString(1, 2, 3, 4, 5, 6) - assertContentEquals(byteArrayOf(1, 2, 3, 4, 5, 6), str.toByteArray()) - assertContentEquals(byteArrayOf(), str.toByteArray(0, 0)) - assertContentEquals(byteArrayOf(1, 2, 3), str.toByteArray(endIndex = 3)) - assertContentEquals(byteArrayOf(4, 5, 6), str.toByteArray(startIndex = 3)) - assertContentEquals(byteArrayOf(2, 3, 4), str.toByteArray(startIndex = 1, endIndex = 4)) - } - - @Test - fun toByteArrayWithInvalidIndex() { - val str = ByteString(1, 2, 3) - assertFailsWith { str.toByteArray(-1, 1) } - assertFailsWith { str.toByteArray(1, 4) } - assertFailsWith { str.toByteArray(-1, 4) } - assertFailsWith { str.toByteArray(2, 0) } - } - - @Test - fun copyTo() { - val str = ByteString(1, 2, 3, 4, 5, 6) - val dest = ByteArray(10) - - str.copyInto(dest) - assertContentEquals(byteArrayOf(1, 2, 3, 4, 5, 6, 0, 0, 0, 0), dest) - - dest.fill(0) - str.copyInto(dest, 2) - assertContentEquals(byteArrayOf(0, 0, 1, 2, 3, 4, 5, 6, 0, 0), dest) - - dest.fill(0) - str.copyInto(dest, destinationOffset = 0, startIndex = 1) - assertContentEquals(byteArrayOf(2, 3, 4, 5, 6, 0, 0, 0, 0, 0), dest) - - dest.fill(0) - str.copyInto(dest, destinationOffset = 0, endIndex = 3) - assertContentEquals(byteArrayOf(1, 2, 3, 0, 0, 0, 0, 0, 0, 0), dest) - - dest.fill(0) - str.copyInto(dest, destinationOffset = 0, startIndex = 3, endIndex = 5) - assertContentEquals(byteArrayOf(4, 5, 0, 0, 0, 0, 0, 0, 0, 0), dest) - - dest.fill(0) - str.copyInto(dest, destinationOffset = 5, endIndex = 5) - assertContentEquals(byteArrayOf(0, 0, 0, 0, 0, 1, 2, 3, 4, 5), dest) - - dest.fill(0) - str.copyInto(dest, startIndex = 3, endIndex = 3) - assertContentEquals(ByteArray(10), dest) - } - - @Test - fun copyToWithInvalidArguments() { - val str = ByteString(1, 2, 3) - val dest = ByteArray(10) - - assertFailsWith { str.copyInto(dest, 0, startIndex = 1, endIndex = 0) } - assertFailsWith { str.copyInto(dest, 9) } - assertFailsWith { str.copyInto(dest, -1) } - assertFailsWith { str.copyInto(dest, 0, startIndex = -1) } - assertFailsWith { str.copyInto(dest, 0, endIndex = 5) } - } - - @Test - fun substring() { - val str = ByteString(1, 2, 3, 4, 5) - - assertEquals(ByteString(), str.substring(0, 0)) - assertEquals(ByteString(1, 2, 3), str.substring(startIndex = 0, endIndex = 3)) - assertEquals(ByteString(3, 4, 5), str.substring(startIndex = 2)) - assertEquals(ByteString(2, 3, 4), str.substring(startIndex = 1, endIndex = 4)) - } - - @Test - fun substringWithInvalidArgs() { - val str = ByteString(1, 2, 3) - - assertFailsWith { str.substring(2, 1) } - assertFailsWith { str.substring(-1) } - assertFailsWith { str.substring(0, 10) } - assertFailsWith { str.substring(-10, 10) } - } - - @Test - fun compareTo() { - assertEquals(0, ByteString().compareTo(ByteString())) - assertEquals(0, ByteString(1, 2, 3).compareTo(ByteString(1, 2, 3))) - assertEquals(-1, ByteString(1, 2).compareTo(ByteString(1, 2, 3))) - assertEquals(-1, ByteString(0, 1, 2).compareTo(ByteString(0, 1, 3))) - assertEquals(1, ByteString(1, 2, 3).compareTo(ByteString(1, 2))) - assertEquals(1, ByteString(1, 2, 3).compareTo(ByteString(0, 1, 2))) - assertEquals(1, ByteString(0xFF.toByte()).compareTo(ByteString(0))) - assertEquals(-1, ByteString(1).compareTo(ByteString(0x81.toByte()))) - } - - @Test - fun size() { - assertEquals(0, ByteString().size) - assertEquals(1, ByteString(0).size) - assertEquals(12345, ByteString(ByteArray(12345)).size) - } - - @Test - fun indices() { - assertEquals(0 until 10, ByteString(ByteArray(10)).indices) - assertTrue(ByteString().indices.isEmpty()) - } - - @Test - fun isEmpty() { - assertTrue(ByteString().isEmpty()) - assertTrue(ByteString(byteArrayOf()).isEmpty()) - assertFalse(ByteString(byteArrayOf(0)).isEmpty()) - } - - @Test - fun isNotEmpty() { - assertFalse(ByteString().isNotEmpty()) - assertFalse(ByteString(byteArrayOf()).isNotEmpty()) - assertTrue(ByteString(byteArrayOf(0)).isNotEmpty()) - } - - @Test - fun indexOfByte() { - val str = ByteString(1, 2, 3, 4) - for (idx in str.indices) { - assertEquals(idx, str.indexOf(str[idx])) - } - - assertEquals(-1, str.indexOf(0)) - assertEquals(-1, str.indexOf(1, 1)) - assertEquals(-1, str.indexOf(4, 4)) - assertEquals(0, str.indexOf(1, -10)) - assertEquals(1, ByteString(0, 1, 1, 1).indexOf(1)) - - assertEquals(-1, ByteString().indexOf(0)) - assertEquals(-1, ByteString().indexOf(0, 100500)) - assertEquals(-1, str.indexOf(1, 100500)) - } - - @Test - fun indexOfByteArray() { - val str = ByteString(1, 2, 3, 4, 5) - - assertEquals(0, str.indexOf(byteArrayOf(1, 2, 3, 4, 5))) - assertEquals(0, str.indexOf(byteArrayOf(1, 2, 3))) - assertEquals(0, str.indexOf(byteArrayOf(1))) - - assertEquals(2, str.indexOf(byteArrayOf(3, 4, 5))) - assertEquals(-1, str.indexOf(byteArrayOf(3, 4, 5, 6))) - assertEquals(0, str.indexOf(byteArrayOf())) - assertEquals(-1, str.indexOf(byteArrayOf(-1))) - - assertEquals(-1, str.indexOf(byteArrayOf(1, 2, 3, 4, 5), 1)) - assertEquals(3, str.indexOf(byteArrayOf(4, 5), 3)) - - assertEquals(0, str.indexOf(byteArrayOf(1, 2, 3), -1000)) - assertEquals(1, str.indexOf(byteArrayOf(2, 3), -1)) - - assertEquals(1, ByteString(0, 1, 0, 1, 0, 1).indexOf(byteArrayOf(1, 0))) - - assertEquals(0, ByteString().indexOf(byteArrayOf())) - assertEquals(0, ByteString().indexOf(byteArrayOf(), -100500)) - assertEquals(0, ByteString().indexOf(byteArrayOf(), 100500)) - assertEquals(-1, str.indexOf(byteArrayOf(1, 2, 3), 100500)) - assertEquals(-1, ByteString().indexOf(byteArrayOf(1, 2, 3, 4, 5))) - assertEquals(-1, str.indexOf(byteArrayOf(2, 3, 5))) - } - - @Test - fun indexOfByteString() { - val str = ByteString(1, 2, 3, 4, 5) - - assertEquals(0, str.indexOf(ByteString(1, 2, 3, 4, 5))) - assertEquals(0, str.indexOf(ByteString(1, 2, 3))) - assertEquals(0, str.indexOf(ByteString(1))) - assertEquals(2, str.indexOf(ByteString(3, 4, 5))) - assertEquals(-1, str.indexOf(ByteString(3, 4, 5, 6))) - assertEquals(0, str.indexOf(ByteString())) - assertEquals(-1, str.indexOf(ByteString(-1))) - assertEquals(-1, str.indexOf(ByteString(1, 2, 3, 4, 5), 1)) - assertEquals(3, str.indexOf(ByteString(4, 5), 3)) - assertEquals(0, str.indexOf(ByteString(1, 2, 3), -1000)) - assertEquals(1, str.indexOf(ByteString(2, 3), -1)) - assertEquals(1, ByteString(0, 1, 0, 1, 0, 1).indexOf(ByteString(1, 0))) - assertEquals(0, ByteString().indexOf(ByteString())) - assertEquals(0, ByteString().indexOf(ByteString(), -100500)) - assertEquals(0, ByteString().indexOf(ByteString(), 100500)) - assertEquals(-1, str.indexOf(ByteString(1, 2, 3), 100500)) - assertEquals(-1, ByteString().indexOf(ByteString(1, 2, 3, 4, 5))) - assertEquals(-1, str.indexOf(ByteString(2, 3, 5))) - } - - @Test - fun lastIndexOfByte() { - val str = ByteString(1, 2, 3, 4) - for (idx in str.indices) { - assertEquals(idx, str.lastIndexOf(str[idx])) - } - - assertEquals(-1, str.lastIndexOf(0)) - assertEquals(-1, str.lastIndexOf(1, 1)) - assertEquals(-1, str.lastIndexOf(4, 4)) - assertEquals(0, str.lastIndexOf(1, -10)) - assertEquals(3, ByteString(0, 1, 1, 1, 0).lastIndexOf(1)) - - assertEquals(-1, ByteString().lastIndexOf(0)) - assertEquals(-1, ByteString().lastIndexOf(0, 100500)) - assertEquals(-1, str.lastIndexOf(1, 1005000)) - } - - @Test - fun lastIndexOfByteArray() { - val str = ByteString(1, 2, 3, 4, 5) - - assertEquals(0, str.lastIndexOf(byteArrayOf(1, 2, 3, 4, 5))) - assertEquals(0, str.lastIndexOf(byteArrayOf(1, 2, 3))) - assertEquals(-1, str.lastIndexOf(byteArrayOf(0, 1, 2))) - assertEquals(2, str.lastIndexOf(byteArrayOf(3, 4, 5))) - assertEquals(-1, str.lastIndexOf(byteArrayOf(1, 2, 3), 1)) - assertEquals(1, str.lastIndexOf(byteArrayOf(2, 3, 4), 1)) - assertEquals(str.size, str.lastIndexOf(byteArrayOf())) - assertEquals(str.size, str.lastIndexOf(byteArrayOf())) - assertEquals(2, str.lastIndexOf(byteArrayOf(3, 4), -1000)) - assertEquals(0, str.lastIndexOf(byteArrayOf(1), -1)) - assertEquals(4, ByteString(1, 1, 1, 1, 1).lastIndexOf(byteArrayOf(1))) - assertEquals(3, ByteString(0, 1, 0, 1, 0).lastIndexOf(byteArrayOf(1, 0))) - assertEquals(0, ByteString().lastIndexOf(byteArrayOf())) - assertEquals(0, ByteString().lastIndexOf(byteArrayOf(), -100500)) - assertEquals(0, ByteString().lastIndexOf(byteArrayOf(), 100500)) - assertEquals(-1, str.lastIndexOf(byteArrayOf(1, 2, 3), 100500)) - assertEquals(-1, ByteString().lastIndexOf(byteArrayOf(1, 2, 3))) - assertEquals(-1, str.lastIndexOf(byteArrayOf(2, 3, 5))) - } - - @Test - fun lastIndexOfByteString() { - val str = ByteString(1, 2, 3, 4, 5) - - assertEquals(0, str.lastIndexOf(ByteString(1, 2, 3, 4, 5))) - assertEquals(0, str.lastIndexOf(ByteString(1, 2, 3))) - assertEquals(-1, str.lastIndexOf(ByteString(0, 1, 2))) - assertEquals(2, str.lastIndexOf(ByteString(3, 4, 5))) - assertEquals(-1, str.lastIndexOf(ByteString(1, 2, 3), 1)) - assertEquals(1, str.lastIndexOf(ByteString(2, 3, 4), 1)) - assertEquals(str.size, str.lastIndexOf(ByteString())) - assertEquals(str.size, str.lastIndexOf(ByteString())) - assertEquals(2, str.lastIndexOf(ByteString(3, 4), -1000)) - assertEquals(0, str.lastIndexOf(ByteString(1), -1)) - assertEquals(4, ByteString(1, 1, 1, 1, 1).lastIndexOf(ByteString(1))) - assertEquals(3, ByteString(0, 1, 0, 1, 0).lastIndexOf(ByteString(1, 0))) - assertEquals(0, ByteString().lastIndexOf(ByteString())) - assertEquals(0, ByteString().lastIndexOf(ByteString(), -100500)) - assertEquals(0, ByteString().lastIndexOf(ByteString(), 100500)) - assertEquals(-1, str.lastIndexOf(ByteString(1, 2, 3), 100500)) - assertEquals(-1, ByteString().lastIndexOf(ByteString(1, 2, 3))) - assertEquals(-1, str.lastIndexOf(ByteString(2, 3, 5))) - } - - @Test - fun startsWithByteArray() { - val str = ByteString(1, 2, 3, 4, 5) - - assertTrue(str.startsWith(byteArrayOf(1, 2, 3, 4, 5))) - assertTrue(str.startsWith(byteArrayOf(1, 2, 3))) - - assertTrue(str.startsWith(byteArrayOf())) - - assertFalse(str.startsWith(byteArrayOf(0, 1, 2, 3))) - assertFalse(str.startsWith(byteArrayOf(2, 3, 4))) - assertFalse(str.startsWith(byteArrayOf(1, 2, 3, 4, 5, 6))) - - assertTrue(ByteString().startsWith(byteArrayOf())) - } - - @Test - fun startWithByteString() { - val str = ByteString(1, 2, 3, 4, 5) - - assertTrue(str.startsWith(ByteString(1, 2, 3, 4, 5))) - assertTrue(str.startsWith(ByteString(1, 2, 3))) - - assertTrue(str.startsWith(ByteString())) - - assertFalse(str.startsWith(ByteString(0, 1, 2, 3))) - assertFalse(str.startsWith(ByteString(2, 3, 4))) - assertFalse(str.startsWith(ByteString(1, 2, 3, 4, 5, 6))) - - assertTrue(ByteString().startsWith(ByteString())) - } - - @Test - fun endsWithByteArray() { - val str = ByteString(1, 2, 3, 4, 5) - - assertTrue(str.endsWith(byteArrayOf(1, 2, 3, 4, 5))) - assertTrue(str.endsWith(byteArrayOf(3, 4, 5))) - - assertTrue(str.endsWith(byteArrayOf())) - - assertFalse(str.endsWith(byteArrayOf(3, 4, 5, 6))) - assertFalse(str.endsWith(byteArrayOf(0, 1, 2, 3, 4, 5))) - assertFalse(str.endsWith(byteArrayOf(2, 3, 4))) - - assertTrue(ByteString().endsWith(byteArrayOf())) - } - - @Test - fun endsWithByteString() { - val str = ByteString(1, 2, 3, 4, 5) - - assertTrue(str.endsWith(ByteString(1, 2, 3, 4, 5))) - assertTrue(str.endsWith(ByteString(3, 4, 5))) - - assertTrue(str.endsWith(ByteString())) - - assertFalse(str.endsWith(ByteString(3, 4, 5, 6))) - assertFalse(str.endsWith(ByteString(0, 1, 2, 3, 4, 5))) - assertFalse(str.endsWith(ByteString(2, 3, 4))) - - assertTrue(ByteString().endsWith(ByteString())) - } - - @Test - fun testToString() { - assertEquals("ByteString(size=0)", ByteString().toString()) - assertEquals("ByteString(size=1 hex=00)", ByteString(0).toString()) - assertEquals( - "ByteString(size=16 hex=000102030405060708090a0b0c0d0e0f)", - ByteString(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15).toString() - ) - assertEquals( - "ByteString(size=64 hex=0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000)", - ByteString(ByteArray(64)).toString() - ) - } - - private val bronzeHorseman = "На берегу пустынных волн" - - @Test - fun utf8() { - val byteString = bronzeHorseman.encodeToByteString() - assertEquals(byteString.toByteArray().toList(), bronzeHorseman.encodeToByteArray().toList()) - assertEquals(byteString, ByteString(*bronzeHorseman.encodeToByteArray())) - assertEquals(byteString.decodeToString(), bronzeHorseman) - } - - @Test - fun contentEquals() { - assertTrue(ByteString().contentEquals(byteArrayOf())) - assertFalse(ByteString(1, 2, 3).contentEquals(byteArrayOf())) - assertFalse(ByteString().contentEquals(byteArrayOf(1, 2, 3))) - - assertTrue(ByteString(1, 2, 3, 4, 5).contentEquals(byteArrayOf(1, 2, 3, 4, 5))) - assertFalse(ByteString(1, 2, 3, 4, 5).contentEquals(byteArrayOf(1, 2, 3, 4, 4))) - assertFalse(ByteString(1, 2, 3, 4, 5).contentEquals(byteArrayOf(1, 2, 3, 4, 5, 6))) - assertFalse(ByteString(1, 2, 3, 4, 5, 6).contentEquals(byteArrayOf(1, 2, 3, 4, 5))) - } - - @OptIn(ExperimentalUnsignedTypes::class) - @Test - fun fromUBytes() { - val str = ByteString(0xDEu, 0xADu, 0xC0u, 0xDEu) - assertContentEquals( - byteArrayOf(0xDE.toByte(), 0xAD.toByte(), 0xC0u.toByte(), 0xDEu.toByte()), - str.toByteArray() - ) - } -} diff --git a/bytestring/common/test/samples/samples.kt b/bytestring/common/test/samples/samples.kt deleted file mode 100644 index 06fe8079c..000000000 --- a/bytestring/common/test/samples/samples.kt +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.bytestring.samples - -import kotlinx.io.bytestring.* -import kotlin.test.* - -class ByteStringSamples { - @Test - fun compareTo() { - 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" - 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 - 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) - } - - @OptIn(ExperimentalUnsignedTypes::class) - @Test - fun constructionFromUBytesSample() { - val byteStringFromBytes = ByteString(0xCAu, 0xFEu) - assertFalse(byteStringFromBytes.isEmpty()) - assertEquals(2, 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) - } -} diff --git a/bytestring/common/test/unsafe/UnsafeByteStringOperationsTest.kt b/bytestring/common/test/unsafe/UnsafeByteStringOperationsTest.kt deleted file mode 100644 index a0b1902df..000000000 --- a/bytestring/common/test/unsafe/UnsafeByteStringOperationsTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.bytestring.unsafe - -import kotlin.test.Test -import kotlin.test.assertTrue -import kotlinx.io.bytestring.encodeToByteString - -@OptIn(UnsafeByteStringApi::class) -class UnsafeByteStringOperationsTest { - @Test - fun callsInPlaceContract() { - val byteString = "hello byte string".encodeToByteString() - - val called: Boolean - UnsafeByteStringOperations.withByteArrayUnsafe(byteString) { - called = true - } - assertTrue(called) - } -} diff --git a/bytestring/jvm/src/-PlatformJvm.kt b/bytestring/jvm/src/-PlatformJvm.kt deleted file mode 100644 index d419802d0..000000000 --- a/bytestring/jvm/src/-PlatformJvm.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2010-2024 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 - -// Incremental compilation doesn't work smoothly with OptionalExpectation (see KT-66317), -// so we have to explicitly actualize the annotation. -@Target(AnnotationTarget.FIELD) -internal actual annotation class BenignDataRace actual constructor() - diff --git a/bytestring/jvm/src/ByteStringJvmExt.kt b/bytestring/jvm/src/ByteStringJvmExt.kt deleted file mode 100644 index ca193ac37..000000000 --- a/bytestring/jvm/src/ByteStringJvmExt.kt +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.bytestring - -import kotlinx.io.bytestring.unsafe.UnsafeByteStringApi -import kotlinx.io.bytestring.unsafe.UnsafeByteStringOperations -import java.nio.BufferOverflowException -import java.nio.ByteBuffer -import java.nio.charset.Charset - -/** - * Decodes the content of a byte string to a string using given [charset]. - * - * @param charset the charset to decode data into a string. - */ -public fun ByteString.decodeToString(charset: Charset): String = getBackingArrayReference().toString(charset) - -/** - * Encodes a string into a byte string using [charset]. - * - * @param charset the encoding. - */ -public fun String.encodeToByteString(charset: Charset): ByteString = ByteString.wrap(toByteArray(charset)) - -/** - * Returns a new read-only heap [ByteBuffer] wrapping [this] ByteString's content. - * - * @sample kotlinx.io.bytestring.samples.ByteStringSamplesJvm.toReadOnlyByteBuffer - */ -@OptIn(UnsafeByteStringApi::class) -public fun ByteString.asReadOnlyByteBuffer(): ByteBuffer { - val data: ByteArray - - UnsafeByteStringOperations.withByteArrayUnsafe(this) { - data = it - } - - return ByteBuffer.wrap(data).asReadOnlyBuffer() -} - -/** - * Reads [length] bytes of data from [this] ByteBuffer starting from the current position and - * wraps them into a new [ByteString]. - * - * Upon successful execution, current position will advance by [length]. - * - * @throws IndexOutOfBoundsException when [length] has negative value or its value exceeds [ByteBuffer.remaining] - * - * @sample kotlinx.io.bytestring.samples.ByteStringSamplesJvm.getByteStringFromBuffer - */ -@OptIn(UnsafeByteStringApi::class) -public fun ByteBuffer.getByteString(length: Int = remaining()): ByteString { - if (length < 0) { - throw IndexOutOfBoundsException("length should be non-negative (was $length)") - } - if (remaining() < length) { - throw IndexOutOfBoundsException("length ($length) exceeds remaining bytes count ({${remaining()}})") - } - val bytes = ByteArray(length) - get(bytes) - return UnsafeByteStringOperations.wrapUnsafe(bytes) -} - -/** - * Reads [length] bytes of data from [this] ByteBuffer starting from [at] index and - * wraps them into a new [ByteString]. - * - * This function does not update [ByteBuffer.position]. - * - * @throws IndexOutOfBoundsException when [at] is negative, greater or equal to [ByteBuffer.limit] - * or [at] + [length] exceeds [ByteBuffer.limit]. - * - * @sample kotlinx.io.bytestring.samples.ByteStringSamplesJvm.getByteStringFromBufferAbsolute - */ -@OptIn(UnsafeByteStringApi::class) -public fun ByteBuffer.getByteString(at: Int, length: Int): ByteString { - checkIndexAndCapacity(at, length) - val bytes = ByteArray(length) - // Absolute get(byte[]) was added only in JDK 13 - for (i in 0..= limit()) { - throw IndexOutOfBoundsException("Index $idx is out of this ByteBuffer's bounds: [0, ${limit()})") - } - if (length < 0) { - throw IndexOutOfBoundsException("length should be non-negative (was $length)") - } - if (idx + length > limit()) { - throw IndexOutOfBoundsException("There's not enough space to put ByteString of length $length starting" + - " from index $idx") - } -} diff --git a/bytestring/jvm/test/ByteStringByteBufferExtensionsTest.kt b/bytestring/jvm/test/ByteStringByteBufferExtensionsTest.kt deleted file mode 100644 index 1a08ae369..000000000 --- a/bytestring/jvm/test/ByteStringByteBufferExtensionsTest.kt +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -package kotlinx.io.bytestring - -import org.junit.jupiter.api.Test -import java.nio.BufferOverflowException -import java.nio.ByteBuffer -import java.nio.ReadOnlyBufferException -import kotlin.test.assertContentEquals -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertTrue - -public class ByteStringByteBufferExtensionsTest { - @Test - fun asReadOnlyByteBuffer() { - val buffer = ByteString(1, 2, 3, 4).asReadOnlyByteBuffer() - - assertTrue(buffer.isReadOnly) - assertEquals(4, buffer.remaining()) - - ByteArray(4).let { - buffer.get(it) - assertContentEquals(byteArrayOf(1, 2, 3, 4), it) - } - } - - @Test - fun getByteString() { - val bb = ByteBuffer.allocate(8) - bb.put(byteArrayOf(1, 2, 3, 4, 5, 6, 7, 8)) - bb.flip() - - assertEquals(ByteString(1, 2, 3, 4, 5, 6, 7, 8), bb.getByteString()) - bb.flip() - - assertEquals(ByteString(1, 2, 3, 4), bb.getByteString(length = 4)) - assertEquals(ByteString(), bb.getByteString(length = 0)) - assertFailsWith { bb.getByteString(length = -1) } - val p = bb.position() - assertFailsWith { bb.getByteString(length = 5) } - assertEquals(p, bb.position()) - bb.clear() - - assertEquals(ByteString(1, 2, 3, 4, 5, 6, 7, 8), bb.getByteString(at = 0, length = 8)) - assertEquals(0, bb.position()) - - assertEquals(ByteString(2, 3, 4, 5), bb.getByteString(at = 1, length = 4)) - assertEquals(0, bb.position()) - - assertFailsWith { bb.getByteString(at = -1, length = 8) } - assertFailsWith { bb.getByteString(at = 9, length = 1) } - assertFailsWith { bb.getByteString(at = 7, length = 2) } - assertFailsWith { bb.getByteString(at = 0, length = -1) } - } - - @Test - fun putString() { - val bb = ByteBuffer.allocate(8) - val string = ByteString(1, 2, 3, 4, 5, 6, 7, 8) - val shortString = ByteString(-1, -2, -3) - - bb.putByteString(string) - assertEquals(8, bb.position()) - bb.flip() - ByteArray(8).let { - bb.get(it) - assertContentEquals(byteArrayOf(1, 2, 3, 4, 5, 6, 7, 8), it) - } - - bb.clear() - bb.position(1) - assertFailsWith { bb.putByteString(string) } - assertEquals(1, bb.position()) - - bb.putByteString(at = 0, string = shortString) - bb.putByteString(at = 5, string = shortString) - assertEquals(1, bb.position()) - bb.clear() - ByteArray(8).let { - bb.get(it) - assertContentEquals(byteArrayOf(-1, -2, -3, 4, 5, -1, -2, -3), it) - } - - assertFailsWith { bb.putByteString(at = 7, string = shortString) } - assertFailsWith { bb.putByteString(at = -1, string = string) } - assertFailsWith { bb.putByteString(at = 8, string = string) } - assertFailsWith { - bb.asReadOnlyBuffer().putByteString(string) - } - assertFailsWith { - bb.asReadOnlyBuffer().putByteString(at = 0, string = string) - } - } -} diff --git a/bytestring/jvm/test/ByteStringJvmTest.kt b/bytestring/jvm/test/ByteStringJvmTest.kt deleted file mode 100644 index 37f9b86ae..000000000 --- a/bytestring/jvm/test/ByteStringJvmTest.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.bytestring - -import kotlin.test.Test -import kotlin.test.assertEquals - -/** - * Set of tests covering JVM-specific [ByteString] extensions. - */ -class ByteStringJvmTest { - @Test - fun createFromString() { - val str = "hello" - - assertEquals(ByteString(byteArrayOf(0x68, 0x65, 0x6c, 0x6c, 0x6f)), str.encodeToByteString(Charsets.UTF_8)) - assertEquals( - ByteString( - byteArrayOf( - 0, 0, 0, 0x68, 0, 0, 0, 0x65, 0, 0, 0, 0x6c, - 0, 0, 0, 0x6c, 0, 0, 0, 0x6f - ) - ), str.encodeToByteString(Charsets.UTF_32) - ) - } - - @Test - fun decodeToString() { - assertEquals( - "Ϭ", - ByteString(0xfeU.toByte(), 0xffU.toByte(), 0x03, 0xecU.toByte()).decodeToString(Charsets.UTF_16) - ) - - assertEquals("123", ByteString("123".encodeToByteArray()).decodeToString(Charsets.UTF_8)) - } -} diff --git a/bytestring/jvm/test/samples/samplesJvm.kt b/bytestring/jvm/test/samples/samplesJvm.kt deleted file mode 100644 index ee97d186f..000000000 --- a/bytestring/jvm/test/samples/samplesJvm.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -package kotlinx.io.bytestring.samples - -import kotlinx.io.bytestring.* -import java.nio.ByteBuffer -import java.nio.ReadOnlyBufferException -import kotlin.test.* - -public class ByteStringSamplesJvm { - @Test - fun toReadOnlyByteBuffer() { - val str = "Hello World".encodeToByteString() - val buffer = str.asReadOnlyByteBuffer() - - assertEquals(11, buffer.remaining()) - assertEquals(0x48656c6c, buffer.getInt()) - - buffer.flip() - assertFailsWith { buffer.put(42) } - } - - @Test - fun getByteStringFromBuffer() { - val buffer = ByteBuffer.wrap("Hello World".encodeToByteArray()) - - // Consume the whole buffer - val byteString = buffer.getByteString() - assertEquals(0, buffer.remaining()) - assertEquals("Hello World".encodeToByteString(), byteString) - - // Reset the buffer - buffer.flip() - // Consume only first 5 bytes from the buffer - assertEquals("Hello".encodeToByteString(), buffer.getByteString(length = 5)) - } - - @Test - fun getByteStringFromBufferAbsolute() { - val buffer = ByteBuffer.wrap("Hello World".encodeToByteArray()) - - // Read 2 bytes starting from offset 6 - val byteString = buffer.getByteString(at = 6, length = 2) - // Buffer's position is not affected - assertEquals(11, buffer.remaining()) - assertEquals(byteString, "Wo".encodeToByteString()) - } - - @Test - fun putByteStringToBuffer() { - val buffer = ByteBuffer.allocate(32) - val byteString = ByteString(0x66, 0xdb.toByte(), 0x11, 0x50) - - // Putting a ByteString into a buffer will advance its position - buffer.putByteString(byteString) - assertEquals(4, buffer.position()) - - buffer.flip() - assertEquals(1725632848, buffer.getInt()) - } - - @Test - fun putByteStringToBufferAbsolute() { - val buffer = ByteBuffer.allocate(8) - val byteString = ByteString(0x78, 0x5e) - - // Putting a ByteString into a buffer using an absolute offset - // won't change buffer's position. - buffer.putByteString(at = 3, string = byteString) - assertEquals(0, buffer.position()) - assertEquals(8, buffer.remaining()) - - assertEquals(0x000000785e000000L, buffer.getLong()) - } -} diff --git a/bytestring/native/src/-PlatformNative.kt b/bytestring/native/src/-PlatformNative.kt deleted file mode 100644 index 7ee278db1..000000000 --- a/bytestring/native/src/-PlatformNative.kt +++ /dev/null @@ -1,9 +0,0 @@ -/* -* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. -* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. -*/ -package kotlinx.io.bytestring - -import kotlin.concurrent.* - -internal actual typealias BenignDataRace = Volatile diff --git a/core/Module.md b/core/Module.md deleted file mode 100644 index 840b92af8..000000000 --- a/core/Module.md +++ /dev/null @@ -1,89 +0,0 @@ -# Module kotlinx-io-core - -The module provides core multiplatform IO primitives and integrates it with platform-specific APIs. - -`kotlinx-io core` aims to provide a concise but powerful API along with efficient implementation. - -The main interfaces for the IO interaction are [kotlinx.io.Source] and [kotlinx.io.Sink] providing buffered read and -write operations for integer types, byte arrays, and other sources and sinks. There are also extension functions -bringing support for strings and other types. -Implementations of these interfaces are built on top of [kotlinx.io.Buffer], [kotlinx.io.RawSource], -and [kotlinx.io.RawSink]. - -A central part of the library, [kotlinx.io.Buffer], is a container optimized to reduce memory allocations and to avoid -data copying when possible. - -[kotlinx.io.RawSource] and [kotlinx.io.RawSink] are interfaces aimed for integration with anything that can provide -or receive data: network interfaces, files, etc. The module provides integration with some platform-specific IO APIs, -but if something not yet supported by the library needs to be integrated, then these interfaces are exactly what should -be implemented for that. - -Example below shows how to manually serialize an object to [BSON](https://bsonspec.org/spec.html) -and then back to an object using `kotlinx.io`. Please note that the example aimed to show `kotlinx-io` API in action, -rather than to provide a robust BSON-serialization. -```kotlin -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 - writeString("timestamp") // field name - writeByte(0) - writeLongLe(timestamp) // field value - writeByte(0x2) // string field - writeString("text") // field name - writeByte(0) - writeIntLe(text.utf8Size().toInt() + 1) // field value: length followed by the string - writeString(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.readString(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.readString(textLen) // read value - source.skip(1) // skip terminator - source.skip(1) // skip end of the document - return Message(timestamp, text) -} -``` - -# Package kotlinx.io - -Core IO primitives. - -# Package kotlinx.io.files - -Basic API for working with files. - -#### Known issues - -- [#312](https://github.com/Kotlin/kotlinx-io/issues/312) For `wasmWasi` target, directory listing ([kotlinx.io.files.FileSystem.list]) does not work with NodeJS runtime on Windows, -as `fd_readdir` function is [not implemented there](https://github.com/nodejs/node/blob/6f4d6011ea1b448cf21f5d363c44e4a4c56ca34c/deps/uvwasi/src/uvwasi.c#L19). diff --git a/core/androidNative/src/files/FileSystemAndroid.kt b/core/androidNative/src/files/FileSystemAndroid.kt deleted file mode 100644 index 65ba74177..000000000 --- a/core/androidNative/src/files/FileSystemAndroid.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.files - -import kotlinx.cinterop.CPointer -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.get -import kotlinx.cinterop.toKString -import kotlinx.io.IOException -import platform.posix.* - -@OptIn(ExperimentalForeignApi::class) -internal actual fun dirnameImpl(path: String): String { - if (!path.contains(SystemPathSeparator)) { - return "" - } - return dirname(path)?.toKString() ?: "" -} - -@OptIn(ExperimentalForeignApi::class) -internal actual fun basenameImpl(path: String): String { - return __posix_basename(path)?.toKString() ?: "" -} - -internal actual fun isAbsoluteImpl(path: String): Boolean = path.startsWith('/') - -@OptIn(ExperimentalForeignApi::class) -internal actual class OpaqueDirEntry(private val dir: CPointer) : AutoCloseable { - actual fun readdir(): String? { - val entry = platform.posix.readdir(dir) ?: return null - return entry[0].d_name.toKString() - } - - actual override fun close() { - closedir(dir) - } -} - -@OptIn(ExperimentalForeignApi::class) -internal actual fun opendir(path: String): OpaqueDirEntry { - val dirent = platform.posix.opendir(path) - if (dirent != null) return OpaqueDirEntry(dirent) - throw IOException("Can't open directory $path: ${strerror(errno)?.toKString() ?: "reason unknown"}") -} diff --git a/core/api/kotlinx-io-core.api b/core/api/kotlinx-io-core.api deleted file mode 100644 index 3d47cde16..000000000 --- a/core/api/kotlinx-io-core.api +++ /dev/null @@ -1,332 +0,0 @@ -public final class kotlinx/io/Buffer : kotlinx/io/Sink, kotlinx/io/Source { - public fun ()V - public final fun clear ()V - public fun close ()V - public final fun copy ()Lkotlinx/io/Buffer; - public final fun copyTo (Lkotlinx/io/Buffer;JJ)V - public static synthetic fun copyTo$default (Lkotlinx/io/Buffer;Lkotlinx/io/Buffer;JJILjava/lang/Object;)V - public fun emit ()V - public fun exhausted ()Z - public fun flush ()V - public final fun get (J)B - public fun getBuffer ()Lkotlinx/io/Buffer; - public final synthetic fun getHead ()Lkotlinx/io/Segment; - public final fun getSize ()J - public final synthetic fun getSizeMut ()J - public final synthetic fun getTail ()Lkotlinx/io/Segment; - public fun hintEmit ()V - public fun peek ()Lkotlinx/io/Source; - public fun readAtMostTo (Lkotlinx/io/Buffer;J)J - public fun readAtMostTo ([BII)I - public fun readByte ()B - public fun readInt ()I - public fun readLong ()J - public fun readShort ()S - public fun readTo (Lkotlinx/io/RawSink;J)V - public final synthetic fun recycleTail ()V - public fun request (J)Z - public fun require (J)V - public final synthetic fun setHead (Lkotlinx/io/Segment;)V - public final synthetic fun setSizeMut (J)V - public final synthetic fun setTail (Lkotlinx/io/Segment;)V - public fun skip (J)V - public fun toString ()Ljava/lang/String; - public fun transferFrom (Lkotlinx/io/RawSource;)J - public fun transferTo (Lkotlinx/io/RawSink;)J - public final synthetic fun writableSegment (I)Lkotlinx/io/Segment; - public fun write (Lkotlinx/io/Buffer;J)V - public fun write (Lkotlinx/io/RawSource;J)V - public fun write ([BII)V - public fun writeByte (B)V - public fun writeInt (I)V - public fun writeLong (J)V - public fun writeShort (S)V -} - -public final class kotlinx/io/BufferKt { - public static final synthetic fun seek (Lkotlinx/io/Buffer;JLkotlin/jvm/functions/Function2;)Ljava/lang/Object; -} - -public final class kotlinx/io/BuffersJvmKt { - public static final fun asByteChannel (Lkotlinx/io/Buffer;)Ljava/nio/channels/ByteChannel; - public static final fun copyTo (Lkotlinx/io/Buffer;Ljava/io/OutputStream;JJ)V - public static synthetic fun copyTo$default (Lkotlinx/io/Buffer;Ljava/io/OutputStream;JJILjava/lang/Object;)V - public static final fun readAtMostTo (Lkotlinx/io/Buffer;Ljava/nio/ByteBuffer;)I - public static final fun readTo (Lkotlinx/io/Buffer;Ljava/io/OutputStream;J)V - public static synthetic fun readTo$default (Lkotlinx/io/Buffer;Ljava/io/OutputStream;JILjava/lang/Object;)V - public static final fun transferFrom (Lkotlinx/io/Buffer;Ljava/io/InputStream;)Lkotlinx/io/Buffer; - public static final fun transferFrom (Lkotlinx/io/Buffer;Ljava/nio/ByteBuffer;)Lkotlinx/io/Buffer; - public static final fun write (Lkotlinx/io/Buffer;Ljava/io/InputStream;J)Lkotlinx/io/Buffer; -} - -public final class kotlinx/io/BuffersKt { - public static final fun indexOf (Lkotlinx/io/Buffer;BJJ)J - public static synthetic fun indexOf$default (Lkotlinx/io/Buffer;BJJILjava/lang/Object;)J - public static final fun snapshot (Lkotlinx/io/Buffer;)Lkotlinx/io/bytestring/ByteString; -} - -public final class kotlinx/io/ByteStringsKt { - public static final fun indexOf (Lkotlinx/io/Buffer;Lkotlinx/io/bytestring/ByteString;J)J - public static final fun indexOf (Lkotlinx/io/Source;Lkotlinx/io/bytestring/ByteString;J)J - public static synthetic fun indexOf$default (Lkotlinx/io/Buffer;Lkotlinx/io/bytestring/ByteString;JILjava/lang/Object;)J - public static synthetic fun indexOf$default (Lkotlinx/io/Source;Lkotlinx/io/bytestring/ByteString;JILjava/lang/Object;)J - public static final fun readByteString (Lkotlinx/io/Source;)Lkotlinx/io/bytestring/ByteString; - public static final fun readByteString (Lkotlinx/io/Source;I)Lkotlinx/io/bytestring/ByteString; - public static final fun write (Lkotlinx/io/Sink;Lkotlinx/io/bytestring/ByteString;II)V - public static synthetic fun write$default (Lkotlinx/io/Sink;Lkotlinx/io/bytestring/ByteString;IIILjava/lang/Object;)V -} - -public final class kotlinx/io/CoreKt { - public static final fun buffered (Lkotlinx/io/RawSink;)Lkotlinx/io/Sink; - public static final fun buffered (Lkotlinx/io/RawSource;)Lkotlinx/io/Source; - public static final fun discardingSink ()Lkotlinx/io/RawSink; -} - -public abstract interface annotation class kotlinx/io/DelicateIoApi : java/lang/annotation/Annotation { -} - -public abstract interface annotation class kotlinx/io/InternalIoApi : java/lang/annotation/Annotation { -} - -public final class kotlinx/io/JvmCoreKt { - public static final fun asSink (Ljava/io/OutputStream;)Lkotlinx/io/RawSink; - public static final fun asSource (Ljava/io/InputStream;)Lkotlinx/io/RawSource; -} - -public abstract interface class kotlinx/io/RawSink : java/io/Flushable, java/lang/AutoCloseable { - public abstract fun close ()V - public abstract fun flush ()V - public abstract fun write (Lkotlinx/io/Buffer;J)V -} - -public abstract interface class kotlinx/io/RawSource : java/lang/AutoCloseable { - public abstract fun close ()V - public abstract fun readAtMostTo (Lkotlinx/io/Buffer;J)J -} - -public final class kotlinx/io/Segment { - public synthetic fun ([BIILkotlinx/io/SegmentCopyTracker;ZLkotlin/jvm/internal/DefaultConstructorMarker;)V - public final synthetic fun dataAsByteArray (Z)[B - public final synthetic fun getLimit ()I - public final synthetic fun getNext ()Lkotlinx/io/Segment; - public final synthetic fun getPos ()I - public final synthetic fun getPrev ()Lkotlinx/io/Segment; - public final synthetic fun getRemainingCapacity ()I - public final synthetic fun getSize ()I - public final synthetic fun setLimit (I)V - public final synthetic fun setNext (Lkotlinx/io/Segment;)V - public final synthetic fun setPos (I)V - public final synthetic fun setPrev (Lkotlinx/io/Segment;)V - public final synthetic fun writeBackData ([BI)V -} - -public final class kotlinx/io/SegmentKt { - public static final fun isEmpty (Lkotlinx/io/Segment;)Z -} - -public abstract interface class kotlinx/io/Sink : kotlinx/io/RawSink { - public abstract fun emit ()V - public abstract fun flush ()V - public abstract fun getBuffer ()Lkotlinx/io/Buffer; - public abstract fun hintEmit ()V - public abstract fun transferFrom (Lkotlinx/io/RawSource;)J - public abstract fun write (Lkotlinx/io/RawSource;J)V - public abstract fun write ([BII)V - public static synthetic fun write$default (Lkotlinx/io/Sink;[BIIILjava/lang/Object;)V - public abstract fun writeByte (B)V - public abstract fun writeInt (I)V - public abstract fun writeLong (J)V - public abstract fun writeShort (S)V -} - -public final class kotlinx/io/SinksJvmKt { - public static final fun asByteChannel (Lkotlinx/io/Sink;)Ljava/nio/channels/WritableByteChannel; - public static final fun asOutputStream (Lkotlinx/io/Sink;)Ljava/io/OutputStream; - public static final fun write (Lkotlinx/io/Sink;Ljava/nio/ByteBuffer;)I - public static final fun writeString (Lkotlinx/io/Sink;Ljava/lang/String;Ljava/nio/charset/Charset;II)V - public static synthetic fun writeString$default (Lkotlinx/io/Sink;Ljava/lang/String;Ljava/nio/charset/Charset;IIILjava/lang/Object;)V -} - -public final class kotlinx/io/SinksKt { - public static final fun writeDecimalLong (Lkotlinx/io/Sink;J)V - public static final fun writeDouble (Lkotlinx/io/Sink;D)V - public static final fun writeDoubleLe (Lkotlinx/io/Sink;D)V - public static final fun writeFloat (Lkotlinx/io/Sink;F)V - public static final fun writeFloatLe (Lkotlinx/io/Sink;F)V - public static final fun writeHexadecimalUnsignedLong (Lkotlinx/io/Sink;J)V - public static final fun writeIntLe (Lkotlinx/io/Sink;I)V - public static final fun writeLongLe (Lkotlinx/io/Sink;J)V - public static final fun writeShortLe (Lkotlinx/io/Sink;S)V - public static final fun writeToInternalBuffer (Lkotlinx/io/Sink;Lkotlin/jvm/functions/Function1;)V - public static final fun writeUByte-EK-6454 (Lkotlinx/io/Sink;B)V - public static final fun writeUInt-Qn1smSk (Lkotlinx/io/Sink;I)V - public static final fun writeUIntLe-Qn1smSk (Lkotlinx/io/Sink;I)V - public static final fun writeULong-2TYgG_w (Lkotlinx/io/Sink;J)V - public static final fun writeULongLe-2TYgG_w (Lkotlinx/io/Sink;J)V - public static final fun writeUShort-i8woANY (Lkotlinx/io/Sink;S)V - public static final fun writeUShortLe-i8woANY (Lkotlinx/io/Sink;S)V -} - -public abstract interface class kotlinx/io/Source : kotlinx/io/RawSource { - public abstract fun exhausted ()Z - public abstract fun getBuffer ()Lkotlinx/io/Buffer; - public abstract fun peek ()Lkotlinx/io/Source; - public abstract fun readAtMostTo ([BII)I - public static synthetic fun readAtMostTo$default (Lkotlinx/io/Source;[BIIILjava/lang/Object;)I - public abstract fun readByte ()B - public abstract fun readInt ()I - public abstract fun readLong ()J - public abstract fun readShort ()S - public abstract fun readTo (Lkotlinx/io/RawSink;J)V - public abstract fun request (J)Z - public abstract fun require (J)V - public abstract fun skip (J)V - public abstract fun transferTo (Lkotlinx/io/RawSink;)J -} - -public final class kotlinx/io/SourcesJvmKt { - public static final fun asByteChannel (Lkotlinx/io/Source;)Ljava/nio/channels/ReadableByteChannel; - public static final fun asInputStream (Lkotlinx/io/Source;)Ljava/io/InputStream; - public static final fun readAtMostTo (Lkotlinx/io/Source;Ljava/nio/ByteBuffer;)I - public static final fun readString (Lkotlinx/io/Source;JLjava/nio/charset/Charset;)Ljava/lang/String; - public static final fun readString (Lkotlinx/io/Source;Ljava/nio/charset/Charset;)Ljava/lang/String; -} - -public final class kotlinx/io/SourcesKt { - public static final fun indexOf (Lkotlinx/io/Source;BJJ)J - public static synthetic fun indexOf$default (Lkotlinx/io/Source;BJJILjava/lang/Object;)J - public static final fun readByteArray (Lkotlinx/io/Source;)[B - public static final fun readByteArray (Lkotlinx/io/Source;I)[B - public static final fun readDecimalLong (Lkotlinx/io/Source;)J - public static final fun readDouble (Lkotlinx/io/Source;)D - public static final fun readDoubleLe (Lkotlinx/io/Source;)D - public static final fun readFloat (Lkotlinx/io/Source;)F - public static final fun readFloatLe (Lkotlinx/io/Source;)F - public static final fun readHexadecimalUnsignedLong (Lkotlinx/io/Source;)J - public static final fun readIntLe (Lkotlinx/io/Source;)I - public static final fun readLongLe (Lkotlinx/io/Source;)J - public static final fun readShortLe (Lkotlinx/io/Source;)S - public static final fun readTo (Lkotlinx/io/Source;[BII)V - public static synthetic fun readTo$default (Lkotlinx/io/Source;[BIIILjava/lang/Object;)V - public static final fun readUByte (Lkotlinx/io/Source;)B - public static final fun readUInt (Lkotlinx/io/Source;)I - public static final fun readUIntLe (Lkotlinx/io/Source;)I - public static final fun readULong (Lkotlinx/io/Source;)J - public static final fun readULongLe (Lkotlinx/io/Source;)J - public static final fun readUShort (Lkotlinx/io/Source;)S - public static final fun readUShortLe (Lkotlinx/io/Source;)S - public static final fun startsWith (Lkotlinx/io/Source;B)Z -} - -public abstract interface annotation class kotlinx/io/UnsafeIoApi : java/lang/annotation/Annotation { -} - -public final class kotlinx/io/Utf8Kt { - public static final fun readCodePointValue (Lkotlinx/io/Source;)I - public static final fun readLine (Lkotlinx/io/Source;)Ljava/lang/String; - public static final fun readLineStrict (Lkotlinx/io/Source;J)Ljava/lang/String; - public static synthetic fun readLineStrict$default (Lkotlinx/io/Source;JILjava/lang/Object;)Ljava/lang/String; - public static final fun readString (Lkotlinx/io/Buffer;)Ljava/lang/String; - public static final fun readString (Lkotlinx/io/Source;)Ljava/lang/String; - public static final fun readString (Lkotlinx/io/Source;J)Ljava/lang/String; - public static final fun writeCodePointValue (Lkotlinx/io/Sink;I)V - public static final fun writeString (Lkotlinx/io/Sink;Ljava/lang/CharSequence;II)V - public static final fun writeString (Lkotlinx/io/Sink;Ljava/lang/String;II)V - public static synthetic fun writeString$default (Lkotlinx/io/Sink;Ljava/lang/CharSequence;IIILjava/lang/Object;)V - public static synthetic fun writeString$default (Lkotlinx/io/Sink;Ljava/lang/String;IIILjava/lang/Object;)V -} - -public final class kotlinx/io/files/FileMetadata { - public fun ()V - public fun (ZZJ)V - public synthetic fun (ZZJILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getSize ()J - public final fun isDirectory ()Z - public final fun isRegularFile ()Z -} - -public abstract interface class kotlinx/io/files/FileSystem { - public abstract fun atomicMove (Lkotlinx/io/files/Path;Lkotlinx/io/files/Path;)V - public abstract fun createDirectories (Lkotlinx/io/files/Path;Z)V - public static synthetic fun createDirectories$default (Lkotlinx/io/files/FileSystem;Lkotlinx/io/files/Path;ZILjava/lang/Object;)V - public abstract fun delete (Lkotlinx/io/files/Path;Z)V - public static synthetic fun delete$default (Lkotlinx/io/files/FileSystem;Lkotlinx/io/files/Path;ZILjava/lang/Object;)V - public abstract fun exists (Lkotlinx/io/files/Path;)Z - public abstract fun list (Lkotlinx/io/files/Path;)Ljava/util/Collection; - public abstract fun metadataOrNull (Lkotlinx/io/files/Path;)Lkotlinx/io/files/FileMetadata; - public abstract fun resolve (Lkotlinx/io/files/Path;)Lkotlinx/io/files/Path; - public abstract fun sink (Lkotlinx/io/files/Path;Z)Lkotlinx/io/RawSink; - public static synthetic fun sink$default (Lkotlinx/io/files/FileSystem;Lkotlinx/io/files/Path;ZILjava/lang/Object;)Lkotlinx/io/RawSink; - public abstract fun source (Lkotlinx/io/files/Path;)Lkotlinx/io/RawSource; -} - -public final class kotlinx/io/files/FileSystemJvmKt { - public static final field SystemFileSystem Lkotlinx/io/files/FileSystem; - public static final field SystemTemporaryDirectory Lkotlinx/io/files/Path; -} - -public final class kotlinx/io/files/Path { - public fun equals (Ljava/lang/Object;)Z - public final fun getName ()Ljava/lang/String; - public final fun getParent ()Lkotlinx/io/files/Path; - public fun hashCode ()I - public final fun isAbsolute ()Z - public fun toString ()Ljava/lang/String; -} - -public final class kotlinx/io/files/PathsJvmKt { - public static final field SystemPathSeparator C - public static final fun Path (Ljava/lang/String;)Lkotlinx/io/files/Path; - public static final fun sink (Lkotlinx/io/files/Path;)Lkotlinx/io/Sink; - public static final fun source (Lkotlinx/io/files/Path;)Lkotlinx/io/Source; -} - -public final class kotlinx/io/files/PathsKt { - public static final fun Path (Ljava/lang/String;[Ljava/lang/String;)Lkotlinx/io/files/Path; - public static final fun Path (Lkotlinx/io/files/Path;[Ljava/lang/String;)Lkotlinx/io/files/Path; - public static final fun sinkDeprecated (Lkotlinx/io/files/Path;)Lkotlinx/io/Sink; - public static final fun sourceDeprecated (Lkotlinx/io/files/Path;)Lkotlinx/io/Source; -} - -public abstract interface class kotlinx/io/unsafe/BufferIterationContext : kotlinx/io/unsafe/SegmentReadContext { - public abstract fun next (Lkotlinx/io/Segment;)Lkotlinx/io/Segment; -} - -public abstract interface class kotlinx/io/unsafe/SegmentReadContext { - public abstract fun getUnchecked (Lkotlinx/io/Segment;I)B -} - -public abstract interface class kotlinx/io/unsafe/SegmentWriteContext { - public abstract fun setUnchecked (Lkotlinx/io/Segment;IB)V - public abstract fun setUnchecked (Lkotlinx/io/Segment;IBB)V - public abstract fun setUnchecked (Lkotlinx/io/Segment;IBBB)V - public abstract fun setUnchecked (Lkotlinx/io/Segment;IBBBB)V -} - -public final class kotlinx/io/unsafe/UnsafeBufferOperations { - public static final field INSTANCE Lkotlinx/io/unsafe/UnsafeBufferOperations; - public final fun forEachSegment (Lkotlinx/io/Buffer;Lkotlin/jvm/functions/Function2;)V - public final fun getMaxSafeWriteCapacity ()I - public final fun iterate (Lkotlinx/io/Buffer;JLkotlin/jvm/functions/Function3;)V - public final fun iterate (Lkotlinx/io/Buffer;Lkotlin/jvm/functions/Function2;)V - public final fun moveToTail (Lkotlinx/io/Buffer;[BII)V - public static synthetic fun moveToTail$default (Lkotlinx/io/unsafe/UnsafeBufferOperations;Lkotlinx/io/Buffer;[BIIILjava/lang/Object;)V - public final fun readFromHead (Lkotlinx/io/Buffer;Lkotlin/jvm/functions/Function2;)I - public final fun readFromHead (Lkotlinx/io/Buffer;Lkotlin/jvm/functions/Function3;)I - public final fun writeToTail (Lkotlinx/io/Buffer;ILkotlin/jvm/functions/Function2;)I - public final fun writeToTail (Lkotlinx/io/Buffer;ILkotlin/jvm/functions/Function3;)I -} - -public final class kotlinx/io/unsafe/UnsafeBufferOperationsJvmKt { - public static final fun readBulk (Lkotlinx/io/unsafe/UnsafeBufferOperations;Lkotlinx/io/Buffer;[Ljava/nio/ByteBuffer;Lkotlin/jvm/functions/Function2;)J - public static final fun readFromHead (Lkotlinx/io/unsafe/UnsafeBufferOperations;Lkotlinx/io/Buffer;Lkotlin/jvm/functions/Function1;)I - public static final fun writeToTail (Lkotlinx/io/unsafe/UnsafeBufferOperations;Lkotlinx/io/Buffer;ILkotlin/jvm/functions/Function1;)I -} - -public final class kotlinx/io/unsafe/UnsafeBufferOperationsKt { - public static final synthetic fun getBufferIterationContextImpl ()Lkotlinx/io/unsafe/BufferIterationContext; - public static final synthetic fun getSegmentReadContextImpl ()Lkotlinx/io/unsafe/SegmentReadContext; - public static final synthetic fun getSegmentWriteContextImpl ()Lkotlinx/io/unsafe/SegmentWriteContext; - public static final synthetic fun withData (Lkotlinx/io/unsafe/SegmentReadContext;Lkotlinx/io/Segment;Lkotlin/jvm/functions/Function3;)V -} - diff --git a/core/api/kotlinx-io-core.klib.api b/core/api/kotlinx-io-core.klib.api deleted file mode 100644 index 2db224bc4..000000000 --- a/core/api/kotlinx-io-core.klib.api +++ /dev/null @@ -1,312 +0,0 @@ -// Klib ABI Dump -// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm32Hfp, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, wasmWasi, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] -// Alias: apple => [iosArm64, iosSimulatorArm64, iosX64, macosArm64, macosX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] -// Rendering settings: -// - Signature version: 2 -// - Show manifest properties: true -// - Show declarations: true - -// Library unique name: -open annotation class kotlinx.io/DelicateIoApi : kotlin/Annotation { // kotlinx.io/DelicateIoApi|null[0] - constructor () // kotlinx.io/DelicateIoApi.|(){}[0] -} - -open annotation class kotlinx.io/InternalIoApi : kotlin/Annotation { // kotlinx.io/InternalIoApi|null[0] - constructor () // kotlinx.io/InternalIoApi.|(){}[0] -} - -open annotation class kotlinx.io/UnsafeIoApi : kotlin/Annotation { // kotlinx.io/UnsafeIoApi|null[0] - constructor () // kotlinx.io/UnsafeIoApi.|(){}[0] -} - -abstract interface kotlinx.io.unsafe/BufferIterationContext : kotlinx.io.unsafe/SegmentReadContext { // kotlinx.io.unsafe/BufferIterationContext|null[0] - abstract fun next(kotlinx.io/Segment): kotlinx.io/Segment? // kotlinx.io.unsafe/BufferIterationContext.next|next(kotlinx.io.Segment){}[0] -} - -abstract interface kotlinx.io.unsafe/SegmentReadContext { // kotlinx.io.unsafe/SegmentReadContext|null[0] - abstract fun getUnchecked(kotlinx.io/Segment, kotlin/Int): kotlin/Byte // kotlinx.io.unsafe/SegmentReadContext.getUnchecked|getUnchecked(kotlinx.io.Segment;kotlin.Int){}[0] -} - -abstract interface kotlinx.io.unsafe/SegmentWriteContext { // kotlinx.io.unsafe/SegmentWriteContext|null[0] - abstract fun setUnchecked(kotlinx.io/Segment, kotlin/Int, kotlin/Byte) // kotlinx.io.unsafe/SegmentWriteContext.setUnchecked|setUnchecked(kotlinx.io.Segment;kotlin.Int;kotlin.Byte){}[0] - abstract fun setUnchecked(kotlinx.io/Segment, kotlin/Int, kotlin/Byte, kotlin/Byte) // kotlinx.io.unsafe/SegmentWriteContext.setUnchecked|setUnchecked(kotlinx.io.Segment;kotlin.Int;kotlin.Byte;kotlin.Byte){}[0] - abstract fun setUnchecked(kotlinx.io/Segment, kotlin/Int, kotlin/Byte, kotlin/Byte, kotlin/Byte) // kotlinx.io.unsafe/SegmentWriteContext.setUnchecked|setUnchecked(kotlinx.io.Segment;kotlin.Int;kotlin.Byte;kotlin.Byte;kotlin.Byte){}[0] - abstract fun setUnchecked(kotlinx.io/Segment, kotlin/Int, kotlin/Byte, kotlin/Byte, kotlin/Byte, kotlin/Byte) // kotlinx.io.unsafe/SegmentWriteContext.setUnchecked|setUnchecked(kotlinx.io.Segment;kotlin.Int;kotlin.Byte;kotlin.Byte;kotlin.Byte;kotlin.Byte){}[0] -} - -abstract interface kotlinx.io/RawSink : kotlin/AutoCloseable { // kotlinx.io/RawSink|null[0] - abstract fun close() // kotlinx.io/RawSink.close|close(){}[0] - abstract fun flush() // kotlinx.io/RawSink.flush|flush(){}[0] - abstract fun write(kotlinx.io/Buffer, kotlin/Long) // kotlinx.io/RawSink.write|write(kotlinx.io.Buffer;kotlin.Long){}[0] -} - -abstract interface kotlinx.io/RawSource : kotlin/AutoCloseable { // kotlinx.io/RawSource|null[0] - abstract fun close() // kotlinx.io/RawSource.close|close(){}[0] - abstract fun readAtMostTo(kotlinx.io/Buffer, kotlin/Long): kotlin/Long // kotlinx.io/RawSource.readAtMostTo|readAtMostTo(kotlinx.io.Buffer;kotlin.Long){}[0] -} - -sealed interface kotlinx.io.files/FileSystem { // kotlinx.io.files/FileSystem|null[0] - abstract fun atomicMove(kotlinx.io.files/Path, kotlinx.io.files/Path) // kotlinx.io.files/FileSystem.atomicMove|atomicMove(kotlinx.io.files.Path;kotlinx.io.files.Path){}[0] - abstract fun createDirectories(kotlinx.io.files/Path, kotlin/Boolean = ...) // kotlinx.io.files/FileSystem.createDirectories|createDirectories(kotlinx.io.files.Path;kotlin.Boolean){}[0] - abstract fun delete(kotlinx.io.files/Path, kotlin/Boolean = ...) // kotlinx.io.files/FileSystem.delete|delete(kotlinx.io.files.Path;kotlin.Boolean){}[0] - abstract fun exists(kotlinx.io.files/Path): kotlin/Boolean // kotlinx.io.files/FileSystem.exists|exists(kotlinx.io.files.Path){}[0] - abstract fun list(kotlinx.io.files/Path): kotlin.collections/Collection // kotlinx.io.files/FileSystem.list|list(kotlinx.io.files.Path){}[0] - abstract fun metadataOrNull(kotlinx.io.files/Path): kotlinx.io.files/FileMetadata? // kotlinx.io.files/FileSystem.metadataOrNull|metadataOrNull(kotlinx.io.files.Path){}[0] - abstract fun resolve(kotlinx.io.files/Path): kotlinx.io.files/Path // kotlinx.io.files/FileSystem.resolve|resolve(kotlinx.io.files.Path){}[0] - abstract fun sink(kotlinx.io.files/Path, kotlin/Boolean = ...): kotlinx.io/RawSink // kotlinx.io.files/FileSystem.sink|sink(kotlinx.io.files.Path;kotlin.Boolean){}[0] - abstract fun source(kotlinx.io.files/Path): kotlinx.io/RawSource // kotlinx.io.files/FileSystem.source|source(kotlinx.io.files.Path){}[0] -} - -sealed interface kotlinx.io/Sink : kotlinx.io/RawSink { // kotlinx.io/Sink|null[0] - abstract val buffer // kotlinx.io/Sink.buffer|{}buffer[0] - abstract fun (): kotlinx.io/Buffer // kotlinx.io/Sink.buffer.|(){}[0] - - abstract fun emit() // kotlinx.io/Sink.emit|emit(){}[0] - abstract fun flush() // kotlinx.io/Sink.flush|flush(){}[0] - abstract fun hintEmit() // kotlinx.io/Sink.hintEmit|hintEmit(){}[0] - abstract fun transferFrom(kotlinx.io/RawSource): kotlin/Long // kotlinx.io/Sink.transferFrom|transferFrom(kotlinx.io.RawSource){}[0] - abstract fun write(kotlin/ByteArray, kotlin/Int = ..., kotlin/Int = ...) // kotlinx.io/Sink.write|write(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] - abstract fun write(kotlinx.io/RawSource, kotlin/Long) // kotlinx.io/Sink.write|write(kotlinx.io.RawSource;kotlin.Long){}[0] - abstract fun writeByte(kotlin/Byte) // kotlinx.io/Sink.writeByte|writeByte(kotlin.Byte){}[0] - abstract fun writeInt(kotlin/Int) // kotlinx.io/Sink.writeInt|writeInt(kotlin.Int){}[0] - abstract fun writeLong(kotlin/Long) // kotlinx.io/Sink.writeLong|writeLong(kotlin.Long){}[0] - abstract fun writeShort(kotlin/Short) // kotlinx.io/Sink.writeShort|writeShort(kotlin.Short){}[0] -} - -sealed interface kotlinx.io/Source : kotlinx.io/RawSource { // kotlinx.io/Source|null[0] - abstract val buffer // kotlinx.io/Source.buffer|{}buffer[0] - abstract fun (): kotlinx.io/Buffer // kotlinx.io/Source.buffer.|(){}[0] - - abstract fun exhausted(): kotlin/Boolean // kotlinx.io/Source.exhausted|exhausted(){}[0] - abstract fun peek(): kotlinx.io/Source // kotlinx.io/Source.peek|peek(){}[0] - abstract fun readAtMostTo(kotlin/ByteArray, kotlin/Int = ..., kotlin/Int = ...): kotlin/Int // kotlinx.io/Source.readAtMostTo|readAtMostTo(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] - abstract fun readByte(): kotlin/Byte // kotlinx.io/Source.readByte|readByte(){}[0] - abstract fun readInt(): kotlin/Int // kotlinx.io/Source.readInt|readInt(){}[0] - abstract fun readLong(): kotlin/Long // kotlinx.io/Source.readLong|readLong(){}[0] - abstract fun readShort(): kotlin/Short // kotlinx.io/Source.readShort|readShort(){}[0] - abstract fun readTo(kotlinx.io/RawSink, kotlin/Long) // kotlinx.io/Source.readTo|readTo(kotlinx.io.RawSink;kotlin.Long){}[0] - abstract fun request(kotlin/Long): kotlin/Boolean // kotlinx.io/Source.request|request(kotlin.Long){}[0] - abstract fun require(kotlin/Long) // kotlinx.io/Source.require|require(kotlin.Long){}[0] - abstract fun skip(kotlin/Long) // kotlinx.io/Source.skip|skip(kotlin.Long){}[0] - abstract fun transferTo(kotlinx.io/RawSink): kotlin/Long // kotlinx.io/Source.transferTo|transferTo(kotlinx.io.RawSink){}[0] -} - -final class kotlinx.io.files/FileMetadata { // kotlinx.io.files/FileMetadata|null[0] - constructor (kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Long = ...) // kotlinx.io.files/FileMetadata.|(kotlin.Boolean;kotlin.Boolean;kotlin.Long){}[0] - - final val isDirectory // kotlinx.io.files/FileMetadata.isDirectory|{}isDirectory[0] - final fun (): kotlin/Boolean // kotlinx.io.files/FileMetadata.isDirectory.|(){}[0] - final val isRegularFile // kotlinx.io.files/FileMetadata.isRegularFile|{}isRegularFile[0] - final fun (): kotlin/Boolean // kotlinx.io.files/FileMetadata.isRegularFile.|(){}[0] - final val size // kotlinx.io.files/FileMetadata.size|{}size[0] - final fun (): kotlin/Long // kotlinx.io.files/FileMetadata.size.|(){}[0] -} - -final class kotlinx.io.files/Path { // kotlinx.io.files/Path|null[0] - final val isAbsolute // kotlinx.io.files/Path.isAbsolute|{}isAbsolute[0] - final fun (): kotlin/Boolean // kotlinx.io.files/Path.isAbsolute.|(){}[0] - final val name // kotlinx.io.files/Path.name|{}name[0] - final fun (): kotlin/String // kotlinx.io.files/Path.name.|(){}[0] - final val parent // kotlinx.io.files/Path.parent|{}parent[0] - final fun (): kotlinx.io.files/Path? // kotlinx.io.files/Path.parent.|(){}[0] - - final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.io.files/Path.equals|equals(kotlin.Any?){}[0] - final fun hashCode(): kotlin/Int // kotlinx.io.files/Path.hashCode|hashCode(){}[0] - final fun toString(): kotlin/String // kotlinx.io.files/Path.toString|toString(){}[0] -} - -final class kotlinx.io/Buffer : kotlinx.io/Sink, kotlinx.io/Source { // kotlinx.io/Buffer|null[0] - constructor () // kotlinx.io/Buffer.|(){}[0] - - final val buffer // kotlinx.io/Buffer.buffer|{}buffer[0] - final fun (): kotlinx.io/Buffer // kotlinx.io/Buffer.buffer.|(){}[0] - final val size // kotlinx.io/Buffer.size|{}size[0] - final fun (): kotlin/Long // kotlinx.io/Buffer.size.|(){}[0] - - final var head // kotlinx.io/Buffer.head|{}head[0] - final fun (): kotlinx.io/Segment? // kotlinx.io/Buffer.head.|(){}[0] - final fun (kotlinx.io/Segment?) // kotlinx.io/Buffer.head.|(kotlinx.io.Segment?){}[0] - final var sizeMut // kotlinx.io/Buffer.sizeMut|{}sizeMut[0] - final fun (): kotlin/Long // kotlinx.io/Buffer.sizeMut.|(){}[0] - final fun (kotlin/Long) // kotlinx.io/Buffer.sizeMut.|(kotlin.Long){}[0] - final var tail // kotlinx.io/Buffer.tail|{}tail[0] - final fun (): kotlinx.io/Segment? // kotlinx.io/Buffer.tail.|(){}[0] - final fun (kotlinx.io/Segment?) // kotlinx.io/Buffer.tail.|(kotlinx.io.Segment?){}[0] - - final fun clear() // kotlinx.io/Buffer.clear|clear(){}[0] - final fun close() // kotlinx.io/Buffer.close|close(){}[0] - final fun copy(): kotlinx.io/Buffer // kotlinx.io/Buffer.copy|copy(){}[0] - final fun copyTo(kotlinx.io/Buffer, kotlin/Long = ..., kotlin/Long = ...) // kotlinx.io/Buffer.copyTo|copyTo(kotlinx.io.Buffer;kotlin.Long;kotlin.Long){}[0] - final fun emit() // kotlinx.io/Buffer.emit|emit(){}[0] - final fun exhausted(): kotlin/Boolean // kotlinx.io/Buffer.exhausted|exhausted(){}[0] - final fun flush() // kotlinx.io/Buffer.flush|flush(){}[0] - final fun get(kotlin/Long): kotlin/Byte // kotlinx.io/Buffer.get|get(kotlin.Long){}[0] - final fun hintEmit() // kotlinx.io/Buffer.hintEmit|hintEmit(){}[0] - final fun peek(): kotlinx.io/Source // kotlinx.io/Buffer.peek|peek(){}[0] - final fun readAtMostTo(kotlin/ByteArray, kotlin/Int, kotlin/Int): kotlin/Int // kotlinx.io/Buffer.readAtMostTo|readAtMostTo(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] - final fun readAtMostTo(kotlinx.io/Buffer, kotlin/Long): kotlin/Long // kotlinx.io/Buffer.readAtMostTo|readAtMostTo(kotlinx.io.Buffer;kotlin.Long){}[0] - final fun readByte(): kotlin/Byte // kotlinx.io/Buffer.readByte|readByte(){}[0] - final fun readInt(): kotlin/Int // kotlinx.io/Buffer.readInt|readInt(){}[0] - final fun readLong(): kotlin/Long // kotlinx.io/Buffer.readLong|readLong(){}[0] - final fun readShort(): kotlin/Short // kotlinx.io/Buffer.readShort|readShort(){}[0] - final fun readTo(kotlinx.io/RawSink, kotlin/Long) // kotlinx.io/Buffer.readTo|readTo(kotlinx.io.RawSink;kotlin.Long){}[0] - final fun recycleTail() // kotlinx.io/Buffer.recycleTail|recycleTail(){}[0] - final fun request(kotlin/Long): kotlin/Boolean // kotlinx.io/Buffer.request|request(kotlin.Long){}[0] - final fun require(kotlin/Long) // kotlinx.io/Buffer.require|require(kotlin.Long){}[0] - final fun skip(kotlin/Long) // kotlinx.io/Buffer.skip|skip(kotlin.Long){}[0] - final fun toString(): kotlin/String // kotlinx.io/Buffer.toString|toString(){}[0] - final fun transferFrom(kotlinx.io/RawSource): kotlin/Long // kotlinx.io/Buffer.transferFrom|transferFrom(kotlinx.io.RawSource){}[0] - final fun transferTo(kotlinx.io/RawSink): kotlin/Long // kotlinx.io/Buffer.transferTo|transferTo(kotlinx.io.RawSink){}[0] - final fun writableSegment(kotlin/Int): kotlinx.io/Segment // kotlinx.io/Buffer.writableSegment|writableSegment(kotlin.Int){}[0] - final fun write(kotlin/ByteArray, kotlin/Int, kotlin/Int) // kotlinx.io/Buffer.write|write(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] - final fun write(kotlinx.io/Buffer, kotlin/Long) // kotlinx.io/Buffer.write|write(kotlinx.io.Buffer;kotlin.Long){}[0] - final fun write(kotlinx.io/RawSource, kotlin/Long) // kotlinx.io/Buffer.write|write(kotlinx.io.RawSource;kotlin.Long){}[0] - final fun writeByte(kotlin/Byte) // kotlinx.io/Buffer.writeByte|writeByte(kotlin.Byte){}[0] - final fun writeInt(kotlin/Int) // kotlinx.io/Buffer.writeInt|writeInt(kotlin.Int){}[0] - final fun writeLong(kotlin/Long) // kotlinx.io/Buffer.writeLong|writeLong(kotlin.Long){}[0] - final fun writeShort(kotlin/Short) // kotlinx.io/Buffer.writeShort|writeShort(kotlin.Short){}[0] -} - -final class kotlinx.io/Segment { // kotlinx.io/Segment|null[0] - final val remainingCapacity // kotlinx.io/Segment.remainingCapacity|{}remainingCapacity[0] - final fun (): kotlin/Int // kotlinx.io/Segment.remainingCapacity.|(){}[0] - final val size // kotlinx.io/Segment.size|{}size[0] - final fun (): kotlin/Int // kotlinx.io/Segment.size.|(){}[0] - - final var limit // kotlinx.io/Segment.limit|{}limit[0] - final fun (): kotlin/Int // kotlinx.io/Segment.limit.|(){}[0] - final fun (kotlin/Int) // kotlinx.io/Segment.limit.|(kotlin.Int){}[0] - final var next // kotlinx.io/Segment.next|{}next[0] - final fun (): kotlinx.io/Segment? // kotlinx.io/Segment.next.|(){}[0] - final fun (kotlinx.io/Segment?) // kotlinx.io/Segment.next.|(kotlinx.io.Segment?){}[0] - final var pos // kotlinx.io/Segment.pos|{}pos[0] - final fun (): kotlin/Int // kotlinx.io/Segment.pos.|(){}[0] - final fun (kotlin/Int) // kotlinx.io/Segment.pos.|(kotlin.Int){}[0] - final var prev // kotlinx.io/Segment.prev|{}prev[0] - final fun (): kotlinx.io/Segment? // kotlinx.io/Segment.prev.|(){}[0] - final fun (kotlinx.io/Segment?) // kotlinx.io/Segment.prev.|(kotlinx.io.Segment?){}[0] - - final fun dataAsByteArray(kotlin/Boolean): kotlin/ByteArray // kotlinx.io/Segment.dataAsByteArray|dataAsByteArray(kotlin.Boolean){}[0] - final fun writeBackData(kotlin/ByteArray, kotlin/Int) // kotlinx.io/Segment.writeBackData|writeBackData(kotlin.ByteArray;kotlin.Int){}[0] -} - -open class kotlinx.io.files/FileNotFoundException : kotlinx.io/IOException { // kotlinx.io.files/FileNotFoundException|null[0] - constructor (kotlin/String?) // kotlinx.io.files/FileNotFoundException.|(kotlin.String?){}[0] -} - -open class kotlinx.io/EOFException : kotlinx.io/IOException { // kotlinx.io/EOFException|null[0] - constructor () // kotlinx.io/EOFException.|(){}[0] - constructor (kotlin/String?) // kotlinx.io/EOFException.|(kotlin.String?){}[0] -} - -open class kotlinx.io/IOException : kotlin/Exception { // kotlinx.io/IOException|null[0] - constructor () // kotlinx.io/IOException.|(){}[0] - constructor (kotlin/String?) // kotlinx.io/IOException.|(kotlin.String?){}[0] - constructor (kotlin/String?, kotlin/Throwable?) // kotlinx.io/IOException.|(kotlin.String?;kotlin.Throwable?){}[0] - constructor (kotlin/Throwable?) // kotlinx.io/IOException.|(kotlin.Throwable?){}[0] -} - -final object kotlinx.io.unsafe/UnsafeBufferOperations { // kotlinx.io.unsafe/UnsafeBufferOperations|null[0] - final val maxSafeWriteCapacity // kotlinx.io.unsafe/UnsafeBufferOperations.maxSafeWriteCapacity|{}maxSafeWriteCapacity[0] - final fun (): kotlin/Int // kotlinx.io.unsafe/UnsafeBufferOperations.maxSafeWriteCapacity.|(){}[0] - - final fun moveToTail(kotlinx.io/Buffer, kotlin/ByteArray, kotlin/Int = ..., kotlin/Int = ...) // kotlinx.io.unsafe/UnsafeBufferOperations.moveToTail|moveToTail(kotlinx.io.Buffer;kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] - final inline fun forEachSegment(kotlinx.io/Buffer, kotlin/Function2) // kotlinx.io.unsafe/UnsafeBufferOperations.forEachSegment|forEachSegment(kotlinx.io.Buffer;kotlin.Function2){}[0] - final inline fun iterate(kotlinx.io/Buffer, kotlin/Function2) // kotlinx.io.unsafe/UnsafeBufferOperations.iterate|iterate(kotlinx.io.Buffer;kotlin.Function2){}[0] - final inline fun iterate(kotlinx.io/Buffer, kotlin/Long, kotlin/Function3) // kotlinx.io.unsafe/UnsafeBufferOperations.iterate|iterate(kotlinx.io.Buffer;kotlin.Long;kotlin.Function3){}[0] - final inline fun readFromHead(kotlinx.io/Buffer, kotlin/Function2): kotlin/Int // kotlinx.io.unsafe/UnsafeBufferOperations.readFromHead|readFromHead(kotlinx.io.Buffer;kotlin.Function2){}[0] - final inline fun readFromHead(kotlinx.io/Buffer, kotlin/Function3): kotlin/Int // kotlinx.io.unsafe/UnsafeBufferOperations.readFromHead|readFromHead(kotlinx.io.Buffer;kotlin.Function3){}[0] - final inline fun writeToTail(kotlinx.io/Buffer, kotlin/Int, kotlin/Function2): kotlin/Int // kotlinx.io.unsafe/UnsafeBufferOperations.writeToTail|writeToTail(kotlinx.io.Buffer;kotlin.Int;kotlin.Function2){}[0] - final inline fun writeToTail(kotlinx.io/Buffer, kotlin/Int, kotlin/Function3): kotlin/Int // kotlinx.io.unsafe/UnsafeBufferOperations.writeToTail|writeToTail(kotlinx.io.Buffer;kotlin.Int;kotlin.Function3){}[0] -} - -final val kotlinx.io.files/SystemFileSystem // kotlinx.io.files/SystemFileSystem|{}SystemFileSystem[0] - final fun (): kotlinx.io.files/FileSystem // kotlinx.io.files/SystemFileSystem.|(){}[0] -final val kotlinx.io.files/SystemPathSeparator // kotlinx.io.files/SystemPathSeparator|{}SystemPathSeparator[0] - final fun (): kotlin/Char // kotlinx.io.files/SystemPathSeparator.|(){}[0] -final val kotlinx.io.files/SystemTemporaryDirectory // kotlinx.io.files/SystemTemporaryDirectory|{}SystemTemporaryDirectory[0] - final fun (): kotlinx.io.files/Path // kotlinx.io.files/SystemTemporaryDirectory.|(){}[0] -final val kotlinx.io.unsafe/BufferIterationContextImpl // kotlinx.io.unsafe/BufferIterationContextImpl|{}BufferIterationContextImpl[0] - final fun (): kotlinx.io.unsafe/BufferIterationContext // kotlinx.io.unsafe/BufferIterationContextImpl.|(){}[0] -final val kotlinx.io.unsafe/SegmentReadContextImpl // kotlinx.io.unsafe/SegmentReadContextImpl|{}SegmentReadContextImpl[0] - final fun (): kotlinx.io.unsafe/SegmentReadContext // kotlinx.io.unsafe/SegmentReadContextImpl.|(){}[0] -final val kotlinx.io.unsafe/SegmentWriteContextImpl // kotlinx.io.unsafe/SegmentWriteContextImpl|{}SegmentWriteContextImpl[0] - final fun (): kotlinx.io.unsafe/SegmentWriteContext // kotlinx.io.unsafe/SegmentWriteContextImpl.|(){}[0] - -final fun (kotlinx.io.files/Path).kotlinx.io.files/sink(): kotlinx.io/Sink // kotlinx.io.files/sink|sink@kotlinx.io.files.Path(){}[0] -final fun (kotlinx.io.files/Path).kotlinx.io.files/source(): kotlinx.io/Source // kotlinx.io.files/source|source@kotlinx.io.files.Path(){}[0] -final fun (kotlinx.io/Buffer).kotlinx.io/indexOf(kotlin/Byte, kotlin/Long = ..., kotlin/Long = ...): kotlin/Long // kotlinx.io/indexOf|indexOf@kotlinx.io.Buffer(kotlin.Byte;kotlin.Long;kotlin.Long){}[0] -final fun (kotlinx.io/Buffer).kotlinx.io/indexOf(kotlinx.io.bytestring/ByteString, kotlin/Long = ...): kotlin/Long // kotlinx.io/indexOf|indexOf@kotlinx.io.Buffer(kotlinx.io.bytestring.ByteString;kotlin.Long){}[0] -final fun (kotlinx.io/Buffer).kotlinx.io/readString(): kotlin/String // kotlinx.io/readString|readString@kotlinx.io.Buffer(){}[0] -final fun (kotlinx.io/Buffer).kotlinx.io/snapshot(): kotlinx.io.bytestring/ByteString // kotlinx.io/snapshot|snapshot@kotlinx.io.Buffer(){}[0] -final fun (kotlinx.io/RawSink).kotlinx.io/buffered(): kotlinx.io/Sink // kotlinx.io/buffered|buffered@kotlinx.io.RawSink(){}[0] -final fun (kotlinx.io/RawSource).kotlinx.io/buffered(): kotlinx.io/Source // kotlinx.io/buffered|buffered@kotlinx.io.RawSource(){}[0] -final fun (kotlinx.io/Segment).kotlinx.io/isEmpty(): kotlin/Boolean // kotlinx.io/isEmpty|isEmpty@kotlinx.io.Segment(){}[0] -final fun (kotlinx.io/Sink).kotlinx.io/write(kotlinx.io.bytestring/ByteString, kotlin/Int = ..., kotlin/Int = ...) // kotlinx.io/write|write@kotlinx.io.Sink(kotlinx.io.bytestring.ByteString;kotlin.Int;kotlin.Int){}[0] -final fun (kotlinx.io/Sink).kotlinx.io/writeCodePointValue(kotlin/Int) // kotlinx.io/writeCodePointValue|writeCodePointValue@kotlinx.io.Sink(kotlin.Int){}[0] -final fun (kotlinx.io/Sink).kotlinx.io/writeDecimalLong(kotlin/Long) // kotlinx.io/writeDecimalLong|writeDecimalLong@kotlinx.io.Sink(kotlin.Long){}[0] -final fun (kotlinx.io/Sink).kotlinx.io/writeDouble(kotlin/Double) // kotlinx.io/writeDouble|writeDouble@kotlinx.io.Sink(kotlin.Double){}[0] -final fun (kotlinx.io/Sink).kotlinx.io/writeDoubleLe(kotlin/Double) // kotlinx.io/writeDoubleLe|writeDoubleLe@kotlinx.io.Sink(kotlin.Double){}[0] -final fun (kotlinx.io/Sink).kotlinx.io/writeFloat(kotlin/Float) // kotlinx.io/writeFloat|writeFloat@kotlinx.io.Sink(kotlin.Float){}[0] -final fun (kotlinx.io/Sink).kotlinx.io/writeFloatLe(kotlin/Float) // kotlinx.io/writeFloatLe|writeFloatLe@kotlinx.io.Sink(kotlin.Float){}[0] -final fun (kotlinx.io/Sink).kotlinx.io/writeHexadecimalUnsignedLong(kotlin/Long) // kotlinx.io/writeHexadecimalUnsignedLong|writeHexadecimalUnsignedLong@kotlinx.io.Sink(kotlin.Long){}[0] -final fun (kotlinx.io/Sink).kotlinx.io/writeIntLe(kotlin/Int) // kotlinx.io/writeIntLe|writeIntLe@kotlinx.io.Sink(kotlin.Int){}[0] -final fun (kotlinx.io/Sink).kotlinx.io/writeLongLe(kotlin/Long) // kotlinx.io/writeLongLe|writeLongLe@kotlinx.io.Sink(kotlin.Long){}[0] -final fun (kotlinx.io/Sink).kotlinx.io/writeShortLe(kotlin/Short) // kotlinx.io/writeShortLe|writeShortLe@kotlinx.io.Sink(kotlin.Short){}[0] -final fun (kotlinx.io/Sink).kotlinx.io/writeString(kotlin/CharSequence, kotlin/Int = ..., kotlin/Int = ...) // kotlinx.io/writeString|writeString@kotlinx.io.Sink(kotlin.CharSequence;kotlin.Int;kotlin.Int){}[0] -final fun (kotlinx.io/Sink).kotlinx.io/writeString(kotlin/String, kotlin/Int = ..., kotlin/Int = ...) // kotlinx.io/writeString|writeString@kotlinx.io.Sink(kotlin.String;kotlin.Int;kotlin.Int){}[0] -final fun (kotlinx.io/Sink).kotlinx.io/writeUByte(kotlin/UByte) // kotlinx.io/writeUByte|writeUByte@kotlinx.io.Sink(kotlin.UByte){}[0] -final fun (kotlinx.io/Sink).kotlinx.io/writeUInt(kotlin/UInt) // kotlinx.io/writeUInt|writeUInt@kotlinx.io.Sink(kotlin.UInt){}[0] -final fun (kotlinx.io/Sink).kotlinx.io/writeUIntLe(kotlin/UInt) // kotlinx.io/writeUIntLe|writeUIntLe@kotlinx.io.Sink(kotlin.UInt){}[0] -final fun (kotlinx.io/Sink).kotlinx.io/writeULong(kotlin/ULong) // kotlinx.io/writeULong|writeULong@kotlinx.io.Sink(kotlin.ULong){}[0] -final fun (kotlinx.io/Sink).kotlinx.io/writeULongLe(kotlin/ULong) // kotlinx.io/writeULongLe|writeULongLe@kotlinx.io.Sink(kotlin.ULong){}[0] -final fun (kotlinx.io/Sink).kotlinx.io/writeUShort(kotlin/UShort) // kotlinx.io/writeUShort|writeUShort@kotlinx.io.Sink(kotlin.UShort){}[0] -final fun (kotlinx.io/Sink).kotlinx.io/writeUShortLe(kotlin/UShort) // kotlinx.io/writeUShortLe|writeUShortLe@kotlinx.io.Sink(kotlin.UShort){}[0] -final fun (kotlinx.io/Source).kotlinx.io/indexOf(kotlin/Byte, kotlin/Long = ..., kotlin/Long = ...): kotlin/Long // kotlinx.io/indexOf|indexOf@kotlinx.io.Source(kotlin.Byte;kotlin.Long;kotlin.Long){}[0] -final fun (kotlinx.io/Source).kotlinx.io/indexOf(kotlinx.io.bytestring/ByteString, kotlin/Long = ...): kotlin/Long // kotlinx.io/indexOf|indexOf@kotlinx.io.Source(kotlinx.io.bytestring.ByteString;kotlin.Long){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readByteArray(): kotlin/ByteArray // kotlinx.io/readByteArray|readByteArray@kotlinx.io.Source(){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readByteArray(kotlin/Int): kotlin/ByteArray // kotlinx.io/readByteArray|readByteArray@kotlinx.io.Source(kotlin.Int){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readByteString(): kotlinx.io.bytestring/ByteString // kotlinx.io/readByteString|readByteString@kotlinx.io.Source(){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readByteString(kotlin/Int): kotlinx.io.bytestring/ByteString // kotlinx.io/readByteString|readByteString@kotlinx.io.Source(kotlin.Int){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readCodePointValue(): kotlin/Int // kotlinx.io/readCodePointValue|readCodePointValue@kotlinx.io.Source(){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readDecimalLong(): kotlin/Long // kotlinx.io/readDecimalLong|readDecimalLong@kotlinx.io.Source(){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readDouble(): kotlin/Double // kotlinx.io/readDouble|readDouble@kotlinx.io.Source(){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readDoubleLe(): kotlin/Double // kotlinx.io/readDoubleLe|readDoubleLe@kotlinx.io.Source(){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readFloat(): kotlin/Float // kotlinx.io/readFloat|readFloat@kotlinx.io.Source(){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readFloatLe(): kotlin/Float // kotlinx.io/readFloatLe|readFloatLe@kotlinx.io.Source(){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readHexadecimalUnsignedLong(): kotlin/Long // kotlinx.io/readHexadecimalUnsignedLong|readHexadecimalUnsignedLong@kotlinx.io.Source(){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readIntLe(): kotlin/Int // kotlinx.io/readIntLe|readIntLe@kotlinx.io.Source(){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readLine(): kotlin/String? // kotlinx.io/readLine|readLine@kotlinx.io.Source(){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readLineStrict(kotlin/Long = ...): kotlin/String // kotlinx.io/readLineStrict|readLineStrict@kotlinx.io.Source(kotlin.Long){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readLongLe(): kotlin/Long // kotlinx.io/readLongLe|readLongLe@kotlinx.io.Source(){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readShortLe(): kotlin/Short // kotlinx.io/readShortLe|readShortLe@kotlinx.io.Source(){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readString(): kotlin/String // kotlinx.io/readString|readString@kotlinx.io.Source(){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readString(kotlin/Long): kotlin/String // kotlinx.io/readString|readString@kotlinx.io.Source(kotlin.Long){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readTo(kotlin/ByteArray, kotlin/Int = ..., kotlin/Int = ...) // kotlinx.io/readTo|readTo@kotlinx.io.Source(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readUByte(): kotlin/UByte // kotlinx.io/readUByte|readUByte@kotlinx.io.Source(){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readUInt(): kotlin/UInt // kotlinx.io/readUInt|readUInt@kotlinx.io.Source(){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readUIntLe(): kotlin/UInt // kotlinx.io/readUIntLe|readUIntLe@kotlinx.io.Source(){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readULong(): kotlin/ULong // kotlinx.io/readULong|readULong@kotlinx.io.Source(){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readULongLe(): kotlin/ULong // kotlinx.io/readULongLe|readULongLe@kotlinx.io.Source(){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readUShort(): kotlin/UShort // kotlinx.io/readUShort|readUShort@kotlinx.io.Source(){}[0] -final fun (kotlinx.io/Source).kotlinx.io/readUShortLe(): kotlin/UShort // kotlinx.io/readUShortLe|readUShortLe@kotlinx.io.Source(){}[0] -final fun (kotlinx.io/Source).kotlinx.io/startsWith(kotlin/Byte): kotlin/Boolean // kotlinx.io/startsWith|startsWith@kotlinx.io.Source(kotlin.Byte){}[0] -final fun kotlinx.io.files/Path(kotlin/String): kotlinx.io.files/Path // kotlinx.io.files/Path|Path(kotlin.String){}[0] -final fun kotlinx.io.files/Path(kotlin/String, kotlin/Array...): kotlinx.io.files/Path // kotlinx.io.files/Path|Path(kotlin.String;kotlin.Array...){}[0] -final fun kotlinx.io.files/Path(kotlinx.io.files/Path, kotlin/Array...): kotlinx.io.files/Path // kotlinx.io.files/Path|Path(kotlinx.io.files.Path;kotlin.Array...){}[0] -final fun kotlinx.io/discardingSink(): kotlinx.io/RawSink // kotlinx.io/discardingSink|discardingSink(){}[0] -final inline fun (kotlinx.io.unsafe/SegmentReadContext).kotlinx.io.unsafe/withData(kotlinx.io/Segment, kotlin/Function3) // kotlinx.io.unsafe/withData|withData@kotlinx.io.unsafe.SegmentReadContext(kotlinx.io.Segment;kotlin.Function3){}[0] -final inline fun (kotlinx.io/Sink).kotlinx.io/writeToInternalBuffer(kotlin/Function1) // kotlinx.io/writeToInternalBuffer|writeToInternalBuffer@kotlinx.io.Sink(kotlin.Function1){}[0] -final inline fun <#A: kotlin/Any?> (kotlinx.io/Buffer).kotlinx.io/seek(kotlin/Long, kotlin/Function2): #A // kotlinx.io/seek|seek@kotlinx.io.Buffer(kotlin.Long;kotlin.Function2){0§}[0] - -// Targets: [apple] -final fun (kotlinx.io/Sink).kotlinx.io/asNSOutputStream(): platform.Foundation/NSOutputStream // kotlinx.io/asNSOutputStream|asNSOutputStream@kotlinx.io.Sink(){}[0] - -// Targets: [apple] -final fun (kotlinx.io/Source).kotlinx.io/asNSInputStream(): platform.Foundation/NSInputStream // kotlinx.io/asNSInputStream|asNSInputStream@kotlinx.io.Source(){}[0] - -// Targets: [apple] -final fun (platform.Foundation/NSInputStream).kotlinx.io/asSource(): kotlinx.io/RawSource // kotlinx.io/asSource|asSource@platform.Foundation.NSInputStream(){}[0] - -// Targets: [apple] -final fun (platform.Foundation/NSOutputStream).kotlinx.io/asSink(): kotlinx.io/RawSink // kotlinx.io/asSink|asSink@platform.Foundation.NSOutputStream(){}[0] diff --git a/core/apple/src/-Util.kt b/core/apple/src/-Util.kt deleted file mode 100644 index 6871df21e..000000000 --- a/core/apple/src/-Util.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -package kotlinx.io - -import kotlinx.cinterop.UnsafeNumber -import platform.Foundation.NSError -import platform.Foundation.NSLocalizedDescriptionKey -import platform.Foundation.NSUnderlyingErrorKey - -@OptIn(UnsafeNumber::class) -internal fun Exception.toNSError() = NSError( - domain = "Kotlin", - code = 0, - userInfo = mapOf( - NSLocalizedDescriptionKey to message, - NSUnderlyingErrorKey to this - ) -) diff --git a/core/apple/src/AppleCore.kt b/core/apple/src/AppleCore.kt deleted file mode 100644 index 7083d6ec7..000000000 --- a/core/apple/src/AppleCore.kt +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -@file:OptIn(UnsafeNumber::class, ExperimentalForeignApi::class) - -package kotlinx.io - -import kotlinx.cinterop.* -import kotlinx.io.unsafe.UnsafeBufferOperations -import platform.Foundation.NSInputStream -import platform.Foundation.NSOutputStream -import platform.Foundation.NSStreamStatusClosed -import platform.Foundation.NSStreamStatusNotOpen -import platform.posix.uint8_tVar - -/** - * Returns [RawSink] that writes to an output stream. - * - * Use [RawSink.buffered] to create a buffered sink from it. - * - * @sample kotlinx.io.samples.KotlinxIoSamplesApple.outputStreamAsSink - */ -public fun NSOutputStream.asSink(): RawSink = OutputStreamSink(this) - -private open class OutputStreamSink( - private val out: NSOutputStream, -) : RawSink { - - init { - if (out.streamStatus == NSStreamStatusNotOpen) out.open() - } - - @OptIn(UnsafeIoApi::class) - override fun write(source: Buffer, byteCount: Long) { - if (out.streamStatus == NSStreamStatusClosed) throw IOException("Stream Closed") - - checkOffsetAndCount(source.size, 0, byteCount) - var remaining = byteCount - var bytesWritten = 0L - while (remaining > 0) { - UnsafeBufferOperations.readFromHead(source) { data, pos, limit -> - val toCopy = minOf(remaining, limit - pos).toInt() - bytesWritten = data.usePinned { - val bytes = it.addressOf(pos).reinterpret() - out.write(bytes, toCopy.convert()).toLong() - } - 0 - } - - if (bytesWritten < 0L) throw IOException(out.streamError?.localizedDescription ?: "Unknown error") - if (bytesWritten == 0L) throw IOException("NSOutputStream reached capacity") - - source.skip(bytesWritten) - remaining -= bytesWritten - } - } - - override fun flush() { - // no-op - } - - override fun close() = out.close() - - override fun toString() = "RawSink($out)" -} - -/** - * Returns [RawSource] that reads from an input stream. - * - * Use [RawSource.buffered] to create a buffered source from it. - * - * @sample kotlinx.io.samples.KotlinxIoSamplesApple.inputStreamAsSource - */ -public fun NSInputStream.asSource(): RawSource = NSInputStreamSource(this) - -private open class NSInputStreamSource( - private val input: NSInputStream, -) : RawSource { - - init { - if (input.streamStatus == NSStreamStatusNotOpen) input.open() - } - - @OptIn(UnsafeIoApi::class) - override fun readAtMostTo(sink: Buffer, byteCount: Long): Long { - if (input.streamStatus == NSStreamStatusClosed) throw IOException("Stream Closed") - - if (byteCount == 0L) return 0L - checkByteCount(byteCount) - - var bytesRead = 0L - UnsafeBufferOperations.writeToTail(sink, 1) { data, pos, limit -> - val maxToCopy = minOf(byteCount, limit - pos) - val read = data.usePinned { ba -> - val bytes = ba.addressOf(pos).reinterpret() - input.read(bytes, maxToCopy.convert()).toLong() - } - bytesRead = read - maxOf(read.toInt(), 0) - } - - if (bytesRead < 0L) throw IOException(input.streamError?.localizedDescription ?: "Unknown error") - if (bytesRead == 0L) return -1 - return bytesRead - } - - override fun close() = input.close() - - override fun toString() = "RawSource($input)" -} diff --git a/core/apple/src/BuffersApple.kt b/core/apple/src/BuffersApple.kt deleted file mode 100644 index 29f698c10..000000000 --- a/core/apple/src/BuffersApple.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -@file:OptIn(UnsafeNumber::class, ExperimentalForeignApi::class) - -package kotlinx.io - -import kotlinx.cinterop.* -import kotlinx.io.unsafe.UnsafeBufferOperations -import kotlinx.io.unsafe.withData -import platform.Foundation.NSData -import platform.Foundation.create -import platform.Foundation.data -import platform.darwin.NSUIntegerMax -import platform.posix.* - -@OptIn(ExperimentalForeignApi::class, UnsafeIoApi::class) -internal fun Buffer.write(source: CPointer, maxLength: Int) { - require(maxLength >= 0) { "maxLength ($maxLength) must not be negative" } - - var currentOffset = 0 - while (currentOffset < maxLength) { - UnsafeBufferOperations.writeToTail(this, 1) { data, pos, limit -> - val toCopy = minOf(maxLength - currentOffset, limit - pos) - data.usePinned { - memcpy(it.addressOf(pos), source + currentOffset, toCopy.convert()) - } - currentOffset += toCopy - toCopy - } - } -} - -@OptIn(UnsafeIoApi::class) -internal fun Buffer.readAtMostTo(sink: CPointer, maxLength: Int): Int { - require(maxLength >= 0) { "maxLength ($maxLength) must not be negative" } - - var toCopy = 0 - UnsafeBufferOperations.readFromHead(this) { data, pos, limit -> - toCopy = minOf(maxLength, limit - pos) - data.usePinned { - memcpy(sink, it.addressOf(pos), toCopy.convert()) - } - toCopy - } - - return toCopy -} - -@OptIn(BetaInteropApi::class, UnsafeIoApi::class) -internal fun Buffer.snapshotAsNSData(): NSData { - if (size == 0L) return NSData.data() - - check(size.toULong() <= NSUIntegerMax) { "Buffer is too long ($size) to be converted into NSData." } - - val bytes = malloc(size.convert())?.reinterpret() - ?: throw Error("malloc failed: ${strerror(errno)?.toKString()}") - - var index = 0 - UnsafeBufferOperations.forEachSegment(this) { ctx, segment -> - ctx.withData(segment) { data, pos, limit -> - val length = limit - pos - data.usePinned { - memcpy(bytes + index, it.addressOf(pos), length.convert()) - } - index += length - } - } - return NSData.create(bytesNoCopy = bytes, length = size.convert()) -} diff --git a/core/apple/src/SinksApple.kt b/core/apple/src/SinksApple.kt deleted file mode 100644 index 7000a2242..000000000 --- a/core/apple/src/SinksApple.kt +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -@file:OptIn(ExperimentalForeignApi::class, ExperimentalNativeApi::class) - -package kotlinx.io - -import kotlinx.cinterop.* -import platform.Foundation.* -import platform.darwin.NSInteger -import platform.darwin.NSUInteger -import platform.posix.uint8_tVar -import kotlin.experimental.ExperimentalNativeApi -import kotlin.native.ref.WeakReference - -/** - * Returns an output stream that writes to this sink. Closing the stream will also close this sink. - * - * The stream supports both polling and run-loop scheduling, please check - * [Apple's documentation](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Streams/Articles/PollingVersusRunloop.html) - * for information about stream events handling. - * - * The stream does not implement initializers - * ([NSOutputStream.initToBuffer](https://developer.apple.com/documentation/foundation/nsoutputstream/1410805-inittobuffer), - * [NSOutputStream.initToMemory](https://developer.apple.com/documentation/foundation/nsoutputstream/1409909-inittomemory), - * [NSOutputStream.initWithURL](https://developer.apple.com/documentation/foundation/nsoutputstream/1414446-initwithurl), - * [NSOutputStream.initToFileAtPath](https://developer.apple.com/documentation/foundation/nsoutputstream/1416367-inittofileatpath)), - * their use will result in a runtime error. - * - * @sample kotlinx.io.samples.KotlinxIoSamplesApple.asStream - */ -public fun Sink.asNSOutputStream(): NSOutputStream = SinkNSOutputStream(this) - -@OptIn(UnsafeNumber::class) -private class SinkNSOutputStream( - private val sink: Sink -) : NSOutputStream(toMemory = Unit), NSStreamDelegateProtocol { - - private val isClosed: () -> Boolean = when (sink) { - is RealSink -> sink::closed - is Buffer -> { - { false } - } - } - - private var status = NSStreamStatusNotOpen - private var error: NSError? = null - set(value) { - status = NSStreamStatusError - field = value - postEvent(NSStreamEventErrorOccurred) - sink.close() - } - - override fun streamStatus() = if (status != NSStreamStatusError && isClosed()) NSStreamStatusClosed else status - - override fun streamError() = error - - override fun open() { - if (status == NSStreamStatusNotOpen) { - status = NSStreamStatusOpening - status = NSStreamStatusOpen - postEvent(NSStreamEventOpenCompleted) - postEvent(NSStreamEventHasSpaceAvailable) - } - } - - override fun close() { - if (status == NSStreamStatusError || status == NSStreamStatusNotOpen) return - status = NSStreamStatusClosed - runLoop = null - runLoopModes = listOf() - sink.close() - } - - @OptIn(DelicateIoApi::class) - override fun write(buffer: CPointer?, maxLength: NSUInteger): NSInteger { - if (streamStatus != NSStreamStatusOpen || buffer == null) return -1 - status = NSStreamStatusWriting - val toWrite = minOf(maxLength, Int.MAX_VALUE.convert()).toInt() - return try { - sink.writeToInternalBuffer { - it.write(buffer, toWrite) - } - status = NSStreamStatusOpen - toWrite.convert() - } catch (e: Exception) { - error = e.toNSError() - -1 - } - } - - override fun hasSpaceAvailable() = !isFinished - - private val isFinished - get() = when (streamStatus) { - NSStreamStatusClosed, NSStreamStatusError -> true - else -> false - } - - @OptIn(InternalIoApi::class) - override fun propertyForKey(key: NSStreamPropertyKey): Any? = when (key) { - NSStreamDataWrittenToMemoryStreamKey -> sink.buffer.snapshotAsNSData() - else -> null - } - - override fun setProperty(property: Any?, forKey: NSStreamPropertyKey) = false - - // WeakReference as delegate should not be retained - // https://developer.apple.com/documentation/foundation/nsstream/1418423-delegate - private var _delegate: WeakReference? = null - private var runLoop: NSRunLoop? = null - private var runLoopModes = listOf() - - private fun postEvent(event: NSStreamEvent) { - val runLoop = runLoop ?: return - runLoop.performInModes(runLoopModes) { - if (runLoop == this.runLoop) { - delegateOrSelf.stream(this, event) - } - } - } - - override fun delegate() = _delegate?.value - - private val delegateOrSelf get() = delegate ?: this - - override fun setDelegate(delegate: NSStreamDelegateProtocol?) { - _delegate = delegate?.let { WeakReference(it) } - } - - override fun stream(aStream: NSStream, handleEvent: NSStreamEvent) { - // no-op - } - - override fun scheduleInRunLoop(aRunLoop: NSRunLoop, forMode: NSRunLoopMode) { - if (runLoop == null) { - runLoop = aRunLoop - } - if (runLoop == aRunLoop) { - runLoopModes += forMode - } - if (status == NSStreamStatusOpen) { - postEvent(NSStreamEventHasSpaceAvailable) - } - } - - override fun removeFromRunLoop(aRunLoop: NSRunLoop, forMode: NSRunLoopMode) { - if (aRunLoop == runLoop) { - runLoopModes -= forMode - if (runLoopModes.isEmpty()) { - runLoop = null - } - } - } - - override fun description() = "$sink.asNSOutputStream()" -} diff --git a/core/apple/src/SourcesApple.kt b/core/apple/src/SourcesApple.kt deleted file mode 100644 index cdb617a9a..000000000 --- a/core/apple/src/SourcesApple.kt +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -@file:OptIn(ExperimentalNativeApi::class, ExperimentalForeignApi::class) - -package kotlinx.io - -import kotlinx.cinterop.* -import platform.Foundation.* -import platform.darwin.NSInteger -import platform.darwin.NSUInteger -import platform.darwin.NSUIntegerVar -import platform.posix.uint8_tVar -import kotlin.experimental.ExperimentalNativeApi -import kotlin.native.ref.WeakReference - -/** - * Returns an input stream that reads from this source. Closing the stream will also close this source. - * - * The stream supports both polling and run-loop scheduling, please check - * [Apple's documentation](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Streams/Articles/PollingVersusRunloop.html) - * for information about stream events handling. - * - * The stream does not implement initializers - * ([NSInputStream.initWithURL](https://developer.apple.com/documentation/foundation/nsinputstream/1417891-initwithurl), - * [NSInputStream.initWithData](https://developer.apple.com/documentation/foundation/nsinputstream/1412470-initwithdata), - * [NSInputStream.initWithFileAtPath](https://developer.apple.com/documentation/foundation/nsinputstream/1408976-initwithfileatpath)), - * their use will result in a runtime error. - * - * @sample kotlinx.io.samples.KotlinxIoSamplesApple.asStream - */ -public fun Source.asNSInputStream(): NSInputStream = SourceNSInputStream(this) - -@OptIn(InternalIoApi::class, UnsafeNumber::class) -private class SourceNSInputStream( - private val source: Source -) : NSInputStream(NSData()), NSStreamDelegateProtocol { - - private val isClosed: () -> Boolean = when (source) { - is RealSource -> source::closed - is Buffer -> { - { false } - } - } - - private var status = NSStreamStatusNotOpen - private var error: NSError? = null - set(value) { - status = NSStreamStatusError - field = value - source.close() - } - - override fun streamStatus() = if (status != NSStreamStatusError && isClosed()) NSStreamStatusClosed else status - - override fun streamError() = error - - override fun open() { - if (status == NSStreamStatusNotOpen) { - status = NSStreamStatusOpening - status = NSStreamStatusOpen - postEvent(NSStreamEventOpenCompleted) - checkBytes() - } - } - - override fun close() { - if (status == NSStreamStatusError || status == NSStreamStatusNotOpen) return - status = NSStreamStatusClosed - runLoop = null - runLoopModes = listOf() - source.close() - } - - override fun read(buffer: CPointer?, maxLength: NSUInteger): NSInteger { - if (streamStatus != NSStreamStatusOpen && streamStatus != NSStreamStatusAtEnd || buffer == null) return -1 - status = NSStreamStatusReading - try { - if (source.exhausted()) { - status = NSStreamStatusAtEnd - return 0 - } - val toRead = minOf(maxLength.toLong(), source.buffer.size, Int.MAX_VALUE.toLong()).toInt() - val read = source.buffer.readAtMostTo(buffer, toRead).convert() - status = NSStreamStatusOpen - checkBytes() - return read - } catch (e: Exception) { - error = e.toNSError() - postEvent(NSStreamEventErrorOccurred) - return -1 - } - } - - override fun getBuffer(buffer: CPointer>?, length: CPointer?) = false - - override fun hasBytesAvailable() = !isFinished - - private val isFinished - get() = when (streamStatus) { - NSStreamStatusClosed, NSStreamStatusError -> true - else -> false - } - - override fun propertyForKey(key: NSStreamPropertyKey): Any? = null - - override fun setProperty(property: Any?, forKey: NSStreamPropertyKey) = false - - // WeakReference as delegate should not be retained - // https://developer.apple.com/documentation/foundation/nsstream/1418423-delegate - private var _delegate: WeakReference? = null - private var runLoop: NSRunLoop? = null - private var runLoopModes = listOf() - - private fun postEvent(event: NSStreamEvent) { - val runLoop = runLoop ?: return - runLoop.performInModes(runLoopModes) { - if (runLoop == this.runLoop) { - delegateOrSelf.stream(this, event) - } - } - } - - private fun checkBytes() { - val runLoop = runLoop ?: return - runLoop.performInModes(runLoopModes) { - if (runLoop != this.runLoop || isFinished) return@performInModes - val event = try { - if (source.exhausted()) { - status = NSStreamStatusAtEnd - NSStreamEventEndEncountered - } else { - NSStreamEventHasBytesAvailable - } - } catch (e: Exception) { - error = e.toNSError() - NSStreamEventErrorOccurred - } - delegateOrSelf.stream(this, event) - } - } - - override fun delegate() = _delegate?.value - - private val delegateOrSelf get() = delegate ?: this - - override fun setDelegate(delegate: NSStreamDelegateProtocol?) { - _delegate = delegate?.let { WeakReference(it) } - } - - override fun stream(aStream: NSStream, handleEvent: NSStreamEvent) { - // no-op - } - - override fun scheduleInRunLoop(aRunLoop: NSRunLoop, forMode: NSRunLoopMode) { - if (runLoop == null) { - runLoop = aRunLoop - } - if (runLoop == aRunLoop) { - runLoopModes += forMode - } - if (status == NSStreamStatusOpen) { - checkBytes() - } - } - - override fun removeFromRunLoop(aRunLoop: NSRunLoop, forMode: NSRunLoopMode) { - if (aRunLoop == runLoop) { - runLoopModes -= forMode - if (runLoopModes.isEmpty()) { - runLoop = null - } - } - } - - override fun description() = "$source.asNSInputStream()" -} diff --git a/core/apple/src/files/FileSystemApple.kt b/core/apple/src/files/FileSystemApple.kt deleted file mode 100644 index 75882b967..000000000 --- a/core/apple/src/files/FileSystemApple.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ -@file:OptIn(ExperimentalForeignApi::class) - -package kotlinx.io.files - -import kotlinx.cinterop.* -import kotlinx.io.IOException -import platform.Foundation.* -import platform.posix.* - - -internal actual fun atomicMoveImpl(source: Path, destination: Path) { - if (rename(source.path, destination.path) != 0) { - throw IOException("Move failed: ${strerror(errno)?.toKString()}") - } -} - -public actual val SystemTemporaryDirectory: Path - get() = Path(NSTemporaryDirectory()) - -internal actual fun dirnameImpl(path: String): String { - if (!path.contains(SystemPathSeparator)) { - return "" - } - memScoped { - return dirname(path.cstr.ptr)?.toKString() ?: "" - } -} - -internal actual fun basenameImpl(path: String): String { - memScoped { - return basename(path.cstr.ptr)?.toKString() ?: "" - } -} - -internal actual fun isAbsoluteImpl(path: String): Boolean = path.startsWith('/') - -internal actual fun mkdirImpl(path: String) { - if (mkdir(path, PermissionAllowAll) != 0) { - throw IOException("mkdir failed: ${strerror(errno)?.toKString()}") - } -} - -internal actual fun realpathImpl(path: String): String { - val res = realpath(path, null) ?: throw IllegalStateException() - try { - return res.toKString() - } finally { - free(res) - } -} - -internal actual fun metadataOrNullImpl(path: Path): FileMetadata? { - val attributes = NSFileManager.defaultManager().fileAttributesAtPath(path.path, traverseLink = true) ?: return null - val fileType = attributes[NSFileType] as String - val isFile = fileType == NSFileTypeRegular - val isDir = fileType == NSFileTypeDirectory - return FileMetadata( - isRegularFile = isFile, - isDirectory = isDir, - size = if (isFile) attributes[NSFileSize] as Long else -1 - ) -} diff --git a/core/apple/test/NSInputStreamSourceTest.kt b/core/apple/test/NSInputStreamSourceTest.kt deleted file mode 100644 index 94bcf39bc..000000000 --- a/core/apple/test/NSInputStreamSourceTest.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -package kotlinx.io - -import kotlinx.io.files.Path -import kotlinx.io.files.SystemFileSystem -import platform.Foundation.NSInputStream -import platform.Foundation.NSURL -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith - -class NSInputStreamSourceTest { - @Test - fun nsInputStreamSource() { - val input = NSInputStream(data = byteArrayOf(0x61).toNSData()) - val source = input.asSource() - val buffer = Buffer() - assertEquals(1, source.readAtMostTo(buffer, 1L)) - assertEquals("a", buffer.readString()) - } - - @Test - fun nsInputStreamSourceFromFile() { - val file = tempFileName() - try { - SystemFileSystem.sink(Path(file)).buffered().use { - it.writeString("example") - } - - val input = NSInputStream(uRL = NSURL.fileURLWithPath(file)) - val source = input.asSource() - val buffer = Buffer() - assertEquals(7, source.readAtMostTo(buffer, 10)) - assertEquals("example", buffer.readString()) - } finally { - SystemFileSystem.delete(Path(file)) - } - } - - @Test - fun sourceFromInputStream() { - val input = NSInputStream(data = ("a" + "b".repeat(Segment.SIZE * 2) + "c").encodeToByteArray().toNSData()) - - // Source: ab...bc - val source: RawSource = input.asSource() - val sink = Buffer() - - // Source: b...bc. Sink: abb. - assertEquals(3, source.readAtMostTo(sink, 3)) - assertEquals("abb", sink.readString(3)) - - // Source: b...bc. Sink: b...b. - assertEquals(Segment.SIZE.toLong(), source.readAtMostTo(sink, 20000)) - assertEquals("b".repeat(Segment.SIZE), sink.readString()) - - // Source: b...bc. Sink: b...bc. - assertEquals((Segment.SIZE - 1).toLong(), source.readAtMostTo(sink, 20000)) - assertEquals("b".repeat(Segment.SIZE - 2) + "c", sink.readString()) - - // Source and sink are empty. - assertEquals(-1, source.readAtMostTo(sink, 1)) - } - - @Test - fun sourceFromInputStreamWithSegmentSize() { - val input = NSInputStream(data = ByteArray(Segment.SIZE).toNSData()) - val source = input.asSource() - val sink = Buffer() - - assertEquals(Segment.SIZE.toLong(), source.readAtMostTo(sink, Segment.SIZE.toLong())) - assertEquals(-1, source.readAtMostTo(sink, Segment.SIZE.toLong())) - - assertNoEmptySegments(sink) - } - - @Test - fun sourceFromInputStreamBounds() { - val source = NSInputStream(data = ByteArray(100).toNSData()).asSource() - assertFailsWith { source.readAtMostTo(Buffer(), -1) } - } -} diff --git a/core/apple/test/NSOutputStreamSinkTest.kt b/core/apple/test/NSOutputStreamSinkTest.kt deleted file mode 100644 index 2b14b6fda..000000000 --- a/core/apple/test/NSOutputStreamSinkTest.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -@file:OptIn(ExperimentalForeignApi::class) - -package kotlinx.io - -import kotlinx.cinterop.* -import platform.Foundation.* -import kotlin.test.Test -import kotlin.test.assertEquals - -class NSOutputStreamSinkTest { - @Test - @OptIn(UnsafeNumber::class) - fun nsOutputStreamSink() { - val out = NSOutputStream.outputStreamToMemory() - val sink = out.asSink() - val buffer = Buffer().apply { - writeString("a") - } - sink.write(buffer, 1L) - val data = out.propertyForKey(NSStreamDataWrittenToMemoryStreamKey) as NSData - assertEquals(1U, data.length) - val bytes = data.bytes!!.reinterpret() - assertEquals(0x61, bytes[0]) - } - - @Test - fun sinkFromOutputStream() { - val data = Buffer().apply { - writeString("a") - writeString("b".repeat(9998)) - writeString("c") - } - val out = NSOutputStream.outputStreamToMemory() - val sink = out.asSink() - - sink.write(data, 3) - val outData = out.propertyForKey(NSStreamDataWrittenToMemoryStreamKey) as NSData - val outString = outData.toByteArray().decodeToString() - assertEquals("abb", outString) - - sink.write(data, data.size) - val outData2 = out.propertyForKey(NSStreamDataWrittenToMemoryStreamKey) as NSData - val outString2 = outData2.toByteArray().decodeToString() - assertEquals("a" + "b".repeat(9998) + "c", outString2) - } -} diff --git a/core/apple/test/SinkNSOutputStreamTest.kt b/core/apple/test/SinkNSOutputStreamTest.kt deleted file mode 100644 index 0c6998521..000000000 --- a/core/apple/test/SinkNSOutputStreamTest.kt +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -@file:OptIn(ExperimentalForeignApi::class) - -package kotlinx.io - -import kotlinx.atomicfu.atomic -import kotlinx.cinterop.* -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.sync.Mutex -import platform.CoreFoundation.CFRunLoopStop -import platform.Foundation.* -import platform.darwin.NSObject -import platform.darwin.NSUInteger -import platform.posix.uint8_tVar -import kotlin.test.* - -private fun NSOutputStream.write(vararg strings: String) { - for (str in strings) { - str.encodeToByteArray().apply { - assertEquals(size, this.write(this@write)) - } - } -} - -@OptIn(UnsafeNumber::class) -class SinkNSOutputStreamTest { - - @Test - fun multipleWrites() { - val buffer = Buffer() - buffer.asNSOutputStream().apply { - open() - write("hello", " ", "world") - close() - } - assertEquals("hello world", buffer.readString()) - - RealSink(buffer).asNSOutputStream().apply { - open() - write("hello", " ", "real", " sink") - close() - } - assertEquals("hello real sink", buffer.readString()) - } - - @Test - fun bufferOutputStream() { - testOutputStream(Buffer(), "abc") - testOutputStream(Buffer(), "a" + "b".repeat(Segment.SIZE * 2) + "c") - } - - @Test - fun realSinkOutputStream() { - testOutputStream(RealSink(Buffer()), "abc") - testOutputStream(RealSink(Buffer()), "a" + "b".repeat(Segment.SIZE * 2) + "c") - } - - @OptIn(InternalIoApi::class) - private fun testOutputStream(sink: Sink, input: String) { - val out = sink.asNSOutputStream() - val byteArray = input.encodeToByteArray() - val size: NSUInteger = input.length.convert() - byteArray.usePinned { - val cPtr = it.addressOf(0).reinterpret() - - assertEquals(NSStreamStatusNotOpen, out.streamStatus) - assertEquals(-1, out.write(cPtr, size)) - out.open() - assertEquals(NSStreamStatusOpen, out.streamStatus) - - assertEquals(size.convert(), out.write(cPtr, size)) - sink.flush() - when (sink) { - is Buffer -> { - val data = out.propertyForKey(NSStreamDataWrittenToMemoryStreamKey) as NSData - assertContentEquals(byteArray, data.toByteArray()) - assertContentEquals(byteArray, sink.buffer.readByteArray()) - } - - is RealSink -> assertContentEquals(byteArray, (sink.sink as Buffer).readByteArray()) - } - } - } - - @Test - @OptIn(DelicateIoApi::class) - fun nsOutputStreamClose() { - val buffer = Buffer() - val sink = RealSink(buffer) - assertFalse(sink.closed) - - val out = sink.asNSOutputStream() - out.open() - out.close() - assertTrue(sink.closed) - assertEquals(NSStreamStatusClosed, out.streamStatus) - - val byteArray = ByteArray(4) - byteArray.usePinned { - val cPtr = it.addressOf(0).reinterpret() - - assertEquals(-1, out.write(cPtr, 4U)) - assertNull(out.streamError) - assertTrue(sink.buffer.readByteArray().isEmpty()) - } - } - - @Test - fun delegateTest() { - val runLoop = startRunLoop() - - fun produceWithDelegate(out: NSOutputStream, data: String) { - val opened = Mutex(true) - val written = atomic(0) - val completed = Mutex(true) - - out.delegate = object : NSObject(), NSStreamDelegateProtocol { - val source = data.encodeToByteArray() - override fun stream(aStream: NSStream, handleEvent: NSStreamEvent) { - assertEquals("run-loop", NSThread.currentThread.name) - when (handleEvent) { - NSStreamEventOpenCompleted -> opened.unlock() - NSStreamEventHasSpaceAvailable -> { - if (source.isNotEmpty()) { - source.usePinned { - assertEquals( - data.length.convert(), - out.write( - it.addressOf(written.getAndAdd(data.length)).reinterpret(), - data.length.convert() - ) - ) - } - } - val writtenData = out.propertyForKey(NSStreamDataWrittenToMemoryStreamKey) as NSData - assertEquals(data, writtenData.toByteArray().decodeToString()) - out.close() - completed.unlock() - } - - else -> fail("unexpected event ${handleEvent.asString()}") - } - } - } - out.scheduleInRunLoop(runLoop, NSDefaultRunLoopMode) - out.open() - runBlocking { - opened.lockWithTimeout() - completed.lockWithTimeout() - } - assertEquals(data.length, written.value) - } - - produceWithDelegate(Buffer().asNSOutputStream(), "custom") - produceWithDelegate(Buffer().asNSOutputStream(), "") - CFRunLoopStop(runLoop.getCFRunLoop()) - } - - @Test - fun testSubscribeAfterOpen() { - val runLoop = startRunLoop() - - fun subscribeAfterOpen(out: NSOutputStream) { - val available = Mutex(true) - - out.delegate = object : NSObject(), NSStreamDelegateProtocol { - override fun stream(aStream: NSStream, handleEvent: NSStreamEvent) { - assertEquals("run-loop", NSThread.currentThread.name) - when (handleEvent) { - NSStreamEventOpenCompleted -> fail("opened before subscribe") - NSStreamEventHasSpaceAvailable -> available.unlock() - else -> fail("unexpected event ${handleEvent.asString()}") - } - } - } - out.open() - out.scheduleInRunLoop(runLoop, NSDefaultRunLoopMode) - runBlocking { - available.lockWithTimeout() - } - out.close() - } - - subscribeAfterOpen(Buffer().asNSOutputStream()) - CFRunLoopStop(runLoop.getCFRunLoop()) - } -} diff --git a/core/apple/test/SourceNSInputStreamTest.kt b/core/apple/test/SourceNSInputStreamTest.kt deleted file mode 100644 index accfbd046..000000000 --- a/core/apple/test/SourceNSInputStreamTest.kt +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -@file:OptIn(ExperimentalForeignApi::class) - -package kotlinx.io - -import kotlinx.atomicfu.atomic -import kotlinx.atomicfu.locks.reentrantLock -import kotlinx.atomicfu.locks.withLock -import kotlinx.cinterop.* -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.sync.Mutex -import platform.CoreFoundation.CFRunLoopStop -import platform.Foundation.* -import platform.darwin.NSObject -import platform.posix.uint8_tVar -import kotlin.test.* - -@OptIn(UnsafeNumber::class) -class SourceNSInputStreamTest { - @Test - fun bufferInputStream() { - val source = Buffer() - source.writeString("abc") - testInputStream(source.asNSInputStream()) - } - - @Test - fun realSourceInputStream() { - val source = Buffer() - source.writeString("abc") - testInputStream(RealSource(source).asNSInputStream()) - } - - private fun testInputStream(input: NSInputStream) { - val byteArray = ByteArray(4) - byteArray.usePinned { - val cPtr = it.addressOf(0).reinterpret() - - assertEquals(NSStreamStatusNotOpen, input.streamStatus) - assertEquals(-1, input.read(cPtr, 4U)) - input.open() - assertEquals(NSStreamStatusOpen, input.streamStatus) - - byteArray.fill(-5) - assertEquals(3, input.read(cPtr, 4U)) - assertEquals("[97, 98, 99, -5]", byteArray.contentToString()) - - byteArray.fill(-7) - assertEquals(0, input.read(cPtr, 4U)) - assertEquals("[-7, -7, -7, -7]", byteArray.contentToString()) - } - } - - @Test - fun bufferInputStreamLongData() { - val source = Buffer() - source.writeString("a" + "b".repeat(Segment.SIZE * 2) + "c") - testInputStreamLongData(source.asNSInputStream()) - } - - @Test - fun realSourceInputStreamLongData() { - val source = Buffer() - source.writeString("a" + "b".repeat(Segment.SIZE * 2) + "c") - testInputStreamLongData(RealSource(source).asNSInputStream()) - } - - private fun testInputStreamLongData(input: NSInputStream) { - val lengthPlusOne = Segment.SIZE * 2 + 3 - val byteArray = ByteArray(lengthPlusOne) - byteArray.usePinned { - val cPtr = it.addressOf(0).reinterpret() - - assertEquals(NSStreamStatusNotOpen, input.streamStatus) - assertEquals(-1, input.read(cPtr, lengthPlusOne.convert())) - input.open() - assertEquals(NSStreamStatusOpen, input.streamStatus) - - byteArray.fill(-5) - assertEquals(Segment.SIZE.convert(), input.read(cPtr, lengthPlusOne.convert())) - assertEquals( - "[97${", 98".repeat(Segment.SIZE - 1)}${", -5".repeat(Segment.SIZE + 3)}]", - byteArray.contentToString() - ) - - byteArray.fill(-6) - assertEquals(Segment.SIZE.convert(), input.read(cPtr, lengthPlusOne.convert())) - assertEquals( - "[98${", 98".repeat(Segment.SIZE - 1)}${", -6".repeat(Segment.SIZE + 3)}]", - byteArray.contentToString() - ) - - byteArray.fill(-7) - assertEquals(2, input.read(cPtr, lengthPlusOne.convert())) - assertEquals("[98, 99${", -7".repeat(Segment.SIZE * 2 + 1)}]", byteArray.contentToString()) - - byteArray.fill(-8) - assertEquals(0, input.read(cPtr, lengthPlusOne.convert())) - assertEquals("[-8${", -8".repeat(lengthPlusOne - 1)}]", byteArray.contentToString()) - } - } - - @Test - fun nsInputStreamClose() { - val buffer = Buffer() - buffer.writeString("abc") - val source = RealSource(buffer) - assertFalse(source.closed) - - val input = source.asNSInputStream() - input.open() - input.close() - assertTrue(source.closed) - assertEquals(NSStreamStatusClosed, input.streamStatus) - - val byteArray = ByteArray(4) - byteArray.usePinned { - val cPtr = it.addressOf(0).reinterpret() - - byteArray.fill(-5) - assertEquals(-1, input.read(cPtr, 4U)) - assertNull(input.streamError) - assertEquals("[-5, -5, -5, -5]", byteArray.contentToString()) - } - } - - @Test - fun delegateTest() { - val runLoop = startRunLoop() - - fun consumeWithDelegate(input: NSInputStream, data: String) { - val opened = Mutex(true) - val read = atomic(0) - val completed = Mutex(true) - - input.delegate = object : NSObject(), NSStreamDelegateProtocol { - val sink = ByteArray(data.length) - override fun stream(aStream: NSStream, handleEvent: NSStreamEvent) { - assertEquals("run-loop", NSThread.currentThread.name) - when (handleEvent) { - NSStreamEventOpenCompleted -> opened.unlock() - NSStreamEventHasBytesAvailable -> { - sink.usePinned { - assertEquals(1, input.read(it.addressOf(read.getAndIncrement()).reinterpret(), 1U)) - } - } - - NSStreamEventEndEncountered -> { - assertEquals(data, sink.decodeToString()) - input.close() - completed.unlock() - } - - else -> fail("unexpected event ${handleEvent.asString()}") - } - } - } - input.scheduleInRunLoop(runLoop, NSDefaultRunLoopMode) - input.open() - runBlocking { - opened.lockWithTimeout() - completed.lockWithTimeout() - } - assertEquals(data.length, read.value) - } - - consumeWithDelegate(Buffer().apply { writeString("custom") }.asNSInputStream(), "custom") - consumeWithDelegate(Buffer().asNSInputStream(), "") - CFRunLoopStop(runLoop.getCFRunLoop()) - } - - @Test - fun testRunLoopSwitch() { - val runLoop1 = startRunLoop("run-loop-1") - val runLoop2 = startRunLoop("run-loop-2") - - fun consumeSwitching(input: NSInputStream, data: String) { - val opened = Mutex(true) - val readLock = reentrantLock() - var read = 0 - val completed = Mutex(true) - - input.delegate = object : NSObject(), NSStreamDelegateProtocol { - val sink = ByteArray(data.length) - override fun stream(aStream: NSStream, handleEvent: NSStreamEvent) { - // Ensure thread safe access to `read` between scheduled run loops - readLock.withLock { - if (read == 0) { - // until first read - assertEquals("run-loop-1", NSThread.currentThread.name) - } else { - // after first read - assertEquals("run-loop-2", NSThread.currentThread.name) - } - when (handleEvent) { - NSStreamEventOpenCompleted -> opened.unlock() - NSStreamEventHasBytesAvailable -> { - if (read == 0) { - // switch to other run loop before first read - input.removeFromRunLoop(runLoop1, NSDefaultRunLoopMode) - input.scheduleInRunLoop(runLoop2, NSDefaultRunLoopMode) - } else if (read >= data.length - 3) { - // unsubscribe before last read - input.removeFromRunLoop(runLoop2, NSDefaultRunLoopMode) - } - sink.usePinned { - val readBytes = input.read(it.addressOf(read).reinterpret(), 3U) - assertNotEquals(0, readBytes) - read += readBytes.toInt() - } - if (read == data.length) { - assertEquals(data, sink.decodeToString()) - completed.unlock() - } - } - - NSStreamEventEndEncountered -> fail("$data shouldn't be subscribed") - else -> fail("unexpected event ${handleEvent.asString()}") - } - } - } - } - input.scheduleInRunLoop(runLoop1, NSDefaultRunLoopMode) - input.open() - runBlocking { - opened.lockWithTimeout() - completed.lockWithTimeout() - // wait a bit to be sure delegate is no longer called - delay(200) - } - input.close() - } - - consumeSwitching(Buffer().apply { writeString("custom") }.asNSInputStream(), "custom") - CFRunLoopStop(runLoop1.getCFRunLoop()) - CFRunLoopStop(runLoop2.getCFRunLoop()) - } - - @Test - fun testSubscribeAfterOpen() { - val runLoop = startRunLoop() - - fun subscribeAfterOpen(input: NSInputStream, data: String) { - val available = Mutex(true) - - input.delegate = object : NSObject(), NSStreamDelegateProtocol { - override fun stream(aStream: NSStream, handleEvent: NSStreamEvent) { - assertEquals("run-loop", NSThread.currentThread.name) - when (handleEvent) { - NSStreamEventOpenCompleted -> fail("opened before subscribe") - NSStreamEventHasBytesAvailable -> { - val sink = ByteArray(data.length) - sink.usePinned { - assertEquals( - data.length.convert(), - input.read(it.addressOf(0).reinterpret(), data.length.convert()) - ) - } - assertEquals(data, sink.decodeToString()) - input.close() - available.unlock() - } - - else -> fail("unexpected event ${handleEvent.asString()}") - } - } - } - input.open() - input.scheduleInRunLoop(runLoop, NSDefaultRunLoopMode) - runBlocking { - available.lockWithTimeout() - } - } - - subscribeAfterOpen(Buffer().apply { writeString("custom") }.asNSInputStream(), "custom") - CFRunLoopStop(runLoop.getCFRunLoop()) - } -} diff --git a/core/apple/test/samples/samplesApple.kt b/core/apple/test/samples/samplesApple.kt deleted file mode 100644 index 99b062ccb..000000000 --- a/core/apple/test/samples/samplesApple.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -package kotlinx.io.samples - -import kotlinx.cinterop.* -import kotlinx.io.* -import platform.Foundation.* -import kotlin.test.Test -import kotlin.test.assertContentEquals - -class KotlinxIoSamplesApple { - @Test - fun inputStreamAsSource() { - val data = ByteArray(100) { it.toByte() } - val inputStream = NSInputStream(data = data.toNSData()) - - val receivedData = inputStream.asSource().buffered().readByteArray() - assertContentEquals(data, receivedData) - } - - @Test - fun outputStreamAsSink() { - val data = ByteArray(100) { it.toByte() } - val outputStream = NSOutputStream.outputStreamToMemory() - - val sink = outputStream.asSink().buffered() - sink.write(data) - sink.flush() - - val writtenData = outputStream.propertyForKey(NSStreamDataWrittenToMemoryStreamKey) as NSData - assertContentEquals(data, writtenData.toByteArray()) - } - - @Test - @OptIn(UnsafeNumber::class, ExperimentalForeignApi::class, BetaInteropApi::class) - fun asStream() { - val buffer = Buffer() - val data = ByteArray(100) { it.toByte() }.toNSData() - - val outputStream = buffer.asNSOutputStream() - outputStream.open() - outputStream.write(data.bytes?.reinterpret(), data.length) - - val inputStream = buffer.asNSInputStream() - inputStream.open() - val readData = NSMutableData.create(length = 100.convert())!! - inputStream.read(readData.bytes?.reinterpret(), 100.convert()) - - assertContentEquals(data.toByteArray(), readData.toByteArray()) - } -} diff --git a/core/apple/test/utilApple.kt b/core/apple/test/utilApple.kt deleted file mode 100644 index eb393be31..000000000 --- a/core/apple/test/utilApple.kt +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -@file:OptIn(UnsafeNumber::class, ExperimentalForeignApi::class) - -package kotlinx.io - -import kotlinx.cinterop.* -import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.withTimeout -import platform.Foundation.* -import platform.posix.memcpy -import kotlin.test.fail -import kotlin.time.Duration -import kotlin.time.Duration.Companion.seconds - -@OptIn(BetaInteropApi::class) -internal fun ByteArray.toNSData() = if (isNotEmpty()) { - usePinned { - NSData.create(bytes = it.addressOf(0), length = size.convert()) - } -} else { - NSData.data() -} - -fun NSData.toByteArray() = ByteArray(length.toInt()).apply { - if (isNotEmpty()) { - memcpy(refTo(0), bytes, length) - } -} - -fun startRunLoop(name: String = "run-loop"): NSRunLoop { - val created = Mutex(true) - lateinit var runLoop: NSRunLoop - val thread = NSThread { - runLoop = NSRunLoop.currentRunLoop - runLoop.addPort(NSMachPort.port(), NSDefaultRunLoopMode) - created.unlock() - runLoop.run() - } - thread.name = name - thread.start() - runBlocking { - created.lockWithTimeout() - } - return runLoop -} - -suspend fun Mutex.lockWithTimeout(timeout: Duration = 5.seconds) { - class MutexSource : Throwable() - val source = MutexSource() - try { - withTimeout(timeout) { lock() } - } catch (e: TimeoutCancellationException) { - fail("Mutex never unlocked", source) - } -} - -fun NSStreamEvent.asString(): String { - return when (this) { - NSStreamEventNone -> "NSStreamEventNone" - NSStreamEventOpenCompleted -> "NSStreamEventOpenCompleted" - NSStreamEventHasBytesAvailable -> "NSStreamEventHasBytesAvailable" - NSStreamEventHasSpaceAvailable -> "NSStreamEventHasSpaceAvailable" - NSStreamEventErrorOccurred -> "NSStreamEventErrorOccurred" - NSStreamEventEndEncountered -> "NSStreamEventEndEncountered" - else -> "Unknown event $this" - } -} - -fun ByteArray.write(to: NSOutputStream): Int { - this.usePinned { - return to.write(it.addressOf(0).reinterpret(), size.convert()).convert() - } -} diff --git a/core/build.gradle.kts b/core/build.gradle.kts deleted file mode 100644 index 39ea02e77..000000000 --- a/core/build.gradle.kts +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -import org.gradle.internal.os.OperatingSystem -import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl - -plugins { - id("kotlinx-io-multiplatform") - id("kotlinx-io-publish") - id("kotlinx-io-dokka") - id("kotlinx-io-android-compat") - alias(libs.plugins.kover) -} - -kotlin { - js { - nodejs { - testTask { - useMocha { - timeout = "300s" - } - } - } - browser { - testTask { - useMocha { - timeout = "300s" - } - } - } - } - @OptIn(ExperimentalWasmDsl::class) - wasmWasi { - nodejs { - testTask { - // fd_readdir is unsupported on Windows: - // https://github.com/nodejs/node/blob/6f4d6011ea1b448cf21f5d363c44e4a4c56ca34c/deps/uvwasi/src/uvwasi.c#L19 - if (OperatingSystem.current().isWindows) { - filter.setExcludePatterns("*SmokeFileTest.listDirectory") - } - } - } - } - - sourceSets { - commonMain.dependencies { - api(project(":kotlinx-io-bytestring")) - } - appleTest.dependencies { - implementation(libs.kotlinx.coroutines.core) - } - } -} - -tasks.named("wasmWasiNodeTest") { - // TODO: remove once https://youtrack.jetbrains.com/issue/KT-65179 solved - doFirst { - val layout = project.layout - val templateFile = layout.projectDirectory.file("wasmWasi/test/test-driver.mjs.template").asFile - - val driverFile = layout.buildDirectory.file( - "compileSync/wasmWasi/test/testDevelopmentExecutable/kotlin/kotlinx-io-kotlinx-io-core-wasm-wasi-test.mjs" - ) - - fun File.mkdirsAndEscape(): String { - mkdirs() - return absolutePath.replace("\\", "\\\\") - } - - val tmpDir = temporaryDir.resolve("kotlinx-io-core-wasi-test").mkdirsAndEscape() - val tmpDir2 = temporaryDir.resolve("kotlinx-io-core-wasi-test-2").mkdirsAndEscape() - - val newDriver = templateFile.readText() - .replace("", tmpDir, false) - .replace("", tmpDir2, false) - - driverFile.get().asFile.writeText(newDriver) - } -} - -animalsniffer { - annotation = "kotlinx.io.files.AnimalSnifferIgnore" -} diff --git a/core/common/src/-CommonPlatform.kt b/core/common/src/-CommonPlatform.kt deleted file mode 100644 index a0c1e75d9..000000000 --- a/core/common/src/-CommonPlatform.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2018 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kotlinx.io - -/** - * Signals about a general issue occurred during I/O operation. - */ -public expect open class IOException : Exception { - public constructor() - public constructor(message: String?) - public constructor(cause: Throwable?) - public constructor(message: String?, cause: Throwable?) -} - -/** - * Signals that the end of the file or stream was reached unexpectedly during an input operation. - */ -public expect open class EOFException : IOException { - public constructor() - public constructor(message: String?) -} diff --git a/core/common/src/-Util.kt b/core/common/src/-Util.kt deleted file mode 100644 index 01848f02e..000000000 --- a/core/common/src/-Util.kt +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2018 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -@file:Suppress("NOTHING_TO_INLINE") - -package kotlinx.io - -internal val HEX_DIGIT_CHARS = - charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f') - -internal fun checkOffsetAndCount(size: Long, offset: Long, byteCount: Long) { - if (offset < 0 || offset > size || size - offset < byteCount || byteCount < 0) { - throw IllegalArgumentException( - "offset ($offset) and byteCount ($byteCount) are not within the range [0..size($size))" - ) - } -} - -internal inline fun checkBounds(size: Int, startIndex: Int, endIndex: Int) = - checkBounds(size.toLong(), startIndex.toLong(), endIndex.toLong()) - -internal fun checkBounds(size: Long, startIndex: Long, endIndex: Long) { - if (startIndex < 0 || endIndex > size) { - throw IndexOutOfBoundsException( - "startIndex ($startIndex) and endIndex ($endIndex) are not within the range [0..size($size))" - ) - } - if (startIndex > endIndex) { - throw IllegalArgumentException("startIndex ($startIndex) > endIndex ($endIndex)") - } -} - -internal inline fun checkByteCount(byteCount: Long) { - require(byteCount >= 0) { "byteCount ($byteCount) < 0" } -} - -internal fun Short.reverseBytes(): Short { - val i = toInt() and 0xffff - val reversed = (i and 0xff00 ushr 8) or - (i and 0x00ff shl 8) - return reversed.toShort() -} - -internal fun Int.reverseBytes(): Int { - return (this and -0x1000000 ushr 24) or - (this and 0x00ff0000 ushr 8) or - (this and 0x0000ff00 shl 8) or - (this and 0x000000ff shl 24) -} - -internal fun Long.reverseBytes(): Long { - return (this and -0x100000000000000L ushr 56) or - (this and 0x00ff000000000000L ushr 40) or - (this and 0x0000ff0000000000L ushr 24) or - (this and 0x000000ff00000000L ushr 8) or - (this and 0x00000000ff000000L shl 8) or - (this and 0x0000000000ff0000L shl 24) or - (this and 0x000000000000ff00L shl 40) or - (this and 0x00000000000000ffL shl 56) -} - -/* ktlint-enable no-multi-spaces indent */ - -internal inline infix fun Int.leftRotate(bitCount: Int): Int { - return (this shl bitCount) or (this ushr (32 - bitCount)) -} - -internal inline infix fun Long.rightRotate(bitCount: Int): Long { - return (this ushr bitCount) or (this shl (64 - bitCount)) -} - -// Syntactic sugar. -internal inline infix fun Byte.shr(other: Int): Int = toInt() shr other - -// Syntactic sugar. -internal inline infix fun Byte.shl(other: Int): Int = toInt() shl other - -// Syntactic sugar. -internal inline infix fun Byte.and(other: Int): Int = toInt() and other - -// Syntactic sugar. -internal inline infix fun Byte.and(other: Long): Long = toLong() and other - -// Pending `kotlin.experimental.xor` becoming stable -internal inline infix fun Byte.xor(other: Byte): Byte = (toInt() xor other.toInt()).toByte() - -// Syntactic sugar. -internal inline infix fun Int.and(other: Long): Long = toLong() and other - -// Syntactic sugar. -internal inline fun minOf(a: Long, b: Int): Long = minOf(a, b.toLong()) - -// Syntactic sugar. -internal inline fun minOf(a: Int, b: Long): Long = minOf(a.toLong(), b) - -internal fun Byte.toHexString(): String { - val result = CharArray(2) - result[0] = HEX_DIGIT_CHARS[this shr 4 and 0xf] - result[1] = HEX_DIGIT_CHARS[this and 0xf] - return result.concatToString() -} - -internal fun Int.toHexString(): String { - if (this == 0) return "0" // Required as code below does not handle 0 - - val result = CharArray(8) - result[0] = HEX_DIGIT_CHARS[this shr 28 and 0xf] - result[1] = HEX_DIGIT_CHARS[this shr 24 and 0xf] - result[2] = HEX_DIGIT_CHARS[this shr 20 and 0xf] - result[3] = HEX_DIGIT_CHARS[this shr 16 and 0xf] - result[4] = HEX_DIGIT_CHARS[this shr 12 and 0xf] - result[5] = HEX_DIGIT_CHARS[this shr 8 and 0xf] - result[6] = HEX_DIGIT_CHARS[this shr 4 and 0xf] - result[7] = HEX_DIGIT_CHARS[this and 0xf] - - // Find the first non-zero index - var i = 0 - while (i < result.size) { - if (result[i] != '0') break - i++ - } - - return result.concatToString(i, result.size) -} - -internal fun Long.toHexString(): String { - if (this == 0L) return "0" // Required as code below does not handle 0 - - val result = CharArray(16) - result[0] = HEX_DIGIT_CHARS[(this shr 60 and 0xf).toInt()] - result[1] = HEX_DIGIT_CHARS[(this shr 56 and 0xf).toInt()] - result[2] = HEX_DIGIT_CHARS[(this shr 52 and 0xf).toInt()] - result[3] = HEX_DIGIT_CHARS[(this shr 48 and 0xf).toInt()] - result[4] = HEX_DIGIT_CHARS[(this shr 44 and 0xf).toInt()] - result[5] = HEX_DIGIT_CHARS[(this shr 40 and 0xf).toInt()] - result[6] = HEX_DIGIT_CHARS[(this shr 36 and 0xf).toInt()] - result[7] = HEX_DIGIT_CHARS[(this shr 32 and 0xf).toInt()] - result[8] = HEX_DIGIT_CHARS[(this shr 28 and 0xf).toInt()] - result[9] = HEX_DIGIT_CHARS[(this shr 24 and 0xf).toInt()] - result[10] = HEX_DIGIT_CHARS[(this shr 20 and 0xf).toInt()] - result[11] = HEX_DIGIT_CHARS[(this shr 16 and 0xf).toInt()] - result[12] = HEX_DIGIT_CHARS[(this shr 12 and 0xf).toInt()] - result[13] = HEX_DIGIT_CHARS[(this shr 8 and 0xf).toInt()] - result[14] = HEX_DIGIT_CHARS[(this shr 4 and 0xf).toInt()] - result[15] = HEX_DIGIT_CHARS[(this and 0xf).toInt()] - - // Find the first non-zero index - var i = 0 - while (i < result.size) { - if (result[i] != '0') break - i++ - } - - return result.concatToString(i, result.size) -} - -/** - * Returns the number of characters required to encode [v] - * as a hexadecimal number without leading zeros (with `v == 0L` being the only exception, - * `hexNumberLength(0) == 1`). - */ -internal inline fun hexNumberLength(v: Long): Int { - if (v == 0L) return 1 - val exactWidth = (Long.SIZE_BITS - v.countLeadingZeroBits()) - // Round up to the nearest full nibble - return ((exactWidth + 3) / 4) -} diff --git a/core/common/src/Annotations.kt b/core/common/src/Annotations.kt deleted file mode 100644 index 82754aa1c..000000000 --- a/core/common/src/Annotations.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2010-2024 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 - -/** - * Marks declarations that should be used carefully and in some cases, may cause data corruption or loss. - * - * Consider using other APIs instead when possible. - * Otherwise, make sure to read documentation describing a delicate API. - */ -@MustBeDocumented -@Retention(AnnotationRetention.BINARY) -@RequiresOptIn( - level = RequiresOptIn.Level.WARNING, - message = "This is a delicate API and its use requires care. " + - "Make sure you fully read and understand documentation of the declaration that is marked as a delicate API." -) -public annotation class DelicateIoApi - -/** - * Marks declarations that are **internal** in IO API. - * These declarations may change or be removed without notice, and not intended for public use. - * Incorrect of declarations marked as internal may cause data corruption or loss. - * - * Consider using other APIs instead when possible. - * Otherwise, make sure to read documentation describing - * an internal API. - */ -@MustBeDocumented -@Retention(AnnotationRetention.BINARY) -@RequiresOptIn( - level = RequiresOptIn.Level.ERROR, - message = "This is an internal API and its use requires care. " + - "It is subject to change or removal and is not intended for use outside the library." + - "Make sure you fully read and understand documentation of the declaration that " + - "is marked as an internal API." -) -public annotation class InternalIoApi - -/** - * Marks API that may cause data corruption or loss or behave unpredictable when used with invalid argument values. - * - * Consider using other APIs instead when possible. - * Otherwise, make sure to read documentation describing an unsafe API. - */ -@Retention(AnnotationRetention.BINARY) -@RequiresOptIn( - level = RequiresOptIn.Level.WARNING, - message = "This is an unsafe API and its use requires care. " + - "Make sure you fully understand documentation of the declaration marked as UnsafeIoApi" -) -public annotation class UnsafeIoApi diff --git a/core/common/src/Buffer.kt b/core/common/src/Buffer.kt deleted file mode 100644 index d2c5dc967..000000000 --- a/core/common/src/Buffer.kt +++ /dev/null @@ -1,677 +0,0 @@ -/* - * Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package kotlinx.io - -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind.EXACTLY_ONCE -import kotlin.contracts.contract -import kotlinx.io.unsafe.UnsafeBufferOperations -import kotlin.jvm.JvmSynthetic - -/** - * A collection of bytes in memory. - * - * The buffer can be viewed as an unbound queue whose size grows with the data being written - * and shrinks with data being consumed. Internally, the buffer consists of data segments, - * and the buffer's capacity grows and shrinks in units of data segments instead of individual bytes. - * - * The buffer was designed to reduce memory allocations when possible. Instead of copying bytes - * from one place in memory to another, this class just changes ownership of the underlying data segments. - * - * To reduce allocations and speed up the buffer's extension, it may use data segments pooling. - * - * [Buffer] implements both [Source] and [Sink] and could be used as a source or a sink, - * but unlike regular sinks and sources its [close], [flush], [emit], [hintEmit] - * does not affect buffer's state and [exhausted] only indicates that a buffer is empty. - */ -public class Buffer : Source, Sink { - @PublishedApi - @get:JvmSynthetic - @set:JvmSynthetic - internal var head: Segment? = null - - @PublishedApi - @get:JvmSynthetic - @set:JvmSynthetic - internal var tail: Segment? = null - - /** - * The number of bytes accessible for read from this buffer. - */ - public val size: Long - get() = sizeMut - - @PublishedApi - @get:JvmSynthetic - @set:JvmSynthetic - internal var sizeMut: Long = 0L - - /** - * Returns the buffer itself. - */ - @InternalIoApi - override val buffer: Buffer get() = this - - override fun exhausted(): Boolean = size == 0L - - override fun require(byteCount: Long) { - require(byteCount >= 0) { "byteCount: $byteCount" } - if (size < byteCount) { - throw EOFException("Buffer doesn't contain required number of bytes (size: $size, required: $byteCount)") - } - } - - override fun request(byteCount: Long): Boolean { - require(byteCount >= 0) { "byteCount: $byteCount < 0" } - return size >= byteCount - } - - override fun readByte(): Byte { - val segment = head ?: throwEof(1) - val segmentSize = segment.size - if (segmentSize == 0) { - recycleHead() - return readByte() - } - val v = segment.readByte() - sizeMut -= 1L - if (segmentSize == 1) { - recycleHead() - } - return v - } - - override fun readShort(): Short { - val segment = head ?: throwEof(2) - val segmentSize = segment.size - if (segmentSize < 2) { - // If the short is split across multiple segments, delegate to readByte(). - require(2) - if (segmentSize == 0) { - recycleHead() - return readShort() - } - return (readByte() and 0xff shl 8 or (readByte() and 0xff)).toShort() - } - val v = segment.readShort() - sizeMut -= 2L - if (segmentSize == 2) { - recycleHead() - } - return v - } - - override fun readInt(): Int { - val segment = head ?: throwEof(4) - val segmentSize = segment.size - if (segmentSize < 4) { - // If the short is split across multiple segments, delegate to readShort(). - require(4) - if (segmentSize == 0) { - recycleHead() - return readInt() - } - return (readShort().toInt() shl 16 or (readShort().toInt() and 0xffff)) - } - val v = segment.readInt() - sizeMut -= 4L - if (segmentSize == 4) { - recycleHead() - } - return v - } - - override fun readLong(): Long { - val segment = head ?: throwEof(8) - val segmentSize = segment.size - if (segmentSize < 8) { - // If the short is split across multiple segments, delegate to readInt(). - require(8) - if (segmentSize == 0) { - recycleHead() - return readLong() - } - return (readInt().toLong() shl 32 or (readInt().toLong() and 0xffffffffL)) - } - val v = segment.readLong() - sizeMut -= 8L - if (segmentSize == 8) { - recycleHead() - } - return v - } - - private fun throwEof(byteCount: Long): Nothing { - throw EOFException("Buffer doesn't contain required number of bytes (size: $size, required: $byteCount)") - } - - /** - * This method does not affect the buffer's content as there is no upstream to write data to. - */ - @InternalIoApi - override fun hintEmit(): Unit = Unit - - /** - * This method does not affect the buffer's content as there is no upstream to write data to. - */ - override fun emit(): Unit = Unit - - /** - * This method does not affect the buffer's content as there is no upstream to write data to. - */ - override fun flush(): Unit = Unit - - /** - * Copies bytes from this buffer's subrange starting at [startIndex] and ending at [endIndex], to [out] buffer. - * This method does not consume data from the buffer. - * - * @param out the destination buffer to copy data into. - * @param startIndex the index (inclusive) of the first byte of data in this buffer to copy, - * 0 by default. - * @param endIndex the index (exclusive) of the last byte of data in this buffer to copy, `buffer.size` by default. - * - * @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, - startIndex: Long = 0L, - endIndex: Long = size - ) { - checkBounds(size, startIndex, endIndex) - if (startIndex == endIndex) return - - var currentOffset = startIndex - var remainingByteCount = endIndex - startIndex - - out.sizeMut += remainingByteCount - - // Skip segments that we aren't copying from. - var s = head - while (currentOffset >= s!!.limit - s.pos) { - currentOffset -= (s.limit - s.pos).toLong() - s = s.next - } - - // Copy one segment at a time. - while (remainingByteCount > 0L) { - val copy = s!!.sharedCopy() - copy.pos += currentOffset.toInt() - copy.limit = minOf(copy.pos + remainingByteCount.toInt(), copy.limit) - out.pushSegment(copy) - remainingByteCount -= (copy.limit - copy.pos).toLong() - currentOffset = 0L - s = s.next - } - } - - /** - * Returns the number of bytes in segments that are fully filled and are no longer writable. - * - * This is the number of bytes that can be flushed immediately to an underlying sink without harming throughput. - */ - internal fun completeSegmentByteCount(): Long { - var result = size - if (result == 0L) return 0L - - // Omit the tail if it's still writable. - val tail = tail!! - if (tail.limit < Segment.SIZE && tail.owner) { - result -= (tail.limit - tail.pos).toLong() - } - - return result - } - - /** - * Returns the byte at [position]. - * - * Use of this method may expose significant performance penalties and it's not recommended to use it - * 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) { - throw IndexOutOfBoundsException("position ($position) is not within the range [0..size($size))") - } - if (position == 0L) { - return head!!.getUnchecked(0) - } - seek(position) { s, offset -> - return s!!.getUnchecked((position - offset).toInt()) - } - } - - /** - * 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) - - /** - * Discards [byteCount] bytes from the head of this buffer. - * - * @throws IllegalArgumentException when [byteCount] is negative. - */ - override fun skip(byteCount: Long) { - checkByteCount(byteCount) - var remainingByteCount = byteCount - while (remainingByteCount > 0) { - val head = head ?: throw EOFException("Buffer exhausted before skipping $byteCount bytes.") - - val toSkip = minOf(remainingByteCount, head.limit - head.pos).toInt() - sizeMut -= toSkip.toLong() - remainingByteCount -= toSkip.toLong() - head.pos += toSkip - - if (head.pos == head.limit) { - recycleHead() - } - } - } - - override fun readAtMostTo(sink: ByteArray, startIndex: Int, endIndex: Int): Int { - checkBounds(sink.size, startIndex, endIndex) - - val s = this.head ?: return -1 - val toCopy = minOf(endIndex - startIndex, s.size) - s.readTo(sink, startIndex, startIndex + toCopy) - sizeMut -= toCopy.toLong() - - if (s.isEmpty()) { - recycleHead() - } - - return toCopy - } - - override fun readAtMostTo(sink: Buffer, byteCount: Long): Long { - checkByteCount(byteCount) - if (size == 0L) return -1L - val bytesWritten = if (byteCount > size) size else byteCount - sink.write(this, bytesWritten) - return bytesWritten - } - - override fun readTo(sink: RawSink, byteCount: Long) { - checkByteCount(byteCount) - if (size < byteCount) { - sink.write(this, size) // Exhaust ourselves. - throw EOFException("Buffer exhausted before writing $byteCount bytes. Only $size bytes were written.") - } - sink.write(this, byteCount) - } - - override fun transferTo(sink: RawSink): Long { - val byteCount = size - if (byteCount > 0L) { - sink.write(this, byteCount) - } - return byteCount - } - - override fun peek(): Source = PeekSource(this).buffered() - - /** - * Returns a tail segment that we can write at least `minimumCapacity` - * bytes to, creating it if necessary. - */ - @PublishedApi - @JvmSynthetic - internal fun writableSegment(minimumCapacity: Int): Segment { - require(minimumCapacity >= 1 && minimumCapacity <= Segment.SIZE) { "unexpected capacity" } - - if (tail == null) { - val result = SegmentPool.take() // Acquire a first segment. - head = result - tail = result - return result - } - - val t = tail!! - if (t.limit + minimumCapacity > Segment.SIZE || !t.owner) { - val newTail = t.push(SegmentPool.take()) // Append a new empty segment to fill up. - tail = newTail - return newTail - } - return t - } - - override fun write(source: ByteArray, startIndex: Int, endIndex: Int) { - checkBounds(source.size, startIndex, endIndex) - var currentOffset = startIndex - while (currentOffset < endIndex) { - val tail = writableSegment(1) - val toCopy = minOf(endIndex - currentOffset, tail.remainingCapacity) - tail.write(source, currentOffset, currentOffset + toCopy) - currentOffset += toCopy - } - sizeMut += endIndex - startIndex - } - - override fun write(source: RawSource, byteCount: Long) { - checkByteCount(byteCount) - var remainingByteCount = byteCount - while (remainingByteCount > 0L) { - val read = source.readAtMostTo(this, remainingByteCount) - if (read == -1L) { - throw EOFException( - "Source exhausted before reading $byteCount bytes. " + - "Only ${byteCount - remainingByteCount} were read." - ) - } - remainingByteCount -= read - } - } - - override fun write(source: Buffer, byteCount: Long) { - // Move bytes from the head of the source buffer to the tail of this buffer - // while balancing two conflicting goals: don't waste CPU and don't waste - // memory. - // - // - // Don't waste CPU (ie. don't copy data around). - // - // Copying large amounts of data is expensive. Instead, we prefer to - // reassign entire segments from one buffer to the other. - // - // - // Don't waste memory. - // - // As an invariant, adjacent pairs of segments in a buffer should be at - // least 50% full, except for the head segment and the tail segment. - // - // The head segment cannot maintain the invariant because the application is - // consuming bytes from this segment, decreasing its level. - // - // The tail segment cannot maintain the invariant because the application is - // producing bytes, which may require new nearly-empty tail segments to be - // appended. - // - // - // Moving segments between buffers - // - // When writing one buffer to another, we prefer to reassign entire segments - // over copying bytes into their most compact form. Suppose we have a buffer - // with these segment levels [91%, 61%]. If we append a buffer with a - // single [72%] segment, that yields [91%, 61%, 72%]. No bytes are copied. - // - // Or suppose we have a buffer with these segment levels: [100%, 2%], and we - // want to append it to a buffer with these segment levels [99%, 3%]. This - // operation will yield the following segments: [100%, 2%, 99%, 3%]. That - // is, we do not spend time copying bytes around to achieve more efficient - // memory use like [100%, 100%, 4%]. - // - // When combining buffers, we will compact adjacent buffers when their - // combined level doesn't exceed 100%. For example, when we start with - // [100%, 40%] and append [30%, 80%], the result is [100%, 70%, 80%]. - // - // - // Splitting segments - // - // Occasionally we write only part of a source buffer to a sink buffer. For - // example, given a sink [51%, 91%], we may want to write the first 30% of - // a source [92%, 82%] to it. To simplify, we first transform the source to - // an equivalent buffer [30%, 62%, 82%] and then move the head segment, - // yielding sink [51%, 91%, 30%] and source [62%, 82%]. - - require(source !== this) { "source == this" } - checkOffsetAndCount(source.sizeMut, 0, byteCount) - - var remainingByteCount = byteCount - - while (remainingByteCount > 0L) { - // Is a prefix of the source's head segment all that we need to move? - if (remainingByteCount < source.head!!.size) { - val tail = tail - if (tail != null && tail.owner && - remainingByteCount + tail.limit - (if (tail.shared) 0 else tail.pos) <= Segment.SIZE - ) { - // Our existing segments are sufficient. Move bytes from source's head to our tail. - source.head!!.writeTo(tail, remainingByteCount.toInt()) - source.sizeMut -= remainingByteCount - sizeMut += remainingByteCount - return - } else { - // We're going to need another segment. Split the source's head - // segment in two, then move the first of those two to this buffer. - source.head = source.head!!.split(remainingByteCount.toInt()) - } - } - - // Remove the source's head segment and append it to our tail. - val segmentToMove = source.head!! - val movedByteCount = segmentToMove.size.toLong() - source.head = segmentToMove.pop() - if (source.head == null) { - source.tail = null - } - pushSegment(segmentToMove, true) - source.sizeMut -= movedByteCount - sizeMut += movedByteCount - remainingByteCount -= movedByteCount - } - } - - override fun transferFrom(source: RawSource): Long { - var totalBytesRead = 0L - while (true) { - val readCount = source.readAtMostTo(this, Segment.SIZE.toLong()) - if (readCount == -1L) break - totalBytesRead += readCount - } - return totalBytesRead - } - - override fun writeByte(byte: Byte) { - writableSegment(1).writeByte(byte) - sizeMut += 1L - } - - override fun writeShort(short: Short) { - writableSegment(2).writeShort(short) - sizeMut += 2L - } - - override fun writeInt(int: Int) { - writableSegment(4).writeInt(int) - sizeMut += 4L - } - - override fun writeLong(long: Long) { - writableSegment(8).writeLong(long) - sizeMut += 8L - } - - /** - * Returns a deep copy of this buffer. - */ - public fun copy(): Buffer { - val result = Buffer() - if (size == 0L) return result - - val head = this.head!! - val headCopy = head.sharedCopy() - - result.head = headCopy - result.tail = headCopy - - var s = head.next - while (s != null) { - result.tail = result.tail!!.push(s.sharedCopy()) - s = s.next - } - - result.sizeMut = size - return result - } - - /** - * This method does not affect the buffer. - */ - override fun close(): Unit = Unit - - /** - * Returns a human-readable string that describes the contents of this buffer. For buffers containing - * 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 - */ - @OptIn(UnsafeIoApi::class) - override fun toString(): String { - if (size == 0L) return "Buffer(size=0)" - - val maxPrintableBytes = 64 - val len = minOf(maxPrintableBytes, size).toInt() - - val builder = StringBuilder(len * 2 + if (size > maxPrintableBytes) 1 else 0) - var bytesWritten = 0 - UnsafeBufferOperations.forEachSegment(this) { ctx, segment -> - var idx = 0 - while (bytesWritten < len && idx < segment.size) { - val b = ctx.getUnchecked(segment, idx++) - bytesWritten++ - builder - .append(HEX_DIGIT_CHARS[(b shr 4) and 0xf]) - .append(HEX_DIGIT_CHARS[b and 0xf]) - } - } - - if (size > maxPrintableBytes) { - builder.append('…') - } - - return "Buffer(size=$size hex=$builder)" - } - - /** - * Unlinks and recycles this buffer's head. - * - * If head had a successor, it'll become a new head. - * Otherwise, both [head] and [tail] will be set to null. - * - * It's up to a caller to ensure that the head exists. - */ - internal fun recycleHead() { - val oldHead = head!! - val nextHead = oldHead.next - head = nextHead - if (nextHead == null) { - tail = null - } else { - nextHead.prev = null - } - oldHead.next = null - SegmentPool.recycle(oldHead) - } - - /** - * Unlinks and recycles this buffer's tail segment. - * - * If tail had a predecessor, it'll become a new tail. - * Otherwise, both [head] and [tail] will be set to null. - * - * It's up to a caller to ensure that the tail exists. - */ - @PublishedApi - @JvmSynthetic - internal fun recycleTail() { - val oldTail = tail!! - val newTail = oldTail.prev - tail = newTail - if (newTail == null) { - head = null - } else { - newTail.next = null - } - oldTail.prev = null - SegmentPool.recycle(oldTail) - } - - @Suppress("NOTHING_TO_INLINE") - private inline fun pushSegment(newTail: Segment, tryCompact: Boolean = false) { - if (head == null) { - head = newTail - tail = newTail - } else if (tryCompact) { - tail = tail!!.push(newTail).compact() - if (tail!!.prev == null) { - head = tail - } - } else { - tail = tail!!.push(newTail) - } - } -} - -/** - * Invoke `lambda` with the segment and offset at `fromIndex`. Searches from the front or the back - * depending on what's closer to `fromIndex`. - */ -@PublishedApi -@JvmSynthetic -@OptIn(ExperimentalContracts::class) -internal inline fun Buffer.seek( - fromIndex: Long, - lambda: (Segment?, Long) -> T -): T { - contract { - callsInPlace(lambda, EXACTLY_ONCE) - } - - if (this.head == null) { - return lambda(null, -1L) - } - - if (size - fromIndex < fromIndex) { - var s = tail - // We're scanning in the back half of this buffer. Find the segment starting at the back. - var offset = size - while (s != null && offset > fromIndex) { - offset -= (s.limit - s.pos).toLong() - if (offset <= fromIndex) break - s = s.prev - } - return lambda(s, offset) - } else { - var s = this.head - // We're scanning in the front half of this buffer. Find the segment starting at the front. - var offset = 0L - while (s != null) { - val nextOffset = offset + (s.limit - s.pos) - if (nextOffset > fromIndex) break - s = s.next - offset = nextOffset - } - return lambda(s, offset) - } -} diff --git a/core/common/src/Buffers.kt b/core/common/src/Buffers.kt deleted file mode 100644 index 479e04cfb..000000000 --- a/core/common/src/Buffers.kt +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io - -import kotlinx.io.bytestring.ByteString -import kotlinx.io.bytestring.buildByteString -import kotlinx.io.unsafe.UnsafeBufferOperations -import kotlinx.io.unsafe.withData - -/** - * Creates a byte string containing a copy of all the data from this buffer. - * - * This call doesn't consume data from the buffer, but instead copies it. - */ -@OptIn(UnsafeIoApi::class) -public fun Buffer.snapshot(): ByteString { - if (size == 0L) return ByteString() - - check(size <= Int.MAX_VALUE) { "Buffer is too long ($size) to be converted into a byte string." } - - return buildByteString(size.toInt()) { - UnsafeBufferOperations.forEachSegment(this@snapshot) { ctx, segment -> - ctx.withData(segment, this::append) - } - } -} - -/** - * Returns an index of [byte] first occurrence in the range of [startIndex] to [endIndex], - * or `-1` when the range doesn't contain [byte]. - * - * The scan terminates at either [endIndex] or buffers' exhaustion, whichever comes first. - * - * @param byte the value to find. - * @param startIndex the start of the range (inclusive) to find [byte], `0` by default. - * @param endIndex the end of the range (exclusive) to find [byte], [Buffer.size] by default. - * - * @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 Buffer.indexOf(byte: Byte, startIndex: Long = 0, endIndex: Long = size): Long { - // For compatibility with Source.indexOf accept endIndices greater than size and truncate them. - val endOffset = minOf(endIndex, size) - checkBounds(size, startIndex, endOffset) - if (startIndex == endOffset) return -1L - - seek(startIndex) { seg, o -> - if (o == -1L) { - return -1L - } - var segment: Segment? = seg - var offset = o - do { - check(endOffset > offset) - segment!! - val idx = segment.indexOf( - byte, - // If start index within this segment, the diff will be positive and - // we'll scan the segment starting from the corresponding offset. - // Otherwise, the diff will be negative and we'll scan the segment from the beginning. - maxOf((startIndex - offset).toInt(), 0), - // If endOffset is within this segment - scan until it, otherwise - scan whole segment. - minOf(segment.size, (endOffset - offset).toInt()) - ) - if (idx != -1) { - // offset corresponds to the segment's start, idx - to offset within the segment. - return offset + idx.toLong() - } - offset += segment.size - segment = segment.next - } while (segment != null && offset < endOffset) - return -1L - } -} diff --git a/core/common/src/ByteStrings.kt b/core/common/src/ByteStrings.kt deleted file mode 100644 index 8dd269c23..000000000 --- a/core/common/src/ByteStrings.kt +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io - -import kotlinx.io.bytestring.ByteString -import kotlinx.io.bytestring.isEmpty -import kotlinx.io.bytestring.unsafe.UnsafeByteStringApi -import kotlinx.io.bytestring.unsafe.UnsafeByteStringOperations -import kotlinx.io.unsafe.UnsafeBufferOperations -import kotlin.math.min - -/** - * Writes subsequence of data from [byteString] starting at [startIndex] and ending at [endIndex] into a sink. - * - * @param byteString the byte string whose subsequence should be written to a sink. - * @param startIndex the first index (inclusive) to copy data from the [byteString]. - * @param endIndex the last index (exclusive) to copy data from the [byteString] - * - * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [byteString] indices. - * @throws IllegalArgumentException when `startIndex > endIndex`. - * @throws IllegalStateException if the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.ByteStringSamples.writeByteString - */ -@OptIn(DelicateIoApi::class, UnsafeByteStringApi::class, UnsafeIoApi::class) -public fun Sink.write(byteString: ByteString, startIndex: Int = 0, endIndex: Int = byteString.size) { - checkBounds(byteString.size, startIndex, endIndex) - if (endIndex == startIndex) { - return - } - - writeToInternalBuffer { buffer -> - var offset = startIndex - - UnsafeByteStringOperations.withByteArrayUnsafe(byteString) { data -> - while (offset < endIndex) { - var written = 0 - UnsafeBufferOperations.writeToTail(buffer, 1) { segData, pos, limit -> - written = min(endIndex - offset, limit - pos) - data.copyInto(segData, pos, offset, offset + written) - written - } - offset += written - } - } - } -} - -/** - * Consumes all bytes from this source and wraps it into a byte string. - * - * @throws IllegalStateException if the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.ByteStringSamples.readByteString - */ -@OptIn(UnsafeByteStringApi::class) -public fun Source.readByteString(): ByteString { - return UnsafeByteStringOperations.wrapUnsafe(readByteArray()) -} - -/** - * Consumes exactly [byteCount] bytes from this source and wraps it into a byte string. - * - * @param byteCount the number of bytes to read from the source. - * - * @throws EOFException when the source is exhausted before reading [byteCount] bytes from it. - * @throws IllegalArgumentException when [byteCount] is negative. - * @throws IllegalStateException if the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.ByteStringSamples.readByteString - */ -@OptIn(UnsafeByteStringApi::class) -public fun Source.readByteString(byteCount: Int): ByteString { - return UnsafeByteStringOperations.wrapUnsafe(readByteArray(byteCount)) -} - -/** - * Returns the index of the first match for [byteString] in the source at or after [startIndex]. This - * expands the source's buffer as necessary until [byteString] is found. This reads an unbounded number of - * bytes into the buffer. Returns `-1` if the stream is exhausted before the requested bytes are found. - * - * @param byteString the sequence of bytes to find within the source. - * @param startIndex the index into the source to start searching from. - * - * @throws IllegalArgumentException if [startIndex] is negative. - * @throws IllegalStateException if the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.ByteStringSamples.indexOfByteString - */ -@OptIn(InternalIoApi::class, UnsafeByteStringApi::class) -public fun Source.indexOf(byteString: ByteString, startIndex: Long = 0): Long { - require(startIndex >= 0) { "startIndex: $startIndex" } - - if (byteString.isEmpty()) { - return 0 - } - - var offset = startIndex - while (request(offset + byteString.size)) { - val idx = buffer.indexOf(byteString, offset) - if (idx < 0) { - // The buffer does not contain the pattern, let's try fetching at least one extra byte - // and start a new search attempt so that the pattern would fit in the suffix of - // the current buffer + 1 extra byte. - offset = buffer.size - byteString.size + 1 - } else { - return idx - } - } - return -1 -} - -@OptIn(UnsafeByteStringApi::class) -public fun Buffer.indexOf(byteString: ByteString, startIndex: Long = 0): Long { - require(startIndex <= size) { - "startIndex ($startIndex) should not exceed size ($size)" - } - if (byteString.isEmpty()) return 0 - if (startIndex > size - byteString.size) return -1L - - UnsafeByteStringOperations.withByteArrayUnsafe(byteString) { byteStringData -> - seek(startIndex) { seg, o -> - if (o == -1L) { - return -1L - } - var segment: Segment? = seg - var offset = o - do { - segment!! - // If start index within this segment, the diff will be positive and - // we'll scan the segment starting from the corresponding offset. - // Otherwise, the diff will be negative and we'll scan the segment from the beginning. - val startOffset = maxOf((startIndex - offset).toInt(), 0) - // Try to search the pattern within the current segment. - val idx = segment.indexOfBytesInbound(byteStringData, startOffset) - if (idx != -1) { - // The offset corresponds to the segment's start, idx - to offset within the segment. - return offset + idx.toLong() - } - // firstOutboundOffset corresponds to a first byte starting reading the pattern from which - // will result in running out of the current segment bounds. - val firstOutboundOffset = maxOf(startOffset, segment.size - byteStringData.size + 1) - // Try to find a pattern in all suffixes shorter than the pattern. These suffixes start - // in the current segment, but ends in the following segments; thus we're using outbound function. - val idx1 = segment.indexOfBytesOutbound(byteStringData, firstOutboundOffset) - if (idx1 != -1) { - // Offset corresponds to the segment's start, idx - to offset within the segment. - return offset + idx1.toLong() - } - - // We scanned the whole segment, so let's go to the next one - offset += segment.size - segment = segment.next - } while (segment != null && offset + byteString.size <= size) - return -1L - } - } - return -1 -} diff --git a/core/common/src/Core.kt b/core/common/src/Core.kt deleted file mode 100644 index be72be491..000000000 --- a/core/common/src/Core.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package kotlinx.io - -/** - * Returns a new source that buffers reads from the source. The returned source will perform bulk - * reads into its in-memory buffer. Use this wherever you read a source to get ergonomic and - * efficient access to data. - */ -public fun RawSource.buffered(): Source = RealSource(this) - -/** - * Returns a new sink that buffers writes to the sink. The returned sink will batch writes to the sink. - * Use this wherever you write to a sink to get ergonomic and efficient access to data. - */ -public fun RawSink.buffered(): Sink = RealSink(this) - -/** - * Returns a sink that discards all data written to it. - */ -public fun discardingSink(): RawSink = DiscardingSink() - -private class DiscardingSink : RawSink { - override fun write(source: Buffer, byteCount: Long) = source.skip(byteCount) - override fun flush() {} - override fun close() {} -} diff --git a/core/common/src/PeekSource.kt b/core/common/src/PeekSource.kt deleted file mode 100644 index f879cf9be..000000000 --- a/core/common/src/PeekSource.kt +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2018 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kotlinx.io - -/** - * A [RawSource] which peeks into an upstream [Source] and allows reading and expanding of the - * buffered data without consuming it. Does this by requesting additional data from the upstream - * source if needed and copying out of the internal buffer of the upstream source if possible. - * - * This source also maintains a snapshot of the starting location of the upstream buffer which it - * validates against on every read. If the upstream buffer is read from, this source will become - * invalid and throw [IllegalStateException] on any future reads. - */ -internal class PeekSource( - private val upstream: Source -) : RawSource { - @OptIn(InternalIoApi::class) - private val buffer = upstream.buffer - private var expectedSegment = buffer.head - private var expectedPos = buffer.head?.pos ?: -1 - - private var closed = false - private var pos = 0L - - override fun readAtMostTo(sink: Buffer, byteCount: Long): Long { - check(!closed) { "Source is closed." } - checkByteCount(byteCount) - // Source becomes invalid if there is an expected Segment and it and the expected position - // do not match the current head and head position of the upstream buffer - check( - expectedSegment == null || - expectedSegment === buffer.head && expectedPos == buffer.head!!.pos - ) { - "Peek source is invalid because upstream source was used" - } - if (byteCount == 0L) return 0L - if (!upstream.request(pos + 1)) return -1L - - if (expectedSegment == null && buffer.head != null) { - // Only once the buffer actually holds data should an expected Segment and position be - // recorded. This allows reads from the peek source to repeatedly return -1 and for data to be - // added later. Unit tests depend on this behavior. - expectedSegment = buffer.head - expectedPos = buffer.head!!.pos - } - - val toCopy = minOf(byteCount, buffer.size - pos) - buffer.copyTo(sink, pos, pos + toCopy) - pos += toCopy - return toCopy - } - - override fun close() { - closed = true - } -} diff --git a/core/common/src/RawSink.kt b/core/common/src/RawSink.kt deleted file mode 100644 index 473d0cf05..000000000 --- a/core/common/src/RawSink.kt +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package kotlinx.io - -/** - * Receives a stream of bytes. RawSink is a base interface for `kotlinx-io` data receivers. - * - * This interface should be implemented to write data wherever it's needed: to the network, storage, - * or a buffer in memory. Sinks may be layered to transform received data, such as to compress, encrypt, throttle, - * or add protocol framing. - * - * Most application code shouldn't operate on a raw sink directly, but rather on a buffered [Sink] which - * 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.Crc32Sample.crc32 - */ -public expect interface RawSink : AutoCloseable { - /** - * Removes [byteCount] bytes from [source] and appends them to this sink. - * - * @param source the source to read data from. - * @param byteCount the number of bytes to write. - * - * @throws IllegalArgumentException when the [source]'s size is below [byteCount] or [byteCount] is negative. - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - */ - public fun write(source: Buffer, byteCount: Long) - - /** - * Pushes all buffered bytes to their final destination. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - */ - public fun flush() - - /** - * Pushes all buffered bytes to their final destination and releases the resources held by this - * sink. It is an error to write a closed sink. It is safe to close a sink more than once. - * - * @throws IOException when some I/O error occurs. - */ - override fun close() -} diff --git a/core/common/src/RawSource.kt b/core/common/src/RawSource.kt deleted file mode 100644 index 1d16c58ca..000000000 --- a/core/common/src/RawSource.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package kotlinx.io - -/** - * Supplies a stream of bytes. RawSource is a base interface for `kotlinx-io` data suppliers. - * - * The interface should be implemented to read data from wherever it's located: from the network, storage, - * or a buffer in memory. Sources may be layered to transform supplied data, such as to decompress, decrypt, - * or remove protocol framing. - * - * Most applications shouldn't operate on a raw source directly, but rather on a buffered [Source] which - * 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.RC4SourceSample.rc4 - */ -public interface RawSource : AutoCloseable { - /** - * Removes at least 1, and up to [byteCount] bytes from this source and appends them to [sink]. - * Returns the number of bytes read, or -1 if this source is exhausted. - * - * @param sink the destination to write the data from this source. - * @param byteCount the number of bytes to read. - * - * @throws IllegalArgumentException when [byteCount] is negative. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readAtMostToSink - */ - public fun readAtMostTo(sink: Buffer, byteCount: Long): Long - - /** - * Closes this source and releases the resources held by this source. It is an error to read a - * closed source. It is safe to close a source more than once. - * - * @throws IOException when some I/O error occurs. - */ - override fun close() -} diff --git a/core/common/src/RealSink.kt b/core/common/src/RealSink.kt deleted file mode 100644 index d19b049e9..000000000 --- a/core/common/src/RealSink.kt +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kotlinx.io - -import kotlin.jvm.JvmField - -@OptIn(InternalIoApi::class) -internal class RealSink( - val sink: RawSink -) : Sink { - @JvmField - var closed: Boolean = false - private val bufferField = Buffer() - - @DelicateIoApi - override val buffer: Buffer - get() = bufferField - - override fun write(source: Buffer, byteCount: Long) { - checkNotClosed() - require(byteCount >= 0) { "byteCount: $byteCount" } - bufferField.write(source, byteCount) - hintEmit() - } - - override fun write(source: ByteArray, startIndex: Int, endIndex: Int) { - checkNotClosed() - checkBounds(source.size, startIndex, endIndex) - bufferField.write(source, startIndex, endIndex) - hintEmit() - } - - override fun transferFrom(source: RawSource): Long { - checkNotClosed() - var totalBytesRead = 0L - while (true) { - val readCount: Long = source.readAtMostTo(bufferField, Segment.SIZE.toLong()) - if (readCount == -1L) break - totalBytesRead += readCount - hintEmit() - } - return totalBytesRead - } - - override fun write(source: RawSource, byteCount: Long) { - checkNotClosed() - require(byteCount >= 0) { "byteCount: $byteCount" } - var remainingByteCount = byteCount - while (remainingByteCount > 0L) { - val read = source.readAtMostTo(bufferField, remainingByteCount) - if (read == -1L) { - val bytesRead = byteCount - remainingByteCount - throw EOFException( - "Source exhausted before reading $byteCount bytes from it (number of bytes read: $bytesRead)." - ) - } - remainingByteCount -= read - hintEmit() - } - } - - override fun writeByte(byte: Byte) { - checkNotClosed() - bufferField.writeByte(byte) - hintEmit() - } - - override fun writeShort(short: Short) { - checkNotClosed() - bufferField.writeShort(short) - hintEmit() - } - - override fun writeInt(int: Int) { - checkNotClosed() - bufferField.writeInt(int) - hintEmit() - } - - override fun writeLong(long: Long) { - checkNotClosed() - bufferField.writeLong(long) - hintEmit() - } - - @InternalIoApi - override fun hintEmit() { - checkNotClosed() - val byteCount = bufferField.completeSegmentByteCount() - if (byteCount > 0L) sink.write(bufferField, byteCount) - } - - override fun emit() { - checkNotClosed() - val byteCount = bufferField.size - if (byteCount > 0L) sink.write(bufferField, byteCount) - } - - override fun flush() { - checkNotClosed() - if (bufferField.size > 0L) { - sink.write(bufferField, bufferField.size) - } - sink.flush() - } - - override fun close() { - if (closed) return - - // Emit buffered data to the underlying sink. If this fails, we still need - // to close the sink; otherwise we risk leaking resources. - var thrown: Throwable? = null - try { - if (bufferField.size > 0) { - sink.write(bufferField, bufferField.size) - } - } catch (e: Throwable) { - thrown = e - } - - try { - sink.close() - } catch (e: Throwable) { - if (thrown == null) thrown = e - } - - closed = true - - if (thrown != null) throw thrown - } - - override fun toString() = "buffered($sink)" - - @Suppress("NOTHING_TO_INLINE") - private inline fun checkNotClosed() { - check(!closed) { "Sink is closed." } - } -} diff --git a/core/common/src/RealSource.kt b/core/common/src/RealSource.kt deleted file mode 100644 index df36e5bc8..000000000 --- a/core/common/src/RealSource.kt +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kotlinx.io - -import kotlin.jvm.JvmField - -internal class RealSource( - val source: RawSource -) : Source { - @JvmField - var closed: Boolean = false - private val bufferField = Buffer() - - @InternalIoApi - override val buffer: Buffer - get() = bufferField - - override fun readAtMostTo(sink: Buffer, byteCount: Long): Long { - checkNotClosed() - require(byteCount >= 0L) { "byteCount: $byteCount" } - - if (bufferField.size == 0L) { - val read = source.readAtMostTo(bufferField, Segment.SIZE.toLong()) - if (read == -1L) return -1L - } - - val toRead = minOf(byteCount, bufferField.size) - return bufferField.readAtMostTo(sink, toRead) - } - - override fun exhausted(): Boolean { - checkNotClosed() - return bufferField.exhausted() && source.readAtMostTo(bufferField, Segment.SIZE.toLong()) == -1L - } - - override fun require(byteCount: Long) { - if (!request(byteCount)) throw EOFException("Source doesn't contain required number of bytes ($byteCount).") - } - - override fun request(byteCount: Long): Boolean { - checkNotClosed() - require(byteCount >= 0L) { "byteCount: $byteCount" } - while (bufferField.size < byteCount) { - if (source.readAtMostTo(bufferField, Segment.SIZE.toLong()) == -1L) return false - } - return true - } - - override fun readByte(): Byte { - require(1) - return bufferField.readByte() - } - - override fun readAtMostTo(sink: ByteArray, startIndex: Int, endIndex: Int): Int { - checkBounds(sink.size, startIndex, endIndex) - - if (bufferField.size == 0L) { - val read = source.readAtMostTo(bufferField, Segment.SIZE.toLong()) - if (read == -1L) return -1 - } - val toRead = minOf(endIndex - startIndex, bufferField.size).toInt() - return bufferField.readAtMostTo(sink, startIndex, startIndex + toRead) - } - - override fun readTo(sink: RawSink, byteCount: Long) { - try { - require(byteCount) - } catch (e: EOFException) { - // The underlying source is exhausted. Copy the bytes we got before rethrowing. - sink.write(bufferField, bufferField.size) - throw e - } - bufferField.readTo(sink, byteCount) - } - - override fun transferTo(sink: RawSink): Long { - var totalBytesWritten: Long = 0 - while (source.readAtMostTo(bufferField, Segment.SIZE.toLong()) != -1L) { - val emitByteCount = bufferField.completeSegmentByteCount() - if (emitByteCount > 0L) { - totalBytesWritten += emitByteCount - sink.write(bufferField, emitByteCount) - } - } - if (bufferField.size > 0L) { - totalBytesWritten += bufferField.size - sink.write(bufferField, bufferField.size) - } - return totalBytesWritten - } - - override fun readShort(): Short { - require(2) - return bufferField.readShort() - } - - override fun readInt(): Int { - require(4) - return bufferField.readInt() - } - - override fun readLong(): Long { - require(8) - return bufferField.readLong() - } - - override fun skip(byteCount: Long) { - checkNotClosed() - require(byteCount >= 0) { "byteCount: $byteCount" } - var remainingByteCount = byteCount - while (remainingByteCount > 0) { - if (bufferField.size == 0L && source.readAtMostTo(bufferField, Segment.SIZE.toLong()) == -1L) { - throw EOFException( - "Source exhausted before skipping $byteCount bytes " + - "(only ${remainingByteCount - byteCount} bytes were skipped)." - ) - } - val toSkip = minOf(remainingByteCount, bufferField.size) - bufferField.skip(toSkip) - remainingByteCount -= toSkip - } - } - - override fun peek(): Source { - checkNotClosed() - return PeekSource(this).buffered() - } - - override fun close() { - if (closed) return - closed = true - source.close() - bufferField.clear() - } - - override fun toString(): String = "buffered($source)" - - @Suppress("NOTHING_TO_INLINE") - private inline fun checkNotClosed() { - check(!closed) { "Source is closed." } - } -} diff --git a/core/common/src/Segment.kt b/core/common/src/Segment.kt deleted file mode 100644 index affaafd48..000000000 --- a/core/common/src/Segment.kt +++ /dev/null @@ -1,528 +0,0 @@ -/* - * Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2014 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package kotlinx.io - -import kotlin.jvm.JvmField -import kotlin.jvm.JvmSynthetic - -/** - * Tracks shared segment copies. - * - * A new [SegmentCopyTracker] instance should be not shared by default (i.e. `shared == false`). - * Any further [addCopy] calls should move the tracker to a shared state (i.e. `shared == true`). - * Once a shared segment copy is recycled, [removeCopy] should be called. - * Depending on implementation, calling [removeCopy] the same number of times as [addCopy] may - * or may not transition the tracked back to unshared stated. - * - * The class is not intended for public use and currently designed to fit the only use case - within JVM SegmentPool - * implementation. - */ -internal abstract class SegmentCopyTracker { - /** - * `true` if a tracker shared by multiple segment copies. - */ - abstract val shared: Boolean - - /** - * Track a new copy created by sharing an associated segment. - */ - abstract fun addCopy() - - /** - * Records reclamation of a shared segment copy associated with this tracker. - * If a tracker was in unshared state, this call should not affect an internal state. - * - * @return `true` if the segment was not shared *before* this called. - */ - abstract fun removeCopy(): Boolean -} - -/** - * Simple [SegmentCopyTracker] that always reports shared state. - */ -internal object AlwaysSharedCopyTracker : SegmentCopyTracker() { - override val shared: Boolean get() = true - override fun addCopy() = Unit - override fun removeCopy(): Boolean = true -} - -/** - * A segment of a buffer. - * - * Each segment in a buffer is a doubly-linked list node referencing the following and - * preceding segments in the buffer. - * - * Each segment in the pool is a singly-linked list node referencing the rest of segments in the pool. - * - * The underlying byte arrays of segments may be shared between buffers and byte strings. When a - * segment's byte array is shared the segment may not be recycled, nor may its byte data be changed. - * The lone exception is that the owner segment is allowed to append to the segment, writing data at - * `limit` and beyond. There is a single owning segment for each byte array. Positions, - * limits, prev, and next references are not shared. - */ -public class Segment { - private val data: ByteArray - - /** The next byte of application data byte to read in this segment. */ - @PublishedApi - @get:JvmSynthetic - @set:JvmSynthetic - internal var pos: Int = 0 - - /** - * The first byte of available data ready to be written to. - * - * If the segment is free and linked in the segment pool, the field contains total - * byte count of this and next segments. - */ - @PublishedApi - @get:JvmSynthetic - @set:JvmSynthetic - internal var limit: Int = 0 - - /** True if other segments or byte strings use the same byte array. */ - internal val shared: Boolean - get() = copyTracker?.shared ?: false - - /** - * Tracks number shared copies - * - * Note that this reference is not `@Volatile` as segments are not thread-safe and it's an error - * to modify the same segment concurrently. - * At the same time, an object [copyTracker] refers to could be modified concurrently. - */ - internal var copyTracker: SegmentCopyTracker? = null - - /** True if this segment owns the byte array and can append to it, extending `limit`. */ - @JvmField - internal var owner: Boolean = false - - /** Next segment in a list, or null. */ - @PublishedApi - @get:JvmSynthetic - @set:JvmSynthetic - internal var next: Segment? = null - - /** Previous segment in the list, or null. */ - @PublishedApi - @get:JvmSynthetic - @set:JvmSynthetic - internal var prev: Segment? = null - - private constructor() { - this.data = ByteArray(SIZE) - this.owner = true - this.copyTracker = null - } - - private constructor(data: ByteArray, pos: Int, limit: Int, shareToken: SegmentCopyTracker?, owner: Boolean) { - this.data = data - this.pos = pos - this.limit = limit - this.copyTracker = shareToken - this.owner = owner - } - - /** - * Returns a new segment that shares the underlying byte array with this. Adjusting pos and limit - * are safe but writes are forbidden. This also marks the current segment as shared, which - * prevents it from being pooled. - */ - internal fun sharedCopy(): Segment { - val t = copyTracker ?: SegmentPool.tracker().also { - copyTracker = it - } - return Segment(data, pos, limit, t.also { it.addCopy() }, false) - } - - /** - * Removes this segment of a list and returns its successor. - * Returns null if the list is now empty. - */ - internal fun pop(): Segment? { - val result = this.next - if (this.prev != null) { - this.prev!!.next = this.next - } - if (this.next != null) { - this.next!!.prev = this.prev - } - this.next = null - this.prev = null - return result - } - - /** - * Appends `segment` after this segment in the list. Returns the pushed segment. - */ - internal fun push(segment: Segment): Segment { - segment.prev = this - segment.next = this.next - if (this.next != null) { - this.next!!.prev = segment - } - this.next = segment - return segment - } - - /** - * Splits this head of a list into two segments. The first segment contains the - * data in `[pos..pos+byteCount)`. The second segment contains the data in - * `[pos+byteCount..limit)`. This can be useful when moving partial segments from one buffer to - * another. - * - * Returns the new head of the list. - */ - internal fun split(byteCount: Int): Segment { - require(byteCount > 0 && byteCount <= limit - pos) { "byteCount out of range" } - val prefix: Segment - - // We have two competing performance goals: - // - Avoid copying data. We accomplish this by sharing segments. - // - Avoid short shared segments. These are bad for performance because they are readonly and - // may lead to long chains of short segments. - // To balance these goals we only share segments when the copy will be large. - if (byteCount >= SHARE_MINIMUM) { - prefix = sharedCopy() - } else { - prefix = SegmentPool.take() - data.copyInto(prefix.data, startIndex = pos, endIndex = pos + byteCount) - } - - prefix.limit = prefix.pos + byteCount - pos += byteCount - if (this.prev != null) { - this.prev!!.push(prefix) - } else { - prefix.next = this - this.prev = prefix - } - return prefix - } - - /** - * Call this when the tail and its predecessor may both be less than half full. This will copy - * data so that segments can be recycled. - */ - internal fun compact(): Segment { - check(this.prev != null) { "cannot compact" } - if (!this.prev!!.owner) return this // Cannot compact: prev isn't writable. - val byteCount = limit - pos - val availableByteCount = SIZE - this.prev!!.limit + if (this.prev!!.shared) 0 else this.prev!!.pos - if (byteCount > availableByteCount) return this // Cannot compact: not enough writable space. - val predecessor = this.prev - writeTo(predecessor!!, byteCount) - val successor = pop() - check(successor == null) - SegmentPool.recycle(this) - return predecessor - } - - internal fun writeByte(byte: Byte) { - data[limit++] = byte - } - - internal fun writeShort(short: Short) { - val data = data - var limit = limit - data[limit++] = (short.toInt() ushr 8 and 0xff).toByte() - data[limit++] = (short.toInt() and 0xff).toByte() - this.limit = limit - } - - internal fun writeInt(int: Int) { - val data = data - var limit = limit - data[limit++] = (int ushr 24 and 0xff).toByte() - data[limit++] = (int ushr 16 and 0xff).toByte() - data[limit++] = (int ushr 8 and 0xff).toByte() - data[limit++] = (int and 0xff).toByte() - this.limit = limit - } - - internal fun writeLong(long: Long) { - val data = data - var limit = limit - data[limit++] = (long ushr 56 and 0xffL).toByte() - data[limit++] = (long ushr 48 and 0xffL).toByte() - data[limit++] = (long ushr 40 and 0xffL).toByte() - data[limit++] = (long ushr 32 and 0xffL).toByte() - data[limit++] = (long ushr 24 and 0xffL).toByte() - data[limit++] = (long ushr 16 and 0xffL).toByte() - data[limit++] = (long ushr 8 and 0xffL).toByte() - data[limit++] = (long and 0xffL).toByte() - this.limit = limit - } - - internal fun readByte(): Byte { - return data[pos++] - } - - internal fun readShort(): Short { - val data = data - var pos = pos - val s = (data[pos++] and 0xff shl 8 or (data[pos++] and 0xff)).toShort() - this.pos = pos - return s - } - - internal fun readInt(): Int { - val data = data - var pos = pos - val i = ( - data[pos++] and 0xff shl 24 - or (data[pos++] and 0xff shl 16) - or (data[pos++] and 0xff shl 8) - or (data[pos++] and 0xff) - ) - this.pos = pos - return i - } - - internal fun readLong(): Long { - val data = data - var pos = pos - val v = ( - data[pos++] and 0xffL shl 56 - or (data[pos++] and 0xffL shl 48) - or (data[pos++] and 0xffL shl 40) - or (data[pos++] and 0xffL shl 32) - or (data[pos++] and 0xffL shl 24) - or (data[pos++] and 0xffL shl 16) - or (data[pos++] and 0xffL shl 8) - or (data[pos++] and 0xffL) - ) - this.pos = pos - return v - } - - /** Moves `byteCount` bytes from this segment to `sink`. */ - internal fun writeTo(sink: Segment, byteCount: Int) { - check(sink.owner) { "only owner can write" } - if (sink.limit + byteCount > SIZE) { - // We can't fit byteCount bytes at the sink's current position. Shift sink first. - if (sink.shared) throw IllegalArgumentException() - if (sink.limit + byteCount - sink.pos > SIZE) throw IllegalArgumentException() - sink.data.copyInto(sink.data, startIndex = sink.pos, endIndex = sink.limit) - sink.limit -= sink.pos - sink.pos = 0 - } - - data.copyInto( - sink.data, destinationOffset = sink.limit, startIndex = pos, - endIndex = pos + byteCount - ) - sink.limit += byteCount - pos += byteCount - } - - internal fun readTo(dst: ByteArray, dstStartOffset: Int, dstEndOffset: Int) { - val len = dstEndOffset - dstStartOffset - data.copyInto(dst, dstStartOffset, pos, pos + len) - pos += len - } - - internal fun write(src: ByteArray, srcStartOffset: Int, srcEndOffset: Int) { - src.copyInto(data, limit, srcStartOffset, srcEndOffset) - limit += srcEndOffset - srcStartOffset - } - - @PublishedApi - @get:JvmSynthetic - internal val size: Int - get() = limit - pos - - @PublishedApi - @get:JvmSynthetic - internal val remainingCapacity: Int - get() = data.size - limit - - /** - * Return a byte-array view over internal data. - * - * Returned array contains data layed out so that a readable slice starts at - * [Segment.pos] and ends at [Segment.limit], writable slice starts at [Segment.limit] - * and spans over [Segment.remainingCapacity] bytes. - * - * This method exists only to preserve binary compatibility if a segment's internal - * container eventually changes from ByteArray to something else. - */ - @PublishedApi - @JvmSynthetic - @Suppress("UNUSED_PARAMETER") - internal fun dataAsByteArray(readOnly: Boolean): ByteArray = data - - /** - * Write back all modifications that were made to a view returned from [dataAsByteArray]. - * - * This method exists only to preserve binary compatibility if a segment's internal - * container eventually changes from ByteArray to something else. - */ - @PublishedApi - @JvmSynthetic - @Suppress("UNUSED_PARAMETER") - internal fun writeBackData(data: ByteArray, bytesToCommit: Int): Unit = Unit - - internal fun getUnchecked(index: Int): Byte { - return data[pos + index] - } - - internal fun setUnchecked(index: Int, value: Byte) { - data[limit + index] = value - } - - internal fun setUnchecked(index: Int, b0: Byte, b1: Byte) { - val d = data - val l = limit - d[l + index] = b0 - d[l + index + 1] = b1 - } - - internal fun setUnchecked(index: Int, b0: Byte, b1: Byte, b2: Byte) { - val d = data - val l = limit - d[l + index] = b0 - d[l + index + 1] = b1 - d[l + index + 2] = b2 - } - - internal fun setUnchecked(index: Int, b0: Byte, b1: Byte, b2: Byte, b3: Byte) { - val d = data - val l = limit - d[l + index] = b0 - d[l + index + 1] = b1 - d[l + index + 2] = b2 - d[l + index + 3] = b3 - } - - internal companion object { - /** The size of all segments in bytes. */ - internal const val SIZE = 8192 - - /** Segments will be shared when doing so avoids `arraycopy()` of this many bytes. */ - internal const val SHARE_MINIMUM = 1024 - - @JvmSynthetic - internal fun new(): Segment = Segment() - - @JvmSynthetic - internal fun new( - data: ByteArray, - pos: Int, - limit: Int, - copyTracker: SegmentCopyTracker?, - owner: Boolean - ): Segment = Segment(data, pos, limit, copyTracker, owner) - } -} - -internal fun Segment.indexOf(byte: Byte, startOffset: Int, endOffset: Int): Int { - require(startOffset in 0 until size) { - "$startOffset" - } - require(endOffset in startOffset..size) { - "$endOffset" - } - val p = pos - val data = dataAsByteArray(true) - for (idx in startOffset until endOffset) { - if (data[p + idx] == byte) { - return idx - } - } - return -1 -} - -/** - * Searches for a `bytes` pattern within this segment starting at the offset `startOffset`. - * `startOffset` is relative and should be within `[0, size)`. - */ -internal fun Segment.indexOfBytesInbound(bytes: ByteArray, startOffset: Int): Int { - var offset = startOffset - val limit = size - bytes.size + 1 - val firstByte = bytes[0] - val data = dataAsByteArray(true) - while (offset < limit) { - val idx = indexOf(firstByte, offset, limit) - if (idx < 0) { - return -1 - } - var found = true - for (innerIdx in 1 until bytes.size) { - if (data[pos + idx + innerIdx] != bytes[innerIdx]) { - found = false - break - } - } - if (found) { - return idx - } else { - offset++ - } - } - return -1 -} - -/** - * Searches for a `bytes` pattern starting in between offset `startOffset` and `size` within this segment - * and continued in the following segments. - * `startOffset` is relative and should be within `[0, size)`. - */ -internal fun Segment.indexOfBytesOutbound(bytes: ByteArray, startOffset: Int): Int { - var offset = startOffset - val firstByte = bytes[0] - - while (offset in 0 until size) { - val idx = indexOf(firstByte, offset, size) - if (idx < 0) { - return -1 - } - // The pattern should start in this segment - var seg = this - var data = seg.dataAsByteArray(true) - var scanOffset = offset - - var found = true - for (element in bytes) { - // We ran out of bytes in this segment, - // so let's take the next one and continue the scan there. - if (scanOffset == seg.size) { - val next = seg.next ?: return -1 - seg = next - data = seg.dataAsByteArray(true) - scanOffset = 0 // we're scanning the next segment right from the beginning - } - if (element != data[seg.pos + scanOffset]) { - found = false - break - } - scanOffset++ - } - if (found) { - return offset - } - offset++ - } - return -1 -} - -@PublishedApi -internal fun Segment.isEmpty(): Boolean = size == 0 diff --git a/core/common/src/SegmentPool.kt b/core/common/src/SegmentPool.kt deleted file mode 100644 index 3bc511d64..000000000 --- a/core/common/src/SegmentPool.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2014 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package kotlinx.io - -/** - * A collection of unused segments, necessary to avoid GC churn and zero-fill. - * This pool is a thread-safe static singleton. - */ -internal expect object SegmentPool { - val MAX_SIZE: Int - - /** - * For testing only. Returns a snapshot of the number of bytes currently in the pool. If the pool - * is segmented such as by thread, this returns the byte count accessible to the calling thread. - */ - val byteCount: Int - - /** Return a segment for the caller's use. */ - fun take(): Segment - - /** Recycle a segment that the caller no longer needs. */ - fun recycle(segment: Segment) - - /** - * Allocates a new copy tracker that'll be associated with a segment from this pool. - * For performance reasons, there's no tracker attached to a segment initially. - * Instead, it's allocated lazily on the first sharing attempt. - */ - fun tracker(): SegmentCopyTracker -} diff --git a/core/common/src/Sink.kt b/core/common/src/Sink.kt deleted file mode 100644 index e2914fd5f..000000000 --- a/core/common/src/Sink.kt +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package kotlinx.io - -/** - * A sink that facilitates typed data writes and keeps a buffer internally so that caller can write some data without - * sending it directly to an upstream. - * - * [Sink] is the main `kotlinx-io` interface to write data in client's code, - * any [RawSink] could be turned into [Sink] using [RawSink.buffered]. - * - * Depending on the kind of upstream and the number of bytes written, buffering may improve the performance - * by hiding the latency of small writes. - * - * Data stored inside the internal buffer could be sent to an upstream using [flush], [emit], or [hintEmit]: - * - [flush] writes the whole buffer to an upstream and then flushes the upstream. - * - [emit] writes all data from the buffer into the upstream without flushing it. - * - [hintEmit] hints the source that current write operation is now finished and a part of data from the buffer - * may be partially emitted into the upstream. - * The latter is aimed to reduce memory footprint by keeping the buffer as small as possible without excessive writes - * to the upstream. - * All write operations implicitly calls [hintEmit]. - * - * ### Write methods' behavior and naming conventions - * - * Methods writing a value of some type are usually named `write`, like [writeByte] or [writeInt], except methods - * writing data from a some collection of bytes, like `write(ByteArray, Int, Int)` or - * `write(source: RawSource, byteCount: Long)`. - * In the latter case, if a collection is consumable (i.e., once data was read from it will no longer be available for - * reading again), write method will consume as many bytes as it was requested to write. - * - * Methods fully consuming its argument are named `transferFrom`, like [transferFrom]. - * - * It is recommended to follow the same naming convention for Sink extensions. - */ -public sealed interface Sink : RawSink { - /** - * This sink's internal buffer. It contains data written to the sink, but not yet flushed to the upstream. - * - * Incorrect use of the buffer may cause data loss or unexpected data being sent to the upstream. - * Consider using alternative APIs to write data into the sink, if possible: - * - write data into separate [Buffer] instance and write that buffer into the sink and then flush the sink to - * ensure that the upstream will receive complete data; - * - implement [RawSink] and wrap an upstream sink into it to intercept data being written. - * - * If there is an actual need to write data directly into the buffer, consider using [Sink.writeToInternalBuffer] instead. - */ - @InternalIoApi - public val buffer: Buffer - - /** - * Writes bytes from [source] array or its subrange to this sink. - * - * @param source the array from which bytes will be written into this sink. - * @param startIndex the start index (inclusive) of the [source] subrange to be written, 0 by default. - * @param endIndex the endIndex (exclusive) of the [source] subrange to be written, size of the [source] by default. - * - * @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. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeByteArrayToSink - */ - public fun write(source: ByteArray, startIndex: Int = 0, endIndex: Int = source.size) - - /** - * Removes all bytes from [source] and write them to this sink. - * Returns the number of bytes read which will be 0 if [source] is exhausted. - * - * @param source the source to consume data from. - * - * @throws IllegalStateException when the sink or [source] is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.transferFrom - */ - public fun transferFrom(source: RawSource): Long - - /** - * Removes [byteCount] bytes from [source] and write them to this sink. - * - * If [source] will be exhausted before reading [byteCount] from it then an exception throws on - * an attempt to read remaining bytes will be propagated to a caller of this method. - * - * @param source the source to consume data from. - * @param byteCount the number of bytes to read from [source] and to write into this sink. - * - * @throws IllegalArgumentException when [byteCount] is negative. - * @throws IllegalStateException when the sink or [source] is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeSourceToSink - */ - public fun write(source: RawSource, byteCount: Long) - - /** - * Writes a byte to this sink. - * - * @param byte the byte to be written. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeByte - */ - public fun writeByte(byte: Byte) - - /** - * Writes two bytes containing [short], in the big-endian order, to this sink. - * - * @param short the short integer to be written. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeShort - */ - public fun writeShort(short: Short) - - /** - * Writes four bytes containing [int], in the big-endian order, to this sink. - * - * @param int the integer to be written. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeInt - */ - public fun writeInt(int: Int) - - /** - * Writes eight bytes containing [long], in the big-endian order, to this sink. - * - * @param long the long integer to be written. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeLong - */ - public fun writeLong(long: Long) - - /** - * Writes all buffered data to the underlying sink, if one exists. - * Then the underlying sink is explicitly flushed. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.flush - */ - override fun flush() - - /** - * Writes all buffered data to the underlying sink if one exists. - * The underlying sink will not be explicitly flushed. - * - * This method behaves like [flush], but has weaker guarantees. - * 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. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.emit - */ - public fun emit() - - /** - * Hints that the buffer may be *partially* emitted (see [emit]) to the underlying sink. - * The underlying sink will not be explicitly flushed. - * There are no guarantees that this call will cause emit of buffered data as well as - * there are no guarantees how many bytes will be emitted. - * - * Typically, application code will not need to call this: it is only necessary when - * application code writes directly to this [buffered]. - * Use this to limit the memory held in the buffer. - * - * Consider using [Sink.writeToInternalBuffer] for writes into [buffered] followed by [hintEmit] call. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - */ - @InternalIoApi - public fun hintEmit() -} diff --git a/core/common/src/Sinks.kt b/core/common/src/Sinks.kt deleted file mode 100644 index c2e87a666..000000000 --- a/core/common/src/Sinks.kt +++ /dev/null @@ -1,376 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io - -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind.EXACTLY_ONCE -import kotlin.contracts.contract -import kotlinx.io.unsafe.UnsafeBufferOperations - -private val HEX_DIGIT_BYTES = ByteArray(16) { - ((if (it < 10) '0'.code else ('a'.code - 10)) + it).toByte() -} - -/** - * Writes two bytes containing [short], in the little-endian order, to this sink. - * - * @param short the short integer to be written. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeShortLe - */ -public fun Sink.writeShortLe(short: Short) { - this.writeShort(short.reverseBytes()) -} - -/** - * Writes four bytes containing [int], in the little-endian order, to this sink. - * - * @param int the integer to be written. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeIntLe - */ -public fun Sink.writeIntLe(int: Int) { - this.writeInt(int.reverseBytes()) -} - -/** - * Writes eight bytes containing [long], in the little-endian order, to this sink. - * - * @param long the long integer to be written. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeLongLe - */ -public fun Sink.writeLongLe(long: Long) { - this.writeLong(long.reverseBytes()) -} - -/** - * Writes [long] to this sink in signed decimal form (i.e., as a string in base 10). - * - * Resulting string will not contain leading zeros, except the `0` value itself. - * - * @param long the long to be written. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeDecimalLong - */ -@OptIn(DelicateIoApi::class, UnsafeIoApi::class) -public fun Sink.writeDecimalLong(long: Long) { - var v = long - if (v == 0L) { - // Both a shortcut and required since the following code can't handle zero. - writeByte('0'.code.toByte()) - return - } - - var negative = false - if (v < 0L) { - v = -v - if (v < 0L) { // Only true for Long.MIN_VALUE. - writeString("-9223372036854775808") - return - } - negative = true - } - - // Binary search for character width which favors matching lower numbers. - var width = - if (v < 100000000L) - if (v < 10000L) - if (v < 100L) - if (v < 10L) 1 - else 2 - else if (v < 1000L) 3 - else 4 - else if (v < 1000000L) - if (v < 100000L) 5 - else 6 - else if (v < 10000000L) 7 - else 8 - else if (v < 1000000000000L) - if (v < 10000000000L) - if (v < 1000000000L) 9 - else 10 - else if (v < 100000000000L) 11 - else 12 - else if (v < 1000000000000000L) - if (v < 10000000000000L) 13 - else if (v < 100000000000000L) 14 - else 15 - else if (v < 100000000000000000L) - if (v < 10000000000000000L) 16 - else 17 - else if (v < 1000000000000000000L) 18 - else 19 - if (negative) { - ++width - } - - writeToInternalBuffer { buffer -> - UnsafeBufferOperations.writeToTail(buffer, width) { ctx, segment -> - for (pos in width - 1 downTo if (negative) 1 else 0) { - val digit = (v % 10).toByte() - ctx.setUnchecked(segment, pos, HEX_DIGIT_BYTES[digit.toInt()]) - v /= 10 - } - if (negative) { - ctx.setUnchecked(segment, 0, '-'.code.toByte()) - } - width - } - } -} - -/** - * Writes [long] to this sink in hexadecimal form (i.e., as a string in base 16). - * - * Resulting string will not contain leading zeros, except the `0` value itself. - * - * @param long the long to be written. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeHexLong - */ -@OptIn(DelicateIoApi::class, UnsafeIoApi::class) -public fun Sink.writeHexadecimalUnsignedLong(long: Long) { - var v = long - if (v == 0L) { - // Both a shortcut and required since the following code can't handle zero. - writeByte('0'.code.toByte()) - return - } - - val width = hexNumberLength(v) - - writeToInternalBuffer { buffer -> - UnsafeBufferOperations.writeToTail(buffer, width) { ctx, segment -> - for (pos in width - 1 downTo 0) { - ctx.setUnchecked(segment, pos, HEX_DIGIT_BYTES[v.toInt().and(0xF)]) - v = v ushr 4 - } - width - } - } -} - -/** - * Writes am unsigned byte to this sink. - * - * @param byte the byte to be written. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeUByte - */ -public fun Sink.writeUByte(byte: UByte) { - writeByte(byte.toByte()) -} - -/** - * Writes two bytes containing [short], in the big-endian order, to this sink. - * - * @param short the unsigned short integer to be written. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeUShort - */ -public fun Sink.writeUShort(short: UShort) { - writeShort(short.toShort()) -} - -/** - * Writes four bytes containing [int], in the big-endian order, to this sink. - * - * @param int the unsigned integer to be written. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeUInt - */ -public fun Sink.writeUInt(int: UInt) { - writeInt(int.toInt()) -} - -/** - * Writes eight bytes containing [long], in the big-endian order, to this sink. - * - * @param long the unsigned long integer to be written. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeULong - */ -public fun Sink.writeULong(long: ULong) { - writeLong(long.toLong()) -} - -/** - * Writes two bytes containing [short], in the little-endian order, to this sink. - * - * @param short the unsigned short integer to be written. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeUShortLe - */ -public fun Sink.writeUShortLe(short: UShort) { - writeShortLe(short.toShort()) -} - -/** - * Writes four bytes containing [int], in the little-endian order, to this sink. - * - * @param int the unsigned integer to be written. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeUIntLe - */ -public fun Sink.writeUIntLe(int: UInt) { - writeIntLe(int.toInt()) -} - -/** - * Writes eight bytes containing [long], in the little-endian order, to this sink. - * - * @param long the unsigned long integer to be written. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeULongLe - */ -public fun Sink.writeULongLe(long: ULong) { - writeLongLe(long.toLong()) -} - -/** - * Writes four bytes of a bit representation of [float], in the big-endian order, to this sink. - * Bit representation of the [float] corresponds to the IEEE 754 floating-point "single format" bit layout. - * - * To obtain a bit representation, the [Float.toBits] function is used. - * - * Should be used with care when working with special values (like `NaN`) as bit patterns obtained for [Float.NaN] may vary depending on a platform. - * - * Note that in Kotlin/JS a value obtained by writing an original [Float] value to a [Sink] using - * [Sink.writeFloat] and then reading it back using [Source.readFloat] may not be equal to the original value. - * Please refer to [Float.toBits] documentation for details. - * - * @param float the floating point number to be written. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeFloat - */ -public fun Sink.writeFloat(float: Float) { - writeInt(float.toBits()) -} - -/** - * Writes eight bytes of a bit representation of [double], in the big-endian order, to this sink. - * Bit representation of the [double] corresponds to the IEEE 754 floating-point "double format" bit layout. - * - * To obtain a bit representation, the [Double.toBits] function is used. - * - * Should be used with care when working with special values (like `NaN`) as bit patterns obtained for [Double.NaN] may vary depending on a platform. - * - * @param double the floating point number to be written. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeDouble - */ -public fun Sink.writeDouble(double: Double) { - writeLong(double.toBits()) -} - -/** - * Writes four bytes of a bit representation of [float], in the little-endian order, to this sink. - * Bit representation of the [float] corresponds to the IEEE 754 floating-point "single format" bit layout. - * - * To obtain a bit representation, the [Float.toBits] function is used. - * - * Should be used with care when working with special values (like `NaN`) as bit patterns obtained for [Float.NaN] may vary depending on a platform. - * - * Note that in Kotlin/JS a value obtained by writing an original [Float] value to a [Sink] using - * [Sink.writeFloatLe] and then reading it back using [Source.readFloatLe] may not be equal to the original value. - * Please refer to [Float.toBits] documentation for details. - * - * @param float the floating point number to be written. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeFloatLe - */ -public fun Sink.writeFloatLe(float: Float) { - writeIntLe(float.toBits()) -} - -/** - * Writes eight bytes of a bit representation of [double], in the little-endian order, to this sink. - * Bit representation of the [double] corresponds to the IEEE 754 floating-point "double format" bit layout. - * - * To obtain a bit representation, the [Double.toBits] function is used. - * - * Should be used with care when working with special values (like `NaN`) as bit patterns obtained for [Double.NaN] may vary depending on a platform. - * - * @param double the floating point number to be written. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeDoubleLe - */ -public fun Sink.writeDoubleLe(double: Double) { - writeLongLe(double.toBits()) -} - -/** - * Provides direct access to the sink's internal buffer and hints its emit before exit. - * - * The internal buffer is passed into [lambda], - * and it may be partially emitted to the underlying sink before returning from this method. - * - * Use this method with care as the data within the buffer is not yet emitted to the underlying sink - * and consumption of data from the buffer will cause its loss. - * - * @param lambda the callback accessing internal buffer. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - */ -@DelicateIoApi -@OptIn(InternalIoApi::class, ExperimentalContracts::class) -public inline fun Sink.writeToInternalBuffer(lambda: (Buffer) -> Unit) { - contract { - callsInPlace(lambda, EXACTLY_ONCE) - } - lambda(this.buffer) - this.hintEmit() -} diff --git a/core/common/src/Source.kt b/core/common/src/Source.kt deleted file mode 100644 index e6a5b349b..000000000 --- a/core/common/src/Source.kt +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package kotlinx.io - -/** - * A source that facilitates typed data reads and keeps a buffer internally so that callers can read chunks of data - * without requesting it from a downstream on every call. - * - * [Source] is the main `kotlinx-io` interface to read data in client's code, - * any [RawSource] could be converted into [Source] using [RawSource.buffered]. - * - * Depending on the kind of downstream and the number of bytes read, buffering may improve the performance by hiding - * the latency of small reads. - * - * The buffer is refilled on reads as necessary, but it is also possible to ensure it contains enough data - * using [require] or [request]. - * [Sink] also allows skipping unneeded prefix of data using [skip] and - * provides look ahead into incoming data, buffering as much as necessary, using [peek]. - * - * Source's read* methods have different guarantees of how much data will be consumed from the source - * and what to expect in case of error. - * - * ### Read methods' behavior and naming conventions - * - * Unless stated otherwise, all read methods consume the exact number of bytes - * requested (or the number of bytes required to represent a value of a requested type). - * If a source contains fewer bytes than requested, these methods will throw an exception. - * - * Methods reading up to requested number of bytes are named as `readAtMost` - * in contrast to methods reading exact number of bytes, which don't have `AtMost` suffix in their names. - * If a source contains fewer bytes than requested, these methods will not treat it as en error and will return - * gracefully. - * - * Methods returning a value as a result are named `read`, like [readInt] or [readByte]. - * These methods don't consume source's content in case of an error. - * - * Methods reading data into a consumer supplied as one of its arguments are named `read*To`, - * like [readTo] or [readAtMostTo]. These methods consume a source even when an error occurs. - * - * Methods moving all data from a source to some other sink are named `transferTo`, like [transferTo]. - * - * It is recommended to follow the same naming convention for Source extensions. - */ -public sealed interface Source : RawSource { - /** - * This source's internal buffer. It contains data fetched from the downstream, but not yet consumed by the upstream. - * - * Incorrect use of the buffer may cause data loss or unexpected data being read by the upstream. - * Consider using alternative APIs to read data from the source, if possible: - * - use [peek] for lookahead into a source; - * - implement [RawSource] and wrap a downstream source into it to intercept data being read. - */ - @InternalIoApi - public val buffer: Buffer - - /** - * Returns true if there are no more bytes in this source. - * - * 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. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.exhausted - */ - public fun exhausted(): Boolean - - /** - * Attempts to fill the buffer with at least [byteCount] bytes of data from the underlying source - * and throw [EOFException] when the source is exhausted before fulfilling the requirement. - * - * If the buffer already contains required number of bytes then there will be no requests to - * the underlying source. - * - * @param byteCount the number of bytes that the buffer should contain. - * - * @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. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.require - */ - public fun require(byteCount: Long) - - /** - * Attempts to fill the buffer with at least [byteCount] bytes of data from the underlying source - * and returns a value indicating if the requirement was successfully fulfilled. - * - * `false` value returned by this method indicates that the underlying source was exhausted before - * filling the buffer with [byteCount] bytes of data. - * - * @param byteCount the number of bytes that the buffer should contain. - * - * @throws IllegalArgumentException when [byteCount] is negative. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.request - */ - public fun request(byteCount: Long): Boolean - - /** - * Removes a byte from this source and returns it. - * - * @throws EOFException when there are no more bytes to read. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readByte - */ - public fun readByte(): Byte - - /** - * Removes two bytes from this source and returns a short integer composed of it according to the big-endian order. - * - * @throws EOFException when there are not enough data to read a short value. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readShort - */ - public fun readShort(): Short - - /** - * Removes four bytes from this source and returns an integer composed of it according to the big-endian order. - * - * @throws EOFException when there are not enough data to read an int value. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readInt - */ - public fun readInt(): Int - - /** - * Removes eight bytes from this source and returns a long integer composed of it according to the big-endian order. - * - * @throws EOFException when there are not enough data to read a long value. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readLong - */ - public fun readLong(): Long - - /** - * Reads and discards [byteCount] bytes from this source. - * - * @param byteCount the number of bytes to be skipped. - * - * @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. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.skip - */ - public fun skip(byteCount: Long) - - /** - * Removes up to `endIndex - startIndex` bytes from this source, copies them into [sink] subrange starting at - * [startIndex] and ending at [endIndex], and returns the number of bytes read, or -1 if this source is exhausted. - * - * @param sink the array to which data will be written from this source. - * @param startIndex the startIndex (inclusive) of the [sink] subrange to read data into, 0 by default. - * @param endIndex the endIndex (exclusive) of the [sink] subrange to read data into, `sink.size` by default. - * - * @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. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readAtMostToByteArray - */ - public fun readAtMostTo(sink: ByteArray, startIndex: Int = 0, endIndex: Int = sink.size): Int - - /** - * Removes exactly [byteCount] bytes from this source and writes them to [sink]. - * - * @param sink the sink to which data will be written from this source. - * @param byteCount the number of bytes that should be written into [sink] - * - * @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. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readSourceToSink - */ - public fun readTo(sink: RawSink, byteCount: Long) - - /** - * Removes all bytes from this source, writes them to [sink], and returns the total number of bytes - * written to [sink]. - * - * Return 0 if this source is exhausted. - * - * @param sink the sink to which data will be written from this source. - * - * @throws IllegalStateException when the source or [sink] is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.transferTo - */ - public fun transferTo(sink: RawSink): Long - - /** - * Returns a new [Source] that can read data from this source without consuming it. - * The returned source becomes invalid once this source is next read or closed. - * - * 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/Sources.kt b/core/common/src/Sources.kt deleted file mode 100644 index c231b9160..000000000 --- a/core/common/src/Sources.kt +++ /dev/null @@ -1,464 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io - -/** - * Removes two bytes from this source and returns a short integer composed of it according to the little-endian order. - * - * @throws EOFException when there are not enough data to read a short value. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readShortLe - */ -public fun Source.readShortLe(): Short { - return readShort().reverseBytes() -} - -/** - * Removes four bytes from this source and returns an integer composed of it according to the little-endian order. - * - * @throws EOFException when there are not enough data to read an int value. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readIntLe - */ -public fun Source.readIntLe(): Int { - return readInt().reverseBytes() -} - -/** - * Removes eight bytes from this source and returns a long integer composed of it according to the little-endian order. - * - * @throws EOFException when there are not enough data to read a long value. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readLongLe - */ -public fun Source.readLongLe(): Long { - return readLong().reverseBytes() -} - -internal const val OVERFLOW_ZONE = Long.MIN_VALUE / 10L -internal const val OVERFLOW_DIGIT_START = Long.MIN_VALUE % 10L + 1 - -/** - * Reads a long from this source in signed decimal form (i.e., as a string in base 10 with - * optional leading `-`). - * - * Source data will be consumed until the source is exhausted, the first occurrence of non-digit byte, - * or overflow happened during resulting value construction. - * - * @throws NumberFormatException if the found digits do not fit into a `long` or a decimal - * number was not present. - * @throws EOFException if the source is exhausted before a call of this method. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readDecimalLong - */ -@OptIn(InternalIoApi::class) -public fun Source.readDecimalLong(): Long { - require(1L) - - var negative = false - var value = 0L - var overflowDigit = OVERFLOW_DIGIT_START - - when (val b = buffer[0]) { - '-'.code.toByte() -> { - negative = true - overflowDigit-- - require(2) - if (buffer[1] !in '0'.code .. '9'.code) { - throw NumberFormatException("Expected a digit but was 0x${buffer[1].toHexString()}") - } - } - - in '0'.code..'9'.code -> { - value = ('0'.code - b).toLong() - } - - else -> { - throw NumberFormatException("Expected a digit or '-' but was 0x${b.toHexString()}") - } - } - - var bufferOffset = 1L - while (request(bufferOffset + 1)) { - val finished = buffer.seek(bufferOffset) { seg, offset -> - seg!! - var currIdx = (bufferOffset - offset).toInt() - val size = seg.size - while (currIdx < size) { - val b = seg.getUnchecked(currIdx) - if (b in '0'.code..'9'.code) { - val digit = '0'.code - b - - // Detect when the digit would cause an overflow. - if (value < OVERFLOW_ZONE || value == OVERFLOW_ZONE && digit < overflowDigit) { - with(Buffer()) { - writeDecimalLong(value) - writeByte(b) - - if (!negative) readByte() // Skip negative sign. - throw NumberFormatException("Number too large: ${readString()}") - } - } - value = value * 10L + digit - currIdx++ - bufferOffset++ - } else { - return@seek true - } - } - false - } - if (finished) break - } - skip(bufferOffset) - - return if (negative) value else -value -} - -/** - * Reads a long form this source in hexadecimal form (i.e., as a string in base 16). - * - * Source data will be consumed until the source is exhausted, the first occurrence of non-digit byte, - * or overflow happened during resulting value construction. - * - * @throws NumberFormatException if the found hexadecimal does not fit into a `long` or - * hexadecimal was not found. - * @throws EOFException if the source is exhausted before a call of this method. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readHexLong - */ -@OptIn(InternalIoApi::class) -public fun Source.readHexadecimalUnsignedLong(): Long { - require(1) - var result = when (val b = buffer[0]) { - in '0'.code..'9'.code -> b - '0'.code - in 'a'.code..'f'.code -> b - 'a'.code + 10 - in 'A'.code..'F'.code -> b - 'A'.code + 10 - else -> throw NumberFormatException("Expected leading [0-9a-fA-F] character but was 0x${b.toHexString()}") - }.toLong() - - var bytesRead = 1L - - while (request(bytesRead + 1L)) { - val stop = buffer.seek(bytesRead) { seg, offset -> - seg!! - val startIndex = (bytesRead - offset).toInt() - for (localOffset in startIndex until seg.size) { - val b = seg.getUnchecked(localOffset) - val bDigit = when (b) { - in '0'.code..'9'.code -> b - '0'.code - in 'a'.code..'f'.code -> b - 'a'.code + 10 - in 'A'.code..'F'.code -> b - 'A'.code + 10 - else -> return@seek true - } - if (result and -0x1000000000000000L != 0L) { - with(Buffer()) { - writeHexadecimalUnsignedLong(result) - writeByte(b) - throw NumberFormatException("Number too large: " + readString()) - } - } - result = result.shl(4) + bDigit - bytesRead++ - } - false - } - if (stop) break - } - skip(bytesRead) - return result -} - -/** - * Returns an index of [byte] first occurrence in the range of [startIndex] to [endIndex], - * or `-1` when the range doesn't contain [byte]. - * - * The scan terminates at either [endIndex] or source's exhaustion, whichever comes first. The - * maximum number of bytes scanned is `toIndex-fromIndex`. - * If [byte] not found in buffered data, [endIndex] is yet to be reached and the underlying source is not yet exhausted - * then new data will be read from the underlying source into the buffer. - * - * @param byte the value to find. - * @param startIndex the start of the range (inclusive) to find [byte], `0` by default. - * @param endIndex the end of the range (exclusive) to find [byte], [Long.MAX_VALUE] by default. - * - * @throws IllegalStateException when the source is closed. - * @throws IllegalArgumentException when `startIndex > endIndex` or either of indices is negative. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.indexOfByteSample - */ -@OptIn(InternalIoApi::class) -public fun Source.indexOf(byte: Byte, startIndex: Long = 0L, endIndex: Long = Long.MAX_VALUE): Long { - require(startIndex in 0..endIndex) { - if (endIndex < 0) { - "startIndex ($startIndex) and endIndex ($endIndex) should be non negative" - } else { - "startIndex ($startIndex) is not within the range [0..endIndex($endIndex))" - } - } - if (startIndex == endIndex) return -1L - - var offset = startIndex - while (offset < endIndex && request(offset + 1)) { - val idx = buffer.indexOf(byte, offset, minOf(endIndex, buffer.size)) - if (idx != -1L) { - return idx - } - offset = buffer.size - } - return -1L -} - -/** - * Removes all bytes from this source and returns them as a byte array. - * - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readToArraySample - */ -public fun Source.readByteArray(): ByteArray { - return readByteArrayImpl(-1) -} - -/** - * Removes [byteCount] bytes from this source and returns them as a byte array. - * - * @param byteCount the number of bytes that should be read from the source. - * - * @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. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readToArraySample - */ -public fun Source.readByteArray(byteCount: Int): ByteArray { - checkByteCount(byteCount.toLong()) - return readByteArrayImpl(byteCount) -} - -@OptIn(InternalIoApi::class) -private fun Source.readByteArrayImpl(size: Int): ByteArray { - var arraySize = size - if (size == -1) { - var fetchSize = Int.MAX_VALUE.toLong() - while (buffer.size < Int.MAX_VALUE && request(fetchSize)) { - fetchSize *= 2 - } - check(buffer.size < Int.MAX_VALUE) { "Can't create an array of size ${buffer.size}" } - arraySize = buffer.size.toInt() - } else { - require(size.toLong()) - } - val array = ByteArray(arraySize) - buffer.readTo(array) - return array -} - - -/** - * Removes exactly `endIndex - startIndex` bytes from this source and copies them into [sink] subrange starting at - * [startIndex] and ending at [endIndex]. - * - * @param sink the array to write data to - * @param startIndex the startIndex (inclusive) of the [sink] subrange to read data into, 0 by default. - * @param endIndex the endIndex (exclusive) of the [sink] subrange to read data into, `sink.size` by default. - * - * @throws EOFException when the requested number of bytes cannot be read. - * @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`. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readToArraySample - */ -public fun Source.readTo(sink: ByteArray, startIndex: Int = 0, endIndex: Int = sink.size) { - checkBounds(sink.size, startIndex, endIndex) - var offset = startIndex - while (offset < endIndex) { - val bytesRead = readAtMostTo(sink, offset, endIndex) - if (bytesRead == -1) { - throw EOFException( - "Source exhausted before reading ${endIndex - startIndex} bytes. " + - "Only $bytesRead bytes were read." - ) - } - offset += bytesRead - } -} - -/** - * Removes an unsigned byte from this source and returns it. - * - * @throws EOFException when there are no more bytes to read. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readUByte - */ -public fun Source.readUByte(): UByte = readByte().toUByte() - -/** - * Removes two bytes from this source and returns an unsigned short integer composed of it - * according to the big-endian order. - * - * @throws EOFException when there are not enough data to read an unsigned short value. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readUShort - */ -public fun Source.readUShort(): UShort = readShort().toUShort() - -/** - * Removes four bytes from this source and returns an unsigned integer composed of it - * according to the big-endian order. - * - * @throws EOFException when there are not enough data to read an unsigned int value. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readUInt - */ -public fun Source.readUInt(): UInt = readInt().toUInt() - -/** - * Removes eight bytes from this source and returns an unsigned long integer composed of it - * according to the big-endian order. - * - * @throws EOFException when there are not enough data to read an unsigned long value. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readULong - */ -public fun Source.readULong(): ULong = readLong().toULong() - -/** - * Removes two bytes from this source and returns an unsigned short integer composed of it - * according to the little-endian order. - * - * @throws EOFException when there are not enough data to read an unsigned short value. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readUShortLe - */ -public fun Source.readUShortLe(): UShort = readShortLe().toUShort() - -/** - * Removes four bytes from this source and returns an unsigned integer composed of it - * according to the little-endian order. - * - * @throws EOFException when there are not enough data to read an unsigned int value. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readUIntLe - */ -public fun Source.readUIntLe(): UInt = readIntLe().toUInt() - -/** - * Removes eight bytes from this source and returns an unsigned long integer composed of it - * according to the little-endian order. - * - * @throws EOFException when there are not enough data to read an unsigned long value. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readULongLe - */ -public fun Source.readULongLe(): ULong = readLongLe().toULong() - -/** - * Removes four bytes from this source and returns a floating point number with type [Float] composed of it - * according to the big-endian order. - * - * The [Float.Companion.fromBits] function is used for decoding bytes into [Float]. - * - * Note that in Kotlin/JS a value obtained by writing an original [Float] value to a [Sink] using - * [Sink.writeFloat] and then reading it back using [Source.readFloat] may not be equal to the original value. - * Please refer to [Float.Companion.fromBits] documentation for details. - * - * @throws EOFException when there are not enough data to read an unsigned int value. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readFloat - */ -public fun Source.readFloat(): Float = Float.fromBits(readInt()) - -/** - * Removes eight bytes from this source and returns a floating point number with type [Double] composed of it - * according to the big-endian order. - * - * The [Double.Companion.fromBits] function is used for decoding bytes into [Double]. - * - * @throws EOFException when there are not enough data to read an unsigned int value. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readDouble - */ -public fun Source.readDouble(): Double = Double.fromBits(readLong()) - -/** - * Removes four bytes from this source and returns a floating point number with type [Float] composed of it - * according to the little-endian order. - * - * The [Float.Companion.fromBits] function is used for decoding bytes into [Float]. - * - * Note that in Kotlin/JS a value obtained by writing an original [Float] value to a [Sink] using - * [Sink.writeFloatLe] and then reading it back using [Source.readFloatLe] may not be equal to the original value. - * Please refer to [Float.Companion.fromBits] documentation for details. - * - * @throws EOFException when there are not enough data to read an unsigned int value. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readFloatLe - */ -public fun Source.readFloatLe(): Float = Float.fromBits(readIntLe()) - -/** - * Removes eight bytes from this source and returns a floating point number with type [Double] composed of it - * according to the little-endian order. - * - * The [Double.Companion.fromBits] function is used for decoding bytes into [Double]. - * - * @throws EOFException when there are not enough data to read an unsigned int value. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readDoubleLe - */ -public fun Source.readDoubleLe(): Double = Double.fromBits(readLongLe()) - -/** - * Return `true` if the next byte to be consumed from this source is equal to [byte]. - * Otherwise, return `false` as well as when the source is exhausted. - * - * If there is no buffered data, this call will result in a fetch from the underlying source. - * - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @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 deleted file mode 100644 index 698faa21a..000000000 --- a/core/common/src/Utf8.kt +++ /dev/null @@ -1,623 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2017 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * `kotlinx-io` assumes most applications use UTF-8 exclusively, and offers optimized implementations of - * common operations on UTF-8 strings. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
[ByteString][Buffer], [BufferedSink], [BufferedSource]
Encode a string[ByteString.encodeUtf8][BufferedSink.writeUtf8]
Encode a code point[BufferedSink.writeUtf8CodePoint]
Decode a string[ByteString.utf8][BufferedSource.readUtf8], [BufferedSource.readUtf8]
Decode a code point[BufferedSource.readUtf8CodePoint]
Decode until the next `\r\n` or `\n`[BufferedSource.readUtf8LineStrict], - * [BufferedSource.readUtf8LineStrict]
Decode until the next `\r\n`, `\n`, or `EOF`[BufferedSource.readUtf8Line]
Measure the bytes in a UTF-8 string[Utf8.size], [Utf8.size]
- */ - -package kotlinx.io - -import kotlinx.io.internal.* -import kotlinx.io.unsafe.UnsafeBufferOperations -import kotlinx.io.unsafe.withData -import kotlin.math.min - -/** - * Returns the number of bytes used to encode the slice of `string` as UTF-8 when using [Sink.writeString]. - * - * @param startIndex the index (inclusive) of the first character to encode, `0` by default. - * @param endIndex the index (exclusive) of the character past the last character to encode, `string.length` by default. - * - * @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) - - var result = 0L - var i = startIndex - while (i < endIndex) { - val c = this[i].code - - if (c < 0x80) { - // A 7-bit character with 1 byte. - result++ - i++ - } else if (c < 0x800) { - // An 11-bit character with 2 bytes. - result += 2 - i++ - } else if (c < 0xd800 || c > 0xdfff) { - // A 16-bit character with 3 bytes. - result += 3 - i++ - } else { - val low = if (i + 1 < endIndex) this[i + 1].code else 0 - if (c > 0xdbff || low < 0xdc00 || low > 0xdfff) { - // A malformed surrogate, which yields '?'. - result++ - i++ - } else { - // A 21-bit character with 4 bytes. - result += 4 - i += 2 - } - } - } - - return result -} - -/** - * Encodes [codePoint] in UTF-8 and writes it to this sink. - * - * [codePoint] should represent valid Unicode code point, meaning that its value should be within the Unicode codespace - * (`U+000000` .. `U+10ffff`), otherwise [IllegalArgumentException] will be thrown. - * - * Note that in general, a value retrieved from [Char.code] could not be written directly - * as it may be a part of a [surrogate pair](https://www.unicode.org/faq/utf_bom.html#utf16-2) (that could be - * detected using [Char.isSurrogate], or [Char.isHighSurrogate] and [Char.isLowSurrogate]). - * Such a pair of characters needs to be manually converted back to a single code point - * which then could be written to a [Sink]. - * Without such a conversion, data written to a [Sink] can not be converted back - * to a string from which a surrogate pair was retrieved. - * - * More specifically, all code points mapping to UTF-16 surrogates (`U+d800`..`U+dfff`) - * will be written as `?` characters (`U+0063`). - * - * @param codePoint the codePoint to be written. - * - * @throws IllegalStateException when the sink is closed. - * @throws IllegalArgumentException when [codePoint] value is negative, or greater than `U+10ffff`. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeUtf8CodePointSample - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeSurrogatePair - */ -@OptIn(DelicateIoApi::class) -public fun Sink.writeCodePointValue(codePoint: Int): Unit = - writeToInternalBuffer { it.commonWriteUtf8CodePoint(codePoint) } - -/** - * Encodes the characters at [startIndex] up to [endIndex] from [string] in UTF-8 and writes it to this sink. - * - * @param string the string to be encoded. - * @param startIndex the index (inclusive) of the first character to encode, 0 by default. - * @param endIndex the index (exclusive) of a character past to a last character to encode, `string.length` by default. - * - * @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. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeUtf8Sample - */ -@OptIn(DelicateIoApi::class) -public fun Sink.writeString(string: String, startIndex: Int = 0, endIndex: Int = string.length) { - checkBounds(string.length, startIndex, endIndex) - - writeToInternalBuffer { it.commonWriteUtf8(startIndex, endIndex, string::get) } -} - -/** - * Encodes the characters at [startIndex] up to [endIndex] from [chars] in UTF-8 and writes it to this sink. - * - * @param chars the string to be encoded. - * @param startIndex the index (inclusive) of the first character to encode, 0 by default. - * @param endIndex the index (exclusive) of a character past to a last character to encode, `chars.length` by default. - * - * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [chars] indices. - * @throws IllegalArgumentException when `startIndex > endIndex`. - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeUtf8SeqSample - */ -@OptIn(DelicateIoApi::class) -public fun Sink.writeString(chars: CharSequence, startIndex: Int = 0, endIndex: Int = chars.length) { - checkBounds(chars.length, startIndex, endIndex) - - writeToInternalBuffer { it.commonWriteUtf8(startIndex, endIndex, chars::get) } -} - -/** - * Removes all bytes from this source, decodes them as UTF-8, and returns the string. - * - * Returns the empty string if this source is empty. - * - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readUtf8 - */ -@OptIn(InternalIoApi::class) -public fun Source.readString(): String { - request(Long.MAX_VALUE) // Request all data - return buffer.commonReadUtf8(buffer.size) -} - -/** - * 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) -} - -/** - * Removes [byteCount] bytes from this source, decodes them as UTF-8, and returns the string. - * - * @param byteCount the number of bytes to read from the source for string decoding. - * - * @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. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readUtf8 - */ -@OptIn(InternalIoApi::class) -public fun Source.readString(byteCount: Long): String { - require(byteCount) - return buffer.commonReadUtf8(byteCount) -} - -/** - * Decodes a single code point value from UTF-8 code units, reading between 1 and 4 bytes as necessary. - * - * If this source is exhausted before a complete code point can be read, this throws an - * [EOFException] and consumes no input. - * - * If this source starts with an ill-formed UTF-8 code units sequence, this method will remove - * 1 or more non-UTF-8 bytes and return the replacement character (`U+fffd`). - * - * The replacement character (`U+fffd`) will be also returned if the source starts with a well-formed - * code units sequences, but a decoded value does not pass further validation, such as - * the value is out of range (beyond the `0x10ffff` limit of Unicode), maps to UTF-16 surrogates (`U+d800`..`U+dfff`), - * or an overlong encoding is detected (such as `0xc080` for the NUL character in modified UTF-8). - * - * Note that in general, returned value may not be directly converted to [Char] as it may be out - * of [Char]'s values range and should be manually converted to a - * [surrogate pair](https://www.unicode.org/faq/utf_bom.html#utf16-2). - * - * @throws EOFException when the source is exhausted before a complete code point can be read. - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readUtf8CodePointSample - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.surrogatePairs - */ -@OptIn(InternalIoApi::class) -public fun Source.readCodePointValue(): Int { - if (this is Buffer) { - return commonReadUtf8CodePoint() - } - require(1) - - val b0 = buffer[0].toInt() - when { - b0 and 0xe0 == 0xc0 -> require(2) - b0 and 0xf0 == 0xe0 -> require(3) - b0 and 0xf8 == 0xf0 -> require(4) - } - - return buffer.commonReadUtf8CodePoint() -} - -/** - * Removes and returns UTF-8 encoded characters up to but not including the next line break. A line break is - * either `"\n"` or `"\r\n"`; these characters are not included in the result. - * - * On the end of the stream this method returns null. If the source doesn't end with a line break, then - * an implicit line break is assumed. Null is returned once the source is exhausted. - * - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readLinesSample - */ -@OptIn(InternalIoApi::class) -public fun Source.readLine(): String? { - if (!request(1)) return null - - var lfIndex = this.indexOf('\n'.code.toByte()) - return when (lfIndex) { - -1L -> readString() - 0L -> { - skip(1) - "" - } - - else -> { - var skipBytes = 1 - if (buffer[lfIndex - 1] == '\r'.code.toByte()) { - lfIndex -= 1 - skipBytes += 1 - } - val string = readString(lfIndex) - skip(skipBytes.toLong()) - string - } - } -} - -/** - * Removes and returns UTF-8 encoded characters up to but not including the next line break, throwing - * [EOFException] if a line break was not encountered. A line break is either `"\n"` or `"\r\n"`; - * these characters are not included in the result. - * - * The returned string will have at most [limit] UTF-8 bytes, and the maximum number of bytes - * scanned is `limit + 2`. If `limit == 0` this will always throw an [EOFException] because no - * bytes will be scanned. - * - * No bytes are discarded if the match fails. - * - * @param limit the maximum UTF-8 bytes constituting a returned string. - * - * @throws EOFException when the source does not contain a string consisting with at most [limit] bytes followed by - * line break characters. - * @throws IllegalStateException when the source is closed. - * @throws IllegalArgumentException when [limit] is negative. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.readLinesSample - */ -@OptIn(InternalIoApi::class) -public fun Source.readLineStrict(limit: Long = Long.MAX_VALUE): String { - require(limit >= 0) { "limit ($limit) < 0" } - require(1) - - var lfIndex = indexOf('\n'.code.toByte(), startIndex = 0, endIndex = limit) - - if (lfIndex == 0L) { - skip(1) - return "" - } - - if (lfIndex > 0) { - var skipBytes = 1L - if (buffer[lfIndex - 1] == '\r'.code.toByte()) { - lfIndex -= 1 - skipBytes += 1 - } - val str = readString(lfIndex) - skip(skipBytes) - return str - } - - // we reached the end of the source before hitting the limit - if (buffer.size < limit) throw EOFException() - // we can't read data anymore - if (limit == Long.MAX_VALUE) throw EOFException() - // there is no more data - if (!request(limit + 1)) throw EOFException() - - val b = buffer[limit] - if (b == '\n'.code.toByte()) { - val str = readString(limit) - skip(1) - return str - } - // check if the last byte is CR and the byte passed it is LF - if (b != '\r'.code.toByte() || !request(limit + 2)) throw EOFException() - if (buffer[limit + 1] != '\n'.code.toByte()) throw EOFException() - val res = readString(limit) - skip(2) - return res -} - -private fun Buffer.commonReadUtf8CodePoint(): Int { - require(1) - - val b0 = this[0] - var codePoint: Int - val byteCount: Int - val min: Int - - when { - b0 and 0x80 == 0 -> { - // 0xxxxxxx. - codePoint = b0 and 0x7f - byteCount = 1 // 7 bits (ASCII). - min = 0x0 - } - - b0 and 0xe0 == 0xc0 -> { - // 0x110xxxxx - codePoint = b0 and 0x1f - byteCount = 2 // 11 bits (5 + 6). - min = 0x80 - } - - b0 and 0xf0 == 0xe0 -> { - // 0x1110xxxx - codePoint = b0 and 0x0f - byteCount = 3 // 16 bits (4 + 6 + 6). - min = 0x800 - } - - b0 and 0xf8 == 0xf0 -> { - // 0x11110xxx - codePoint = b0 and 0x07 - byteCount = 4 // 21 bits (3 + 6 + 6 + 6). - min = 0x10000 - } - - else -> { - // We expected the first byte of a code point but got something else. - skip(1) - return REPLACEMENT_CODE_POINT - } - } - - if (size < byteCount) { - throw EOFException("size < $byteCount: $size (to read code point prefixed 0x${b0.toHexString()})") - } - - // Read the continuation bytes. If we encounter a non-continuation byte, the sequence consumed - // thus far is truncated and is decoded as the replacement character. That non-continuation byte - // is left in the stream for processing by the next call to readUtf8CodePoint(). - for (i in 1 until byteCount) { - val b = this[i.toLong()] - if (b and 0xc0 == 0x80) { - // 0x10xxxxxx - codePoint = codePoint shl 6 - codePoint = codePoint or (b and 0x3f) - } else { - skip(i.toLong()) - return REPLACEMENT_CODE_POINT - } - } - - skip(byteCount.toLong()) - - return when { - codePoint > 0x10ffff -> { - REPLACEMENT_CODE_POINT // Reject code points larger than the Unicode maximum. - } - - codePoint in 0xd800..0xdfff -> { - REPLACEMENT_CODE_POINT // Reject partial surrogates. - } - - codePoint < min -> { - REPLACEMENT_CODE_POINT // Reject overlong code points. - } - - else -> codePoint - } -} - -@OptIn(UnsafeIoApi::class) -private inline fun Buffer.commonWriteUtf8(beginIndex: Int, endIndex: Int, charAt: (Int) -> Char) { - // Transcode a UTF-16 chars to UTF-8 bytes. - var i = beginIndex - while (i < endIndex) { - var c = charAt(i).code - - when { - c < 0x80 -> { - UnsafeBufferOperations.writeToTail(this, 1) { ctx, segment -> - val segmentOffset = -i - val runLimit = minOf(endIndex, i + segment.remainingCapacity) - - // Emit a 7-bit character with 1 byte. - ctx.setUnchecked(segment, segmentOffset + i++, c.toByte()) // 0xxxxxxx - - // Fast-path contiguous runs of ASCII characters. This is ugly, but yields a ~4x performance - // improvement over independent calls to writeByte(). - while (i < runLimit) { - c = charAt(i).code - if (c >= 0x80) break - ctx.setUnchecked(segment, segmentOffset + i++, c.toByte()) // 0xxxxxxx - } - - i + segmentOffset // Equivalent to i - (previous i). - } - } - - c < 0x800 -> { - // Emit a 11-bit character with 2 bytes. - UnsafeBufferOperations.writeToTail(this, 2) { ctx, segment -> - ctx.setUnchecked( - segment, 0, - (c shr 6 or 0xc0).toByte(), // 110xxxxx - (c and 0x3f or 0x80).toByte() // 10xxxxxx - ) - 2 - } - i++ - } - - c < 0xd800 || c > 0xdfff -> { - // Emit a 16-bit character with 3 bytes. - UnsafeBufferOperations.writeToTail(this, 3) { ctx, segment -> - ctx.setUnchecked( - segment, 0, - (c shr 12 or 0xe0).toByte(), // 1110xxxx - (c shr 6 and 0x3f or 0x80).toByte(), // 10xxxxxx - (c and 0x3f or 0x80).toByte() // 10xxxxxx - ) - 3 - } - i++ - } - - else -> { - // c is a surrogate. Make sure it is a high surrogate & that its successor is a low - // surrogate. If not, the UTF-16 is invalid, in which case we emit a replacement - // character. - val low = (if (i + 1 < endIndex) charAt(i + 1).code else 0) - if (c > 0xdbff || low !in 0xdc00..0xdfff) { - writeByte('?'.code.toByte()) - i++ - } else { - // UTF-16 high surrogate: 110110xxxxxxxxxx (10 bits) - // UTF-16 low surrogate: 110111yyyyyyyyyy (10 bits) - // Unicode code point: 00010000000000000000 + xxxxxxxxxxyyyyyyyyyy (21 bits) - val codePoint = 0x010000 + (c and 0x03ff shl 10 or (low and 0x03ff)) - - // Emit a 21-bit character with 4 bytes. - UnsafeBufferOperations.writeToTail(this, 4) { ctx, segment -> - ctx.setUnchecked(segment, 0, - (codePoint shr 18 or 0xf0).toByte(), // 11110xxx - (codePoint shr 12 and 0x3f or 0x80).toByte(), // 10xxxxxx - (codePoint shr 6 and 0x3f or 0x80).toByte(), // 10xxyyyy - (codePoint and 0x3f or 0x80).toByte() // 10yyyyyy - ) - 4 - } - i += 2 - } - } - } - } -} - -@OptIn(UnsafeIoApi::class) -private fun Buffer.commonWriteUtf8CodePoint(codePoint: Int) { - when { - codePoint < 0 || codePoint > 0x10ffff -> { - throw IllegalArgumentException( - "Code point value is out of Unicode codespace 0..0x10ffff: 0x${codePoint.toHexString()} ($codePoint)" - ) - } - - codePoint < 0x80 -> { - // Emit a 7-bit code point with 1 byte. - writeByte(codePoint.toByte()) - } - - codePoint < 0x800 -> { - // Emit a 11-bit code point with 2 bytes. - UnsafeBufferOperations.writeToTail(this, 2) { ctx, segment -> - ctx.setUnchecked(segment, 0, (codePoint shr 6 or 0xc0).toByte()) // 110xxxxx - ctx.setUnchecked(segment, 1, (codePoint and 0x3f or 0x80).toByte()) // 10xxxxxx - 2 - } - } - - codePoint in 0xd800..0xdfff -> { - // Emit a replacement character for a partial surrogate. - writeByte('?'.code.toByte()) - } - - codePoint < 0x10000 -> { - // Emit a 16-bit code point with 3 bytes. - UnsafeBufferOperations.writeToTail(this, 3) { ctx, segment -> - ctx.setUnchecked(segment, 0, (codePoint shr 12 or 0xe0).toByte()) // 1110xxxx - ctx.setUnchecked(segment, 1, (codePoint shr 6 and 0x3f or 0x80).toByte()) // 10xxxxxx - ctx.setUnchecked(segment, 2, (codePoint and 0x3f or 0x80).toByte()) // 10xxxxxx - 3 - } - } - - else -> { // [0x10000, 0x10ffff] - // Emit a 21-bit code point with 4 bytes. - UnsafeBufferOperations.writeToTail(this, 4) { ctx, segment -> - ctx.setUnchecked(segment,0, (codePoint shr 18 or 0xf0).toByte()) // 11110xxx - ctx.setUnchecked(segment,1, (codePoint shr 12 and 0x3f or 0x80).toByte()) // 10xxxxxx - ctx.setUnchecked(segment,2, (codePoint shr 6 and 0x3f or 0x80).toByte()) // 10xxyyyy - ctx.setUnchecked(segment,3, (codePoint and 0x3f or 0x80).toByte()) // 10yyyyyy - 4 - } - } - } -} - -@OptIn(UnsafeIoApi::class) -private fun Buffer.commonReadUtf8(byteCount: Long): String { - // Invariant: byteCount was request()'ed into this buffer beforehand - if (byteCount == 0L) return "" - - UnsafeBufferOperations.forEachSegment(this) { ctx, segment -> - if (segment.size >= byteCount) { - var result = "" - ctx.withData(segment) { data, pos, limit -> - result = data.commonToUtf8String(pos, min(limit, pos + byteCount.toInt())) - skip(byteCount) - return result - } - } - // If the string spans multiple segments, delegate to readBytes() - return readByteArray(byteCount.toInt()).commonToUtf8String() - } - error("Unreacheable") -} diff --git a/core/common/src/files/FileSystem.kt b/core/common/src/files/FileSystem.kt deleted file mode 100644 index de000d447..000000000 --- a/core/common/src/files/FileSystem.kt +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.files - -import kotlinx.io.IOException -import kotlinx.io.RawSink -import kotlinx.io.RawSource - -/** - * An interface providing basic operations on a filesystem, such as reading and writing files, - * creating directories, gathering file metadata and so on. - * - * Default implementation of this interface is [SystemFileSystem]. It provides access to files and directories - * on disk. - * - * The interface is sealed until API stabilization, but in the future it will allow - * creating custom filesystems that could be used as mocks for testing or provide - * access to some network resources and allow working with them as with regular files, for example. - * - * **This API is unstable and subject to change.** - */ -public sealed interface FileSystem { - /** - * Returns `true` if there is a filesystem entity a [path] points to, - * otherwise returns `false`. - * - * @param path the path that should be checked for existence. - * - * @throws kotlinx.io.IOException when the attempt to check the existence of the [path] failed. - */ - public fun exists(path: Path): Boolean - - /** - * Deletes a file or directory the [path] points to from a filesystem. - * If there is no filesystem entity represented by the [path] - * this method throws [kotlinx.io.files.FileNotFoundException] when [mustExist] is `true`. - * - * Note that in the case of a directory, this method will not attempt to delete it recursively, - * so deletion of non-empty directory will fail. - * - * @param path the path to a file or directory to be deleted. - * @param mustExist the flag indicating whether missing [path] is an error, `true` by default. - * - * @throws kotlinx.io.files.FileNotFoundException when [path] does not exist and [mustExist] is `true`. - * @throws kotlinx.io.IOException if deletion failed. - */ - public fun delete(path: Path, mustExist: Boolean = true) - - /** - * Creates a directory tree represented by the [path]. - * If [path] already exists then the method throws [kotlinx.io.IOException] when [mustCreate] is `true`. - * The call will attempt to create only missing directories. - * The method is not atomic and if it fails after creating some - * directories, these directories will not be deleted automatically. - * Permissions for created directories are platform-specific. - * - * @param path the path to be created. - * @param mustCreate the flag indicating that existence of [path] should be treated as an error, - * by default it is `false`. - * - * @throws kotlinx.io.IOException when [path] already exists and [mustCreate] is `true`. - * @throws kotlinx.io.IOException when the creation of one of the directories fails. - * @throws kotlinx.io.IOException when [path] is an existing file and [mustCreate] is `false`. - */ - public fun createDirectories(path: Path, mustCreate: Boolean = false) - - /** - * Atomically renames [source] to [destination] overriding [destination] if it already exists. - * - * When the filesystem does not support atomic move of [source] and [destination] corresponds to different - * filesystems (or different volumes, on Windows) and the operation could not be performed atomically, - * [UnsupportedOperationException] is thrown. - * - * On some platforms, like Wasm-WASI, there is no way to tell if the underlying filesystem supports atomic move. - * In such cases, the move will be performed and no [UnsupportedOperationException] will be thrown. - * - * When [destination] is an existing directory, the operation may fail on some platforms - * (on Windows, particularly). - * - * @param source the path to rename. - * @param destination desired path name. - * - * @throws kotlinx.io.files.FileNotFoundException when the [source] does not exist. - * @throws kotlinx.io.IOException when the move failed. - * @throws kotlin.UnsupportedOperationException when the filesystem does not support atomic move. - */ - public fun atomicMove(source: Path, destination: Path) - - /** - * Returns [RawSource] to read from a file the [path] points to. - * - * How a source will read the data is implementation-specific and failures caused - * by the missing file or, for example, lack of permissions may not be reported immediately, - * but postponed until the source will try to fetch data. - * - * If [path] points to a directory, this method will fail with [IOException]. - * - * @param path the path to read from. - * - * @throws kotlinx.io.files.FileNotFoundException when the file does not exist. - * @throws kotlinx.io.IOException when it's not possible to open the file for reading. - */ - public fun source(path: Path): RawSource - - /** - * Returns [RawSink] to write into a file the [path] points to. - * Depending on [append] value, the file will be overwritten or data will be appened to it. - * File will be created if it does not exist yet. - * - * How a sink will write the data is implementation-specific and failures caused, - * for example, by the lack of permissions may not be reported immediately, - * but postponed until the sink will try to store data. - * - * If [path] points to a directory, this method will fail with [IOException] - * - * @param path the path to a file to write data to. - * @param append the flag indicating whether the data should be appended to an existing file or it - * should be overwritten, `false` by default, meaning the file will be overwritten. - * - * @throws kotlinx.io.IOException when it's not possible to open the file for writing. - */ - public fun sink(path: Path, append: Boolean = false): RawSink - - /** - * Return [FileMetadata] associated with a file or directory the [path] points to. - * If there is no such file or directory, or it's impossible to fetch metadata, - * `null` is returned. - * - * @param path the path to get the metadata for. - */ - public fun metadataOrNull(path: Path): FileMetadata? - - /** - * Returns an absolute path to the same file or directory the [path] is pointing to. - * All symbolic links are solved, extra path separators and references to current (`.`) or - * parent (`..`) directories are removed. - * If the [path] is a relative path then it'll be resolved against current working directory. - * If there is no file or directory to which the [path] is pointing to then [FileNotFoundException] will be thrown. - * - * @param path the path to resolve. - * @return a resolved path. - * @throws FileNotFoundException if there is no file or directory corresponding to the specified path. - */ - public fun resolve(path: Path): Path - - /** - * Returns paths corresponding to [directory]'s immediate children. - * - * There are no guarantees on children paths order within a returned collection. - * - * If path [directory] was an absolute path, a returned collection will also contain absolute paths. - * If it was a relative path, a returned collection will contain relative paths. - * - * *For `wasmWasi` target, function does not work with NodeJS runtime on Windows, - * as `fd_readdir` function is [not implemented there](https://github.com/nodejs/node/blob/6f4d6011ea1b448cf21f5d363c44e4a4c56ca34c/deps/uvwasi/src/uvwasi.c#L19).* - * - * @param directory a directory to list. - * @return a collection of [directory]'s immediate children. - * @throws FileNotFoundException if [directory] does not exist. - * @throws IOException if [directory] points to something other than directory. - * @throws IOException if there was an underlying error preventing listing [directory] children. - */ - public fun list(directory: Path): Collection -} - -internal abstract class SystemFileSystemImpl : FileSystem - -/** - * An instance of [FileSystem] representing a default system-wide filesystem. - */ -public expect val SystemFileSystem: FileSystem - -/** - * Path to a directory suitable for creating temporary files. - * - * This path is not bound to a particular filesystem, but rather a system-wide temporary directory. - */ -public expect val SystemTemporaryDirectory: Path - -/** - * Represents information about a file or directory obtainable from a filesystem like - * a type of filesystem node (is it a directory or a file?), its size, and so on. - */ -public class FileMetadata( - /** - * Flag indicating that the metadata was retrieved for a regular file. - */ - public val isRegularFile: Boolean = false, - /** - * Flag indicating that the metadata was retrieved for a directory. - */ - public val isDirectory: Boolean = false, - /** - * File size. Defined only for regular files, for other filesystem entities it will be `-1`. - */ - public val size: Long = 0L -) - -/** - * Signals an I/O operation's failure due to a missing file or directory. - */ -public expect class FileNotFoundException(message: String?) : IOException - -internal const val WindowsPathSeparator: Char = '\\' -internal const val UnixPathSeparator: Char = '/' - -internal expect val isWindows: Boolean diff --git a/core/common/src/files/Paths.kt b/core/common/src/files/Paths.kt deleted file mode 100644 index 7f75cfb12..000000000 --- a/core/common/src/files/Paths.kt +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -package kotlinx.io.files - -import kotlinx.io.* -import kotlin.jvm.JvmName - -/** - * A wrapper around a string representing a file path allowing to read from and write to a - * corresponding file using [Sink] and [Source]. - * - * **This API is unstable and subject to change.** - */ -public expect class Path { - /** - * Returns a path representing a parent directory for this path, - * or `null` if there is no parent directory for this path. - * - * How the parent path is resolved is platform-specific. - */ - public val parent: Path? - - /** - * Returns the name of the file or directory represented by this path. - * - * How the name is resolved is platform-specific. - * - * In general, one may expect that for a path like `Path("home", "Downloads", "file.txt")` - * the name is `file.txt`. - */ - public val name: String - - /** - * Returns `true` if this path is absolute, `false` otherwise. - * - * How an absolute path is resolved is platform-specific. - */ - public val isAbsolute: Boolean - - /** - * Returns a string representation of this path. - * - * Note that the returned value will represent the same path as the value - * passed to [Path], but it may not be identical to it. - */ - override fun toString(): String - - /** - * Returns hash code of this Path. - * The hash code is calculated for the path's string representations ([toString]). - */ - override fun hashCode(): Int - - /** - * Compares two paths for equality using its string representation ([toString]). - */ - override fun equals(other: Any?): Boolean -} - -/** - * A platform-specific character separating parts of the path. - * It's inherited from the default system's filesystem. - * It should not be used if an application is working with multiple filesystems having different separators. - * - * For example, the separator is usually `/` on Unix and `\` on Windows. - */ -public expect val SystemPathSeparator: Char - -/** - * Returns Path for the given string without much of a validation. - */ -public expect fun Path(path: String): Path - -/** - * Returns Path for the given [base] path concatenated with [parts] using [SystemPathSeparator]. - */ -public fun Path(base: String, vararg parts: String): Path { - // Parameter name has to be specified explicitly to overcome https://youtrack.jetbrains.com/issue/KT-22520 - return Path(path = buildString { - append(base) - parts.forEach { - if (isNotEmpty() && !endsWith(SystemPathSeparator)) { - append(SystemPathSeparator) - } - append(it) - } - }) -} - -/** - * Returns Path for the given [base] path concatenated with [parts] using [SystemPathSeparator]. - */ -public fun Path(base: Path, vararg parts: String): Path { - return Path(base.toString(), *parts) -} - -/** - * Returns [RawSource] for the given file or throws if path is not a file or does not exist - * - * Use of this method is deprecated with warning since kotlinx-io 0.3.0. The method will be removed in 0.6.0. - */ -@Deprecated( - message = "Use FileSystem.source instead", - replaceWith = ReplaceWith( - expression = "SystemFileSystem.source(this).buffered()", - imports = arrayOf("kotlinx.io.files.FileSystem") - ), - level = DeprecationLevel.WARNING -) -@JvmName("sourceDeprecated") -public fun Path.source(): Source = SystemFileSystem.source(this).buffered() - -/** - * Returns [RawSink] for the given path, creates file if it doesn't exist, throws if it's a directory, - * overwrites contents. - * - * Use of this method is deprecated with warning since kotlinx-io 0.3.0. The method will be removed in 0.6.0. - */ -@Deprecated( - message = "Use FileSystem.sink instead", - replaceWith = ReplaceWith( - expression = "SystemFileSystem.sink(this).buffered()", - imports = arrayOf("kotlinx.io.files.FileSystem") - ), - level = DeprecationLevel.WARNING -) -@JvmName("sinkDeprecated") -public fun Path.sink(): Sink = SystemFileSystem.sink(this).buffered() - -internal fun removeTrailingSeparators(path: String, /* only for testing */ isWindows_: Boolean = isWindows): String { - if (isWindows_) { - // don't trim the path separator right after the drive name - val limit = if (path.length > 1) { - if (path[1] == ':') { - 3 - } else if (isUnc(path)) { - 2 - } else { - 1 - } - } else { - 1 - } - return removeTrailingSeparatorsWindows(limit, path) - } - return removeTrailingSeparatorsUnix(path) -} - -private fun isUnc(path: String): Boolean { - if (path.length < 2) return false - if (path.startsWith("$WindowsPathSeparator$WindowsPathSeparator")) return true - if (path.startsWith("$UnixPathSeparator$UnixPathSeparator")) return true - return false -} - -private fun removeTrailingSeparatorsUnix(path: String): String { - var idx = path.length - while (idx > 1 && path[idx - 1] == UnixPathSeparator) { - idx-- - } - return path.substring(0, idx) -} - -private fun removeTrailingSeparatorsWindows(suffixLength: Int, path: String): String { - require(suffixLength >= 1) - var idx = path.length - while (idx > suffixLength) { - val c = path[idx - 1] - if (c != WindowsPathSeparator && c != UnixPathSeparator) break - idx-- - } - return path.substring(0, idx) -} diff --git a/core/common/src/internal/-Utf8.kt b/core/common/src/internal/-Utf8.kt deleted file mode 100644 index 08a22b588..000000000 --- a/core/common/src/internal/-Utf8.kt +++ /dev/null @@ -1,405 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2018 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kotlinx.io.internal - -import kotlinx.io.and -import kotlinx.io.shr - -internal fun ByteArray.commonToUtf8String(beginIndex: Int = 0, endIndex: Int = size): String { - if (beginIndex < 0 || endIndex > size || beginIndex > endIndex) { - throw IndexOutOfBoundsException("size=$size beginIndex=$beginIndex endIndex=$endIndex") - } - val chars = CharArray(endIndex - beginIndex) - - var length = 0 - processUtf16Chars(beginIndex, endIndex) { c -> - chars[length++] = c - } - - return chars.concatToString(0, length) -} - -internal const val REPLACEMENT_BYTE: Byte = '?'.code.toByte() -internal const val REPLACEMENT_CHARACTER: Char = '\ufffd' -internal const val REPLACEMENT_CODE_POINT: Int = REPLACEMENT_CHARACTER.code - -@Suppress("NOTHING_TO_INLINE") // Syntactic sugar. -internal inline fun isIsoControl(codePoint: Int): Boolean = - (codePoint in 0x00..0x1F) || (codePoint in 0x7F..0x9F) - -@Suppress("NOTHING_TO_INLINE") // Syntactic sugar. -internal inline fun isUtf8Continuation(byte: Byte): Boolean { - // 0b10xxxxxx - return byte and 0xc0 == 0x80 -} - -internal inline fun ByteArray.processUtf8CodePoints( - beginIndex: Int, - endIndex: Int, - yield: (Int) -> Unit -) { - var index = beginIndex - while (index < endIndex) { - val b0 = this[index] - when { - b0 >= 0 -> { - // 0b0xxxxxxx - yield(b0.toInt()) - index++ - - // Assume there is going to be more ASCII - while (index < endIndex && this[index] >= 0) { - yield(this[index++].toInt()) - } - } - - b0 shr 5 == -2 -> { - // 0b110xxxxx - index += process2Utf8Bytes(index, endIndex) { yield(it) } - } - - b0 shr 4 == -2 -> { - // 0b1110xxxx - index += process3Utf8Bytes(index, endIndex) { yield(it) } - } - - b0 shr 3 == -2 -> { - // 0b11110xxx - index += process4Utf8Bytes(index, endIndex) { yield(it) } - } - - else -> { - // 0b10xxxxxx - Unexpected continuation - // 0b111111xxx - Unknown encoding - yield(REPLACEMENT_CODE_POINT) - index++ - } - } - } -} - -// Value added to the high UTF-16 surrogate after shifting -internal const val HIGH_SURROGATE_HEADER = 0xd800 - (0x010000 ushr 10) - -// Value added to the low UTF-16 surrogate after masking -internal const val LOG_SURROGATE_HEADER = 0xdc00 - -internal inline fun ByteArray.processUtf16Chars( - beginIndex: Int, - endIndex: Int, - yield: (Char) -> Unit -) { - var index = beginIndex - while (index < endIndex) { - val b0 = this[index] - when { - b0 >= 0 -> { - // 0b0xxxxxxx - yield(b0.toInt().toChar()) - index++ - - // Assume there is going to be more ASCII - // This is almost double the performance of the outer loop - while (index < endIndex && this[index] >= 0) { - yield(this[index++].toInt().toChar()) - } - } - - b0 shr 5 == -2 -> { - // 0b110xxxxx - index += process2Utf8Bytes(index, endIndex) { yield(it.toChar()) } - } - - b0 shr 4 == -2 -> { - // 0b1110xxxx - index += process3Utf8Bytes(index, endIndex) { yield(it.toChar()) } - } - - b0 shr 3 == -2 -> { - // 0b11110xxx - index += process4Utf8Bytes(index, endIndex) { codePoint -> - if (codePoint != REPLACEMENT_CODE_POINT) { - // Unicode code point: 00010000000000000000 + xxxxxxxxxxyyyyyyyyyy (21 bits) - // UTF-16 high surrogate: 110110xxxxxxxxxx (10 bits) - // UTF-16 low surrogate: 110111yyyyyyyyyy (10 bits) - /* ktlint-disable no-multi-spaces paren-spacing */ - yield(((codePoint ushr 10) + HIGH_SURROGATE_HEADER).toChar()) - /* ktlint-enable no-multi-spaces paren-spacing */ - yield(((codePoint and 0x03ff) + LOG_SURROGATE_HEADER).toChar()) - } else { - yield(REPLACEMENT_CHARACTER) - } - } - } - - else -> { - // 0b10xxxxxx - Unexpected continuation - // 0b111111xxx - Unknown encoding - yield(REPLACEMENT_CHARACTER) - index++ - } - } - } -} - -// ===== UTF-8 Encoding and Decoding ===== // -/* -The following 3 methods take advantage of using XOR on 2's complement store -numbers to quickly and efficiently combine the important data of UTF-8 encoded -bytes. This will be best explained using an example, so lets take the following -encoded character '∇' = \u2207. - -Using the Unicode code point for this character, 0x2207, we will split the -binary representation into 3 sections as follows: - - 0x2207 = 0b0010 0010 0000 0111 - xxxx yyyy yyzz zzzz - -Now take each section of bits and add the appropriate header: - - utf8(0x2207) = 0b1110 xxxx 0b10yy yyyy 0b10zz zzzz - = 0b1110 0010 0b1000 1000 0b1000 0111 - = 0xe2 0x88 0x87 - -We have now just encoded this as a 3 byte UTF-8 character. More information -about different sizes of characters can be found here: - https://en.wikipedia.org/wiki/UTF-8 - -Encoding was pretty easy, but decoding is a bit more complicated. We need to -first determine the number of bytes used to represent the character, strip all -the headers, and then combine all the bits into a single integer. Let's use the -character we just encoded and work backwards, taking advantage of 2's complement -integer representation and the XOR function. - -Let's look at the decimal representation of these bytes: - - 0xe2, 0x88, 0x87 = -30, -120, -121 - -The first interesting thing to notice is that UTF-8 headers all start with 1 - -except for ASCII which is encoded as a single byte - which means all UTF-8 bytes -will be negative. So converting these to integers results in a lot of 1's added -because they are store as 2's complement: - - 0xe2 = -30 = 0xffff ffe2 - 0x88 = -120 = 0xffff ff88 - 0x87 = -121 = 0xffff ff87 - -Now let's XOR these with their corresponding UTF-8 byte headers to see what -happens: - - 0xffff ffe2 xor 0xffff ffe0 = 0x0000 0002 - 0xffff ff88 xor 0xffff ff80 = 0x0000 0008 - 0xffff ff87 xor 0xffff ff80 = 0x0000 0007 - -***This is why we must first convert the byte header mask to a byte and then -back to an integer, so it is properly converted to a 2's complement negative -number which can be applied to each byte.*** - -Now let's look at the binary representation to see how we can combine these to -create the Unicode code point: - - 0b0000 0010 0b0000 1000 0b0000 0111 - 0b1110 xxxx 0b10yy yyyy 0b10zz zzzz - -Combining each section will require some bit shifting, but then they can just -be OR'd together. They can also be XOR'd together which makes use of a single, -COMMUTATIVE, operator through the entire calculation. - - << 12 = 00000010 - << 6 = 00001000 - << 0 = 00000111 - XOR = 00000010001000000111 - - code point = 0b0010 0010 0000 0111 - = 0x2207 - -And there we have it! The decoded UTF-8 character '∇'! And because the XOR -operator is commutative, we can re-arrange all this XOR and shifting to create -a single mask that can be applied to 3-byte UTF-8 characters after their bytes -have been shifted and XOR'd together. - */ - -// Mask used to remove byte headers from a 2 byte encoded UTF-8 character -internal const val MASK_2BYTES = 0x0f80 -// MASK_2BYTES = -// (0xc0.toByte() shl 6) xor -// (0x80.toByte().toInt()) - -internal inline fun ByteArray.process2Utf8Bytes( - beginIndex: Int, - endIndex: Int, - yield: (Int) -> Unit -): Int { - if (endIndex <= beginIndex + 1) { - yield(REPLACEMENT_CODE_POINT) - // Only 1 byte remaining - underflow - return 1 - } - - val b0 = this[beginIndex] - val b1 = this[beginIndex + 1] - if (!isUtf8Continuation(b1)) { - yield(REPLACEMENT_CODE_POINT) - return 1 - } - - val codePoint = (MASK_2BYTES - xor (b1.toInt()) - xor (b0.toInt() shl 6)) - - when { - codePoint < 0x80 -> yield(REPLACEMENT_CODE_POINT) // Reject overlong code points. - else -> yield(codePoint) - } - return 2 -} - -// Mask used to remove byte headers from a 3 byte encoded UTF-8 character -internal const val MASK_3BYTES = -0x01e080 -// MASK_3BYTES = -// (0xe0.toByte() shl 12) xor -// (0x80.toByte() shl 6) xor -// (0x80.toByte().toInt()) - -internal inline fun ByteArray.process3Utf8Bytes( - beginIndex: Int, - endIndex: Int, - yield: (Int) -> Unit -): Int { - if (endIndex <= beginIndex + 2) { - // At least 2 bytes remaining - yield(REPLACEMENT_CODE_POINT) - if (endIndex <= beginIndex + 1 || !isUtf8Continuation(this[beginIndex + 1])) { - // Only 1 byte remaining - underflow - // Or 2nd byte is not a continuation - malformed - return 1 - } else { - // Only 2 bytes remaining - underflow - return 2 - } - } - - val b0 = this[beginIndex] - val b1 = this[beginIndex + 1] - if (!isUtf8Continuation(b1)) { - yield(REPLACEMENT_CODE_POINT) - return 1 - } - val b2 = this[beginIndex + 2] - if (!isUtf8Continuation(b2)) { - yield(REPLACEMENT_CODE_POINT) - return 2 - } - - val codePoint = (MASK_3BYTES - xor (b2.toInt()) - xor (b1.toInt() shl 6) - xor (b0.toInt() shl 12)) - - when { - codePoint < 0x800 -> { - yield(REPLACEMENT_CODE_POINT) // Reject overlong code points. - } - - codePoint in 0xd800..0xdfff -> { - yield(REPLACEMENT_CODE_POINT) // Reject partial surrogates. - } - - else -> { - yield(codePoint) - } - } - return 3 -} - -// Mask used to remove byte headers from a 4 byte encoded UTF-8 character -internal const val MASK_4BYTES = 0x381f80 -// MASK_4BYTES = -// (0xf0.toByte() shl 18) xor -// (0x80.toByte() shl 12) xor -// (0x80.toByte() shl 6) xor -// (0x80.toByte().toInt()) - -internal inline fun ByteArray.process4Utf8Bytes( - beginIndex: Int, - endIndex: Int, - yield: (Int) -> Unit -): Int { - if (endIndex <= beginIndex + 3) { - // At least 3 bytes remaining - yield(REPLACEMENT_CODE_POINT) - if (endIndex <= beginIndex + 1 || !isUtf8Continuation(this[beginIndex + 1])) { - // Only 1 byte remaining - underflow - // Or 2nd byte is not a continuation - malformed - return 1 - } else if (endIndex <= beginIndex + 2 || !isUtf8Continuation(this[beginIndex + 2])) { - // Only 2 bytes remaining - underflow - // Or 3rd byte is not a continuation - malformed - return 2 - } else { - // Only 3 bytes remaining - underflow - return 3 - } - } - - val b0 = this[beginIndex] - val b1 = this[beginIndex + 1] - if (!isUtf8Continuation(b1)) { - yield(REPLACEMENT_CODE_POINT) - return 1 - } - val b2 = this[beginIndex + 2] - if (!isUtf8Continuation(b2)) { - yield(REPLACEMENT_CODE_POINT) - return 2 - } - val b3 = this[beginIndex + 3] - if (!isUtf8Continuation(b3)) { - yield(REPLACEMENT_CODE_POINT) - return 3 - } - - val codePoint = (MASK_4BYTES - xor (b3.toInt()) - xor (b2.toInt() shl 6) - xor (b1.toInt() shl 12) - xor (b0.toInt() shl 18)) - - when { - codePoint > 0x10ffff -> { - yield(REPLACEMENT_CODE_POINT) // Reject code points larger than the Unicode maximum. - } - - codePoint in 0xd800..0xdfff -> { - yield(REPLACEMENT_CODE_POINT) // Reject partial surrogates. - } - - codePoint < 0x10000 -> { - yield(REPLACEMENT_CODE_POINT) // Reject overlong code points. - } - - else -> { - yield(codePoint) - } - } - return 4 -} \ No newline at end of file diff --git a/core/common/src/unsafe/UnsafeBufferOperations.kt b/core/common/src/unsafe/UnsafeBufferOperations.kt deleted file mode 100644 index f6cceceb6..000000000 --- a/core/common/src/unsafe/UnsafeBufferOperations.kt +++ /dev/null @@ -1,567 +0,0 @@ -/* - * Copyright 2010-2024 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.unsafe - -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind.EXACTLY_ONCE -import kotlin.contracts.contract -import kotlinx.io.* -import kotlin.jvm.JvmSynthetic - -@UnsafeIoApi -@OptIn(ExperimentalContracts::class) -public object UnsafeBufferOperations { - /** - * Maximum value that is safe to pass to [writeToTail]. - */ - public val maxSafeWriteCapacity: Int get() = Segment.SIZE - - /** - * Moves [bytes] to the end of the [buffer]. - * - * Only the region of the [bytes] array spanning from [startIndex] until [endIndex] is considered readable. - * - * The array is wrapped into the buffer without any copies, if possible. - * - * Attempts to write data into [bytes] array once it was moved may lead to data corruption - * and should be considered as an error. - * - * @param buffer a buffer to which data will be added - * @param bytes an array that needs to be added to the buffer - * @param startIndex an index of the first byte readable from the array, `0` by default. - * @param endIndex an index of the byte past the last readable array byte, `bytes.size` byte default. - * - * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] are not within [bytes] bounds - * @throws IllegalArgumentException when `startIndex > endIndex` - * - * @sample kotlinx.io.samples.unsafe.UnsafeBufferOperationsSamples.moveToTail - */ - public fun moveToTail(buffer: Buffer, bytes: ByteArray, startIndex: Int = 0, endIndex: Int = bytes.size) { - checkBounds(bytes.size, startIndex, endIndex) - val segment = Segment.new( - bytes, startIndex, endIndex, - AlwaysSharedCopyTracker, /* to prevent recycling */ - owner = false /* can't append to it */ - ) - val tail = buffer.tail - if (tail == null) { - buffer.head = segment - buffer.tail = segment - } else { - buffer.tail = tail.push(segment) - } - buffer.sizeMut += endIndex - startIndex - } - - /** - * Provides read-only access to the data from the head of a [buffer] by calling the [readAction] on head's data and - * optionally consumes the data at the end of the action. - * - * The [readAction] receives the byte array with buffer head's data and a pair of indices, startIndex and endIndex, - * denoting the subarray containing meaningful data. - * It's considered an error to read data outside of that range. - * The data array is provided for read-only purposes, updating it may affect buffer's data - * and may lead to undefined behavior when performed outside the provided range. - * - * The [readAction] should return the number of bytes consumed, the buffer's size will be decreased by that value, - * and data from the consumed prefix will be no longer available for read. - * If the operation does not consume anything, the action should return `0`. - * It's considered an error to return a negative value or a value exceeding the size of a readable range. - * This value will also be propagated as the function return value. - * - * If [readAction] ends execution by throwing an exception, no data will be consumed from the buffer. - * - * If the buffer is empty, [IllegalArgumentException] will be thrown. - * - * The data is passed to the [readAction] directly from the buffer's internal storage without copying on - * the best effort basis, meaning that there are no strong zero-copy guarantees - * and the copy will be created if it could not be omitted. - * - * @return Number of bytes consumed as returned by [readAction]. - * - * @throws IllegalStateException when [readAction] returns negative value or a values exceeding - * the `endIndexExclusive - startIndexInclusive` value. - * @throws IllegalArgumentException when the [buffer] is empty. - * - * @sample kotlinx.io.samples.unsafe.UnsafeBufferOperationsSamples.readByteArrayFromHead - */ - public inline fun readFromHead( - buffer: Buffer, - readAction: (bytes: ByteArray, startIndexInclusive: Int, endIndexExclusive: Int) -> Int - ): Int { - contract { - callsInPlace(readAction, EXACTLY_ONCE) - } - - require(!buffer.exhausted()) { "Buffer is empty" } - val head = buffer.head!! - val bytesRead = readAction(head.dataAsByteArray(true), head.pos, head.limit) - if (bytesRead != 0) { - if (bytesRead < 0) throw IllegalStateException("Returned negative read bytes count") - if (bytesRead > head.size) throw IllegalStateException("Returned too many bytes") - buffer.skip(bytesRead.toLong()) - } - return bytesRead - } - - /** - * Provides read-only access to the data from the head of a [buffer] by calling the [readAction] on head's data and - * optionally consumes the data at the end of the action. - * - * The [readAction] receives the buffer head data and an instance of [SegmentReadContext] allowing to read data from - * the segment. - * The head segment is provided for read-only purposes, updating it may affect buffer's data - * and may lead to undefined behavior when performed outside the provided range. - * - * The [readAction] should return the number of bytes consumed, the buffer's size will be decreased by that value, - * and data from the consumed prefix will be no longer available for read. - * If the operation does not consume anything, the action should return `0`. - * It's considered an error to return a negative value or a value exceeding the [Segment.size]. - * This value will also be propagated as the function return value. - * - * Both [readAction] arguments are valid only within [readAction] scope, - * it's an error to store and reuse it later. - * - * If the buffer is empty, [IllegalArgumentException] will be thrown. - * - * @return Number of bytes consumed as returned by [readAction]. - * - * @throws IllegalStateException when [readAction] returns negative value or a values exceeding - * the [Segment.size] value. - * @throws IllegalArgumentException when the [buffer] is empty. - * - * @sample kotlinx.io.samples.unsafe.UnsafeBufferOperationsSamples.readUleb128 - */ - public inline fun readFromHead(buffer: Buffer, readAction: (SegmentReadContext, Segment) -> Int): Int { - contract { - callsInPlace(readAction, EXACTLY_ONCE) - } - - require(!buffer.exhausted()) { "Buffer is empty" } - val head = buffer.head!! - val bytesRead = readAction(SegmentReadContextImpl, head) - if (bytesRead != 0) { - if (bytesRead < 0) throw IllegalStateException("Returned negative read bytes count") - if (bytesRead > head.size) throw IllegalStateException("Returned too many bytes") - buffer.skip(bytesRead.toLong()) - } - return bytesRead - } - - /** - * Provides write access to the buffer, allowing to write data - * into a not yet committed portion of the buffer's tail using a [writeAction]. - * - * The [writeAction] receives the byte array and the pair of indices, startIndex and endIndex, - * denoting the range of indices available for writing. It's considered an error to write data outside that range. - * Writing outside the range may corrupt buffer's data. - * - * It's guaranteed that the size of the range is at least [minimumCapacity], - * but if the [minimumCapacity] bytes could not be provided for writing, - * the method will throw [IllegalStateException]. - * It is safe to use any [minimumCapacity] value below [maxSafeWriteCapacity], but unless exact minimum number of - * available bytes is required, it's recommended to use `1` as [minimumCapacity] value. - * - * The value returned by the [writeAction] denotes the number of bytes successfully written to the buffer. - * If no data was written, `0` should be returned. - * It's an error to return a negative value or a value exceeding the size of the provided writeable range. - * This value will also be propagated as the function return value. - * - * If [writeAction] ends execution by throwing an exception, no data will be written to the buffer. - * - * The data array is passed to the [writeAction] directly from the buffer's internal storage without copying - * on the best-effort basis, meaning that there are no strong zero-copy guarantees - * and the copy will be created if it could not be omitted. - * - * @return Number of bytes written as returned by [writeAction]. - * - * @throws IllegalStateException when [minimumCapacity] is too large and could not be fulfilled. - * @throws IllegalStateException when [writeAction] returns a negative value or a value exceeding - * the `endIndexExclusive - startIndexInclusive` value. - * - * @sample kotlinx.io.samples.unsafe.UnsafeBufferOperationsSamples.writeByteArrayToTail - */ - public inline fun writeToTail( - buffer: Buffer, minimumCapacity: Int, - writeAction: (bytes: ByteArray, startIndexInclusive: Int, endIndexExclusive: Int) -> Int - ): Int { - contract { - callsInPlace(writeAction, EXACTLY_ONCE) - } - - val tail = buffer.writableSegment(minimumCapacity) - - val data = tail.dataAsByteArray(false) - // If writeAction throws an exception, we may end up with an empty segment in tail. - // That's fine as long as we don't treat the presence of a segment as a sing of a buffer being non-empty. - val bytesWritten = writeAction(data, tail.limit, data.size) - - // fast path - if (bytesWritten == minimumCapacity) { - tail.writeBackData(data, bytesWritten) - tail.limit += bytesWritten - buffer.sizeMut += bytesWritten - return bytesWritten - } - - check(bytesWritten in 0..tail.remainingCapacity) { - "Invalid number of bytes written: $bytesWritten. Should be in 0..${tail.remainingCapacity}" - } - if (bytesWritten != 0) { - tail.writeBackData(data, bytesWritten) - tail.limit += bytesWritten - buffer.sizeMut += bytesWritten - return bytesWritten - } - if (tail.isEmpty()) { - buffer.recycleTail() - } - return bytesWritten - } - - /** - * Provides write access to the [buffer], allowing to write data - * into a not yet committed portion of the buffer's tail using a [writeAction]. - * - * The [writeAction] receives the segment to write data into and an instance of [SegmentWriteContext] allowing to - * write data. - * - * It's guaranteed that the [Segment.remainingCapacity] for the provided segment is at least [minimumCapacity], - * but if the [minimumCapacity] bytes could not be provided for writing, - * the method will throw [IllegalStateException]. - * It is safe to use any [minimumCapacity] value below [maxSafeWriteCapacity], but unless exact minimum number of - * available bytes is required, it's recommended to use `1` as [minimumCapacity] value. - * - * The value returned by the [writeAction] denotes the number of bytes successfully written to the buffer. - * If no data was written, `0` should be returned. - * It's an error to return a negative value or a value exceeding the [Segment.remainingCapacity]. - * This value will also be propagated as the function return value. - * - * Both [writeAction] arguments are valid only within [writeAction] scope, - * it's an error to store and reuse it later. - * - * @return Number of bytes written as returned by [writeAction]. - * - * @throws IllegalStateException when [minimumCapacity] is too large and could not be fulfilled. - * @throws IllegalStateException when [writeAction] returns a negative value or a value exceeding - * the [Segment.remainingCapacity] value for the provided segment. - * - * @sample kotlinx.io.samples.unsafe.UnsafeBufferOperationsSamples.writeUleb128 - */ - public inline fun writeToTail( - buffer: Buffer, - minimumCapacity: Int, - writeAction: (context: SegmentWriteContext, tail: Segment) -> Int - ): Int { - contract { - callsInPlace(writeAction, EXACTLY_ONCE) - } - val tail = buffer.writableSegment(minimumCapacity) - val bytesWritten = writeAction(SegmentWriteContextImpl, tail) - - // fast path - if (bytesWritten == minimumCapacity) { - tail.limit += bytesWritten - buffer.sizeMut += bytesWritten - return bytesWritten - } - - check(bytesWritten in 0..tail.remainingCapacity) { - "Invalid number of bytes written: $bytesWritten. Should be in 0..${tail.remainingCapacity}" - } - if (bytesWritten != 0) { - tail.limit += bytesWritten - buffer.sizeMut += bytesWritten - return bytesWritten - } - - if (tail.isEmpty()) { - buffer.recycleTail() - } - return bytesWritten - } - - /** - * Provides access to [buffer] segments starting from the head. - * - * [iterationAction] is invoked with an instance of [BufferIterationContext] - * allowing to iterate over [buffer]'s segments - * and a reference to [buffer]'s head segment, which could be null in case of an empty buffer. - * - * It's considered an error to use a [BufferIterationContext] or a [Segment] instances outside the scope of - * the [iterationAction]. - * - * Both [iterationAction] arguments are valid only within [iterationAction] scope, - * it is an error to store and reuse it later. - * - * For a full iteration over buffer's segments, see [forEachSegment]. - * - * @param buffer a buffer to iterate over - * @param iterationAction a callback to invoke with the head reference and an iteration context instance - * - * @sample kotlinx.io.samples.unsafe.UnsafeBufferOperationsSamples.crc32GetUnchecked - */ - public inline fun iterate( - buffer: Buffer, - iterationAction: (context: BufferIterationContext, head: Segment?) -> Unit - ) { - contract { - callsInPlace(iterationAction, EXACTLY_ONCE) - } - iterationAction(BufferIterationContextImpl, buffer.head) - } - - /** - * Provides access to [buffer] segments starting from a segment spanning over a specified [offset]. - * - * [iterationAction] is invoked with an instance of [BufferIterationContext] - * allowing to iterate over [buffer]'s segments, a reference to a [buffer]'s segment, - * an offset corresponding to the beginning of the segment. - * - * If the [buffer] is empty, [iterationAction] will be invoked with a null segment. - * - * To locate [buffer]'s [offset]'th byte within the supplied segment, one has to subtract [offset] from the supplied - * offset value. - * - * All [iterationAction] arguments are valid only within [iterationAction] scope, - * it's an error to store and reuse it later. - * - * @param buffer a buffer to iterate over - * @param iterationAction a callback to invoke with an iteration context instance, a segment reference and - * an offset corresponding to the segment's beginning. - * - * @throws IllegalArgumentException when [offset] is negative - * @throws IndexOutOfBoundsException when [offset] is greater or equal to [Buffer.size] - */ - public inline fun iterate( - buffer: Buffer, offset: Long, - iterationAction: (context: BufferIterationContext, segment: Segment?, startOfTheSegmentOffset: Long) -> Unit - ) { - contract { - callsInPlace(iterationAction, EXACTLY_ONCE) - } - - require(offset >= 0) { "Offset must be non-negative: $offset" } - if (offset >= buffer.size) { - throw IndexOutOfBoundsException("Offset should be less than buffer's size (${buffer.size}): $offset") - } - - buffer.seek(offset) { s, o -> - iterationAction(BufferIterationContextImpl, s, o) - } - } - - /** - * Iterates over [buffer] segments starting from the head. - * - * [action] is invoked with an instance of [SegmentReadContext] - * allowing to read and write in an unchecked manner from [buffer]'s segments - * - * It is considered an error to use a [SegmentReadContext] or a [Segment] instances outside the scope of - * the [action]. - * - * Both [action] arguments are valid only within [action] scope, it is an error to store and reuse it later. - * The action might never be invoked if the given [buffer] is empty. - * - * @param buffer a buffer to iterate over - * @param action a callback to invoke with the head reference and an iteration context instance - * @sample kotlinx.io.samples.unsafe.UnsafeReadWriteSamplesJvm.messageDigest - * @sample kotlinx.io.samples.unsafe.UnsafeBufferOperationsSamples.crc32Unsafe - */ - public inline fun forEachSegment( - buffer: Buffer, - action: (context: SegmentReadContext, segment: Segment) -> Unit - ) { - var curr: Segment? = buffer.head - while (curr != null) { - action(SegmentReadContextImpl, curr) - curr = curr.next - } - } -} - -/** - * Provides read access to [Segment]'s data. - */ -@UnsafeIoApi -public interface SegmentReadContext { - /** - * Reads [offset]'s byte from [segment]. - * - * This operation does not perform any checks, and it's caller's responsibility to ensure - * that [offset] is between `0` and [Segment.size]. - * - * @param segment a segment to read from - * @param offset an offset into segment data - * - * @sample kotlinx.io.samples.unsafe.UnsafeBufferOperationsSamples.crc32GetUnchecked - * - */ - public fun getUnchecked(segment: Segment, offset: Int): Byte -} - -/** - * Provides read-only access to [segment]'s data by passing it to [readAction] along with two indices, startIndex - * and endIndex, denoting a readable portion of the array. - * - * It's considered an error to read data outside of that range. - * The data array is provided for read-only purposes, updating it may affect segment's data - * and may lead to undefined behavior when performed outside the provided range. - * - * The data is passed to the [readAction] directly from the segment's internal storage without copying on - * the best effort basis, meaning that there are no strong zero-copy guarantees - * and the copy will be created if it could not be omitted. - * - * @param segment a segment to access data from - * @param readAction an action to invoke on segment's data - * - * @sample kotlinx.io.samples.unsafe.UnsafeReadWriteSamplesJvm.messageDigest - * @sample kotlinx.io.samples.unsafe.UnsafeBufferOperationsSamples.crc32Unsafe - */ -@UnsafeIoApi -@JvmSynthetic -@OptIn(ExperimentalContracts::class) -public inline fun SegmentReadContext.withData( - segment: Segment, - readAction: (bytes: ByteArray, startIndexInclusive: Int, endIndexExclusive: Int) -> Unit -) { - contract { - callsInPlace(readAction, EXACTLY_ONCE) - } - readAction(segment.dataAsByteArray(true), segment.pos, segment.limit) -} - -/** - * Provides write access to [Segment]'s data. - */ -@UnsafeIoApi -public interface SegmentWriteContext { - /** - * Writes [value] to an uncommitted portion of the [segment] at [offset]. - * - * This operation does not perform any checks, and it's caller's responsibility to ensure - * that [offset] is between `0` and [Segment.remainingCapacity]. - * - * Writing outside that range is considered an error and may lead to data corruption. - * - * @param segment a segment to write into - * @param offset an offset inside [segment]'s uncommitted area to write to - * @param value a value to be written - * - * @sample kotlinx.io.samples.unsafe.UnsafeBufferOperationsSamples.writeUleb128 - */ - public fun setUnchecked(segment: Segment, offset: Int, value: Byte) - - /** - * Writes two bytes, [b0] and [b1] to an uncommitted portion of the [segment] starting at [offset]. - * - * The [b0] is always written at `offset` and [b1] is written at `offset + 1`. - * - * This operation does not perform any checks, and it's caller's responsibility to ensure - * that [offset] is between `0` and [Segment.remainingCapacity] - 1. - * - * Writing outside that range is considered an error and may lead to data corruption. - * - * @param segment a segment to write into - * @param offset an offset inside [segment]'s uncommitted area to write to - * @param b0 a first byte to be written - * @param b1 a second byte to be written - */ - public fun setUnchecked(segment: Segment, offset: Int, b0: Byte, b1: Byte) - - /** - * Writes three bytes, [b0], [b1] and [b2] to an uncommitted portion of the [segment] starting at [offset]. - * - * The [b0] is always written at `offset`, [b1] is written at `offset + 1` and [b2] is written at `offset + 2`. - * - * This operation does not perform any checks, and it's caller's responsibility to ensure - * that [offset] is between `0` and [Segment.remainingCapacity] - 2. - * - * Writing outside that range is considered an error and may lead to data corruption. - * - * @param segment a segment to write into - * @param offset an offset inside [segment]'s uncommitted area to write to - * @param b0 a first byte to be written - * @param b1 a second byte to be written - * @param b2 a third byte to be written - */ - public fun setUnchecked(segment: Segment, offset: Int, b0: Byte, b1: Byte, b2: Byte) - - /** - * Writes three bytes, [b0], [b1], [b2] and [b3] to an uncommitted portion of the [segment] starting at [offset]. - * - * The [b0] is always written at `offset`, [b1] is written at `offset + 1`, - * [b2] is written at `offset + 2`, and [b3] is written at `offset + 3`. - * - * This operation does not perform any checks, and it's caller's responsibility to ensure - * that [offset] is between `0` and [Segment.remainingCapacity] - 3. - * - * Writing outside that range is considered an error and may lead to data corruption. - * - * @param segment a segment to write into - * @param offset an offset inside [segment]'s uncommitted area to write to - * @param b0 a first byte to be written - * @param b1 a second byte to be written - * @param b2 a third byte to be written - * @param b3 a fourth byte to be written - * - */ - public fun setUnchecked(segment: Segment, offset: Int, b0: Byte, b1: Byte, b2: Byte, b3: Byte) -} - -/** - * Allows iterating over [Buffer]'s segments and reading its data. - */ -@UnsafeIoApi -public interface BufferIterationContext : SegmentReadContext { - /** - * Return a segment succeeding [segment] in the buffer, or `null`, if there is no such segment (meaning that the - * [segment] is [Buffer]'s tail). - * - * @param segment a segment for which a successor needs to be found - * - * @sample kotlinx.io.samples.unsafe.UnsafeBufferOperationsSamples.crc32GetUnchecked - */ - public fun next(segment: Segment): Segment? -} - -@UnsafeIoApi -@PublishedApi -@get:JvmSynthetic -internal val SegmentReadContextImpl: SegmentReadContext = object : SegmentReadContext { - override fun getUnchecked(segment: Segment, offset: Int): Byte = segment.getUnchecked(offset) -} - -@UnsafeIoApi -@PublishedApi -@get:JvmSynthetic -internal val SegmentWriteContextImpl: SegmentWriteContext = object : SegmentWriteContext { - override fun setUnchecked(segment: Segment, offset: Int, value: Byte) { - segment.setUnchecked(offset, value) - } - - override fun setUnchecked(segment: Segment, offset: Int, b0: Byte, b1: Byte) { - segment.setUnchecked(offset, b0, b1) - } - - override fun setUnchecked(segment: Segment, offset: Int, b0: Byte, b1: Byte, b2: Byte) { - segment.setUnchecked(offset, b0, b1, b2) - } - - override fun setUnchecked(segment: Segment, offset: Int, b0: Byte, b1: Byte, b2: Byte, b3: Byte) { - segment.setUnchecked(offset, b0, b1, b2, b3) - } -} - -@UnsafeIoApi -@PublishedApi -@get:JvmSynthetic -internal val BufferIterationContextImpl: BufferIterationContext = object : BufferIterationContext { - override fun next(segment: Segment): Segment? = segment.next - - override fun getUnchecked(segment: Segment, offset: Int): Byte = - SegmentReadContextImpl.getUnchecked(segment, offset) -} diff --git a/core/common/test/AbstractSinkTest.kt b/core/common/test/AbstractSinkTest.kt deleted file mode 100644 index f52f5eb7b..000000000 --- a/core/common/test/AbstractSinkTest.kt +++ /dev/null @@ -1,514 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kotlinx.io - -import kotlinx.io.bytestring.ByteString -import kotlinx.io.bytestring.encodeToByteString -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith - -class BufferSinkTest : AbstractSinkTest(SinkFactory.BUFFER) -class RealSinkTest : AbstractSinkTest(SinkFactory.REAL_BUFFERED_SINK) - -abstract class AbstractSinkTest internal constructor( - factory: SinkFactory -) { - private val data: Buffer = Buffer() - private val sink: Sink = factory.create(data) - - @Test - fun writeByteArray() { - val source = byteArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) - sink.write(source) - sink.flush() - assertEquals("Buffer(size=10 hex=00010203040506070809)", data.toString()) - data.clear() - - sink.write(source, 3) - sink.flush() - assertEquals("Buffer(size=7 hex=03040506070809)", data.toString()) - data.clear() - - sink.write(source, 0, 3) - sink.flush() - assertEquals("Buffer(size=3 hex=000102)", data.toString()) - data.clear() - - assertFailsWith { - sink.write(source, startIndex = -1, endIndex = 1) - } - assertEquals(0, data.size) - - assertFailsWith { - sink.write(source, startIndex = 1, endIndex = source.size + 1) - } - assertEquals(0, data.size) - } - - @Test - fun writeNothing() { - sink.writeString("") - sink.flush() - assertEquals(0, data.size) - } - - @Test - fun writeByte() { - sink.writeByte(0xba.toByte()) - sink.flush() - assertEquals("Buffer(size=1 hex=ba)", data.toString()) - } - - @Test - fun writeBytes() { - sink.writeByte(0xab.toByte()) - sink.writeByte(0xcd.toByte()) - sink.flush() - assertEquals("Buffer(size=2 hex=abcd)", data.toString()) - } - - @Test - fun writeLastByteInSegment() { - sink.writeString("a".repeat(Segment.SIZE - 1)) - sink.writeByte(0x20) - sink.writeByte(0x21) - sink.flush() - assertEquals(listOf(Segment.SIZE, 1), segmentSizes(data)) - assertEquals("a".repeat(Segment.SIZE - 1), data.readString(Segment.SIZE - 1L)) - assertEquals("Buffer(size=2 hex=2021)", data.toString()) - } - - @Test - fun writeShort() { - sink.writeShort(0xab01.toShort()) - sink.flush() - assertEquals("Buffer(size=2 hex=ab01)", data.toString()) - } - - @Test - fun writeShorts() { - sink.writeShort(0xabcd.toShort()) - sink.writeShort(0x4321) - sink.flush() - assertEquals("Buffer(size=4 hex=abcd4321)", data.toString()) - } - - @Test - fun writeShortLe() { - sink.writeShortLe(0xcdab.toShort()) - sink.writeShortLe(0x2143) - sink.flush() - assertEquals("Buffer(size=4 hex=abcd4321)", data.toString()) - } - - @Test - fun writeInt() { - sink.writeInt(0x197760) - sink.flush() - assertEquals("Buffer(size=4 hex=00197760)", data.toString()) - } - - @Test - fun writeInts() { - sink.writeInt(-0x543210ff) - sink.writeInt(-0x789abcdf) - sink.flush() - assertEquals("Buffer(size=8 hex=abcdef0187654321)", data.toString()) - } - - @Test - fun writeLastIntegerInSegment() { - sink.writeString("a".repeat(Segment.SIZE - 4)) - sink.writeInt(-0x543210ff) - sink.writeInt(-0x789abcdf) - sink.flush() - assertEquals(listOf(Segment.SIZE, 4), segmentSizes(data)) - assertEquals("a".repeat(Segment.SIZE - 4), data.readString(Segment.SIZE - 4L)) - assertEquals("Buffer(size=8 hex=abcdef0187654321)", data.toString()) - } - - @Test - fun writeIntegerDoesNotQuiteFitInSegment() { - sink.writeString("a".repeat(Segment.SIZE - 3)) - sink.writeInt(-0x543210ff) - sink.writeInt(-0x789abcdf) - sink.flush() - assertEquals(listOf(Segment.SIZE - 3, 8), segmentSizes(data)) - assertEquals("a".repeat(Segment.SIZE - 3), data.readString(Segment.SIZE - 3L)) - assertEquals("Buffer(size=8 hex=abcdef0187654321)", data.toString()) - } - - @Test - fun writeIntLe() { - sink.writeIntLe(-0x543210ff) - sink.writeIntLe(-0x789abcdf) - sink.flush() - assertEquals("Buffer(size=8 hex=01efcdab21436587)", data.toString()) - } - - @Test - fun writeLong() { - sink.writeLong(0x123456789abcdef0L) - sink.flush() - assertEquals("Buffer(size=8 hex=123456789abcdef0)", data.toString()) - } - - @Test - fun writeLongs() { - sink.writeLong(-0x543210fe789abcdfL) - sink.writeLong(-0x350145414f4ea400L) - sink.flush() - assertEquals("Buffer(size=16 hex=abcdef0187654321cafebabeb0b15c00)", data.toString()) - } - - @Test - fun writeLongLe() { - sink.writeLongLe(-0x543210fe789abcdfL) - sink.writeLongLe(-0x350145414f4ea400L) - sink.flush() - assertEquals("Buffer(size=16 hex=2143658701efcdab005cb1b0bebafeca)", data.toString()) - } - - @Test - fun writeAll() { - val source = Buffer() - source.writeString("abcdef") - - assertEquals(6, sink.transferFrom(source)) - assertEquals(0, source.size) - sink.flush() - assertEquals("abcdef", data.readString()) - } - - @Test - fun writeAllExhausted() { - val source = Buffer() - assertEquals(0, sink.transferFrom(source)) - assertEquals(0, source.size) - } - - @Test - fun writeSource() { - val source = Buffer() - source.writeString("abcdef") - - // Force resolution of the Source method overload. - sink.write(source as RawSource, 4) - sink.flush() - assertEquals("abcd", data.readString()) - assertEquals("ef", source.readString()) - } - - @Test - fun writeSourceReadsFully() { - val source = object : RawSource by Buffer() { - override fun readAtMostTo(sink: Buffer, byteCount: Long): Long { - sink.writeString("abcd") - return 4 - } - } - - sink.write(source, 8) - sink.flush() - assertEquals("abcdabcd", data.readString()) - } - - @Test - fun writeSourcePropagatesEof() { - val source: RawSource = Buffer().also { it.writeString("abcd") } - - assertFailsWith { - sink.write(source, 8) - } - - // Ensure that whatever was available was correctly written. - sink.flush() - assertEquals("abcd", data.readString()) - } - - @Test - fun writeBufferThrowsIAE() { - val source = Buffer() - source.writeString("abcd") - - assertFailsWith { - sink.write(source, 8) - } - - sink.flush() - assertEquals("", data.readString()) - } - - @Test - fun writeSourceWithNegativeBytesCount() { - val source: RawSource = Buffer().also { it.writeByte(0) } - - assertFailsWith { - sink.write(source, -1L) - } - } - - @Test - fun writeBufferWithNegativeBytesCount() { - val source = Buffer().also { it.writeByte(0) } - - assertFailsWith { - sink.write(source, -1L) - } - } - - @Test - fun writeSourceWithZeroIsNoOp() { - // This test ensures that a zero byte count never calls through to read the source. It may be - // tied to something like a socket which will potentially block trying to read a segment when - // ultimately we don't want any data. - val source = object : RawSource by Buffer() { - override fun readAtMostTo(sink: Buffer, byteCount: Long): Long { - throw AssertionError() - } - } - sink.write(source, 0) - assertEquals(0, data.size) - } - - @Test - fun closeEmitsBufferedBytes() { - sink.writeByte('a'.code.toByte()) - sink.close() - assertEquals('a', data.readByte().toInt().toChar()) - } - - /** - * This test hard codes the results of Long.toString() because that function rounds large values - * when using Kotlin/JS IR. https://youtrack.jetbrains.com/issue/KT-39891 - */ - @Test - fun longDecimalString() { - assertLongDecimalString("0", 0) - assertLongDecimalString("-9223372036854775808", Long.MIN_VALUE) - assertLongDecimalString("9223372036854775807", Long.MAX_VALUE) - assertLongDecimalString("9", 9L) - assertLongDecimalString("99", 99L) - assertLongDecimalString("999", 999L) - assertLongDecimalString("9999", 9999L) - assertLongDecimalString("99999", 99999L) - assertLongDecimalString("999999", 999999L) - assertLongDecimalString("9999999", 9999999L) - assertLongDecimalString("99999999", 99999999L) - assertLongDecimalString("999999999", 999999999L) - assertLongDecimalString("9999999999", 9999999999L) - assertLongDecimalString("99999999999", 99999999999L) - assertLongDecimalString("999999999999", 999999999999L) - assertLongDecimalString("9999999999999", 9999999999999L) - assertLongDecimalString("99999999999999", 99999999999999L) - assertLongDecimalString("999999999999999", 999999999999999L) - assertLongDecimalString("9999999999999999", 9999999999999999L) - assertLongDecimalString("99999999999999999", 99999999999999999L) - assertLongDecimalString("999999999999999999", 999999999999999999L) - assertLongDecimalString("10", 10L) - assertLongDecimalString("100", 100L) - assertLongDecimalString("1000", 1000L) - assertLongDecimalString("10000", 10000L) - assertLongDecimalString("100000", 100000L) - assertLongDecimalString("1000000", 1000000L) - assertLongDecimalString("10000000", 10000000L) - assertLongDecimalString("100000000", 100000000L) - assertLongDecimalString("1000000000", 1000000000L) - assertLongDecimalString("10000000000", 10000000000L) - assertLongDecimalString("100000000000", 100000000000L) - assertLongDecimalString("1000000000000", 1000000000000L) - assertLongDecimalString("10000000000000", 10000000000000L) - assertLongDecimalString("100000000000000", 100000000000000L) - assertLongDecimalString("1000000000000000", 1000000000000000L) - assertLongDecimalString("10000000000000000", 10000000000000000L) - assertLongDecimalString("100000000000000000", 100000000000000000L) - assertLongDecimalString("1000000000000000000", 1000000000000000000L) - } - - private fun assertLongDecimalString(string: String, value: Long) { - with(sink) { - writeDecimalLong(value) - writeString("zzz") - flush() - } - val expected = "${string}zzz" - val actual = data.readString() - assertEquals(expected, actual, "$value expected $expected but was $actual") - } - - @Test - fun longHexString() { - assertLongHexString(0) - assertLongHexString(Long.MIN_VALUE) - assertLongHexString(Long.MAX_VALUE) - - for (i in 0..62) { - assertLongHexString((1L shl i) - 1) - assertLongHexString(1L shl i) - } - } - - private fun assertLongHexString(value: Long) { - with(sink) { - writeHexadecimalUnsignedLong(value) - writeString("zzz") - flush() - } - val expected = "${value.toHexString()}zzz" - val actual = data.readString() - assertEquals(expected, actual, "$value expected $expected but was $actual") - } - - @Test - fun writeUtf8FromIndex() { - sink.writeString("12345", 3) - sink.emit() - assertEquals("45", data.readString()) - } - - @Test - fun writeUtf8FromRange() { - sink.writeString("0123456789", 4, 7) - sink.emit() - assertEquals("456", data.readString()) - } - - @Test - fun writeUtf8WithInvalidIndexes() { - assertFailsWith { sink.writeString("hello", startIndex = -1) } - assertFailsWith { sink.writeString("hello", startIndex = 0, endIndex = 6) } - assertFailsWith { sink.writeString("hello", startIndex = 6) } - } - - @Test - fun writeCharSequenceFromIndex() { - sink.writeString(StringBuilder("12345"), 3) - sink.emit() - assertEquals("45", data.readString()) - } - - @Test - fun writeCharSequenceFromRange() { - sink.writeString(StringBuilder("0123456789"), 4, 7) - sink.emit() - assertEquals("456", data.readString()) - } - - @Test - fun writeCharSequenceWithInvalidIndexes() { - assertFailsWith { sink.writeString(StringBuilder("hello"), startIndex = -1) } - assertFailsWith { sink.writeString(StringBuilder("hello"), startIndex = 0, endIndex = 6) } - assertFailsWith { sink.writeString(StringBuilder("hello"), startIndex = 6) } - } - - @Test - fun writeUByte() { - sink.writeUByte(0xffu) - sink.flush() - assertEquals(-1, data.readByte()) - } - - @Test - fun writeUShort() { - sink.writeUShort(0xffffu) - sink.flush() - assertEquals(-1, data.readShort()) - } - - @Test - fun writeUShortLe() { - sink.writeUShortLe(0x1234u) - sink.flush() - assertEquals("Buffer(size=2 hex=3412)", data.toString()) - } - - @Test - fun writeUInt() { - sink.writeUInt(0xffffffffu) - sink.flush() - assertEquals(-1, data.readInt()) - } - - @Test - fun writeUIntLe() { - sink.writeUIntLe(0x12345678u) - sink.flush() - assertEquals("Buffer(size=4 hex=78563412)", data.toString()) - } - - @Test - fun writeULong() { - sink.writeULong(0xffffffffffffffffu) - sink.flush() - assertEquals(-1, data.readLong()) - } - - @Test - fun writeULongLe() { - sink.writeULongLe(0x1234567890abcdefu) - sink.flush() - assertEquals("Buffer(size=8 hex=efcdab9078563412)", data.toString()) - } - - @Test - fun writeFloat() { - sink.writeFloat(12345.678F) - sink.flush() - assertEquals(12345.678F.toBits(), data.readInt()) - } - - @Test - fun writeFloatLe() { - sink.writeFloatLe(12345.678F) - sink.flush() - assertEquals(12345.678F.toBits(), data.readIntLe()) - } - - @Test - fun writeDouble() { - sink.writeDouble(123456.78901) - sink.flush() - assertEquals(123456.78901.toBits(), data.readLong()) - } - - @Test - fun writeDoubleLe() { - sink.writeDoubleLe(123456.78901) - sink.flush() - assertEquals(123456.78901.toBits(), data.readLongLe()) - } - - @Test - fun writeByteString() { - sink.write("təˈranəˌsôr".encodeToByteString()) - sink.flush() - assertEquals(ByteString("74c999cb8872616ec999cb8c73c3b472".decodeHex()), data.readByteString()) - } - - @Test - fun writeByteStringOffset() { - sink.write("təˈranəˌsôr".encodeToByteString(), 5, 10) - sink.flush() - assertEquals(ByteString("72616ec999".decodeHex()), data.readByteString()) - } -} diff --git a/core/common/test/AbstractSourceTest.kt b/core/common/test/AbstractSourceTest.kt deleted file mode 100644 index 71347afc0..000000000 --- a/core/common/test/AbstractSourceTest.kt +++ /dev/null @@ -1,1785 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kotlinx.io - -import kotlinx.io.bytestring.ByteString -import kotlinx.io.bytestring.decodeToString -import kotlinx.io.bytestring.encodeToByteString -import kotlin.test.* - -private const val SEGMENT_SIZE = Segment.SIZE - -class BufferSourceTest : AbstractBufferedSourceTest(SourceFactory.BUFFER) -class RealBufferedSourceTest : AbstractBufferedSourceTest(SourceFactory.REAL_BUFFERED_SOURCE) -class OneByteAtATimeBufferedSourceTest : AbstractBufferedSourceTest(SourceFactory.ONE_BYTE_AT_A_TIME_BUFFERED_SOURCE) -class OneByteAtATimeBufferTest : AbstractBufferedSourceTest(SourceFactory.ONE_BYTE_AT_A_TIME_BUFFER) -class PeekBufferTest : AbstractBufferedSourceTest(SourceFactory.PEEK_BUFFER) -class PeekBufferedSourceTest : AbstractBufferedSourceTest(SourceFactory.PEEK_BUFFERED_SOURCE) - -abstract class AbstractBufferedSourceTest internal constructor( - private val factory: SourceFactory -) { - private val sink: Sink - private val source: Source - - init { - val pipe = factory.pipe() - sink = pipe.sink - source = pipe.source - } - - @Test - fun exhausted() { - assertTrue(source.exhausted()) - } - - @Test - fun readBytes() { - sink.write(byteArrayOf(0xab.toByte(), 0xcd.toByte())) - sink.emit() - assertEquals(0xab, (source.readByte() and 0xff).toLong()) - assertEquals(0xcd, (source.readByte() and 0xff).toLong()) - assertTrue(source.exhausted()) - } - - @Test - fun readByteTooShortThrows() { - assertFailsWith { - source.readByte() - } - } - - @Test - fun readShort() { - sink.write(byteArrayOf(0xab.toByte(), 0xcd.toByte(), 0xef.toByte(), 0x01.toByte())) - sink.emit() - assertEquals(0xabcd.toShort().toLong(), source.readShort().toLong()) - assertEquals(0xef01.toShort().toLong(), source.readShort().toLong()) - assertTrue(source.exhausted()) - } - - @Test - fun readShortLe() { - sink.write(byteArrayOf(0xab.toByte(), 0xcd.toByte(), 0xef.toByte(), 0x10.toByte())) - sink.emit() - assertEquals(0xcdab.toShort().toLong(), source.readShortLe().toLong()) - assertEquals(0x10ef.toShort().toLong(), source.readShortLe().toLong()) - assertTrue(source.exhausted()) - } - - @Test - fun readShortSplitAcrossMultipleSegments() { - sink.writeString("a".repeat(Segment.SIZE - 1)) - sink.write(byteArrayOf(0xab.toByte(), 0xcd.toByte())) - sink.emit() - source.skip((Segment.SIZE - 1).toLong()) - assertEquals(0xabcd.toShort().toLong(), source.readShort().toLong()) - assertTrue(source.exhausted()) - } - - @Test - fun readShortTooShortThrows() { - sink.writeShort(Short.MAX_VALUE) - sink.emit() - source.readByte() - assertFailsWith { - source.readShort() - } - assertEquals(1, source.readByteArray().size) - } - - @Test - fun readShortLeTooShortThrows() { - sink.writeShortLe(Short.MAX_VALUE) - sink.emit() - source.readByte() - assertFailsWith { - source.readShortLe() - } - assertEquals(1, source.readByteArray().size) - } - - @Test - fun readInt() { - sink.write( - byteArrayOf( - 0xab.toByte(), - 0xcd.toByte(), - 0xef.toByte(), - 0x01.toByte(), - 0x87.toByte(), - 0x65.toByte(), - 0x43.toByte(), - 0x21.toByte() - ) - ) - sink.emit() - assertEquals(-0x543210ff, source.readInt().toLong()) - assertEquals(-0x789abcdf, source.readInt().toLong()) - assertTrue(source.exhausted()) - } - - @Test - fun readIntLe() { - sink.write( - byteArrayOf( - 0xab.toByte(), - 0xcd.toByte(), - 0xef.toByte(), - 0x10.toByte(), - 0x87.toByte(), - 0x65.toByte(), - 0x43.toByte(), - 0x21.toByte() - ) - ) - sink.emit() - assertEquals(0x10efcdab, source.readIntLe().toLong()) - assertEquals(0x21436587, source.readIntLe().toLong()) - assertTrue(source.exhausted()) - } - - @Test - fun readIntSplitAcrossMultipleSegments() { - sink.writeString("a".repeat(Segment.SIZE - 3)) - sink.write(byteArrayOf(0xab.toByte(), 0xcd.toByte(), 0xef.toByte(), 0x01.toByte())) - sink.emit() - source.skip((Segment.SIZE - 3).toLong()) - assertEquals(-0x543210ff, source.readInt().toLong()) - assertTrue(source.exhausted()) - } - - @Test - fun readIntTooShortThrows() { - sink.writeInt(Int.MAX_VALUE) - sink.emit() - source.readByte() - assertFailsWith { - source.readInt() - } - assertEquals(3, source.readByteArray().size) - } - - @Test - fun readIntLeTooShortThrows() { - sink.writeIntLe(Int.MAX_VALUE) - sink.emit() - source.readByte() - assertFailsWith { - source.readIntLe() - } - assertEquals(3, source.readByteArray().size) - } - - @Test - fun readLong() { - sink.write( - byteArrayOf( - 0xab.toByte(), - 0xcd.toByte(), - 0xef.toByte(), - 0x10.toByte(), - 0x87.toByte(), - 0x65.toByte(), - 0x43.toByte(), - 0x21.toByte(), - 0x36.toByte(), - 0x47.toByte(), - 0x58.toByte(), - 0x69.toByte(), - 0x12.toByte(), - 0x23.toByte(), - 0x34.toByte(), - 0x45.toByte() - ) - ) - sink.emit() - assertEquals(-0x543210ef789abcdfL, source.readLong()) - assertEquals(0x3647586912233445L, source.readLong()) - assertTrue(source.exhausted()) - } - - @Test - fun readLongLe() { - sink.write( - byteArrayOf( - 0xab.toByte(), - 0xcd.toByte(), - 0xef.toByte(), - 0x10.toByte(), - 0x87.toByte(), - 0x65.toByte(), - 0x43.toByte(), - 0x21.toByte(), - 0x36.toByte(), - 0x47.toByte(), - 0x58.toByte(), - 0x69.toByte(), - 0x12.toByte(), - 0x23.toByte(), - 0x34.toByte(), - 0x45.toByte() - ) - ) - sink.emit() - assertEquals(0x2143658710efcdabL, source.readLongLe()) - assertEquals(0x4534231269584736L, source.readLongLe()) - assertTrue(source.exhausted()) - } - - @Test - fun readLongSplitAcrossMultipleSegments() { - sink.writeString("a".repeat(Segment.SIZE - 7)) - sink.write( - byteArrayOf( - 0xab.toByte(), - 0xcd.toByte(), - 0xef.toByte(), - 0x01.toByte(), - 0x87.toByte(), - 0x65.toByte(), - 0x43.toByte(), - 0x21.toByte() - ) - ) - sink.emit() - source.skip((Segment.SIZE - 7).toLong()) - assertEquals(-0x543210fe789abcdfL, source.readLong()) - assertTrue(source.exhausted()) - } - - @Test - fun readLongTooShortThrows() { - sink.writeLong(Long.MAX_VALUE) - sink.emit() - source.readByte() - assertFailsWith { - source.readLong() - } - assertEquals(7, source.readByteArray().size) - } - - @Test - fun readLongLeTooShortThrows() { - sink.writeLongLe(Long.MAX_VALUE) - sink.emit() - source.readByte() - assertFailsWith { - source.readLongLe() - } - assertEquals(7, source.readByteArray().size) - } - - @OptIn(InternalIoApi::class) - @Test - fun transferTo() { - source.buffer.writeString("abc") - sink.writeString("def") - sink.emit() - - val sink = Buffer() - assertEquals(6, source.transferTo(sink)) - assertEquals("abcdef", sink.readString()) - assertTrue(source.exhausted()) - } - - @Test - fun transferToExhausted() { - val mockSink = MockSink() - assertEquals(0, source.transferTo(mockSink)) - assertTrue(source.exhausted()) - mockSink.assertLog() - } - - @Test - fun readExhaustedSource() { - val sink = Buffer() - sink.writeString("a".repeat(10)) - assertEquals(-1, source.readAtMostTo(sink, 10)) - assertEquals(10, sink.size) - assertTrue(source.exhausted()) - } - - @Test - fun readZeroBytesFromSource() { - val sink = Buffer() - sink.writeString("a".repeat(10)) - - // Either 0 or -1 is reasonable here. For consistency with Android's - // ByteArrayInputStream we return 0. - assertEquals(-1, source.readAtMostTo(sink, 0)) - assertEquals(10, sink.size) - assertTrue(source.exhausted()) - } - - @Test - fun readNegativeBytesFromSource() { - assertFailsWith { - source.readAtMostTo(Buffer(), -1L) - } - } - - @Test - fun readFromClosedSource() { - if (source is Buffer) { - return - } - - source.close() - assertFailsWith { - source.readAtMostTo(Buffer(), 1L) - } - } - - @Test - fun readAtMostToBufferFromSourceWithFilledBuffer() { - sink.writeByte(42) - sink.flush() - - source.request(1) - assertEquals(1, source.readAtMostTo(Buffer(), 128)) - } - - @Test - fun readAtMostToNonEmptyBufferFromSourceWithFilledBuffer() { - if (factory.isOneByteAtATime) { - return - } - - val expectedReadSize = 123 - - sink.write(ByteArray(expectedReadSize)) - sink.flush() - - source.request(1) - val buffer = Buffer().also { it.write(ByteArray(SEGMENT_SIZE - expectedReadSize)) } - assertEquals(expectedReadSize.toLong(), source.readAtMostTo(buffer, SEGMENT_SIZE.toLong())) - - assertTrue(source.exhausted()) - sink.write(ByteArray(expectedReadSize)) - sink.flush() - - source.request(1) - buffer.clear() - assertEquals(42L, source.readAtMostTo(buffer, 42L)) - } - - @Test - fun readAtMostToByteArrayFromSourceWithFilledBuffer() { - sink.writeByte(42) - sink.flush() - - source.request(1) - assertEquals(1, source.readAtMostTo(ByteArray(128))) - } - - @Test - fun readToSink() { - sink.writeString("a".repeat(10000)) - sink.emit() - val sink = Buffer() - source.readTo(sink, 9999) - assertEquals("a".repeat(9999), sink.readString()) - assertEquals("a", source.readString()) - } - - @Test - fun readToSinkTooShortThrows() { - sink.writeString("Hi") - sink.emit() - val sink = Buffer() - assertFailsWith { - source.readTo(sink, 5) - } - - // Verify we read all that we could from the source. - assertEquals("Hi", sink.readString()) - assertTrue(source.exhausted()) - } - - @Test - fun readToSinkWithNegativeByteCount() { - val sink = Buffer() - assertFailsWith { - source.readTo(sink, -1) - } - } - - @Test - fun readToSinkZeroBytes() { - sink.writeString("test") - sink.flush() - val sink = Buffer() - source.readTo(sink, 0) - assertEquals(0, sink.size) - assertEquals("test", source.readString()) - } - - @Test - fun readToByteArray() { - val data = Buffer() - data.writeString("Hello") - data.writeString("e".repeat(Segment.SIZE)) - - val expected = data.copy().readByteArray() - sink.write(data, data.size) - sink.emit() - - val sink = ByteArray(Segment.SIZE + 5) - source.readTo(sink) - assertArrayEquals(expected, sink) - } - - @Test - fun readToByteArraySubrange() { - val buffer = Buffer() - val source: Source = buffer - - val sink = ByteArray(8) - - buffer.writeString("hello") - source.readTo(sink, 0, 3) - assertContentEquals(byteArrayOf('h'.code.toByte(), 'e'.code.toByte(), 'l'.code.toByte(), 0, 0, 0, 0, 0), sink) - assertEquals("lo", source.readString()) - - sink.fill(0) - buffer.writeString("hello") - source.readTo(sink, 3) - assertContentEquals( - byteArrayOf( - 0, 0, 0, 'h'.code.toByte(), 'e'.code.toByte(), 'l'.code.toByte(), 'l'.code.toByte(), - 'o'.code.toByte() - ), sink - ) - assertTrue(source.exhausted()) - - sink.fill(0) - buffer.writeString("hello") - source.readTo(sink, 3, 4) - assertContentEquals(byteArrayOf(0, 0, 0, 'h'.code.toByte(), 0, 0, 0, 0), sink) - assertEquals("ello", source.readString()) - } - - @Test - fun readToByteArrayInvalidArguments() { - val source: Source = Buffer() - val sink = ByteArray(32) - - assertFailsWith { source.readTo(sink, 2, 0) } - assertFailsWith { source.readTo(sink, -1) } - assertFailsWith { source.readTo(sink, 33, endIndex = 34) } - assertFailsWith { source.readTo(sink, endIndex = 33) } - } - - @Test - fun readToByteArrayTooShortThrows() { - sink.writeString("Hello") - sink.emit() - - val array = ByteArray(6) - assertFailsWith { - source.readTo(array) - } - - // Verify we read all that we could from the source. - assertArrayEquals( - byteArrayOf( - 'H'.code.toByte(), - 'e'.code.toByte(), - 'l'.code.toByte(), - 'l'.code.toByte(), - 'o'.code.toByte(), - 0 - ), - array - ) - } - - @Test - fun readAtMostToByteArray() { - sink.writeString("abcd") - sink.emit() - - val sink = ByteArray(3) - val read = source.readAtMostTo(sink) - if (factory.isOneByteAtATime) { - assertEquals(1, read.toLong()) - val expected = byteArrayOf('a'.code.toByte(), 0, 0) - assertArrayEquals(expected, sink) - } else { - assertEquals(3, read.toLong()) - val expected = byteArrayOf('a'.code.toByte(), 'b'.code.toByte(), 'c'.code.toByte()) - assertArrayEquals(expected, sink) - } - } - - @Test - fun readAtMostToByteArrayNotEnough() { - sink.writeString("abcd") - sink.emit() - - val sink = ByteArray(5) - val read = source.readAtMostTo(sink) - if (factory.isOneByteAtATime) { - assertEquals(1, read.toLong()) - val expected = byteArrayOf('a'.code.toByte(), 0, 0, 0, 0) - assertArrayEquals(expected, sink) - } else { - assertEquals(4, read.toLong()) - val expected = - byteArrayOf('a'.code.toByte(), 'b'.code.toByte(), 'c'.code.toByte(), 'd'.code.toByte(), 0) - assertArrayEquals(expected, sink) - } - } - - @Test - fun readAtMostToByteArrayOffsetAndCount() { - sink.writeString("abcd") - sink.emit() - - val sink = ByteArray(7) - val bytesToRead = 3 - val read = source.readAtMostTo(sink, startIndex = 2, endIndex = 2 + bytesToRead) - if (factory.isOneByteAtATime) { - assertEquals(1, read.toLong()) - val expected = byteArrayOf(0, 0, 'a'.code.toByte(), 0, 0, 0, 0) - assertArrayEquals(expected, sink) - } else { - assertEquals(3, read.toLong()) - val expected = - byteArrayOf(0, 0, 'a'.code.toByte(), 'b'.code.toByte(), 'c'.code.toByte(), 0, 0) - assertArrayEquals(expected, sink) - } - } - - @Test - fun readAtMostToByteArrayFromOffset() { - sink.writeString("abcd") - sink.emit() - - val sink = ByteArray(7) - val read = source.readAtMostTo(sink, 4) - if (factory.isOneByteAtATime) { - assertEquals(1, read.toLong()) - val expected = byteArrayOf(0, 0, 0, 0, 'a'.code.toByte(), 0, 0) - assertArrayEquals(expected, sink) - } else { - assertEquals(3, read.toLong()) - val expected = - byteArrayOf(0, 0, 0, 0, 'a'.code.toByte(), 'b'.code.toByte(), 'c'.code.toByte()) - assertArrayEquals(expected, sink) - } - } - - @Test - fun readAtMostToByteArrayWithInvalidArguments() { - sink.write(ByteArray(10)) - sink.emit() - - val sink = ByteArray(4) - - assertFailsWith { - source.readAtMostTo(sink, 4, 1) - } - - assertFailsWith { - source.readAtMostTo(sink, 1, 5) - } - - assertFailsWith { - source.readAtMostTo(sink, -1, 2) - } - } - - @Test - fun readByteArray() { - val string = "abcd" + "e".repeat(Segment.SIZE) - sink.writeString(string) - sink.emit() - assertArrayEquals(string.commonAsUtf8ToByteArray(), source.readByteArray()) - } - - @Test - fun readByteArrayPartial() { - sink.writeString("abcd") - sink.emit() - assertEquals("[97, 98, 99]", source.readByteArray(3).contentToString()) - assertEquals("d", source.readString(1)) - } - - @Test - fun readByteArrayTooShortThrows() { - sink.writeString("abc") - sink.emit() - assertFailsWith { - source.readByteArray(4) - } - - assertEquals("abc", source.readString()) // The read shouldn't consume any data. - } - - @Test - fun readByteArrayWithNegativeSizeThrows() { - assertFailsWith { source.readByteArray(-20) } - } - - @Test - fun readUtf8SpansSegments() { - sink.writeString("a".repeat(Segment.SIZE * 2)) - sink.emit() - source.skip((Segment.SIZE - 1).toLong()) - assertEquals("aa", source.readString(2)) - } - - @Test - fun readUtf8Segment() { - sink.writeString("a".repeat(Segment.SIZE)) - sink.emit() - assertEquals("a".repeat(Segment.SIZE), source.readString(Segment.SIZE.toLong())) - } - - @Test - fun readUtf8PartialBuffer() { - sink.writeString("a".repeat(Segment.SIZE + 20)) - sink.emit() - assertEquals("a".repeat(Segment.SIZE + 10), source.readString((Segment.SIZE + 10).toLong())) - } - - @Test - fun readUtf8EntireBuffer() { - sink.writeString("a".repeat(Segment.SIZE * 2)) - sink.emit() - assertEquals("a".repeat(Segment.SIZE * 2), source.readString()) - } - - @Test - fun readUtf8TooShortThrows() { - sink.writeString("abc") - sink.emit() - assertFailsWith { - source.readString(4L) - } - - assertEquals("abc", source.readString()) // The read shouldn't consume any data. - } - - @Test - fun skip() { - sink.writeString("a") - sink.writeString("b".repeat(Segment.SIZE)) - sink.writeString("c") - sink.emit() - source.skip(1) - assertEquals('b'.code.toLong(), (source.readByte() and 0xff).toLong()) - source.skip((Segment.SIZE - 2).toLong()) - assertEquals('b'.code.toLong(), (source.readByte() and 0xff).toLong()) - source.skip(1) - assertTrue(source.exhausted()) - } - - @Test - fun skipInsufficientData() { - sink.writeString("a") - sink.emit() - assertFailsWith { - source.skip(2) - } - } - - @Test - fun skipNegativeNumberOfBytes() { - assertFailsWith { source.skip(-1) } - } - - @Test - fun indexOf() { - // The segment is empty. - assertEquals(-1, source.indexOf('a'.code.toByte())) - - // The segment has one value. - sink.writeString("a") // a - sink.emit() - assertEquals(0, source.indexOf('a'.code.toByte())) - assertEquals(-1, source.indexOf('b'.code.toByte())) - - // The segment has lots of data. - sink.writeString("b".repeat(Segment.SIZE - 2)) // ab...b - sink.emit() - assertEquals(0, source.indexOf('a'.code.toByte())) - assertEquals(1, source.indexOf('b'.code.toByte())) - assertEquals(-1, source.indexOf('c'.code.toByte())) - - // The segment doesn't start at 0, it starts at 2. - source.skip(2) // b...b - assertEquals(-1, source.indexOf('a'.code.toByte())) - assertEquals(0, source.indexOf('b'.code.toByte())) - assertEquals(-1, source.indexOf('c'.code.toByte())) - - // The segment is full. - sink.writeString("c") // b...bc - sink.emit() - assertEquals(-1, source.indexOf('a'.code.toByte())) - assertEquals(0, source.indexOf('b'.code.toByte())) - assertEquals((Segment.SIZE - 3).toLong(), source.indexOf('c'.code.toByte())) - - // The segment doesn't start at 2, it starts at 4. - source.skip(2) // b...bc - assertEquals(-1, source.indexOf('a'.code.toByte())) - assertEquals(0, source.indexOf('b'.code.toByte())) - assertEquals((Segment.SIZE - 5).toLong(), source.indexOf('c'.code.toByte())) - - // Two segments. - sink.writeString("d") // b...bcd, d is in the 2nd segment. - sink.emit() - assertEquals((Segment.SIZE - 4).toLong(), source.indexOf('d'.code.toByte())) - assertEquals(-1, source.indexOf('e'.code.toByte())) - } - - @Test - fun indexOfByteWithStartOffset() { - with(sink) { - writeString("a") - writeString("b".repeat(Segment.SIZE)) - writeString("c") - emit() - } - assertEquals(-1, source.indexOf('a'.code.toByte(), 1)) - assertEquals(15, source.indexOf('b'.code.toByte(), 15)) - } - - @Test - fun indexOfByteWithIndices() { - if (factory.isOneByteAtATime) { - // When run on CI this causes out-of-memory errors. - return - } - val a = 'a'.code.toByte() - val c = 'c'.code.toByte() - - val size = Segment.SIZE * 5 - val bytes = ByteArray(size) { a } - - // These are tricky places where the buffer - // starts, ends, or segments come together. - val points = intArrayOf( - 0, - 1, - 2, - Segment.SIZE - 1, - Segment.SIZE, - Segment.SIZE + 1, - size / 2 - 1, - size / 2, - size / 2 + 1, - size - Segment.SIZE - 1, - size - Segment.SIZE, - size - Segment.SIZE + 1, - size - 3, - size - 2, - size - 1 - ) - - // In each iteration, we write c to the known point and then search for it using different - // windows. Some of the windows don't overlap with c's position, and therefore a match shouldn't - // be found. - for (p in points) { - bytes[p] = c - sink.write(bytes) - sink.emit() - - assertEquals(p.toLong(), source.indexOf(c, 0, size.toLong())) - assertEquals(p.toLong(), source.indexOf(c, 0, (p + 1).toLong())) - assertEquals(p.toLong(), source.indexOf(c, p.toLong(), size.toLong())) - assertEquals(p.toLong(), source.indexOf(c, p.toLong(), (p + 1).toLong())) - assertEquals(p.toLong(), source.indexOf(c, (p / 2).toLong(), (p * 2 + 1).toLong())) - assertEquals(-1, source.indexOf(c, 0, (p / 2).toLong())) - assertEquals(-1, source.indexOf(c, 0, p.toLong())) - assertEquals(-1, source.indexOf(c, 0, 0)) - assertEquals(-1, source.indexOf(c, p.toLong(), p.toLong())) - - // Reset. - source.readString() - bytes[p] = a - } - } - - @Test - fun indexOfByteInvalidBoundsThrows() { - sink.writeString("abc") - sink.emit() - assertFailsWith("Expected failure: fromIndex < 0") { - source.indexOf('a'.code.toByte(), -1) - } - assertFailsWith("Expected failure: fromIndex > toIndex") { - source.indexOf('a'.code.toByte(), 10, 0) - } - } - - @Test - fun indexOfByteWithFromIndex() { - sink.writeString("aaa") - sink.emit() - assertEquals(0, source.indexOf('a'.code.toByte())) - assertEquals(0, source.indexOf('a'.code.toByte(), 0)) - assertEquals(1, source.indexOf('a'.code.toByte(), 1)) - assertEquals(2, source.indexOf('a'.code.toByte(), 2)) - } - - @Test - fun request() { - with(sink) { - writeString("a") - writeString("b".repeat(Segment.SIZE)) - writeString("c") - emit() - } - assertTrue(source.request((Segment.SIZE + 2).toLong())) - assertFalse(source.request((Segment.SIZE + 3).toLong())) - } - - @Test - fun requestZeroBytes() { - assertTrue(source.request(0)) - } - - @Test - fun requestNegativeNumberOfBytes() { - assertFailsWith { source.request(-1) } - } - - @Test - fun require() { - with(sink) { - writeString("a") - writeString("b".repeat(Segment.SIZE)) - writeString("c") - emit() - } - source.require((Segment.SIZE + 2).toLong()) - assertFailsWith { - source.require((Segment.SIZE + 3).toLong()) - } - } - - @Test - fun requireZeroBytes() { - source.require(0L) // should not throw - } - - @Test - fun requireNegativeNumberOfBytes() { - assertFailsWith { source.require(-1) } - } - - @Test - fun longHexString() { - assertLongHexString("8000000000000000", Long.MIN_VALUE) - assertLongHexString("fffffffffffffffe", -0x2L) - assertLongHexString("FFFFFFFFFFFFFFFe", -0x2L) - assertLongHexString("ffffffffffffffff", -0x1L) - assertLongHexString("FFFFFFFFFFFFFFFF", -0x1L) - assertLongHexString("0000000000000000", 0x0L) - assertLongHexString("0000000000000001", 0x1L) - assertLongHexString("7999999999999999", 0x7999999999999999L) - - assertLongHexString("FF", 0xFF) - assertLongHexString("0000000000000001", 0x1) - } - - @Test - fun hexStringWithManyLeadingZeros() { - assertLongHexString("00000000000000001", 0x1) - assertLongHexString("0000000000000000ffffffffffffffff", -0x1L) - assertLongHexString("00000000000000007fffffffffffffff", 0x7fffffffffffffffL) - assertLongHexString("0".repeat(Segment.SIZE + 1) + "1", 0x1) - } - - private fun assertLongHexString(s: String, expected: Long) { - sink.writeString(s) - sink.emit() - val actual = source.readHexadecimalUnsignedLong() - assertEquals(expected, actual, "$s --> $expected") - } - - @Test - fun longHexStringAcrossSegment() { - with(sink) { - writeString("a".repeat(Segment.SIZE - 8)) - writeString("FFFFFFFFFFFFFFFF") - emit() - } - source.skip((Segment.SIZE - 8).toLong()) - assertEquals(-1, source.readHexadecimalUnsignedLong()) - } - - @Test - fun longHexTerminatedByNonDigit() { - sink.writeString("abcd,") - sink.emit() - assertEquals(0xabcdL, source.readHexadecimalUnsignedLong()) - } - - @Test - fun longHexAlphabet() { - sink.writeString("7896543210abcdef") - sink.emit() - assertEquals(0x7896543210abcdefL, source.readHexadecimalUnsignedLong()) - sink.writeString("ABCDEF") - sink.emit() - assertEquals(0xabcdefL, source.readHexadecimalUnsignedLong()) - } - - @Test - fun longHexStringTooLongThrows() { - val value = "fffffffffffffffff" - sink.writeString(value) - sink.emit() - - val e = assertFailsWith { - source.readHexadecimalUnsignedLong() - } - assertEquals("Number too large: fffffffffffffffff", e.message) - assertEquals(value, source.readString()) - } - - @Test - fun longHexStringTooShortThrows() { - sink.writeString(" ") - sink.emit() - - val e = assertFailsWith { - source.readHexadecimalUnsignedLong() - } - assertEquals("Expected leading [0-9a-fA-F] character but was 0x20", e.message) - assertEquals(" ", source.readString()) - } - - @Test - fun longHexEmptySourceThrows() { - sink.writeString("") - sink.emit() - assertFailsWith { source.readHexadecimalUnsignedLong() } - } - - @Test - fun longDecimalString() { - assertLongDecimalString("-9223372036854775808", Long.MIN_VALUE) - assertLongDecimalString("-1", -1L) - assertLongDecimalString("0", 0L) - assertLongDecimalString("1", 1L) - assertLongDecimalString("9223372036854775807", Long.MAX_VALUE) - - assertLongDecimalString("00000001", 1L) - assertLongDecimalString("-000001", -1L) - } - - private fun assertLongDecimalString(s: String, expected: Long) { - sink.writeString(s) - sink.writeString("zzz") - sink.emit() - val actual = source.readDecimalLong() - assertEquals(expected, actual, "$s --> $expected") - assertEquals("zzz", source.readString()) - } - - @Test - fun longDecimalStringAcrossSegment() { - with(sink) { - writeString("a".repeat(Segment.SIZE - 8)) - writeString("1234567890123456") - writeString("zzz") - emit() - } - source.skip((Segment.SIZE - 8).toLong()) - assertEquals(1234567890123456L, source.readDecimalLong()) - assertEquals("zzz", source.readString()) - } - - @Test - fun longDecimalStringTooLongThrows() { - val value = "12345678901234567890" - sink.writeString(value) // Too many digits. - sink.emit() - - val e = assertFailsWith { - source.readDecimalLong() - } - assertEquals("Number too large: 12345678901234567890", e.message) - assertEquals(value, source.readString()) - } - - @Test - fun longDecimalStringTooHighThrows() { - val value = "9223372036854775808" - sink.writeString(value) // Right size but cannot fit. - sink.emit() - - val e = assertFailsWith { - source.readDecimalLong() - } - assertEquals("Number too large: 9223372036854775808", e.message) - assertEquals(value, source.readString()) - } - - @Test - fun longDecimalStringTooLowThrows() { - val value = "-9223372036854775809" - sink.writeString(value) // Right size but cannot fit. - sink.emit() - - val e = assertFailsWith { - source.readDecimalLong() - } - assertEquals("Number too large: -9223372036854775809", e.message) - assertEquals(value, source.readString()) - } - - @Test - fun longDecimalStringTooShortThrows() { - sink.writeString(" ") - sink.emit() - - val e = assertFailsWith { - source.readDecimalLong() - } - assertEquals("Expected a digit or '-' but was 0x20", e.message) - assertEquals(" ", source.readString()) - } - - @Test - fun longDecimalEmptyThrows() { - sink.writeString("") - sink.emit() - assertFailsWith { - source.readDecimalLong() - } - } - - @Test - fun longDecimalLoneDashThrows() { - sink.writeString("-") - sink.emit() - assertFailsWith { - source.readDecimalLong() - } - assertEquals("-", source.readString()) - } - - @Test - fun longDecimalDashFollowedByNonDigitThrows() { - sink.writeString("- ") - sink.emit() - assertFailsWith { - source.readDecimalLong() - } - assertEquals("- ", source.readString()) - } - - @Test - fun codePoints() { - with(sink) { - writeByte(0x7f) - emit() - assertEquals(0x7f, source.readCodePointValue().toLong()) - - writeByte(0xdf.toByte()) - writeByte(0xbf.toByte()) - emit() - assertEquals(0x07ff, source.readCodePointValue().toLong()) - - writeByte(0xef.toByte()) - writeByte(0xbf.toByte()) - writeByte(0xbf.toByte()) - emit() - assertEquals(0xffff, source.readCodePointValue().toLong()) - - writeByte(0xf4.toByte()) - writeByte(0x8f.toByte()) - writeByte(0xbf.toByte()) - writeByte(0xbf.toByte()) - emit() - assertEquals(0x10ffff, source.readCodePointValue().toLong()) - } - } - - @Test - fun codePointsFromExhaustedSource() { - with(sink) { - writeByte(0xdf.toByte()) // a second byte is missing - emit() - assertFailsWith { source.readCodePointValue() } - assertEquals(1, source.readByteArray().size) - - writeByte(0xe2.toByte()) - writeByte(0x98.toByte()) // a third byte is missing - emit() - assertFailsWith { source.readCodePointValue() } - assertEquals(2, source.readByteArray().size) - - writeByte(0xf0.toByte()) - writeByte(0x9f.toByte()) - writeByte(0x92.toByte()) // a forth byte is missing - emit() - assertFailsWith { source.readCodePointValue() } - assertEquals(3, source.readByteArray().size) - } - } - - @Test - fun decimalStringWithManyLeadingZeros() { - assertLongDecimalString("00000000000000001", 1) - assertLongDecimalString("00000000000000009223372036854775807", Long.MAX_VALUE) - assertLongDecimalString("-00000000000000009223372036854775808", Long.MIN_VALUE) - assertLongDecimalString("0".repeat(Segment.SIZE + 1) + "1", 1) - } - - @Test - fun peek() { - sink.writeString("abcdefghi") - sink.emit() - - assertEquals("abc", source.readString(3)) - - val peek = source.peek() - assertEquals("def", peek.readString(3)) - assertEquals("ghi", peek.readString(3)) - assertFalse(peek.request(1)) - - assertEquals("def", source.readString(3)) - } - - @Test - fun peekMultiple() { - sink.writeString("abcdefghi") - sink.emit() - - assertEquals("abc", source.readString(3)) - - val peek1 = source.peek() - val peek2 = source.peek() - - assertEquals("def", peek1.readString(3)) - - assertEquals("def", peek2.readString(3)) - assertEquals("ghi", peek2.readString(3)) - assertFalse(peek2.request(1)) - - assertEquals("ghi", peek1.readString(3)) - assertFalse(peek1.request(1)) - - assertEquals("def", source.readString(3)) - } - - @Test - fun peekLarge() { - if (factory.isOneByteAtATime) { - // When run on CI this causes out-of-memory errors. - return - } - sink.writeString("abcdef") - sink.writeString("g".repeat(2 * Segment.SIZE)) - sink.writeString("hij") - sink.emit() - - assertEquals("abc", source.readString(3)) - - val peek = source.peek() - assertEquals("def", peek.readString(3)) - peek.skip((2 * Segment.SIZE).toLong()) - assertEquals("hij", peek.readString(3)) - assertFalse(peek.request(1)) - - assertEquals("def", source.readString(3)) - source.skip((2 * Segment.SIZE).toLong()) - assertEquals("hij", source.readString(3)) - } - - @Test - fun peekInvalid() { - sink.writeString("abcdefghi") - sink.emit() - - assertEquals("abc", source.readString(3)) - - val peek = source.peek() - assertEquals("def", peek.readString(3)) - assertEquals("ghi", peek.readString(3)) - assertFalse(peek.request(1)) - - assertEquals("def", source.readString(3)) - - val e = assertFailsWith { - peek.readString() - } - assertEquals("Peek source is invalid because upstream source was used", e.message) - } - - @OptIn(InternalIoApi::class) - @Test - fun peekSegmentThenInvalid() { - sink.writeString("abc") - sink.writeString("d".repeat(2 * Segment.SIZE)) - sink.emit() - - assertEquals("abc", source.readString(3)) - - // Peek a little data and skip the rest of the upstream source - val peek = source.peek() - assertEquals("ddd", peek.readString(3)) - source.transferTo(discardingSink()) - - // Skip the rest of the buffered data - peek.skip(peek.buffer.size) - - val e = assertFailsWith { - peek.readByte() - } - assertEquals("Peek source is invalid because upstream source was used", e.message) - } - - @OptIn(InternalIoApi::class) - @Test - fun peekDoesntReadTooMuch() { - // 6 bytes in source's buffer plus 3 bytes upstream. - sink.writeString("abcdef") - sink.emit() - source.require(6L) - sink.writeString("ghi") - sink.emit() - - val peek = source.peek() - - // Read 3 bytes. This reads some of the buffered data. - assertTrue(peek.request(3)) - if (source !is Buffer) { - assertEquals(6, source.buffer.size) - assertEquals(6, peek.buffer.size) - } - assertEquals("abc", peek.readString(3L)) - - // Read 3 more bytes. This exhausts the buffered data. - assertTrue(peek.request(3)) - if (source !is Buffer) { - assertEquals(6, source.buffer.size) - assertEquals(3, peek.buffer.size) - } - assertEquals("def", peek.readString(3L)) - - // Read 3 more bytes. This draws new bytes. - assertTrue(peek.request(3)) - assertEquals(9, source.buffer.size) - assertEquals(3, peek.buffer.size) - assertEquals("ghi", peek.readString(3L)) - } - - @OptIn(InternalIoApi::class) - @Test - fun factorySegmentSizes() { - sink.writeString("abc") - sink.emit() - source.require(3) - if (factory.isOneByteAtATime) { - assertEquals(listOf(1, 1, 1), segmentSizes(source.buffer)) - } else { - assertEquals(listOf(3), segmentSizes(source.buffer)) - } - } - - @Test - fun readUtf8Line() { - sink.writeString("first line\nsecond line\n") - sink.flush() - assertEquals("first line", source.readLine()) - assertEquals("second line\n", source.readString()) - assertEquals(null, source.readLine()) - - sink.writeString("\nnext line\n") - sink.flush() - assertEquals("", source.readLine()) - assertEquals("next line", source.readLine()) - - sink.writeString("There is no newline!") - sink.flush() - assertEquals("There is no newline!", source.readLine()) - - sink.writeString("Wot do u call it?\r\nWindows") - sink.flush() - assertEquals("Wot do u call it?", source.readLine()) - source.transferTo(discardingSink()) - - sink.writeString("reo\rde\red\n") - sink.flush() - assertEquals("reo\rde\red", source.readLine()) - } - - @Test - fun readUtf8LineStrict() { - sink.writeString("first line\nsecond line\n") - sink.flush() - assertEquals("first line", source.readLineStrict()) - assertEquals("second line\n", source.readString()) - assertFailsWith { source.readLineStrict() } - - sink.writeString("\nnext line\n") - sink.flush() - assertEquals("", source.readLineStrict()) - assertEquals("next line", source.readLineStrict()) - - sink.writeString("There is no newline!") - sink.flush() - assertFailsWith { source.readLineStrict() } - assertEquals("There is no newline!", source.readString()) - - sink.writeString("Wot do u call it?\r\nWindows") - sink.flush() - assertEquals("Wot do u call it?", source.readLineStrict()) - source.transferTo(discardingSink()) - - sink.writeString("reo\rde\red\n") - sink.flush() - assertEquals("reo\rde\red", source.readLineStrict()) - - sink.writeString("line\n") - sink.flush() - assertFailsWith { source.readLineStrict(3) } - assertEquals("line", source.readLineStrict(4)) - assertTrue(source.exhausted()) - - sink.writeString("line\r\n") - sink.flush() - assertFailsWith { source.readLineStrict(3) } - assertEquals("line", source.readLineStrict(4)) - assertTrue(source.exhausted()) - - sink.writeString("line\n") - sink.flush() - assertEquals("line", source.readLineStrict(5)) - assertTrue(source.exhausted()) - } - - @Test - fun readUnsignedByte() { - with(sink) { - writeByte(0) - writeByte(-1) - writeByte(-128) - writeByte(127) - flush() - } - - assertEquals(0u, source.readUByte()) - assertEquals(255u, source.readUByte()) - assertEquals(128u, source.readUByte()) - assertEquals(127u, source.readUByte()) - assertTrue(source.exhausted()) - } - - @Test - fun readTooShortUnsignedByteThrows() { - assertFailsWith { source.readUByte() } - } - - @Test - fun readUnsignedShort() { - with(sink) { - writeShort(0) - writeShort(-1) - writeShort(-32768) - writeShort(32767) - flush() - } - - assertEquals(0u, source.readUShort()) - assertEquals(65535u, source.readUShort()) - assertEquals(32768u, source.readUShort()) - assertEquals(32767u, source.readUShort()) - assertTrue(source.exhausted()) - } - - @Test - fun readUnsignedShortLe() { - sink.write(byteArrayOf(0x12, 0x34)) - sink.flush() - assertEquals(0x3412u, source.readUShortLe()) - } - - @Test - fun readTooShortUnsignedShortThrows() { - assertFailsWith { source.readUShort() } - sink.writeByte(0) - sink.flush() - assertFailsWith { source.readUShort() } - assertTrue(source.request(1)) - } - - @Test - fun readTooShortUnsignedShortLeThrows() { - assertFailsWith { source.readUShortLe() } - sink.writeByte(0) - sink.flush() - assertFailsWith { source.readUShortLe() } - assertTrue(source.request(1)) - } - - @Test - fun readUnsignedInt() { - with(sink) { - writeInt(0) - writeInt(-1) - writeInt(Int.MIN_VALUE) - writeInt(Int.MAX_VALUE) - flush() - } - - assertEquals(0u, source.readUInt()) - assertEquals(UInt.MAX_VALUE, source.readUInt()) - assertEquals(2147483648u, source.readUInt()) - assertEquals(Int.MAX_VALUE.toUInt(), source.readUInt()) - assertTrue(source.exhausted()) - } - - @Test - fun readUnsignedIntLe() { - sink.write(byteArrayOf(0x12, 0x34, 0x56, 0x78)) - sink.flush() - assertEquals(0x78563412u, source.readUIntLe()) - } - - @Test - fun readFloat() { - sink.write(byteArrayOf(70, 64, -26, -74)) - sink.flush() - assertEquals(12345.678F.toBits(), source.readFloat().toBits()) - } - - @Test - fun readDouble() { - sink.write(byteArrayOf(64, -2, 36, 12, -97, -56, -13, 35)) - sink.flush() - assertEquals(123456.78901, source.readDouble()) - } - - @Test - fun readFloatLe() { - sink.write(byteArrayOf(-74, -26, 64, 70)) - sink.flush() - assertEquals(12345.678F.toBits(), source.readFloatLe().toBits()) - } - - @Test - fun readDoubleLe() { - sink.write(byteArrayOf(35, -13, -56, -97, 12, 36, -2, 64)) - sink.flush() - assertEquals(123456.78901, source.readDoubleLe()) - } - - @Test - fun readTooShortFloatThrows() { - assertFailsWith { source.readFloat() } - sink.writeByte(0) - sink.flush() - assertFailsWith { source.readFloat() } - assertTrue(source.request(1)) - } - - @Test - fun readTooShortDoubleThrows() { - assertFailsWith { source.readDouble() } - sink.writeByte(0) - sink.flush() - assertFailsWith { source.readDouble() } - assertTrue(source.request(1)) - } - - @Test - fun readTooShortFloatLeThrows() { - assertFailsWith { source.readFloatLe() } - sink.writeByte(0) - sink.flush() - assertFailsWith { source.readFloatLe() } - assertTrue(source.request(1)) - } - - @Test - fun readTooShortDoubleLeThrows() { - assertFailsWith { source.readDoubleLe() } - sink.writeByte(0) - sink.flush() - assertFailsWith { source.readDoubleLe() } - assertTrue(source.request(1)) - } - - @Test - fun readTooShortUnsignedIntThrows() { - assertFailsWith { source.readUInt() } - sink.writeByte(0) - sink.flush() - assertFailsWith { source.readUInt() } - sink.writeByte(0) - sink.flush() - assertFailsWith { source.readUInt() } - sink.writeByte(0) - sink.flush() - assertFailsWith { source.readUInt() } - assertTrue(source.request(3)) - } - - @Test - fun readTooShortUnsignedIntLeThrows() { - assertFailsWith { source.readUIntLe() } - sink.writeByte(0) - sink.flush() - assertFailsWith { source.readUIntLe() } - sink.writeByte(0) - sink.flush() - assertFailsWith { source.readUIntLe() } - sink.writeByte(0) - sink.flush() - assertFailsWith { source.readUIntLe() } - assertTrue(source.request(3)) - } - - @Test - fun readUnsignedLong() { - with(sink) { - writeLong(0) - writeLong(-1) - writeLong(Long.MIN_VALUE) - writeLong(Long.MAX_VALUE) - flush() - } - - assertEquals(0u, source.readULong()) - assertEquals(ULong.MAX_VALUE, source.readULong()) - assertEquals(9223372036854775808u, source.readULong()) - assertEquals(Long.MAX_VALUE.toULong(), source.readULong()) - assertTrue(source.exhausted()) - } - - @Test - fun readUnsignedLongLe() { - sink.write(byteArrayOf(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xff.toByte())) - sink.flush() - assertEquals(0xff07060504030201u, source.readULongLe()) - } - - @Test - fun readTooShortUnsignedLongThrows() { - assertFailsWith { source.readULong() } - for (i in 0 until 7) { - sink.writeByte(0) - sink.flush() - assertFailsWith { source.readULong() } - } - assertTrue(source.request(7)) - } - - @Test - fun readTooShortUnsignedLongLeThrows() { - assertFailsWith { source.readULongLe() } - for (i in 0 until 7) { - sink.writeByte(0) - sink.flush() - assertFailsWith { source.readULongLe() } - } - assertTrue(source.request(7)) - } - - @Test - fun readByteString() { - with(sink) { - writeString("abcd") - writeString("e".repeat(Segment.SIZE)) - emit() - } - assertEquals("abcd" + "e".repeat(Segment.SIZE), source.readByteString().decodeToString()) - } - - @Test - fun readByteStringPartial() { - with(sink) { - writeString("abcd") - writeString("e".repeat(Segment.SIZE)) - emit() - } - assertEquals("abc", source.readByteString(3).decodeToString()) - assertEquals("d", source.readString(1)) - } - - @Test - fun readByteStringTooShortThrows() { - sink.writeString("abc") - sink.emit() - assertFailsWith { source.readByteString(4) } - - assertEquals("abc", source.readString()) // The read shouldn't consume any data. - } - - @Test - fun indexOfByteString() { - assertEquals(-1, source.indexOf("flop".encodeToByteString())) - - sink.writeString("flip flop") - sink.emit() - assertEquals(5, source.indexOf("flop".encodeToByteString())) - source.readString() // Clear stream. - - // Make sure we backtrack and resume searching after partial match. - sink.writeString("hi hi hi hey") - sink.emit() - assertEquals(3, source.indexOf("hi hi hey".encodeToByteString())) - } - - @Test - fun indexOfByteStringAtSegmentBoundary() { - sink.writeString("a".repeat(Segment.SIZE - 1)) - sink.writeString("bcd") - sink.emit() - assertEquals( - (Segment.SIZE - 3).toLong(), - source.indexOf("aabc".encodeToByteString(), (Segment.SIZE - 4).toLong()), - ) - assertEquals( - (Segment.SIZE - 3).toLong(), - source.indexOf("aabc".encodeToByteString(), (Segment.SIZE - 3).toLong()), - ) - assertEquals( - (Segment.SIZE - 2).toLong(), - source.indexOf("abcd".encodeToByteString(), (Segment.SIZE - 2).toLong()), - ) - assertEquals( - (Segment.SIZE - 2).toLong(), - source.indexOf("abc".encodeToByteString(), (Segment.SIZE - 2).toLong()), - ) - assertEquals( - (Segment.SIZE - 2).toLong(), - source.indexOf("abc".encodeToByteString(), (Segment.SIZE - 2).toLong()), - ) - assertEquals( - (Segment.SIZE - 2).toLong(), - source.indexOf("ab".encodeToByteString(), (Segment.SIZE - 2).toLong()), - ) - assertEquals( - (Segment.SIZE - 2).toLong(), - source.indexOf("a".encodeToByteString(), (Segment.SIZE - 2).toLong()), - ) - assertEquals( - (Segment.SIZE - 1).toLong(), - source.indexOf("bc".encodeToByteString(), (Segment.SIZE - 2).toLong()), - ) - assertEquals( - (Segment.SIZE - 1).toLong(), - source.indexOf("b".encodeToByteString(), (Segment.SIZE - 2).toLong()), - ) - assertEquals( - Segment.SIZE.toLong(), - source.indexOf("c".encodeToByteString(), (Segment.SIZE - 2).toLong()), - ) - assertEquals( - Segment.SIZE.toLong(), - source.indexOf("c".encodeToByteString(), Segment.SIZE.toLong()), - ) - assertEquals( - (Segment.SIZE + 1).toLong(), - source.indexOf("d".encodeToByteString(), (Segment.SIZE - 2).toLong()), - ) - assertEquals( - (Segment.SIZE + 1).toLong(), - source.indexOf("d".encodeToByteString(), (Segment.SIZE + 1).toLong()), - ) - } - - @Test - fun indexOfDoesNotWrapAround() { - sink.writeString("a".repeat(Segment.SIZE - 1)) - sink.writeString("bcd") - sink.emit() - assertEquals(-1, source.indexOf("abcda".encodeToByteString(), (Segment.SIZE - 3).toLong())) - } - - @Test - fun indexOfByteStringWithOffset() { - assertEquals(-1, source.indexOf("flop".encodeToByteString(), 1)) - - sink.writeString("flop flip flop") - sink.emit() - assertEquals(10, source.indexOf("flop".encodeToByteString(), 1)) - source.readString() // Clear stream - - // Make sure we backtrack and resume searching after partial match. - sink.writeString("hi hi hi hi hey") - sink.emit() - assertEquals(6, source.indexOf("hi hi hey".encodeToByteString(), 1)) - } - - @Test - fun indexOfEmptyByteString() { - assertEquals(0, source.indexOf(ByteString())) - - sink.writeString("blablabla") - sink.emit() - assertEquals(0, source.indexOf(ByteString())) - } - - @Test - fun indexOfByteStringInvalidArgumentsThrows() { - assertFailsWith { - source.indexOf("hi".encodeToByteString(), -1) - } - } - - /** - * With [BufferedSourceFactory.ONE_BYTE_AT_A_TIME_BUFFERED_SOURCE], this code was extremely slow. - * https://github.com/square/okio/issues/171 - */ - @Test - fun indexOfByteStringAcrossSegmentBoundaries() { - sink.writeString("a".repeat(Segment.SIZE * 2 - 3)) - sink.writeString("bcdefg") - sink.emit() - assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("ab".encodeToByteString())) - assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abc".encodeToByteString())) - assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcd".encodeToByteString())) - assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcde".encodeToByteString())) - assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcdef".encodeToByteString())) - assertEquals((Segment.SIZE * 2 - 4).toLong(), source.indexOf("abcdefg".encodeToByteString())) - assertEquals((Segment.SIZE * 2 - 3).toLong(), source.indexOf("bcdefg".encodeToByteString())) - assertEquals((Segment.SIZE * 2 - 2).toLong(), source.indexOf("cdefg".encodeToByteString())) - assertEquals((Segment.SIZE * 2 - 1).toLong(), source.indexOf("defg".encodeToByteString())) - assertEquals((Segment.SIZE * 2).toLong(), source.indexOf("efg".encodeToByteString())) - assertEquals((Segment.SIZE * 2 + 1).toLong(), source.indexOf("fg".encodeToByteString())) - assertEquals((Segment.SIZE * 2 + 2).toLong(), source.indexOf("g".encodeToByteString())) - } - - @Test - fun indexOfByteStringSpanningAcrossMultipleSegments() { - sink.writeString("a".repeat(SEGMENT_SIZE)) - sink.emit() - sink.writeString("bbbb") - sink.emit() - sink.write(Buffer().also { it.writeString("c".repeat(SEGMENT_SIZE)) }, SEGMENT_SIZE.toLong()) - sink.emit() - - source.skip(SEGMENT_SIZE - 10L) - assertEquals(9, source.indexOf("abbbbc".encodeToByteString())) - } -} diff --git a/core/common/test/AlwaysSharedCopyTrackerTest.kt b/core/common/test/AlwaysSharedCopyTrackerTest.kt deleted file mode 100644 index 62418c8c0..000000000 --- a/core/common/test/AlwaysSharedCopyTrackerTest.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io - -import kotlin.test.Test -import kotlin.test.assertTrue - -class AlwaysSharedCopyTrackerTest { - @Test - fun stateTransition() { - val tracker = AlwaysSharedCopyTracker - assertTrue(tracker.shared) - - assertTrue(tracker.removeCopy()) - assertTrue(tracker.shared) - - tracker.addCopy() - assertTrue(tracker.shared) - - assertTrue(tracker.removeCopy()) - assertTrue(tracker.shared) - } -} diff --git a/core/common/test/CommonBufferTest.kt b/core/common/test/CommonBufferTest.kt deleted file mode 100644 index d009c5f62..000000000 --- a/core/common/test/CommonBufferTest.kt +++ /dev/null @@ -1,621 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2014 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package kotlinx.io - -import kotlinx.io.bytestring.ByteString -import kotlinx.io.bytestring.encodeToByteString -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertTrue - -private const val SEGMENT_SIZE = Segment.SIZE - -/** - * Tests solely for the behavior of Buffer's implementation. For generic BufferedSink or - * BufferedSource behavior, use BufferedSinkTest or BufferedSourceTest, respectively. - */ -class CommonBufferTest { - @Test - fun readAndWriteUtf8() { - val buffer = Buffer() - buffer.writeString("ab") - assertEquals(2, buffer.size) - buffer.writeString("cdef") - assertEquals(6, buffer.size) - assertEquals("abcd", buffer.readString(4)) - assertEquals(2, buffer.size) - assertEquals("ef", buffer.readString(2)) - assertEquals(0, buffer.size) - assertFailsWith { - buffer.readString(1) - } - } - - @Test - fun bufferToString() { - assertEquals("Buffer(size=0)", Buffer().toString()) - - assertEquals( - "Buffer(size=10 hex=610d0a620a630d645c65)", - Buffer().also { it.writeString("a\r\nb\nc\rd\\e") }.toString() - ) - - assertEquals( - "Buffer(size=11 hex=547972616e6e6f73617572)", - Buffer().also { it.writeString("Tyrannosaur") }.toString() - ) - - assertEquals( - "Buffer(size=16 hex=74c999cb8872616ec999cb8c73c3b472)", - Buffer().also { it.write("74c999cb8872616ec999cb8c73c3b472".decodeHex()) }.toString() - ) - - assertEquals( - "Buffer(size=64 hex=00000000000000000000000000000000000000000000000000000000000000000000000" + - "000000000000000000000000000000000000000000000000000000000)", - Buffer().also { it.write(ByteArray(64)) }.toString() - ) - - assertEquals( - "Buffer(size=66 hex=000000000000000000000000000000000000000000000000000000000000" + - "00000000000000000000000000000000000000000000000000000000000000000000…)", - Buffer().also { it.write(ByteArray(66)) }.toString() - ) - } - - @Test - fun multipleSegmentBuffers() { - val buffer = Buffer() - buffer.writeString('a'.repeat(1000)) - buffer.writeString('b'.repeat(2500)) - buffer.writeString('c'.repeat(5000)) - buffer.writeString('d'.repeat(10000)) - buffer.writeString('e'.repeat(25000)) - buffer.writeString('f'.repeat(50000)) - - assertEquals('a'.repeat(999), buffer.readString(999)) // a...a - assertEquals("a" + 'b'.repeat(2500) + "c", buffer.readString(2502)) // ab...bc - assertEquals('c'.repeat(4998), buffer.readString(4998)) // c...c - assertEquals("c" + 'd'.repeat(10000) + "e", buffer.readString(10002)) // cd...de - assertEquals('e'.repeat(24998), buffer.readString(24998)) // e...e - assertEquals("e" + 'f'.repeat(50000), buffer.readString(50001)) // ef...f - assertEquals(0, buffer.size) - } - - @Test - fun fillAndDrainPool() { - val buffer = Buffer() - - // Take 2 * MAX_SIZE segments. This will drain the pool, even if other tests filled it. - buffer.write(ByteArray(SegmentPool.MAX_SIZE)) - buffer.write(ByteArray(SegmentPool.MAX_SIZE)) - assertEquals(0, SegmentPool.byteCount) - - // Recycle MAX_SIZE segments. They're all in the pool. - buffer.skip(SegmentPool.MAX_SIZE.toLong()) - assertEquals(SegmentPool.MAX_SIZE, SegmentPool.byteCount) - - // Recycle MAX_SIZE more segments. The pool is full so they get garbage collected. - buffer.skip(SegmentPool.MAX_SIZE.toLong()) - assertEquals(SegmentPool.MAX_SIZE, SegmentPool.byteCount) - - // Take MAX_SIZE segments to drain the pool. - buffer.write(ByteArray(SegmentPool.MAX_SIZE)) - assertEquals(0, SegmentPool.byteCount) - - // Take MAX_SIZE more segments. The pool is drained so these will need to be allocated. - buffer.write(ByteArray(SegmentPool.MAX_SIZE)) - assertEquals(0, SegmentPool.byteCount) - } - - @Test - fun moveBytesBetweenBuffersShareSegment() { - val size = Segment.SIZE / 2 - 1 - val segmentSizes = moveBytesBetweenBuffers('a'.repeat(size), 'b'.repeat(size)) - assertEquals(listOf(size * 2), segmentSizes) - } - - @Test - fun moveBytesBetweenBuffersReassignSegment() { - val size = Segment.SIZE / 2 + 1 - val segmentSizes = moveBytesBetweenBuffers('a'.repeat(size), 'b'.repeat(size)) - assertEquals(listOf(size, size), segmentSizes) - } - - @Test - fun moveBytesBetweenBuffersMultipleSegments() { - val size = 3 * Segment.SIZE + 1 - val segmentSizes = moveBytesBetweenBuffers('a'.repeat(size), 'b'.repeat(size)) - assertEquals( - listOf( - Segment.SIZE, Segment.SIZE, Segment.SIZE, 1, - Segment.SIZE, Segment.SIZE, Segment.SIZE, 1 - ), - segmentSizes - ) - } - - private fun moveBytesBetweenBuffers(vararg contents: String): List { - val expected = StringBuilder() - val buffer = Buffer() - for (s in contents) { - val source = Buffer() - source.writeString(s) - buffer.transferFrom(source) - expected.append(s) - } - val segmentSizes = segmentSizes(buffer) - assertEquals(expected.toString(), buffer.readString(expected.length.toLong())) - return segmentSizes - } - - /** The big part of source's first segment is being moved. */ - @Test - fun writeSplitSourceBufferLeft() { - val writeSize = Segment.SIZE / 2 + 1 - - val sink = Buffer() - sink.writeString('b'.repeat(Segment.SIZE - 10)) - - val source = Buffer() - source.writeString('a'.repeat(Segment.SIZE * 2)) - sink.write(source, writeSize.toLong()) - - assertEquals(listOf(Segment.SIZE - 10, writeSize), segmentSizes(sink)) - assertEquals(listOf(Segment.SIZE - writeSize, Segment.SIZE), segmentSizes(source)) - } - - /** The big part of source's first segment is staying put. */ - @Test - fun writeSplitSourceBufferRight() { - val writeSize = Segment.SIZE / 2 - 1 - - val sink = Buffer() - sink.writeString('b'.repeat(Segment.SIZE - 10)) - - val source = Buffer() - source.writeString('a'.repeat(Segment.SIZE * 2)) - sink.write(source, writeSize.toLong()) - - assertEquals(listOf(Segment.SIZE - 10, writeSize), segmentSizes(sink)) - assertEquals(listOf(Segment.SIZE - writeSize, Segment.SIZE), segmentSizes(source)) - } - - @Test - fun writePrefixDoesntSplit() { - val sink = Buffer() - sink.writeString('b'.repeat(10)) - - val source = Buffer() - source.writeString('a'.repeat(Segment.SIZE * 2)) - sink.write(source, 20) - - assertEquals(listOf(30), segmentSizes(sink)) - assertEquals(listOf(Segment.SIZE - 20, Segment.SIZE), segmentSizes(source)) - assertEquals(30, sink.size) - assertEquals((Segment.SIZE * 2 - 20).toLong(), source.size) - } - - @Test - fun writePrefixDoesntSplitButRequiresCompact() { - val sink = Buffer() - sink.writeString('b'.repeat(Segment.SIZE - 10)) // limit = size - 10 - sink.readString((Segment.SIZE - 20).toLong()) // pos = size = 20 - - val source = Buffer() - source.writeString('a'.repeat(Segment.SIZE * 2)) - sink.write(source, 20) - - assertEquals(listOf(30), segmentSizes(sink)) - assertEquals(listOf(Segment.SIZE - 20, Segment.SIZE), segmentSizes(source)) - assertEquals(30, sink.size) - assertEquals((Segment.SIZE * 2 - 20).toLong(), source.size) - } - - @Test - fun writeSourceWithNegativeNumberOfBytes() { - val sink = Buffer() - val source: Source = Buffer() - - assertFailsWith { sink.write(source, -1L) } - } - - @Test - fun moveAllRequestedBytesWithRead() { - val sink = Buffer() - sink.writeString('a'.repeat(10)) - - val source = Buffer() - source.writeString('b'.repeat(15)) - - assertEquals(10, source.readAtMostTo(sink, 10)) - assertEquals(20, sink.size) - assertEquals(5, source.size) - assertEquals('a'.repeat(10) + 'b'.repeat(10), sink.readString(20)) - } - - @Test - fun moveFewerThanRequestedBytesWithRead() { - val sink = Buffer() - sink.writeString('a'.repeat(10)) - - val source = Buffer() - source.writeString('b'.repeat(20)) - - assertEquals(20, source.readAtMostTo(sink, 25)) - assertEquals(30, sink.size) - assertEquals(0, source.size) - assertEquals('a'.repeat(10) + 'b'.repeat(20), sink.readString(30)) - } - - @Test - fun indexOfWithOffset() { - val buffer = Buffer() - val halfSegment = Segment.SIZE / 2 - buffer.writeString('a'.repeat(halfSegment)) - buffer.writeString('b'.repeat(halfSegment)) - buffer.writeString('c'.repeat(halfSegment)) - buffer.writeString('d'.repeat(halfSegment)) - assertEquals(0, buffer.indexOf('a'.code.toByte(), 0)) - assertEquals((halfSegment - 1).toLong(), buffer.indexOf('a'.code.toByte(), (halfSegment - 1).toLong())) - assertEquals(halfSegment.toLong(), buffer.indexOf('b'.code.toByte(), (halfSegment - 1).toLong())) - assertEquals((halfSegment * 2).toLong(), buffer.indexOf('c'.code.toByte(), (halfSegment - 1).toLong())) - assertEquals((halfSegment * 3).toLong(), buffer.indexOf('d'.code.toByte(), (halfSegment - 1).toLong())) - assertEquals((halfSegment * 3).toLong(), buffer.indexOf('d'.code.toByte(), (halfSegment * 2).toLong())) - assertEquals((halfSegment * 3).toLong(), buffer.indexOf('d'.code.toByte(), (halfSegment * 3).toLong())) - assertEquals((halfSegment * 4 - 1).toLong(), buffer.indexOf('d'.code.toByte(), (halfSegment * 4 - 1).toLong())) - } - - @Test - fun byteAt() { - val buffer = Buffer() - buffer.writeString("a") - buffer.writeString('b'.repeat(Segment.SIZE)) - buffer.writeString("c") - assertEquals('a'.code.toLong(), buffer[0].toLong()) - assertEquals('a'.code.toLong(), buffer[0].toLong()) // getByte doesn't mutate! - assertEquals('c'.code.toLong(), buffer[buffer.size - 1].toLong()) - assertEquals('b'.code.toLong(), buffer[buffer.size - 2].toLong()) - assertEquals('b'.code.toLong(), buffer[buffer.size - 3].toLong()) - } - - @Test - fun getByteOfEmptyBuffer() { - val buffer = Buffer() - assertFailsWith { - buffer[0] - } - } - - @Test - fun getByteByInvalidIndex() { - val buffer = Buffer().also { it.write(ByteArray(10)) } - - assertFailsWith { buffer[-1] } - assertFailsWith { buffer[buffer.size] } - } - - @Test - fun writePrefixToEmptyBuffer() { - val sink = Buffer() - val source = Buffer() - source.writeString("abcd") - sink.write(source, 2) - assertEquals("ab", sink.readString(2)) - } - - // Buffer don't override equals and hashCode - @Test - fun equalsAndHashCode() { - val a = Buffer().also { it.writeString("dog") } - assertEquals(a, a) - - val b = Buffer().also { it.writeString("hotdog") } - assertTrue(a != b) - - b.readString(3) // Leaves b containing 'dog'. - assertTrue(a != b) - } - - /** - * When writing data that's already buffered, there's no reason to page the - * data by segment. - */ - @Test - fun readAllWritesAllSegmentsAtOnce() { - val write1 = Buffer() - write1.writeString( - 'a'.repeat(Segment.SIZE) + - 'b'.repeat(Segment.SIZE) + - 'c'.repeat(Segment.SIZE) - ) - - val source = Buffer() - source.writeString( - 'a'.repeat(Segment.SIZE) + - 'b'.repeat(Segment.SIZE) + - 'c'.repeat(Segment.SIZE) - ) - - val mockSink = MockSink() - - assertEquals((Segment.SIZE * 3).toLong(), source.transferTo(mockSink)) - assertEquals(0, source.size) - mockSink.assertLog("write($write1, ${write1.size})") - } - - @Test - fun writeAllMultipleSegments() { - val source = Buffer().also { it.writeString('a'.repeat(Segment.SIZE * 3)) } - val sink = Buffer() - - assertEquals((Segment.SIZE * 3).toLong(), sink.transferFrom(source)) - assertEquals(0, source.size) - assertEquals('a'.repeat(Segment.SIZE * 3), sink.readString()) - } - - @Test - fun copyTo() { - val source = Buffer() - source.writeString("party") - - val target = Buffer() - source.copyTo(target, startIndex = 1, endIndex = 4) - - assertEquals("art", target.readString()) - assertEquals("party", source.readString()) - } - - @Test - fun copyToAll() { - val source = Buffer() - source.writeString("hello") - - val target = Buffer() - source.copyTo(target) - - assertEquals("hello", source.readString()) - assertEquals("hello", target.readString()) - } - - @Test - fun copyToWithOnlyStartIndex() { - val source = Buffer() - source.writeString("hello") - - val target = Buffer() - source.copyTo(target, startIndex = 1) - - assertEquals("hello", source.readString()) - assertEquals("ello", target.readString()) - } - - @Test - fun copyToWithOnlyEndIndex() { - val source = Buffer() - source.writeString("hello") - - val target = Buffer() - source.copyTo(target, endIndex = 1) - - assertEquals("hello", source.readString()) - assertEquals("h", target.readString()) - } - - @Test - fun copyToOnSegmentBoundary() { - val aStr = 'a'.repeat(Segment.SIZE) - val bs = 'b'.repeat(Segment.SIZE) - val cs = 'c'.repeat(Segment.SIZE) - val ds = 'd'.repeat(Segment.SIZE) - - val source = Buffer() - source.writeString(aStr) - source.writeString(bs) - source.writeString(cs) - - val target = Buffer() - target.writeString(ds) - - source.copyTo( - target, startIndex = aStr.length.toLong(), - endIndex = aStr.length.toLong() + (bs.length + cs.length).toLong() - ) - assertEquals(ds + bs + cs, target.readString()) - } - - @Test - fun copyToOffSegmentBoundary() { - val aStr = 'a'.repeat(Segment.SIZE - 1) - val bs = 'b'.repeat(Segment.SIZE + 2) - val cs = 'c'.repeat(Segment.SIZE - 4) - val ds = 'd'.repeat(Segment.SIZE + 8) - - val source = Buffer() - source.writeString(aStr) - source.writeString(bs) - source.writeString(cs) - - val target = Buffer() - target.writeString(ds) - - source.copyTo( - target, startIndex = aStr.length.toLong(), - endIndex = aStr.length.toLong() + (bs.length + cs.length).toLong() - ) - assertEquals(ds + bs + cs, target.readString()) - } - - @Test - fun copyToSourceAndTargetCanBeTheSame() { - val aStr = 'a'.repeat(Segment.SIZE) - val bs = 'b'.repeat(Segment.SIZE) - - val source = Buffer() - source.writeString(aStr) - source.writeString(bs) - - source.copyTo(source, startIndex = 0, endIndex = source.size) - assertEquals(aStr + bs + aStr + bs, source.readString()) - } - - @Test - fun copyToEmptySource() { - val source = Buffer() - val target = Buffer().also { it.writeString("aaa") } - source.copyTo(target, startIndex = 0L, endIndex = 0L) - assertEquals("", source.readString()) - assertEquals("aaa", target.readString()) - } - - @Test - fun copyToEmptyTarget() { - val source = Buffer().also { it.writeString("aaa") } - val target = Buffer() - source.copyTo(target, startIndex = 0L, endIndex = 3L) - assertEquals("aaa", source.readString()) - assertEquals("aaa", target.readString()) - } - - @Test - fun completeSegmentByteCountOnEmptyBuffer() { - val buffer = Buffer() - assertEquals(0, buffer.completeSegmentByteCount()) - } - - @Test - fun completeSegmentByteCountOnBufferWithFullSegments() { - val buffer = Buffer() - buffer.writeString("a".repeat(Segment.SIZE * 4)) - assertEquals((Segment.SIZE * 4).toLong(), buffer.completeSegmentByteCount()) - } - - @Test - fun completeSegmentByteCountOnBufferWithIncompleteTailSegment() { - val buffer = Buffer() - buffer.writeString("a".repeat(Segment.SIZE * 4 - 10)) - assertEquals((Segment.SIZE * 3).toLong(), buffer.completeSegmentByteCount()) - } - - @Test - fun cloneDoesNotObserveWritesToOriginal() { - val original = Buffer() - val clone: Buffer = original.copy() - original.writeString("abc") - assertEquals(0, clone.size) - } - - @Test - fun cloneDoesNotObserveReadsFromOriginal() { - val original = Buffer() - original.writeString("abc") - val clone: Buffer = original.copy() - assertEquals("abc", original.readString(3)) - assertEquals(3, clone.size) - assertEquals("ab", clone.readString(2)) - } - - @Test - fun originalDoesNotObserveWritesToClone() { - val original = Buffer() - val clone: Buffer = original.copy() - clone.writeString("abc") - assertEquals(0, original.size) - } - - @Test - fun originalDoesNotObserveReadsFromClone() { - val original = Buffer() - original.writeString("abc") - val clone: Buffer = original.copy() - assertEquals("abc", clone.readString(3)) - assertEquals(3, original.size) - assertEquals("ab", original.readString(2)) - } - - @Test - fun cloneMultipleSegments() { - val original = Buffer() - original.writeString("a".repeat(SEGMENT_SIZE * 3)) - val clone: Buffer = original.copy() - original.writeString("b".repeat(SEGMENT_SIZE * 3)) - clone.writeString("c".repeat(SEGMENT_SIZE * 3)) - - assertEquals( - "a".repeat(SEGMENT_SIZE * 3) + "b".repeat(SEGMENT_SIZE * 3), - original.readString((SEGMENT_SIZE * 6).toLong()) - ) - assertEquals( - "a".repeat(SEGMENT_SIZE * 3) + "c".repeat(SEGMENT_SIZE * 3), - clone.readString((SEGMENT_SIZE * 6).toLong()) - ) - } - - @Test - fun readAndWriteToSelf() { - val buffer = Buffer().also { it.writeByte(1) } - val src: Source = buffer - val dst: Sink = buffer - - assertFailsWith { src.transferTo(dst) } - assertFailsWith { dst.transferFrom(src) } - assertFailsWith { src.readAtMostTo(buffer, 1) } - assertFailsWith { src.readTo(dst, 1) } - assertFailsWith { dst.write(buffer, 1) } - assertFailsWith { dst.write(src, 1) } - } - - @Test - fun transferCopy() { - val buffer = Buffer().also { it.writeByte(42) } - val copy = buffer.copy() - copy.transferTo(buffer) - assertArrayEquals(byteArrayOf(42, 42), buffer.readByteArray()) - } - - @Test - fun snapshot() { - val buffer = Buffer() - assertEquals(ByteString(), buffer.snapshot()) - buffer.writeString("hello") - assertEquals("hello".encodeToByteString(), buffer.snapshot()) - buffer.clear() - assertEquals(ByteString(), buffer.snapshot()) - } - - @Test - fun splitHead() { - val dst = Buffer() - val src = Buffer().also { it.write(ByteArray(SEGMENT_SIZE)) } - - dst.write(src, src.size / 2) - assertEquals(src.size, dst.size) - - assertEquals(src.head, src.tail) - assertEquals(null, src.head?.prev) - assertEquals(null, src.tail?.next) - - assertEquals(dst.head, dst.tail) - assertEquals(null, dst.head?.prev) - assertEquals(null, dst.tail?.next) - } -} diff --git a/core/common/test/CommonPlatformTest.kt b/core/common/test/CommonPlatformTest.kt deleted file mode 100644 index 2cf8faa20..000000000 --- a/core/common/test/CommonPlatformTest.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kotlinx.io - -import kotlin.test.Test -import kotlin.test.assertEquals - -class CommonPlatformTest { - @Test - fun sourceBuffer() { - val source = Buffer().also { it.writeString("a") } - val buffered = (source as RawSource).buffered() - assertEquals(buffered.readString(), "a") - assertEquals(source.size, 0L) - } - - @Test - fun sinkBuffer() { - val sink = Buffer() - val buffered = (sink as RawSink).buffered() - buffered.writeString("a") - assertEquals(sink.size, 0L) - buffered.flush() - assertEquals(sink.size, 1L) - } - - @Test - fun discardingSinkTest() { - discardingSink().write(Buffer().also { it.writeString("a") }, 1L) - } -} diff --git a/core/common/test/CommonRealSinkTest.kt b/core/common/test/CommonRealSinkTest.kt deleted file mode 100644 index f6b454e97..000000000 --- a/core/common/test/CommonRealSinkTest.kt +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kotlinx.io - -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith - -/** - * Tests solely for the behavior of RealBufferedSink's implementation. For generic - * BufferedSink behavior use BufferedSinkTest. - */ -@OptIn(InternalIoApi::class) -class CommonRealSinkTest { - @Test - fun bufferedSinkEmitsTailWhenItIsComplete() { - val sink = Buffer() - val bufferedSink = (sink as RawSink).buffered() - bufferedSink.writeString("a".repeat(Segment.SIZE - 1)) - assertEquals(0, sink.size) - bufferedSink.writeByte(0) - assertEquals(Segment.SIZE.toLong(), sink.size) - assertEquals(0, bufferedSink.buffer.size) - } - - @Test - fun bufferedSinkEmitMultipleSegments() { - val sink = Buffer() - val bufferedSink = (sink as RawSink).buffered() - bufferedSink.writeString("a".repeat(Segment.SIZE * 4 - 1)) - assertEquals(Segment.SIZE.toLong() * 3L, sink.size) - assertEquals(Segment.SIZE.toLong() - 1L, bufferedSink.buffer.size) - } - - @Test - fun bufferedSinkFlush() { - val sink = Buffer() - val bufferedSink = (sink as RawSink).buffered() - bufferedSink.writeByte('a'.code.toByte()) - assertEquals(0, sink.size) - bufferedSink.flush() - assertEquals(0, bufferedSink.buffer.size) - assertEquals(1, sink.size) - } - - @Test - fun bytesEmittedToSinkWithFlush() { - val sink = Buffer() - val bufferedSink = (sink as RawSink).buffered() - bufferedSink.writeString("abc") - bufferedSink.flush() - assertEquals(3, sink.size) - } - - @Test - fun bytesNotEmittedToSinkWithoutFlush() { - val sink = Buffer() - val bufferedSink = (sink as RawSink).buffered() - bufferedSink.writeString("abc") - assertEquals(0, sink.size) - } - - @Test - fun bytesEmittedToSinkWithEmit() { - val sink = Buffer() - val bufferedSink = (sink as RawSink).buffered() - bufferedSink.writeString("abc") - bufferedSink.emit() - assertEquals(3, sink.size) - } - - @Test - fun completeSegmentsEmitted() { - val sink = Buffer() - val bufferedSink = (sink as RawSink).buffered() - bufferedSink.writeString("a".repeat(Segment.SIZE * 3)) - assertEquals(Segment.SIZE.toLong() * 3L, sink.size) - } - - @Test - fun incompleteSegmentsNotEmitted() { - val sink = Buffer() - val bufferedSink = (sink as RawSink).buffered() - bufferedSink.writeString("a".repeat(Segment.SIZE * 3 - 1)) - assertEquals(Segment.SIZE.toLong() * 2L, sink.size) - } - - @Test - fun closeWithExceptionWhenWriting() { - val mockSink = MockSink() - mockSink.scheduleThrow(0, IOException()) - val bufferedSink = mockSink.buffered() - bufferedSink.writeByte('a'.code.toByte()) - assertFailsWith { - bufferedSink.close() - } - - mockSink.assertLog("write(Buffer(size=1 hex=61), 1)", "close()") - } - - @Test - fun closeWithExceptionWhenClosing() { - val mockSink = MockSink() - mockSink.scheduleThrow(1, IOException()) - val bufferedSink = mockSink.buffered() - bufferedSink.writeByte('a'.code.toByte()) - assertFailsWith { - bufferedSink.close() - } - - mockSink.assertLog("write(Buffer(size=1 hex=61), 1)", "close()") - } - - @Test - fun closeWithExceptionWhenWritingAndClosing() { - val mockSink = MockSink() - mockSink.scheduleThrow(0, IOException("first")) - mockSink.scheduleThrow(1, IOException("second")) - val bufferedSink = mockSink.buffered() - bufferedSink.writeByte('a'.code.toByte()) - assertFailsWith("first.*") { - bufferedSink.close() - } - - mockSink.assertLog("write(Buffer(size=1 hex=61), 1)", "close()") - } - - @Test - fun operationsAfterClose() { - val mockSink = MockSink() - val bufferedSink = mockSink.buffered() - bufferedSink.writeByte('a'.code.toByte()) - bufferedSink.close() - - // Test a sample set of methods. - assertFailsWith { bufferedSink.writeByte('a'.code.toByte()) } - assertFailsWith { bufferedSink.write(ByteArray(10)) } - assertFailsWith { bufferedSink.hintEmit() } - assertFailsWith { bufferedSink.emit() } - assertFailsWith { bufferedSink.flush() } - } - - @Test - fun writeAll() { - val mockSink = MockSink() - val bufferedSink = mockSink.buffered() - - bufferedSink.buffer.writeString("abc") - assertEquals(3, bufferedSink.transferFrom(Buffer().also { it.writeString("def") })) - - assertEquals(6, bufferedSink.buffer.size) - assertEquals("abcdef", bufferedSink.buffer.readString(6)) - mockSink.assertLog() // No writes. - } - - @Test - fun writeAllExhausted() { - val mockSink = MockSink() - val bufferedSink = mockSink.buffered() - - assertEquals(0, bufferedSink.transferFrom(Buffer())) - assertEquals(0, bufferedSink.buffer.size) - mockSink.assertLog() // No writes. - } - - @Test - fun writeAllWritesOneSegmentAtATime() { - val write1 = Buffer().also { it.writeString("a".repeat(Segment.SIZE)) } - val write2 = Buffer().also { it.writeString("b".repeat(Segment.SIZE)) } - val write3 = Buffer().also { it.writeString("c".repeat(Segment.SIZE)) } - - val source = Buffer() - source.writeString( - "${"a".repeat(Segment.SIZE)}${"b".repeat(Segment.SIZE)}${"c".repeat(Segment.SIZE)}" - ) - - val mockSink = MockSink() - val bufferedSink = mockSink.buffered() - assertEquals(Segment.SIZE.toLong() * 3L, bufferedSink.transferFrom(source)) - - mockSink.assertLog( - "write($write1, ${write1.size})", - "write($write2, ${write2.size})", - "write($write3, ${write3.size})" - ) - } - - @Test - fun closeMultipleTimes() { - var closeCalls = 0 - val rawSink: RawSink = object : RawSink { - override fun write(source: Buffer, byteCount: Long) = Unit - override fun flush() = Unit - override fun close() { - closeCalls++ - } - } - val sink = rawSink.buffered() - - sink.close() - assertFailsWith { sink.writeByte(0) } - sink.close() // should do nothing - assertEquals(1, closeCalls) - } -} diff --git a/core/common/test/CommonRealSourceTest.kt b/core/common/test/CommonRealSourceTest.kt deleted file mode 100644 index b345516f2..000000000 --- a/core/common/test/CommonRealSourceTest.kt +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kotlinx.io - -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith - -/** - * Tests solely for the behavior of RealBufferedSource's implementation. For generic - * BufferedSource behavior, use BufferedSourceTest. - */ -@OptIn(InternalIoApi::class) -class CommonRealSourceTest { - @Test - fun indexOfStopsReadingAtLimit() { - val buffer = Buffer().also { it.writeString("abcdef") } - val bufferedSource = ( - object : RawSource by buffer { - override fun readAtMostTo(sink: Buffer, byteCount: Long): Long { - return buffer.readAtMostTo(sink, minOf(1, byteCount)) - } - } - ).buffered() - - assertEquals(6, buffer.size) - assertEquals(-1, bufferedSource.indexOf('e'.code.toByte(), 0, 4)) - assertEquals(2, buffer.size) - } - - @Test - fun requireTracksBufferFirst() { - val source = Buffer() - source.writeString("bb") - - val bufferedSource = (source as RawSource).buffered() - bufferedSource.buffer.writeString("aa") - - bufferedSource.require(2) - assertEquals(2, bufferedSource.buffer.size) - assertEquals(2, source.size) - } - - @Test - fun requireIncludesBufferBytes() { - val source = Buffer() - source.writeString("b") - - val bufferedSource = (source as RawSource).buffered() - bufferedSource.buffer.writeString("a") - - bufferedSource.require(2) - assertEquals("ab", bufferedSource.buffer.readString(2)) - } - - @Test - fun requireInsufficientData() { - val source = Buffer() - source.writeString("a") - - val bufferedSource = (source as RawSource).buffered() - - assertFailsWith { - bufferedSource.require(2) - } - } - - @Test - fun requireReadsOneSegmentAtATime() { - val source = Buffer() - source.writeString("a".repeat(Segment.SIZE)) - source.writeString("b".repeat(Segment.SIZE)) - - val bufferedSource = (source as RawSource).buffered() - - bufferedSource.require(2) - assertEquals(Segment.SIZE.toLong(), source.size) - assertEquals(Segment.SIZE.toLong(), bufferedSource.buffer.size) - } - - @Test - fun skipReadsOneSegmentAtATime() { - val source = Buffer() - source.writeString("a".repeat(Segment.SIZE)) - source.writeString("b".repeat(Segment.SIZE)) - val bufferedSource = (source as RawSource).buffered() - bufferedSource.skip(2) - assertEquals(Segment.SIZE.toLong(), source.size) - assertEquals(Segment.SIZE.toLong() - 2L, bufferedSource.buffer.size) - } - - @Test - fun skipTracksBufferFirst() { - val source = Buffer() - source.writeString("bb") - - val bufferedSource = (source as RawSource).buffered() - bufferedSource.buffer.writeString("aa") - - bufferedSource.skip(2) - assertEquals(0, bufferedSource.buffer.size) - assertEquals(2, source.size) - } - - @Test - fun operationsAfterClose() { - val source = Buffer() - val bufferedSource = (source as RawSource).buffered() - bufferedSource.close() - - // Test a sample set of methods. - assertFailsWith { bufferedSource.indexOf(1.toByte()) } - assertFailsWith { bufferedSource.skip(1) } - assertFailsWith { bufferedSource.readByte() } - assertFailsWith { bufferedSource.exhausted() } - assertFailsWith { bufferedSource.require(1) } - assertFailsWith { bufferedSource.readByteArray() } - assertFailsWith { bufferedSource.peek() } - } - - /** - * We don't want transferTo to buffer an unbounded amount of data. Instead it - * should buffer a segment, write it, and repeat. - */ - @Test - fun transferToReadsOneSegmentAtATime() { - val write1 = Buffer().also { it.writeString("a".repeat(Segment.SIZE)) } - val write2 = Buffer().also { it.writeString("b".repeat(Segment.SIZE)) } - val write3 = Buffer().also { it.writeString("c".repeat(Segment.SIZE)) } - - val source = Buffer() - source.writeString( - "${"a".repeat(Segment.SIZE)}${"b".repeat(Segment.SIZE)}${"c".repeat(Segment.SIZE)}" - ) - - val mockSink = MockSink() - val bufferedSource = (source as RawSource).buffered() - assertEquals(Segment.SIZE.toLong() * 3L, bufferedSource.transferTo(mockSink)) - mockSink.assertLog( - "write($write1, ${write1.size})", - "write($write2, ${write2.size})", - "write($write3, ${write3.size})" - ) - } - - @Test - fun closeMultipleTimes() { - var closeCalls = 0 - val rawSource: RawSource = object : RawSource { - override fun readAtMostTo(sink: Buffer, byteCount: Long): Long = -1 - override fun close() { - closeCalls++ - } - } - val source = rawSource.buffered() - - source.close() - assertFailsWith { source.readByte() } - source.close() // should do nothing - assertEquals(1, closeCalls) - } - - @Test - fun readAtMostFromEmptySource() { - val rawSource = object : RawSource { - override fun readAtMostTo(sink: Buffer, byteCount: Long): Long { - return -1 - } - - override fun close() {} - } - - assertEquals(-1, rawSource.buffered().readAtMostTo(Buffer(), 1024)) - } - - @Test - fun readAtMostFromFinite() { - val rawSource = object : RawSource { - var remainingBytes: Long = 10 - override fun readAtMostTo(sink: Buffer, byteCount: Long): Long { - if (remainingBytes == 0L) return -1 - val toWrite = minOf(remainingBytes, byteCount) - remainingBytes -= toWrite - sink.write(ByteArray(toWrite.toInt())) - return toWrite - } - - override fun close() {} - } - - val source = rawSource.buffered() - assertEquals(10, source.readAtMostTo(Buffer(), 1024)) - assertEquals(-1, source.readAtMostTo(Buffer(), 1024)) - } -} diff --git a/core/common/test/DelicateApiTest.kt b/core/common/test/DelicateApiTest.kt deleted file mode 100644 index 70abc821b..000000000 --- a/core/common/test/DelicateApiTest.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io - -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertTrue - -@OptIn(DelicateIoApi::class) -class DelicateApiTest { - @Test - fun callsInPlaceContract() { - val sink: Sink = Buffer() - - val called: Boolean - sink.writeToInternalBuffer { - called = true - } - assertTrue(called) - } - - @Test - @OptIn(InternalIoApi::class) - fun testWriteIntoBuffer() { - val sink = Buffer() - val rawSink = sink as RawSink - val bufferedSink = rawSink.buffered() - - bufferedSink.writeToInternalBuffer { - it.writeByte(42) - } - - assertEquals(0, sink.size) - assertEquals(1, bufferedSink.buffer.size) - - bufferedSink.writeToInternalBuffer { - // 1 byte missing for segment to be complete - it.write(ByteArray(Segment.SIZE - 2)) - // skip everything (skip everything but one last byte) - it.skip(Segment.SIZE - 2L) - // this will complete the segment - it.writeByte(0x12) - // this will start a new one - it.writeByte(0x34) - } - - assertArrayEquals(byteArrayOf(0x0, 0x12), sink.readByteArray()) - assertArrayEquals(byteArrayOf(0x34), bufferedSink.buffer.readByteArray()) - } -} \ No newline at end of file diff --git a/core/common/test/MockSink.kt b/core/common/test/MockSink.kt deleted file mode 100644 index 30725f753..000000000 --- a/core/common/test/MockSink.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2014 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package kotlinx.io - -import kotlin.test.assertEquals -import kotlin.test.assertTrue - -/** A scriptable sink. Like Mockito, but worse and requiring less configuration. */ -class MockSink : RawSink { - private val log = mutableListOf() - private val callThrows = mutableMapOf() - - fun assertLog(vararg messages: String) { - assertEquals(messages.toList(), log) - } - - fun assertLogContains(message: String) { - assertTrue(message in log) - } - - fun scheduleThrow(call: Int, e: IOException) { - callThrows[call] = e - } - - private fun throwIfScheduled() { - val exception = callThrows[log.size - 1] - if (exception != null) throw exception - } - - override fun write(source: Buffer, byteCount: Long) { - log.add("write($source, $byteCount)") - source.skip(byteCount) - throwIfScheduled() - } - - override fun flush() { - log.add("flush()") - throwIfScheduled() - } - - override fun close() { - log.add("close()") - throwIfScheduled() - } -} diff --git a/core/common/test/SinkFactory.kt b/core/common/test/SinkFactory.kt deleted file mode 100644 index 54a4d54c8..000000000 --- a/core/common/test/SinkFactory.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kotlinx.io - -internal interface SinkFactory { - - fun create(data: Buffer): Sink - - companion object { - val BUFFER: SinkFactory = object : SinkFactory { - override fun create(data: Buffer): Sink { - return data - } - } - - val REAL_BUFFERED_SINK: SinkFactory = object : SinkFactory { - override fun create(data: Buffer): Sink { - return (data as RawSink).buffered() - } - } - } -} diff --git a/core/common/test/SourceFactory.kt b/core/common/test/SourceFactory.kt deleted file mode 100644 index 554c7f74f..000000000 --- a/core/common/test/SourceFactory.kt +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kotlinx.io - -interface SourceFactory { - class Pipe( - var sink: Sink, - var source: Source - ) - - val isOneByteAtATime: Boolean - - fun pipe(): Pipe - - companion object { - val BUFFER: SourceFactory = object : SourceFactory { - - override val isOneByteAtATime: Boolean - get() = false - - override fun pipe(): Pipe { - val buffer = Buffer() - return Pipe( - buffer, - buffer - ) - } - } - - val REAL_BUFFERED_SOURCE: SourceFactory = object : - SourceFactory { - - override val isOneByteAtATime: Boolean - get() = false - - override fun pipe(): Pipe { - val buffer = Buffer() - return Pipe( - buffer, - (buffer as RawSource).buffered() - ) - } - } - - /** - * A factory deliberately written to create buffers whose internal segments are always 1 byte - * long. We like testing with these segments because are likely to trigger bugs! - */ - val ONE_BYTE_AT_A_TIME_BUFFERED_SOURCE: SourceFactory = object : - SourceFactory { - - override val isOneByteAtATime: Boolean - get() = true - - override fun pipe(): Pipe { - val buffer = Buffer() - return Pipe( - buffer, - object : RawSource by buffer { - override fun readAtMostTo(sink: Buffer, byteCount: Long): Long { - // Read one byte into a new buffer, then clone it so that the segment is shared. - // Shared segments cannot be compacted so we'll get a long chain of short segments. - val box = Buffer() - val result = buffer.readAtMostTo(box, minOf(byteCount, 1L)) - if (result > 0L) sink.write(box.copy(), result) - return result - } - }.buffered() - ) - } - } - - val ONE_BYTE_AT_A_TIME_BUFFER: SourceFactory = object : - SourceFactory { - - override val isOneByteAtATime: Boolean - get() = true - - override fun pipe(): Pipe { - val buffer = Buffer() - return Pipe( - object : RawSink by buffer { - override fun write(source: Buffer, byteCount: Long) { - // Write each byte into a new buffer, then clone it so that the segments are shared. - // Shared segments cannot be compacted so we'll get a long chain of short segments. - for (i in 0 until byteCount) { - val box = Buffer() - box.write(source, 1) - buffer.write(box.copy(), 1) - } - } - }.buffered(), - buffer - ) - } - } - - val PEEK_BUFFER: SourceFactory = object : SourceFactory { - - override val isOneByteAtATime: Boolean - get() = false - - override fun pipe(): Pipe { - val buffer = Buffer() - return Pipe( - buffer, - buffer.peek() - ) - } - } - - val PEEK_BUFFERED_SOURCE: SourceFactory = object : - SourceFactory { - - override val isOneByteAtATime: Boolean - get() = false - - override fun pipe(): Pipe { - val buffer = Buffer() - return Pipe( - buffer, - (buffer as RawSource).buffered().peek() - ) - } - } - - val PARAMETERIZED_TEST_VALUES = mutableListOf>( - arrayOf(BUFFER), - arrayOf(REAL_BUFFERED_SOURCE), - arrayOf(ONE_BYTE_AT_A_TIME_BUFFERED_SOURCE), - arrayOf(ONE_BYTE_AT_A_TIME_BUFFER), - arrayOf(PEEK_BUFFER), - arrayOf(PEEK_BUFFERED_SOURCE) - ) - } -} diff --git a/core/common/test/Utf8Test.kt b/core/common/test/Utf8Test.kt deleted file mode 100644 index 2f1853985..000000000 --- a/core/common/test/Utf8Test.kt +++ /dev/null @@ -1,509 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2018 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kotlinx.io - -import kotlinx.io.internal.REPLACEMENT_CHARACTER -import kotlinx.io.internal.REPLACEMENT_CODE_POINT -import kotlinx.io.internal.processUtf8CodePoints -import kotlin.test.* - -class Utf8Test { - @Test - fun oneByteCharacters() { - assertEncoded("00", 0x00) // Smallest 1-byte character. - assertEncoded("20", ' '.code) - assertEncoded("7e", '~'.code) - assertEncoded("7f", 0x7f) // Largest 1-byte character. - } - - @Test - fun twoByteCharacters() { - assertEncoded("c280", 0x0080) // Smallest 2-byte character. - assertEncoded("c3bf", 0x00ff) - assertEncoded("c480", 0x0100) - assertEncoded("dfbf", 0x07ff) // Largest 2-byte character. - } - - @Test - fun threeByteCharacters() { - assertEncoded("e0a080", 0x0800) // Smallest 3-byte character. - assertEncoded("e0bfbf", 0x0fff) - assertEncoded("e18080", 0x1000) - assertEncoded("e1bfbf", 0x1fff) - assertEncoded("ed8080", 0xd000) - assertEncoded("ed9fbf", 0xd7ff) // Largest character lower than the min surrogate. - assertEncoded("ee8080", 0xe000) // Smallest character greater than the max surrogate. - assertEncoded("eebfbf", 0xefff) - assertEncoded("ef8080", 0xf000) - assertEncoded("efbfbf", 0xffff) // Largest 3-byte character. - } - - @Test - fun fourByteCharacters() { - assertEncoded("f0908080", 0x010000) // Smallest surrogate pair. - assertEncoded("f48fbfbf", 0x10ffff) // Largest code point expressible by UTF-16. - } - - @Test - fun unknownBytes() { - assertCodePointDecoded("f8", REPLACEMENT_CODE_POINT) // Too large - assertCodePointDecoded("f0f8", REPLACEMENT_CODE_POINT, REPLACEMENT_CODE_POINT) - assertCodePointDecoded("ff", REPLACEMENT_CODE_POINT) // Largest - assertCodePointDecoded("f0ff", REPLACEMENT_CODE_POINT, REPLACEMENT_CODE_POINT) - - // Lone continuation - assertCodePointDecoded("80", REPLACEMENT_CODE_POINT) // Smallest - assertCodePointDecoded("bf", REPLACEMENT_CODE_POINT) // Largest - } - - @Test - fun overlongSequences() { - // Overlong representation of the NUL character - assertCodePointDecoded("c080", REPLACEMENT_CODE_POINT) - assertCodePointDecoded("e08080", REPLACEMENT_CODE_POINT) - assertCodePointDecoded("f0808080", REPLACEMENT_CODE_POINT) - - // Maximum overlong sequences - assertCodePointDecoded("c1bf", REPLACEMENT_CODE_POINT) - assertCodePointDecoded("e09fbf", REPLACEMENT_CODE_POINT) - assertCodePointDecoded("f08fbfbf", REPLACEMENT_CODE_POINT) - } - - @Test - fun danglingHighSurrogate() { - assertStringEncoded("3f", "\ud800") // "?" - assertCodePointDecoded("eda080", REPLACEMENT_CODE_POINT) - } - - @Test - fun lowSurrogateWithoutHighSurrogate() { - assertStringEncoded("3f", "\udc00") // "?" - assertCodePointDecoded("edb080", REPLACEMENT_CODE_POINT) - } - - @Test - fun highSurrogateFollowedByNonSurrogate() { - assertStringEncoded("3fee8080", "\ud800\ue000") // "?\ue000": Following character is too high. - assertCodePointDecoded("f090ee8080", REPLACEMENT_CODE_POINT, '\ue000'.code) - - assertStringEncoded("3f61", "\ud800\u0061") // "?a": Following character is too low. - assertCodePointDecoded("f09061", REPLACEMENT_CODE_POINT, 'a'.code) - } - - @Test - fun doubleLowSurrogate() { - assertStringEncoded("3f3f", "\udc00\udc00") // "??" - assertCodePointDecoded("edb080edb080", REPLACEMENT_CODE_POINT, REPLACEMENT_CODE_POINT) - } - - @Test - fun doubleHighSurrogate() { - assertStringEncoded("3f3f", "\ud800\ud800") // "??" - assertCodePointDecoded("eda080eda080", REPLACEMENT_CODE_POINT, REPLACEMENT_CODE_POINT) - } - - @Test - fun lowSurrogateHighSurrogate() { - assertStringEncoded("3f3f", "\udc00\ud800") // "??" - assertCodePointDecoded("edb080eda080", REPLACEMENT_CODE_POINT, REPLACEMENT_CODE_POINT) - } - - @Test - fun writeSurrogateCodePoint() { - assertStringEncoded("ed9fbf", "\ud7ff") // Below lowest surrogate is okay. - assertCodePointDecoded("ed9fbf", '\ud7ff'.code) - - assertStringEncoded("3f", "\ud800") // Lowest surrogate gets '?'. - assertCodePointDecoded("eda080", REPLACEMENT_CODE_POINT) - - assertStringEncoded("3f", "\udfff") // Highest surrogate gets '?'. - assertCodePointDecoded("edbfbf", REPLACEMENT_CODE_POINT) - - assertStringEncoded("ee8080", "\ue000") // Above highest surrogate is okay. - assertCodePointDecoded("ee8080", '\ue000'.code) - } - - @Test - fun bufferWriteCodePoints() { - bufferWriteCodePointsCheck(0) - } - - @Test - fun bufferWriteCodePointsCrossSegments() { - bufferWriteCodePointsCheck(Segment.SIZE - 1) - } - - private fun bufferWriteCodePointsCheck(prefixLength: Int) { - val buffer = Buffer() - buffer.assertCodePointEncoded("40", '@'.code, prefixLength) - buffer.assertCodePointEncoded("7f", '\u007f'.code, prefixLength) - buffer.assertCodePointEncoded("c280", '\u0080'.code, prefixLength) - buffer.assertCodePointEncoded("c2a9", '\u00a9'.code, prefixLength) - buffer.assertCodePointEncoded("c3bf", '\u00ff'.code, prefixLength) - buffer.assertCodePointEncoded("dfbf", '\u07ff'.code, prefixLength) - buffer.assertCodePointEncoded("e0a080", '\u0800'.code, prefixLength) - buffer.assertCodePointEncoded("e1839a", '\u10da'.code, prefixLength) - buffer.assertCodePointEncoded("efbfbf", '\uffff'.code, prefixLength) - buffer.assertCodePointEncoded("f0908080", 0x10000, prefixLength) - buffer.assertCodePointEncoded("f48087bf", 0x1001FF, prefixLength) - } - - @Test - fun bufferReadCodePoints() { - bufferReadCodePointsCheck(0) - } - - @Test - fun bufferReadCodePointsCrossSegments() { - bufferReadCodePointsCheck(Segment.SIZE - 1) - } - - private fun bufferReadCodePointsCheck(prefixLength: Int) { - val buffer = Buffer() - buffer.assertCodePointDecoded('@'.code, "40", prefixLength) - buffer.assertCodePointDecoded('\u007f'.code, "7f", prefixLength) - buffer.assertCodePointDecoded('\u0080'.code, "c280", prefixLength) - buffer.assertCodePointDecoded('\u00a9'.code, "c2a9", prefixLength) - buffer.assertCodePointDecoded('\u00ff'.code, "c3bf", prefixLength) - buffer.assertCodePointDecoded('\u07ff'.code, "dfbf", prefixLength) - buffer.assertCodePointDecoded('\u0800'.code, "e0a080", prefixLength) - buffer.assertCodePointDecoded('\u10da'.code, "e1839a", prefixLength) - buffer.assertCodePointDecoded('\uffff'.code, "efbfbf", prefixLength) - buffer.assertCodePointDecoded(0x10000, "f0908080", prefixLength) - buffer.assertCodePointDecoded(0x1001FF, "f48087bf", prefixLength) - } - - @Test - fun bufferWriteUtf8String() { - bufferWriteUtf8StringCheck(0) - } - - @Test - fun bufferWriteUtf8StringCrossSegments() { - bufferWriteUtf8StringCheck(Segment.SIZE - 1) - } - - @Test - fun bufferWriteUtf8CharSequence() { - bufferWriteUtf8StringCheck(0) { buffer, string -> buffer.writeString(StringBuilder(string)) } - } - - @Test - fun bufferWriteUtf8CharSequenceCrossSegments() { - bufferWriteUtf8StringCheck(Segment.SIZE - 1) { buffer, string -> - buffer.writeString(StringBuilder(string)) - } - } - - private inline fun bufferWriteUtf8StringCheck( - prefixLength: Int, - writeAction: (Buffer, String) -> Unit = { b, s -> b.writeString(s) } - ) { - val buffer = Buffer() - buffer.assertUtf8StringEncoded("68656c6c6f", "hello", prefixLength, writeAction) - buffer.assertUtf8StringEncoded("cf87ceb5cf81ceb5cf84ceb9cf83cebccf8ccf82", "χερετισμός", - prefixLength, writeAction) - buffer.assertUtf8StringEncoded( - "e18392e18390e1839be18390e183a0e183afe1839de18391e18390", - "გამარჯობა", - prefixLength, - writeAction - ) - buffer.assertUtf8StringEncoded( - "f093878bf0938bb4f09380a5", - "\uD80C\uDDCB\uD80C\uDEF4\uD80C\uDC25",/* 𓇋𓋴𓀥, to hail, AN EGYPTIAN HIEROGLYPHIC DICTIONARY, p. 79b */ - prefixLength, writeAction - ) - - // two consecutive high surrogates, replace with '?' - buffer.assertUtf8StringEncoded("3f3f", "\ud801\uD801", prefixLength, writeAction) - } - - @Test - fun bufferReadUtf8String() { - bufferReadUtf8StringCheck(0) - } - - @Test - fun bufferReadUtf8StringCrossSegments() { - bufferReadUtf8StringCheck(Segment.SIZE - 1) - } - - private fun bufferReadUtf8StringCheck(prefixLength: Int) { - val buffer = Buffer() - buffer.assertUtf8StringDecoded("hello","68656c6c6f", prefixLength) - buffer.assertUtf8StringDecoded("χερετισμός", "cf87ceb5cf81ceb5cf84ceb9cf83cebccf8ccf82", - prefixLength) - buffer.assertUtf8StringDecoded( - "გამარჯობა", - "e18392e18390e1839be18390e183a0e183afe1839de18391e18390", - prefixLength - ) - buffer.assertUtf8StringDecoded( - "\uD80C\uDDCB\uD80C\uDEF4\uD80C\uDC25",/* 𓇋𓋴𓀥, to hail, AN EGYPTIAN HIEROGLYPHIC DICTIONARY, p. 79b */ - "f093878bf0938bb4f09380a5", - prefixLength - ) - } - - @Test - fun size() { - assertEquals(0, "".utf8Size()) - assertEquals(3, "abc".utf8Size()) - assertEquals(16, "təˈranəˌsôr".utf8Size()) - } - - @Test - fun sizeWithBounds() { - assertEquals(0, "".utf8Size(0, 0)) - assertEquals(0, "abc".utf8Size(0, 0)) - assertEquals(1, "abc".utf8Size(1, 2)) - assertEquals(2, "abc".utf8Size(0, 2)) - assertEquals(3, "abc".utf8Size(0, 3)) - assertEquals(16, "təˈranəˌsôr".utf8Size(0, 11)) - assertEquals(5, "təˈranəˌsôr".utf8Size(3, 7)) - } - - @Test - fun sizeBoundsCheck() { - assertFailsWith { - "abc".utf8Size(-1, 2) - } - - assertFailsWith { - "abc".utf8Size(2, 1) - } - - assertFailsWith { - "abc".utf8Size(1, 4) - } - } - - @Test - fun readCodePointFromEmptyBufferThrowsEofException() { - val buffer = Buffer() - assertFailsWith { buffer.readCodePointValue() } - } - - @Test - fun readLeadingContinuationByteReturnsReplacementCharacter() { - val buffer = Buffer() - buffer.writeByte(0xbf.toByte()) - assertEquals(REPLACEMENT_CODE_POINT, buffer.readCodePointValue()) - assertTrue(buffer.exhausted()) - } - - @Test - fun readMissingContinuationBytesThrowsEofException() { - val buffer = Buffer() - buffer.writeByte(0xdf.toByte()) - assertFailsWith { buffer.readCodePointValue() } - assertFalse(buffer.exhausted()) // Prefix byte wasn't consumed. - } - - @Test - fun readTooLargeCodepointReturnsReplacementCharacter() { - // 5-byte and 6-byte code points are not supported. - val buffer = Buffer() - buffer.write("f888808080".decodeHex()) - assertEquals(REPLACEMENT_CODE_POINT, buffer.readCodePointValue()) - assertEquals(REPLACEMENT_CODE_POINT, buffer.readCodePointValue()) - assertEquals(REPLACEMENT_CODE_POINT, buffer.readCodePointValue()) - assertEquals(REPLACEMENT_CODE_POINT, buffer.readCodePointValue()) - assertEquals(REPLACEMENT_CODE_POINT, buffer.readCodePointValue()) - assertTrue(buffer.exhausted()) - - buffer.write(ByteArray(Segment.SIZE - 2)) - buffer.write("f888808080".decodeHex()) - buffer.skip(Segment.SIZE - 2L) - assertEquals(REPLACEMENT_CODE_POINT, buffer.readCodePointValue()) - assertEquals(REPLACEMENT_CODE_POINT, buffer.readCodePointValue()) - assertEquals(REPLACEMENT_CODE_POINT, buffer.readCodePointValue()) - assertEquals(REPLACEMENT_CODE_POINT, buffer.readCodePointValue()) - assertEquals(REPLACEMENT_CODE_POINT, buffer.readCodePointValue()) - assertTrue(buffer.exhausted()) - } - - @Test - fun readNonContinuationBytesReturnsReplacementCharacter() { - // Use a non-continuation byte where a continuation byte is expected. - val buffer = Buffer() - buffer.write("df20".decodeHex()) - assertEquals(REPLACEMENT_CODE_POINT, buffer.readCodePointValue()) - assertEquals(0x20, buffer.readCodePointValue()) // Non-continuation character not consumed. - assertTrue(buffer.exhausted()) - } - - @Test - fun readCodePointBeyondUnicodeMaximum() { - // A 4-byte encoding with data above the U+10ffff Unicode maximum. - val buffer = Buffer() - buffer.write("f4908080".decodeHex()) - assertEquals(REPLACEMENT_CODE_POINT, buffer.readCodePointValue()) - assertTrue(buffer.exhausted()) - } - - @Test - fun readSurrogateCodePoint() { - val buffer = Buffer() - buffer.write("eda080".decodeHex()) - assertEquals(REPLACEMENT_CODE_POINT, buffer.readCodePointValue()) - assertTrue(buffer.exhausted()) - buffer.write("edbfbf".decodeHex()) - assertEquals(REPLACEMENT_CODE_POINT, buffer.readCodePointValue()) - assertTrue(buffer.exhausted()) - } - - @Test - fun readOverlongCodePoint() { - // Use 2 bytes to encode data that only needs 1 byte. - val buffer = Buffer() - buffer.write("c080".decodeHex()) - assertEquals(REPLACEMENT_CODE_POINT, buffer.readCodePointValue()) - assertTrue(buffer.exhausted()) - } - - @Test - fun writeCodePointBeyondUnicodeMaximum() { - val buffer = Buffer() - val ex = assertFailsWith { - buffer.writeCodePointValue(0x110000) - } - assertEquals("Code point value is out of Unicode codespace 0..0x10ffff: 0x110000 (1114112)", - ex.message) - } - - @Test - fun writeCodePointBelowUnicodeMinimum() { - val buffer = Buffer() - val ex = assertFailsWith { - buffer.writeCodePointValue(-1) - } - assertEquals("Code point value is out of Unicode codespace 0..0x10ffff: 0xffffffff (-1)", - ex.message) - } - - @Test - fun readStringWithUnderflow() { - val buffer = Buffer() - // 3 byte-encoded, last byte missing - buffer.assertUtf8StringDecoded(REPLACEMENT_CHARACTER.toString(), "e183") - // 3 byte-encoded, last two bytes missing - buffer.assertUtf8StringDecoded(REPLACEMENT_CHARACTER.toString(), "e1") - // 2 byte-encoded, last byte missing - buffer.assertUtf8StringDecoded(REPLACEMENT_CHARACTER.toString(), "cf") - // 4 byte encoded, various underflows - buffer.assertUtf8StringDecoded(REPLACEMENT_CHARACTER.toString(), "f09383") - buffer.assertUtf8StringDecoded(REPLACEMENT_CHARACTER.toString(), "f093") - buffer.assertUtf8StringDecoded(REPLACEMENT_CHARACTER.toString(), "f0") - } - - @Test - fun readStringWithoutContinuationByte() { - val buffer = Buffer() - // 2 byte-encoded, last byte corrupted - buffer.assertUtf8StringDecoded("${REPLACEMENT_CHARACTER}a", "cf61") - // 3 byte-encoded, last byte corrupted - buffer.assertUtf8StringDecoded("${REPLACEMENT_CHARACTER}a", "e18361") - // 3 byte-encoded, last two bytes corrupted - buffer.assertUtf8StringDecoded("${REPLACEMENT_CHARACTER}aa", "e16161") - // 4 byte-encoded, various bytes corrupterd - buffer.assertUtf8StringDecoded("${REPLACEMENT_CHARACTER}a", "f0938361") - buffer.assertUtf8StringDecoded("${REPLACEMENT_CHARACTER}aa", "f0936161") - buffer.assertUtf8StringDecoded("${REPLACEMENT_CHARACTER}aaa", "f0616161") - } - - @OptIn(ExperimentalStdlibApi::class) - @Test - fun encodeUtf16SurrogatePair() { - val buffer = Buffer() - buffer.writeString("\uD852\uDF62") - println(buffer.readByteArray().toHexString()) - } - - private fun assertEncoded(hex: String, vararg codePoints: Int) { - assertCodePointDecoded(hex, *codePoints) - } - - private fun assertCodePointDecoded(hex: String, vararg codePoints: Int) { - val bytes = hex.decodeHex() - var i = 0 - bytes.processUtf8CodePoints(0, bytes.size) { codePoint -> - if (i < codePoints.size) assertEquals(codePoints[i], codePoint, "index=$i") - i++ - } - assertEquals(i, codePoints.size) // Checked them all - } - - private fun Buffer.assertCodePointEncoded(expectedHex: String, codePoint: Int, prefixLength: Int = 0) { - write(ByteArray(prefixLength)) - writeCodePointValue(codePoint) - skip(prefixLength.toLong()) - assertArrayEquals(expectedHex.decodeHex(), readByteArray()) - } - - private fun Buffer.assertCodePointDecoded(expectedCodePoint: Int, hex: String, prefixLength: Int = 0) { - write(ByteArray(prefixLength)) - write(hex.decodeHex()) - skip(prefixLength.toLong()) - assertEquals(expectedCodePoint, readCodePointValue()) - } - - private inline fun Buffer.assertUtf8StringEncoded(expectedHex: String, string: String, prefixLength: Int = 0, - writeAction: (Buffer, String) -> Unit) { - write(ByteArray(prefixLength)) - writeAction(this, string) - skip(prefixLength.toLong()) - assertArrayEquals(expectedHex.decodeHex(), readByteArray()) - } - - private fun Buffer.assertUtf8StringDecoded(expectedString: String, hex: String, prefixLength: Int = 0) { - write(ByteArray(prefixLength)) - write(hex.decodeHex()) - skip(prefixLength.toLong()) - assertEquals(expectedString, readString()) - } - - private fun assertStringEncoded(hex: String, string: String) { - val expectedUtf8 = hex.decodeHex() - - // Confirm our expectations are consistent with the platform. - val platformUtf8 = string.asUtf8ToByteArray() - assertArrayEquals(expectedUtf8, platformUtf8) - - // Confirm our implementations matches those expectations. - val actualUtf8 = string.commonAsUtf8ToByteArray() - assertArrayEquals(expectedUtf8, actualUtf8) - - // Confirm we are consistent when writing one code point at a time. - val bufferUtf8 = Buffer() - for (charIdx in string.indices) { - val c = string[charIdx] - bufferUtf8.writeCodePointValue(c.code) - } - assertArrayEquals(expectedUtf8, bufferUtf8.readByteArray()) - - // Confirm we are consistent when measuring lengths. - assertEquals(expectedUtf8.size.toLong(), string.utf8Size()) - assertEquals(expectedUtf8.size.toLong(), string.utf8Size(0, string.length)) - } -} diff --git a/core/common/test/UtilsTest.kt b/core/common/test/UtilsTest.kt deleted file mode 100644 index 392dc7baf..000000000 --- a/core/common/test/UtilsTest.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io - -import kotlin.test.Test -import kotlin.test.assertEquals - -class UtilsTest { - @Test - fun hexNumberLength() { - val num2length: Map = mapOf( - 0x1L to 1, - 0x10L to 2, - 0x100L to 3, - 0x1000L to 4, - 0x10000L to 5, - 0x100000L to 6, - 0x1000000L to 7, - 0x10000000L to 8, - 0x100000000L to 9, - 0x1000000000L to 10, - 0x10000000000L to 11, - 0x100000000000L to 12, - 0x1000000000000L to 13, - 0x10000000000000L to 14, - 0x100000000000000L to 15, - 0x1000000000000000L to 16, - -1L to 16, - 0x3fL to 2, - 0x7fL to 2, - 0xffL to 2, - 0L to 1 - ) - - num2length.forEach { (num, length) -> - assertEquals(length, hexNumberLength(num), "Wrong length for 0x${num.toString(16)}") - } - } -} diff --git a/core/common/test/files/SmokeFileTest.kt b/core/common/test/files/SmokeFileTest.kt deleted file mode 100644 index a4242b398..000000000 --- a/core/common/test/files/SmokeFileTest.kt +++ /dev/null @@ -1,472 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -package kotlinx.io.files - -import kotlinx.io.* -import kotlin.test.* - -class SmokeFileTest { - private val files: MutableList = arrayListOf() - - @AfterTest - fun cleanup() { - var lastException: Throwable? = null - files.forEach { - try { - SystemFileSystem.delete(it, false) - } catch (t: Throwable) { - lastException = t - } - } - if (lastException != null) { - throw lastException!! - } - } - - private fun createTempPath(): Path { - val f = Path(tempFileName()) - files.add(f) - return f - } - - private fun removeOnExit(path: Path) { - files.add(path) - } - - @Test - fun readWriteFile() { - val path = createTempPath() - - SystemFileSystem.sink(path).buffered().use { - it.writeString("example") - } - - SystemFileSystem.source(path).buffered().use { - assertEquals("example", it.readLine()) - } - } - - @Test - fun writeFlush() { - val path = createTempPath() - SystemFileSystem.sink(path).buffered().use { - it.writeString("hello") - it.flush() - it.writeString(" world") - it.flush() - } - - SystemFileSystem.source(path).buffered().use { - assertEquals("hello world", it.readLine()) - } - } - - @Test - fun readNotExistingFile() { - assertFailsWith { - SystemFileSystem.source(createTempPath()) - } - } - - @Test - fun readWriteMultipleSegments() { - val path = createTempPath() - - val data = ByteArray((Segment.SIZE * 2.5).toInt()) { it.toByte() } - - SystemFileSystem.sink(path).buffered().use { - it.write(data) - } - - SystemFileSystem.source(path).buffered().use { - assertArrayEquals(data, it.readByteArray()) - } - } - - @Test - fun basicFileSystemOps() { - val path = createTempPath() - assertFalse(SystemFileSystem.exists(path)) - SystemFileSystem.sink(path).buffered().use { - it.writeString("hi") - } - assertTrue(SystemFileSystem.exists(path)) - SystemFileSystem.delete(path) - assertFalse(SystemFileSystem.exists(path)) - } - - @Test - fun checkTmpDir() { - assertTrue(SystemFileSystem.exists(SystemTemporaryDirectory)) - } - - @Test - fun atomicMove() { - val src = createTempPath() - val dst = createTempPath() - SystemFileSystem.sink(src).buffered().use { - it.writeString("hello") - } - SystemFileSystem.atomicMove(src, dst) - assertFalse(SystemFileSystem.exists(src)) - assertTrue(SystemFileSystem.exists(dst)) - - SystemFileSystem.source(dst).buffered().use { - assertEquals("hello", it.readString()) - } - } - - @Test - fun atomicMoveDir() { - val src = createTempPath() - val dst = createTempPath() - SystemFileSystem.createDirectories(src) - - SystemFileSystem.atomicMove(src, dst) - assertFalse(SystemFileSystem.exists(src)) - assertTrue(SystemFileSystem.exists(dst)) - } - - @Test - fun deleteFile() { - val p = createTempPath() - assertFailsWith { - SystemFileSystem.delete(p, mustExist = true) - } - // Should not fail - SystemFileSystem.delete(p, false) - } - - @Test - fun moveNonExistingFile() { - assertFailsWith { - SystemFileSystem.atomicMove(createTempPath(), createTempPath()) - } - } - - @Test - fun createDirectories() { - val p = Path(createTempPath(), "a", "b", "c") - SystemFileSystem.createDirectories(p) - assertTrue(SystemFileSystem.exists(p)) - - assertFailsWith { - SystemFileSystem.createDirectories(p, true) - } - SystemFileSystem.createDirectories(p, false) - - val p1 = Path(p, "d") - SystemFileSystem.createDirectories(p1) - assertTrue(SystemFileSystem.exists(p1)) - - var pr = p1 - for (i in 0..3) { - SystemFileSystem.delete(pr) - pr = pr.parent!! - } - - val p2 = createTempPath() - SystemFileSystem.sink(p2).buffered().use { - it.writeString("hello") - } - assertFailsWith { SystemFileSystem.createDirectories(p2, false) } - } - - @Test - fun trailingSeparatorsTrimming() { - assertEquals(Path(".").toString(), Path(".///").toString()) - assertEquals(Path("/").toString(), Path("/").toString()) - assertEquals(Path("/..").toString(), Path("/../").toString()) - assertEquals(Path("/a/b/c").toString(), Path("/a/b/c").toString()) - } - - @Test - fun pathParent() { - val p = Path(SystemPathSeparator.toString(), "a", "b", "c") - assertEquals(constructAbsolutePath("a", "b"), p.parent?.toString()) - assertEquals(constructAbsolutePath("a"), p.parent?.parent?.toString()) - assertEquals(constructAbsolutePath(), p.parent?.parent?.parent?.toString()) - assertNull(p.parent?.parent?.parent?.parent) - - val p1 = Path("home", "..", "lib") - assertEquals(constructRelativePath("home", ".."), p1.parent?.toString()) - assertEquals("home", p1.parent?.parent?.toString()) - assertNull(p1.parent?.parent?.parent) - - assertNull(Path("").parent) - assertNull(Path(".").parent) - assertNull(Path("..").parent) - assertNull(Path(SystemPathSeparator.toString()).parent) - - assertEquals("..", Path("..${SystemPathSeparator}..").parent?.toString()) - - assertEquals(" ", Path(SystemFileSystem.resolve(Path(".")), " ", "ws").parent?.name) - assertEquals(" ", Path(" $SystemPathSeparator.").parent?.name) - assertNull(Path(" ").parent) - assertNull(Path(" /").parent) - - assertNull(Path("path////").parent) - assertEquals(Path("."), Path("./child").parent) - assertNull(Path("./").parent) - } - - @Test - fun pathConcat() { - assertEquals( - constructAbsolutePath("a", "b", "c"), - Path(Path(Path(Path(SystemPathSeparator.toString()), "a"), "b"), "c").toString() - ) - - assertEquals( - constructAbsolutePath("a", "b", "..", "c"), - Path("${SystemPathSeparator}a", "b", "..${SystemPathSeparator}c").toString() - ) - - assertEquals( - constructRelativePath("a", "b", "c"), - Path("", "a", "b", "c").toString() - ) - } - - @Test - fun fileName() { - assertEquals("", Path("").name) - assertEquals("hello", Path("hello").name) - assertEquals("", Path(SystemPathSeparator.toString()).name) - assertEquals(".", Path(".").name) - assertEquals("..", Path("..").name) - assertEquals("hello.txt", Path("base", "hello.txt").name) - assertEquals("dir", Path("dir${SystemPathSeparator}").name) - assertEquals(" ", Path(" ").name) - assertEquals(" ", Path(" / ").name) - } - - @Test - fun isAbsolute() { - // to make it work on both Windows and Unix, just repeat the separator twice - val rootPath = SystemPathSeparator.repeat(2) - assertTrue(Path(rootPath).isAbsolute) - assertFalse(Path("").isAbsolute) - assertFalse(Path("..").isAbsolute) - assertFalse(Path(".").isAbsolute) - assertTrue(Path(rootPath, "a", "b", "c").isAbsolute) - assertFalse(Path("hello", "filesystem").isAbsolute) - assertTrue(Path(rootPath, "lib", "..", "usr", "lib").isAbsolute) - } - - @Test - fun fileMetadata() { - val path = createTempPath() - assertNull(SystemFileSystem.metadataOrNull(path)) - - SystemFileSystem.createDirectories(path) - val dirMetadata = SystemFileSystem.metadataOrNull(path) - assertNotNull(dirMetadata) - assertTrue(dirMetadata.isDirectory) - assertFalse(dirMetadata.isRegularFile) - - val filePath = Path(path, "test.txt") - assertNull(SystemFileSystem.metadataOrNull(filePath)) - SystemFileSystem.sink(filePath).buffered().use { - it.writeString("blablabla") - } - - try { - val fileMetadata = SystemFileSystem.metadataOrNull(filePath) - assertNotNull(fileMetadata) - assertFalse(fileMetadata.isDirectory) - assertTrue(fileMetadata.isRegularFile) - } finally { - SystemFileSystem.delete(filePath, false) - } - } - - @Test - fun fileSize() { - val path = createTempPath() - val expectedSize = 123 - SystemFileSystem.sink(path).buffered().use { - it.write(ByteArray(expectedSize)) - } - val metadata = SystemFileSystem.metadataOrNull(path) - assertNotNull(metadata) - assertEquals(expectedSize.toLong(), metadata.size) - - assertEquals(-1L, SystemFileSystem.metadataOrNull(path.parent!!)!!.size) - } - - @Test - fun pathEquality() { - val p0 = Path("/", "a", "b", "c") - assertEquals(p0, p0) - - assertEquals(p0, Path("/", "a", "b", "c")) - - // Paths compared by the string representation, so even if two Paths represent - // the same entity in the file system, they may not be equal - // (but it depends on how a platform treats paths). - assertNotEquals(p0, Path(p0, "d", "..")) - } - - @Test - fun deleteNonEmptyDirectory() { - val basePath = createTempPath() - val childPath = Path(basePath, "child") - - SystemFileSystem.createDirectories(childPath, true) - assertFailsWith { SystemFileSystem.delete(basePath) } - - SystemFileSystem.delete(childPath) - SystemFileSystem.delete(basePath) - } - - @Test - fun readDirectory() { - val dir = createTempPath() - SystemFileSystem.createDirectories(dir) - - assertFailsWith { SystemFileSystem.source(dir).buffered().readByte() } - } - - @OptIn(ExperimentalStdlibApi::class) - @Test - fun writeDirectory() { - val dir = createTempPath() - SystemFileSystem.createDirectories(dir) - - assertFailsWith { - SystemFileSystem.sink(dir).buffered().use { - it.writeByte(0) - } - } - } - - @Test - fun appendToFile() { - val path = createTempPath() - - SystemFileSystem.sink(path).buffered().use { - it.writeString("first") - } - SystemFileSystem.sink(path).buffered().use { - it.writeString("second") - } - assertEquals("second", - SystemFileSystem.source(path).buffered().use { it.readString() }) - - SystemFileSystem.sink(path, append = true).buffered().use { - it.writeString(" third") - } - assertEquals("second third", - SystemFileSystem.source(path).buffered().use { it.readString() }) - } - - @Test - fun resolve() { - assertFailsWith("Non-existing path resolution should fail") { - SystemFileSystem.resolve(createTempPath()) - } - - val cwd = SystemFileSystem.resolve(Path(".")) - - SystemFileSystem.createDirectories(Path("a")) - removeOnExit(Path("a")) - - val childRel = Path("a", "..") - assertEquals(cwd, SystemFileSystem.resolve(childRel)) - - assertEquals( - cwd, SystemFileSystem.resolve(cwd), - "Absolute path resolution should not alter the path" - ) - - // root - // |-> a -> b - // |-> c -> d - val root = createTempPath() - SystemFileSystem.createDirectories(Path(root, "a", "b")) - val tgt = Path(root, "c", "d") - SystemFileSystem.createDirectories(tgt) - - val src = Path(root, "a", "..", "a", ".", "b", "..", "..", "c", ".", "d") - try { - // root/a/../a/./b/../../c/./d -> root/c/d - assertEquals(SystemFileSystem.resolve(tgt), SystemFileSystem.resolve(src)) - } finally { - // TODO: remove as soon as recursive file removal is implemented - SystemFileSystem.delete(Path(root, "a", "b")) - SystemFileSystem.delete(Path(root, "a")) - SystemFileSystem.delete(Path(root, "c", "d")) - SystemFileSystem.delete(Path(root, "c")) - } - } - - @Test - fun createAnEmptyFileUsingSink() { - val path = createTempPath() - assertFalse(SystemFileSystem.exists(path)) - - SystemFileSystem.sink(path).close() - assertTrue(SystemFileSystem.exists(path)) - assertTrue(SystemFileSystem.metadataOrNull(path)!!.isRegularFile) - } - - @Test - fun closeFileSinkTwice() { - val path = createTempPath() - val sink = SystemFileSystem.sink(path) - sink.close() - sink.close() // there should be no error - } - - @Test - fun closeFileSourceTwice() { - val path = createTempPath() - SystemFileSystem.sink(path).close() - assertTrue(SystemFileSystem.exists(path)) - val source = SystemFileSystem.source(path) - source.close() - source.close() // there should be no error - } - - @Test - fun listDirectory() { - assertFailsWith { SystemFileSystem.list(createTempPath()) } - - val tmpFile = createTempPath().also { - SystemFileSystem.sink(it).close() - } - assertFailsWith { SystemFileSystem.list(tmpFile) } - - val dir = createTempPath().also { - SystemFileSystem.createDirectories(it) - } - assertEquals(emptyList(), SystemFileSystem.list(dir)) - - val subdir = Path(dir, "subdir").also { - SystemFileSystem.createDirectories(it) - SystemFileSystem.sink(Path(it, "file")).close() - } - assertEquals(listOf(subdir), SystemFileSystem.list(dir)) - - val file = Path(dir, "file").also { - SystemFileSystem.sink(it).close() - } - assertEquals(setOf(file, subdir), SystemFileSystem.list(dir).toSet()) - - SystemFileSystem.delete(file) - SystemFileSystem.delete(Path(subdir, "file")) - SystemFileSystem.delete(subdir) - } - - private fun constructAbsolutePath(vararg parts: String): String { - return SystemPathSeparator.toString() + parts.joinToString(SystemPathSeparator.toString()) - } - - private fun constructRelativePath(vararg parts: String): String { - return parts.joinToString(SystemPathSeparator.toString()) - } -} diff --git a/core/common/test/files/SmokeFileTestWindows.kt b/core/common/test/files/SmokeFileTestWindows.kt deleted file mode 100644 index 23994807e..000000000 --- a/core/common/test/files/SmokeFileTestWindows.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2010-2024 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.files - -import kotlin.test.* - -class SmokeFileTestWindows { - @Test - fun isAbsolute() { - if (!isWindows) return - assertFalse(Path("C:").isAbsolute) - assertTrue(Path("C:\\").isAbsolute) - assertTrue(Path("C:/").isAbsolute) - assertTrue(Path("C:/../").isAbsolute) - assertFalse(Path("C:file").isAbsolute) - assertFalse(Path("bla\\bla\\bla").isAbsolute) - assertTrue(Path("\\\\server\\share").isAbsolute) - } - - @Test - fun getParent() { - if (!isWindows) return - assertNull(Path("C:").parent) - assertNull(Path("C:\\").parent) - assertNull(Path("a\\b").parent?.parent) - assertEquals(Path("C:\\"), Path("C:\\Program Files").parent) - assertEquals(Path("C:\\Program Files"), Path("C:\\Program Files/Java").parent) - } - - @Test - fun trailingSeparatorsTrimming() { - if (!isWindows) return - assertEquals(".", Path(".\\").toString()) - assertEquals("C:\\", Path("C:\\").toString()) - assertEquals("C:\\", Path("C:\\\\").toString()) - assertEquals("\\\\", Path("\\\\").toString()) - assertEquals(".\\a", Path(".\\a\\//\\//\\\\////").toString()) - - // this path could be transformed to use canonical separator on JVM - assertEquals(Path("//").toString(), Path("//").toString()) - } -} diff --git a/core/common/test/files/UtilsTest.kt b/core/common/test/files/UtilsTest.kt deleted file mode 100644 index 496ef4239..000000000 --- a/core/common/test/files/UtilsTest.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2010-2024 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.files - -import kotlin.test.Test -import kotlin.test.assertEquals - -class UtilsTest { - private fun removeTrailingSeparatorsU(path: String): String = removeTrailingSeparators(path, false) - private fun removeTrailingSeparatorsW(path: String): String = removeTrailingSeparators(path, true) - - - @Test - fun testPathTrimmingUnix() { - assertEquals("", removeTrailingSeparatorsU("")) - assertEquals("/", removeTrailingSeparatorsU("/")) - assertEquals("/", removeTrailingSeparatorsU("//")) - assertEquals("// ", removeTrailingSeparatorsU("// ")) - assertEquals("/", removeTrailingSeparatorsU("///")) - assertEquals("@@@", removeTrailingSeparatorsU("@@@")) - assertEquals("/a", removeTrailingSeparatorsU("/a/")) - assertEquals("\\", removeTrailingSeparatorsU("\\")) - assertEquals("\\\\", removeTrailingSeparatorsU("\\\\")) - assertEquals("\\a\\", removeTrailingSeparatorsU("\\a\\")) - assertEquals("/\\/ ", removeTrailingSeparatorsU("/\\/ ")) - - assertEquals("a//\\////\\\\//\\/\\", removeTrailingSeparatorsU("a//\\////\\\\//\\/\\")) - assertEquals("C:\\", removeTrailingSeparatorsU("C:\\")) - assertEquals("C:\\/\\", removeTrailingSeparatorsU("C:\\/\\")) - } - - @Test - fun testPathTrimmingWindows() { - assertEquals("", removeTrailingSeparatorsW("")) - assertEquals("/", removeTrailingSeparatorsW("/")) - assertEquals("//", removeTrailingSeparatorsW("//")) - assertEquals("// ", removeTrailingSeparatorsW("// ")) - assertEquals("//", removeTrailingSeparatorsW("///")) - assertEquals("@@@", removeTrailingSeparatorsW("@@@")) - assertEquals("/a", removeTrailingSeparatorsW("/a/")) - assertEquals("\\", removeTrailingSeparatorsW("\\")) - assertEquals("\\\\", removeTrailingSeparatorsW("\\\\")) - assertEquals("\\a", removeTrailingSeparatorsW("\\a\\")) - assertEquals("\\a", removeTrailingSeparatorsW("\\a\\\\")) - assertEquals("/\\/ ", removeTrailingSeparatorsW("/\\/ ")) - - assertEquals("a", removeTrailingSeparatorsW("a//\\////\\\\//\\/\\")) - assertEquals("C:a", removeTrailingSeparatorsW("C:a//\\////\\\\//\\/\\")) - assertEquals("C:\\", removeTrailingSeparatorsW("C:\\")) - assertEquals("C:\\", removeTrailingSeparatorsW("C:\\/\\")) - } -} diff --git a/core/common/test/samples/byteStringSample.kt b/core/common/test/samples/byteStringSample.kt deleted file mode 100644 index 6b3c74332..000000000 --- a/core/common/test/samples/byteStringSample.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.samples - -import kotlinx.io.* -import kotlinx.io.bytestring.ByteString -import kotlinx.io.bytestring.encodeToByteString -import kotlin.test.* - -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.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 deleted file mode 100644 index fd2f3f6cc..000000000 --- a/core/common/test/samples/moduleDescriptionSample.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.samples - -import kotlinx.io.* -import kotlin.test.* - -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 - writeString("timestamp") // field name - writeByte(0) - writeLongLe(timestamp) // field value - writeByte(0x2) // string field - writeString("text") // field name - writeByte(0) - writeIntLe(text.utf8Size().toInt() + 1) // field value: length followed by the string - writeString(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.readString(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.readString(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)) - } -} diff --git a/core/common/test/samples/rawSinkSample.kt b/core/common/test/samples/rawSinkSample.kt deleted file mode 100644 index ae25a396d..000000000 --- a/core/common/test/samples/rawSinkSample.kt +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.samples - -import kotlinx.io.* -import kotlin.test.* - -class Crc32Sample { - @OptIn(ExperimentalUnsignedTypes::class) - @Test - fun crc32() { - /** - * 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. - */ - 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.toUInt().xor(crc32).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() - - 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 - } - } - - val crc32Sink = CRC32Sink(discardingSink()) - - crc32Sink.buffered().use { - it.writeString("hello crc32") - } - - assertEquals(0x9896d398U, crc32Sink.crc32()) - } -} diff --git a/core/common/test/samples/rawSourceSample.kt b/core/common/test/samples/rawSourceSample.kt deleted file mode 100644 index 6d25dba77..000000000 --- a/core/common/test/samples/rawSourceSample.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.samples - -import kotlinx.io.* -import kotlin.test.* - -class RC4SourceSample { - @Test - fun rc4() { - /** - * 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 inner 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 - } - } - } - - 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.readString()) - } -} diff --git a/core/common/test/samples/samples.kt b/core/common/test/samples/samples.kt deleted file mode 100644 index 870bfa764..000000000 --- a/core/common/test/samples/samples.kt +++ /dev/null @@ -1,816 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.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)) - assertFalse(buffer.exhausted()) - buffer.clear() - assertTrue(buffer.exhausted()) - } - - @Test - fun bufferGetByte() { - val buffer = Buffer() - buffer.writeString("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.writeString("some string") - - val copy = Buffer() - copy.writeString("sub") - buffer.copyTo(copy, startIndex = 5) - - assertEquals("some string", buffer.readString()) - assertEquals("substring", copy.readString()) - } - - @Test - fun transferFrom() { - 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.readString()) - } - - @Test - fun transferTo() { - 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.readString()) - } - - @Test - fun peekSample() { - val source: Source = Buffer().also { it.writeString("hello world") } - - val peek = source.peek().buffered() - assertEquals("hello", peek.readString(5)) - peek.skip(1) - assertEquals("world", peek.readString(5)) - assertTrue(peek.exhausted()) - - assertEquals("hello world", source.readString()) - } - - @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() - - // Basic Latin (a.k.a. ASCII) characters are encoded with a single byte - buffer.writeCodePointValue('Y'.code) - assertContentEquals(byteArrayOf(0x59), buffer.readByteArray()) - - // wider characters are encoded into multiple UTF-8 code units - buffer.writeCodePointValue('Δ'.code) - assertContentEquals(byteArrayOf(0xce.toByte(), 0x94.toByte()), buffer.readByteArray()) - - // note the difference: writeInt won't encode the code point, like writeCodePointValue did - buffer.writeInt('Δ'.code) - assertContentEquals(byteArrayOf(0, 0, 0x3, 0x94.toByte()), buffer.readByteArray()) - } - - @Test - fun writeSurrogatePair() { - val buffer = Buffer() - - // U+1F31E (a.k.a. "sun with face") is too wide to fit in a single UTF-16 character, - // so it's represented using a surrogate pair. - val chars = "🌞".toCharArray() - assertEquals(2, chars.size) - - // such a pair has to be manually converted to a single code point - assertTrue(chars[0].isHighSurrogate()) - assertTrue(chars[1].isLowSurrogate()) - - val highSurrogate = chars[0].code - val lowSurrogate = chars[1].code - - // see https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF for details - val codePoint = 0x10000 + (highSurrogate - 0xD800).shl(10).or(lowSurrogate - 0xDC00) - assertEquals(0x1F31E, codePoint) - - // now we can write the code point - buffer.writeCodePointValue(codePoint) - // and read the correct string back - assertEquals("🌞", buffer.readString()) - - // we won't achieve that by writing surrogates as it is - buffer.apply { - writeCodePointValue(highSurrogate) - writeCodePointValue(lowSurrogate) - } - assertNotEquals("🌞", buffer.readString()) - } - - @Test - fun readUtf8CodePointSample() { - val buffer = Buffer() - - buffer.writeUShort(0xce94U) - assertEquals(0x394, buffer.readCodePointValue()) // decodes a single UTF-8 encoded code point - } - - @Test - fun surrogatePairs() { - val buffer = Buffer() - - // that's a U+1F31A, a.k.a. "new moon with face" - buffer.writeString("🌚") - // it should be encoded with 4 code units - assertEquals(4, buffer.size) - - // let's read it back as a single code point - val moonCodePoint = buffer.readCodePointValue() - // all code units were consumed - assertEquals(0, buffer.size) - - // the moon is too wide to fit in a single UTF-16 character! - assertNotEquals(moonCodePoint, moonCodePoint.toChar().code) - // "too wide" means in the [U+010000, U+10FFFF] range - assertTrue(moonCodePoint in 0x10000..0x10FFFF) - - // See https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF for details - val highSurrogate = (0xD800 + (moonCodePoint - 0x10000).ushr(10)).toChar() - val lowSurrogate = (0xDC00 + (moonCodePoint - 0x10000).and(0x3FF)).toChar() - - assertContentEquals(charArrayOf(highSurrogate, lowSurrogate), "🌚".toCharArray()) - } - - @Test - fun readLinesSample() { - val buffer = Buffer() - buffer.writeString("No new line here.") - - assertFailsWith { buffer.readLineStrict() } - assertEquals("No new line here.", buffer.readLine()) - - buffer.writeString("Line1\n\nLine2") - assertEquals("Line1", buffer.readLineStrict()) - assertEquals("\nLine2", buffer.readString()) - } - - @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.writeString("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.writeString(", ") - buffer.writeDecimalLong(-24) - - assertEquals("1024, -24", buffer.readString()) - } - - @Test - fun writeHexLong() { - val buffer = Buffer() - - buffer.writeHexadecimalUnsignedLong(10) - assertEquals("a", buffer.readString()) - - buffer.writeHexadecimalUnsignedLong(-10) - assertEquals("fffffffffffffff6", buffer.readString()) - } - - @Test - fun readHexLong() { - val buffer = Buffer() - - buffer.writeString("0000a") - assertEquals(10L, buffer.readHexadecimalUnsignedLong()) - - buffer.writeString("fffFffFffFfffff6") - assertEquals(-10L, buffer.readHexadecimalUnsignedLong()) - - buffer.writeString("dear friend!") - assertEquals(0xdea, buffer.readHexadecimalUnsignedLong()) - assertEquals("r friend!", buffer.readString()) - } - - @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.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)) - } - - @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.writeString("hello", startIndex = 1, endIndex = 4) - assertContentEquals( - byteArrayOf( - 'e'.code.toByte(), - 'l'.code.toByte(), - 'l'.code.toByte() - ), buffer.readByteArray() - ) - - buffer.writeString("Δ") - assertContentEquals(byteArrayOf(0xce.toByte(), 0x94.toByte()), buffer.readByteArray()) - } - - @Test - fun writeUtf8SeqSample() { - val buffer = Buffer() - - buffer.writeString(StringBuilder("hello"), startIndex = 1, endIndex = 4) - assertContentEquals( - byteArrayOf( - 'e'.code.toByte(), - 'l'.code.toByte(), - 'l'.code.toByte() - ), buffer.readByteArray() - ) - - buffer.writeString(StringBuilder("Δ")) - assertContentEquals(byteArrayOf(0xce.toByte(), 0x94.toByte()), buffer.readByteArray()) - } - - @Test - fun readUtf8() { - val buffer = Buffer() - - buffer.write("hello world".encodeToByteArray()) - assertEquals("hello", buffer.readString(5)) - assertEquals(" world", buffer.readString()) - - buffer.write(byteArrayOf(0xce.toByte(), 0x94.toByte())) - assertEquals("Δ", buffer.readString()) - } - - @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 readFloat() { - val buffer = Buffer() - buffer.write(byteArrayOf(70, 64, -26, -74)) - assertEquals(12345.678F.toBits(), buffer.readFloat().toBits()) - } - - @Test - fun readDouble() { - val buffer = Buffer() - buffer.write(byteArrayOf(64, -2, 36, 12, -97, -56, -13, 35)) - - assertEquals(123456.78901, buffer.readDouble()) - } - - @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 writeFloat() { - val buffer = Buffer() - buffer.writeFloat(12345.678F) - - assertContentEquals(byteArrayOf(70, 64, -26, -74), buffer.readByteArray()) - } - - @Test - fun writeDouble() { - val buffer = Buffer() - buffer.writeDouble(123456.78901) - - assertContentEquals(byteArrayOf(64, -2, 36, 12, -97, -56, -13, 35), 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 readFloatLe() { - val buffer = Buffer() - buffer.write(byteArrayOf(-74, -26, 64, 70)) - assertEquals(12345.678F.toBits(), buffer.readFloatLe().toBits()) - } - - @Test - fun readDoubleLe() { - val buffer = Buffer() - buffer.write(byteArrayOf(35, -13, -56, -97, 12, 36, -2, 64)) - assertEquals(123456.78901, buffer.readDoubleLe()) - } - - @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()) - } - - @Test - fun writeFloatLe() { - val buffer = Buffer() - buffer.writeFloatLe(12345.678F) - - assertContentEquals(byteArrayOf(-74, -26, 64, 70), buffer.readByteArray()) - } - - @Test - fun writeDoubleLe() { - val buffer = Buffer() - buffer.writeDoubleLe(123456.78901) - - assertContentEquals(byteArrayOf(35, -13, -56, -97, 12, 36, -2, 64), buffer.readByteArray()) - } -} diff --git a/core/common/test/samples/unsafe/unsafeSamples.kt b/core/common/test/samples/unsafe/unsafeSamples.kt deleted file mode 100644 index 18ec78918..000000000 --- a/core/common/test/samples/unsafe/unsafeSamples.kt +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright 2010-2024 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.unsafe - -import kotlinx.io.* -import kotlinx.io.bytestring.ByteString -import kotlinx.io.unsafe.UnsafeBufferOperations -import kotlinx.io.unsafe.withData -import kotlin.io.encoding.Base64 -import kotlin.io.encoding.ExperimentalEncodingApi -import kotlin.math.min -import kotlin.random.Random -import kotlin.test.* - -class UnsafeBufferOperationsSamples { - @OptIn(UnsafeIoApi::class) - @Test - fun writeByteArrayToTail() { - fun Buffer.writeRandomBytes(byteCount: Int) { - require(byteCount > 0) { "byteCount should be positive. Was: $byteCount" } - var remaining = byteCount - while (remaining > 0) { - UnsafeBufferOperations.writeToTail(this, 1) { data, startIndex, endIndex -> - // data's slice from startIndex to endIndex is available for writing, - // but that slice could be much larger than what remained to write. - val correctedEndIndex = min(endIndex, startIndex + remaining) - // write random bytes - Random.Default.nextBytes(data, startIndex, correctedEndIndex) - // number of bytes written - val written = correctedEndIndex - startIndex - remaining -= written - // that many bytes will be committed to the buffer - written - } - } - } - - val buffer = Buffer() - - buffer.writeRandomBytes(42) - assertEquals(42L, buffer.size) - - buffer.writeRandomBytes(10000) - assertEquals(10042L, buffer.size) - } - - @OptIn(ExperimentalEncodingApi::class, UnsafeIoApi::class) - @Test - fun moveToTail() { - fun Buffer.writeBase64(data: ByteArray, encoder: Base64 = Base64.Default) { - UnsafeBufferOperations.moveToTail(this, encoder.encodeToByteArray(data)) - } - - val buffer = Buffer() - buffer.writeBase64(byteArrayOf(-1, 0, -2, 0)) - - assertEquals("/wD+AA==", buffer.readString()) - } - - @OptIn(UnsafeIoApi::class) - @Test - fun readByteArrayFromHead() { - // see https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128 - fun Buffer.readULeb128(): ULong { - var shift = 0 - var result = 0L - var complete = false - - while (!complete) { - require(1) // check if we still have something to read - - UnsafeBufferOperations.readFromHead(this) { data, startOffset, endOffset -> - var offset = startOffset - do { - val b = data[offset++] - result = result.or(0x7fL.and(b.toLong()).shl(shift)) - shift += 7 - complete = b >= 0 // we're done if the most significant bit was not set - } while (!complete && offset < endOffset) - // return the number of consumed bytes - offset - startOffset - } - } - - return result.toULong() - } - - val buffer = Buffer().apply { - write(byteArrayOf(0)) // 0 - write(byteArrayOf(0xed.toByte(), 0x9b.toByte(), 0xb0.toByte(), 0x6f)) // dec0ded - write(byteArrayOf(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1)) // ffffffffffffffff - } - assertEquals(0U, buffer.readULeb128()) - assertEquals(0xDEC0DEDu, buffer.readULeb128()) - assertEquals((-1).toULong(), buffer.readULeb128()) - } - - @OptIn(UnsafeIoApi::class) - @Test - fun readUleb128() { - // Decode unsigned integer encoded using unsigned LEB128 format: - // https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128 - fun Buffer.readULEB128UInt(): UInt { - var result = 0u - var shift = 0 - var finished = false - while (!finished) { // Read until a number is fully fetched - if (exhausted()) throw EOFException() - // Pick the first segment and read from it either until the segment exhausted, - // or the number if finished. - UnsafeBufferOperations.readFromHead(this) { readCtx, segment -> - // Iterate over every byte contained in the segment - for (offset in 0..< segment.size) { - if (shift > 28) throw NumberFormatException("Overflow") - // Read the byte at the offset - val b = readCtx.getUnchecked(segment, offset) - val lsb = b.toUInt() and 0x7fu - result = result or (lsb shl shift) - shift += 7 - if (b >= 0) { - finished = true - // We're done, return how many bytes were consumed from the segment - return@readFromHead offset + 1 - } - } - // We read all the data from segment, but not finished yet. - // Return segment.size to indicate that the head segment was consumed in full. - segment.size - } - } - return result - } - - val buffer = Buffer().also { it.write(ByteString(0xe5.toByte(), 0x8e.toByte(), 0x26)) } - assertEquals(624485u, buffer.readULEB128UInt()) - assertTrue(buffer.exhausted()) - - buffer.write(ByteArray(8191)) - buffer.write(ByteString(0xe5.toByte(), 0x8e.toByte(), 0x26)) - buffer.skip(8191) - assertEquals(624485u, buffer.readULEB128UInt()) - assertTrue(buffer.exhausted()) - } - - @OptIn(UnsafeIoApi::class) - @Test - fun writeUleb128() { - // Encode an unsigned integer using unsigned LEB128 format: - // https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128 - fun Buffer.writeULEB128(value: UInt) { - // In the worst case, int will be encoded using 5 bytes - val minCapacity = 5 - // Acquire a segment that can fit at least 5 bytes - UnsafeBufferOperations.writeToTail(this, minCapacity) { ctx, segment -> - // Count how many bytes were actually written - var bytesWritten = 0 - var remainingBits = value - do { - var b = remainingBits and 0x7fu - remainingBits = remainingBits shr 7 - if (remainingBits != 0u) { - b = 0x80u or b - } - // Append a byte to the segment - ctx.setUnchecked(segment, bytesWritten++, b.toByte()) - } while (remainingBits != 0u) - // Return how many bytes were actually written - bytesWritten - } - } - - val buffer = Buffer() - buffer.writeULEB128(624485u) - assertEquals(ByteString(0xe5.toByte(), 0x8e.toByte(), 0x26), buffer.readByteString()) - } - - @OptIn(UnsafeIoApi::class) - private fun Buffer.writeULEB128(value: UInt) { - // update buffer's state after writing all bytes - - val minCapacity = 5 // in the worst case, int will be encoded using 5 bytes - UnsafeBufferOperations.writeToTail(this, minCapacity) { ctx, seg -> - var bytesWritten = 0 - var remainingBits = value - do { - var b = remainingBits and 0x7fu - remainingBits = remainingBits shr 7 - if (remainingBits != 0u) { - b = 0x80u or b - } - ctx.setUnchecked(seg, bytesWritten++, b.toByte()) - } while (remainingBits != 0u) - // return how many bytes were actually written - bytesWritten - } - } - - @OptIn(ExperimentalUnsignedTypes::class, UnsafeIoApi::class) - @Test - fun writeUleb128Array() { - // Encode multiple unsigned integers using unsigned LEB128 format: - // https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128 - fun Buffer.writeULEB128(data: UIntArray) { - // Encode array length - writeULEB128(data.size.toUInt()) - - var index = 0 - while (index < data.size) { - val value = data[index++] - // optimize small values encoding: anything below 127 will be encoded using a single byte anyway - if (value < 0x80u) { - // we need a space for a single byte, but if there's more - we'll try to fill it - UnsafeBufferOperations.writeToTail(this, 1) { ctx, seg -> - var bytesWritten = 0 - ctx.setUnchecked(seg, bytesWritten++, value.toByte()) - - // let's save as much succeeding small values as possible - val remainingDataLength = data.size - index - val remainingCapacity = seg.remainingCapacity - 1 - for (i in 0 until min(remainingDataLength, remainingCapacity)) { - val b = data[index] - if (b >= 0x80u) break - ctx.setUnchecked(seg, bytesWritten++, b.toByte()) - index++ - } - bytesWritten - } - } else { - writeULEB128(value) - } - } - } - - val buffer = Buffer() - val data = UIntArray(10) { it.toUInt() } - buffer.writeULEB128(data) - assertEquals(ByteString(10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9), buffer.readByteString()) - } - - @Test - @OptIn(ExperimentalUnsignedTypes::class) - fun crc32Unsafe() { - 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 - } - - val crc32Table = generateCrc32Table() - - // Compute a CRC32 checksum over Buffer's content without consuming it - @OptIn(UnsafeIoApi::class) - fun Buffer.crc32(): UInt { - var crc32 = 0xffffffffU - // Iterate over segments starting from buffer's head - UnsafeBufferOperations.forEachSegment(this) { ctx, segment -> - var currentSegment = segment - // Get data from a segment - ctx.withData(currentSegment) { data, startIndex, endIndex -> - for (idx in startIndex.. - var currentSegment = head - while (currentSegment != null) { - // Get data from a segment - for (offset in 0.. - fail() - } - } - - @Test - fun singleSegment() { - var counter = 0 - UnsafeBufferOperations.forEachSegment(Buffer().also { it.writeByte(1) }) { ctx, segment -> - ++counter - assertEquals(1, segment.size) - } - assertEquals(1, counter) - } - - @Test - fun multipleSegments() { - val buffer = Buffer() - - val expectedSegments = 10 - for (i in 0 ..< expectedSegments) { - UnsafeBufferOperations.moveToTail(buffer, byteArrayOf(i.toByte())) - } - - val storedBytes = ByteArray(expectedSegments) - var idx = 0 - UnsafeBufferOperations.forEachSegment(buffer) { ctx, segment -> - assertTrue(idx < expectedSegments) - storedBytes[idx++] = ctx.getUnchecked(segment, 0) - } - - assertArrayEquals(ByteArray(expectedSegments) { it.toByte() }, storedBytes) - } - - @Test - fun acquireDataDuringIteration() { - val buffer = Buffer().also { it.writeString("hello buffer") } - - val expectedSize = buffer.size - - UnsafeBufferOperations.forEachSegment(buffer) { ctx, segment -> - ctx.withData(segment) { data, startIndex, endIndex -> - assertEquals("hello buffer", data.decodeToString(startIndex, endIndex)) - } - } - - assertEquals(expectedSize, buffer.size) - } -} diff --git a/core/common/test/unsafe/UnsafeBufferOperationsIterationTest.kt b/core/common/test/unsafe/UnsafeBufferOperationsIterationTest.kt deleted file mode 100644 index 16b07c12a..000000000 --- a/core/common/test/unsafe/UnsafeBufferOperationsIterationTest.kt +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2010-2024 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. - */ -@file:OptIn(UnsafeIoApi::class) - -package kotlinx.io.unsafe - -import kotlinx.io.Buffer -import kotlinx.io.UnsafeIoApi -import kotlinx.io.assertArrayEquals -import kotlinx.io.writeString -import kotlin.test.* - -class UnsafeBufferOperationsIterationTest { - @Test - fun callsInPlaceContract() { - val buffer = Buffer().also { it.writeString("hello buffer") } - - val called: Boolean - UnsafeBufferOperations.iterate(buffer) { ctx, segment -> - called = true - - val withDataCalled: Boolean - ctx.withData(segment!!) { _, _, _ -> - withDataCalled = true - } - assertTrue(withDataCalled) - } - assertTrue(called) - - val offsetCalled: Boolean - UnsafeBufferOperations.iterate(buffer, 1) { ctx, segment, _ -> - offsetCalled = true - - val withDataCalled: Boolean - ctx.withData(segment!!) { _, _, _ -> - withDataCalled = true - } - assertTrue(withDataCalled) - } - assertTrue(offsetCalled) - } - - @Test - fun emptyBuffer() { - UnsafeBufferOperations.iterate(Buffer()) { _, head -> - assertNull(head) - } - } - - @Test - fun singleSegment() { - UnsafeBufferOperations.iterate(Buffer().also { it.writeByte(1) }) { ctx, head -> - assertNotNull(head) - assertEquals(1, head.size) - - assertNull(ctx.next(head)) - } - } - - @Test - fun multipleSegments() { - val buffer = Buffer() - - val expectedSegments = 10 - for (i in 0 ..< expectedSegments) { - UnsafeBufferOperations.moveToTail(buffer, byteArrayOf(i.toByte())) - } - - val storedBytes = ByteArray(expectedSegments) - - UnsafeBufferOperations.iterate(buffer) { ctx, head -> - var idx = 0 - var seg = head - - do { - seg!! - - assertTrue(idx < expectedSegments) - storedBytes[idx++] = ctx.getUnchecked(seg, 0) - seg = ctx.next(seg) - } while (seg != null) - } - - assertArrayEquals(ByteArray(expectedSegments) { it.toByte() }, storedBytes) - } - - @Test - fun acquireDataDuringIteration() { - val buffer = Buffer().also { it.writeString("hello buffer") } - - val expectedSize = buffer.size - - UnsafeBufferOperations.iterate(buffer) { ctx, head -> - assertNotNull(head) - - ctx.withData(head) { data, startIndex, endIndex -> - assertEquals("hello buffer", data.decodeToString(startIndex, endIndex)) - } - } - - assertEquals(expectedSize, buffer.size) - } - - @Test - fun seekStartOffsets() { - val firstSegmentData = "hello buffer".encodeToByteArray() - val buffer = Buffer().also { it.write(firstSegmentData) } - - UnsafeBufferOperations.iterate(buffer, offset = 6) { _, segment, offset -> - assertNotNull(segment) - assertEquals(0, offset) - } - - val secondSegmentData = "; that's a second segment".encodeToByteArray() - UnsafeBufferOperations.moveToTail(buffer, secondSegmentData) - val thirdSegmentData = "; that's a third segment".encodeToByteArray() - UnsafeBufferOperations.moveToTail(buffer, thirdSegmentData) - - val startOfSegmentOffsets = longArrayOf( - 0, - firstSegmentData.size.toLong(), - (firstSegmentData.size + secondSegmentData.size).toLong(), - (firstSegmentData.size + secondSegmentData.size + thirdSegmentData.size).toLong() - ) - - val segmentsData = arrayOf(firstSegmentData, secondSegmentData, thirdSegmentData) - - for (limitIdx in 1 ..< startOfSegmentOffsets.size) { - val startOffset = startOfSegmentOffsets[limitIdx - 1] - val limitOffset = startOfSegmentOffsets[limitIdx] - - for (offset in startOffset ..< limitOffset) { - UnsafeBufferOperations.iterate(buffer, offset) { ctx, seg, o -> - assertNotNull(seg) - assertEquals(startOffset, o) - - ctx.withData(seg) { data, startIndex, endIndex -> - val slice = data.sliceArray(startIndex ..< endIndex) - assertArrayEquals(segmentsData[limitIdx - 1], slice) - } - } - } - } - } - - @Test - fun seekOutOfBounds() { - assertFailsWith { - UnsafeBufferOperations.iterate(Buffer(), offset = -1) { _, _, _ -> fail() } - } - assertFailsWith { - UnsafeBufferOperations.iterate(Buffer(), offset = 0) { _, _, _ -> fail() } - } - assertFailsWith { - UnsafeBufferOperations.iterate(Buffer(), offset = 9001) { _, _, _ -> fail() } - } - - val buffer = Buffer().also { it.writeInt(42) } - assertFailsWith { - UnsafeBufferOperations.iterate(buffer, offset = -1) { _, _, _ -> fail() } - } - assertFailsWith { - UnsafeBufferOperations.iterate(buffer, offset = 4) { _, _, _ -> fail() } - } - } -} diff --git a/core/common/test/unsafe/UnsafeBufferOperationsMoveTest.kt b/core/common/test/unsafe/UnsafeBufferOperationsMoveTest.kt deleted file mode 100644 index b251c5592..000000000 --- a/core/common/test/unsafe/UnsafeBufferOperationsMoveTest.kt +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2010-2024 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.unsafe - -import kotlinx.io.* -import kotlin.test.* - -@OptIn(UnsafeIoApi::class) -class UnsafeBufferOperationsMoveTest { - @Test - fun moveArrayFully() { - val buffer = Buffer() - val data = "hello unsafe new world!".encodeToByteArray() - - UnsafeBufferOperations.moveToTail(buffer, data) - assertFalse(buffer.exhausted()) - assertEquals(data.size.toLong(), buffer.size) - - assertEquals("hello unsafe new world!", buffer.readString()) - } - - @Test - fun moveArraySlice() { - val buffer = Buffer() - val data = "hello unsafe new world!".encodeToByteArray() - - UnsafeBufferOperations.moveToTail(buffer, data, 6, 12) - assertEquals(6L, buffer.size) - assertEquals("unsafe", buffer.readString()) - } - - @Test - fun movedArrayIsReadOnly() { - val firstBuffer = Buffer().also { it.writeString("this is ") } - val secondBuffer = Buffer().also { it.writeString("this is ") } - - val data = "first second third".encodeToByteArray() - - UnsafeBufferOperations.moveToTail(firstBuffer, data, 0, 5) - firstBuffer.writeString(" buffer") - - UnsafeBufferOperations.moveToTail(secondBuffer, data, 6, 12) - secondBuffer.writeString(" buffer") - - assertArrayEquals("first second third".encodeToByteArray(), data) - - assertEquals("this is first buffer", firstBuffer.readString()) - assertEquals("this is second buffer", secondBuffer.readString()) - } - - @Test - fun moveEmptySlice() { - val buffer = Buffer() - - UnsafeBufferOperations.moveToTail(buffer, ByteArray(0)) - assertTrue(buffer.exhausted()) - - UnsafeBufferOperations.moveToTail(buffer, ByteArray(10), 5, 5) - assertTrue(buffer.exhausted()) - } - - @Test - fun illegalArgumentsHandling() { - assertFailsWith { - UnsafeBufferOperations.moveToTail(Buffer(), ByteArray(1), -1) - } - - assertFailsWith { - UnsafeBufferOperations.moveToTail(Buffer(), ByteArray(10), 2, 0) - } - - assertFailsWith { - UnsafeBufferOperations.moveToTail(Buffer(), ByteArray(10), 11, 12) - } - } - - @Test - fun moveMultipleSegments() { - val buffer = Buffer() - val segmentsCount = 10 - for (i in 0 ..< segmentsCount) { - UnsafeBufferOperations.moveToTail(buffer, byteArrayOf(i.toByte())) - } - assertEquals(10, buffer.size) - assertEquals(listOf(1, 1, 1, 1, 1, 1, 1, 1, 1, 1), segmentSizes(buffer)) - assertContentEquals(ByteArray(segmentsCount) { it.toByte() }, buffer.readByteArray()) - } -} diff --git a/core/common/test/unsafe/UnsafeBufferOperationsReadTest.kt b/core/common/test/unsafe/UnsafeBufferOperationsReadTest.kt deleted file mode 100644 index 34ec9392a..000000000 --- a/core/common/test/unsafe/UnsafeBufferOperationsReadTest.kt +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2010-2024 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.unsafe - -import kotlinx.io.Buffer -import kotlinx.io.UnsafeIoApi -import kotlinx.io.assertArrayEquals -import kotlinx.io.writeString -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertTrue -import kotlin.test.fail - -@OptIn(UnsafeIoApi::class) -class UnsafeBufferOperationsReadTest { - private class TestException : RuntimeException() - - @Test - fun callsInPlaceContract() { - val buffer = Buffer().apply { writeString("hello world") } - - val bytesCalled: Boolean - UnsafeBufferOperations.readFromHead(buffer) { _, _, _ -> - bytesCalled = true - 0 - } - assertTrue(bytesCalled) - - val segmentsCalled: Boolean - UnsafeBufferOperations.readFromHead(buffer) { _, _ -> - segmentsCalled = true - 0 - } - assertTrue(segmentsCalled) - } - - @Test - fun bufferCapacity() { - val buffer = Buffer().apply { writeString("hello world") } - - val head = buffer.head!! - UnsafeBufferOperations.readFromHead(buffer) { data, startIndex, endIndex -> - assertTrue(endIndex <= data.size) - assertEquals(0, startIndex) - assertEquals(head.size, endIndex) - 0 - } - } - - @Test - fun consumeByteByByte() { - val expectedData = "hello world".encodeToByteArray() - val actualData = ByteArray(expectedData.size) - - val buffer = Buffer().apply { write(expectedData) } - for (idx in actualData.indices) { - val read = UnsafeBufferOperations.readFromHead(buffer) { data, startIndex, _ -> - actualData[idx] = data[startIndex] - 1 - } - assertEquals(1, read) - assertEquals(actualData.size - idx - 1, buffer.size.toInt()) - } - assertTrue(buffer.exhausted()) - assertArrayEquals(expectedData, actualData) - } - - @Test - fun readNothing() { - val buffer = Buffer().apply { writeInt(42) } - val read1 = UnsafeBufferOperations.readFromHead(buffer) { _, _, _ -> 0 } - assertEquals(0, read1) - assertEquals(42, buffer.readInt()) - - buffer.writeInt(42) - val read2 = UnsafeBufferOperations.readFromHead(buffer) { _, _ -> 0 } - assertEquals(0, read2) - assertEquals(42, buffer.readInt()) - } - - @Test - fun readEverything() { - val buffer = Buffer().apply { writeString("hello world") } - val read1 = UnsafeBufferOperations.readFromHead(buffer) { _, startIndex, endIndex -> - endIndex - startIndex - } - assertEquals(11, read1) - assertTrue(buffer.exhausted()) - - buffer.writeString("hello world") - val read2 = UnsafeBufferOperations.readFromHead(buffer) { _, seg -> seg.size } - assertEquals(11, read2) - assertTrue(buffer.exhausted()) - } - - @Test - fun readFromEmptyBuffer() { - val buffer = Buffer() - assertFailsWith { - UnsafeBufferOperations.readFromHead(buffer) { _, _, _ -> fail() } - } - - assertFailsWith { - UnsafeBufferOperations.readFromHead(buffer) { _, _ -> fail() } - } - } - - @Test - fun readFromTheSegmentEnd() { - val segmentSize = UnsafeBufferOperations.maxSafeWriteCapacity - val extraBytesCount = 128 - val bytesToSkip = segmentSize - 2 - - val buffer = Buffer().apply { write(ByteArray(segmentSize + extraBytesCount) { 0xff.toByte() }) } - buffer.skip(bytesToSkip.toLong()) - val head = buffer.head!! - assertEquals(bytesToSkip, head.pos) - - UnsafeBufferOperations.readFromHead(buffer) { _, startIndex, endIndex -> - assertEquals(2, endIndex - startIndex) - 2 - } - - assertEquals(extraBytesCount, buffer.size.toInt()) - } - - @Test - fun readFromTheSegmentEndUsingCtx() { - val segmentSize = UnsafeBufferOperations.maxSafeWriteCapacity - val extraBytesCount = 128 - val bytesToSkip = segmentSize - 2 - - val buffer = Buffer().apply { write(ByteArray(segmentSize + extraBytesCount) { 0xff.toByte() }) } - buffer.skip(bytesToSkip.toLong()) - val head = buffer.head!! - assertEquals(bytesToSkip, head.pos) - - UnsafeBufferOperations.readFromHead(buffer) { _, seg -> - assertEquals(2, seg.size) - 2 - } - - assertEquals(extraBytesCount, buffer.size.toInt()) - } - - @Test - fun returnIllegalReadCount() { - val buffer = Buffer().apply { writeInt(0) } - - assertFailsWith { - UnsafeBufferOperations.readFromHead(buffer) { _, _, _ -> -1 } - } - assertEquals(4L, buffer.size) - - assertFailsWith { - UnsafeBufferOperations.readFromHead(buffer) { _, _ -> -1 } - } - assertEquals(4L, buffer.size) - - assertFailsWith { - UnsafeBufferOperations.readFromHead(buffer) { _, f, t -> (t - f + 1) } - } - assertEquals(4L, buffer.size) - - assertFailsWith { - UnsafeBufferOperations.readFromHead(buffer) { _, seg -> seg.remainingCapacity + 1 } - } - assertEquals(4L, buffer.size) - } - - @Test - fun resetReadOnException() { - val buffer = Buffer().apply { writeString("hello world") } - - val sizeBeforeRead = buffer.size - assertFailsWith { - UnsafeBufferOperations.readFromHead(buffer) { _, _, _ -> - throw TestException() - } - } - assertEquals(buffer.size, sizeBeforeRead) - - assertFailsWith { - UnsafeBufferOperations.readFromHead(buffer) { _, _ -> - throw TestException() - } - } - assertEquals(buffer.size, sizeBeforeRead) - } -} diff --git a/core/common/test/unsafe/UnsafeBufferOperationsWriteTest.kt b/core/common/test/unsafe/UnsafeBufferOperationsWriteTest.kt deleted file mode 100644 index b1c2a3f50..000000000 --- a/core/common/test/unsafe/UnsafeBufferOperationsWriteTest.kt +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright 2010-2024 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.unsafe - -import kotlinx.io.* -import kotlin.test.* - -@OptIn(UnsafeIoApi::class) -class UnsafeBufferOperationsWriteTest { - private class TestException : RuntimeException() - - @Test - fun callsInPlaceContract() { - val bytesCalled: Boolean - UnsafeBufferOperations.writeToTail(Buffer(), 1) { _, _, _ -> - bytesCalled = true - 0 - } - assertTrue(bytesCalled) - - val segmentsCalled: Boolean - UnsafeBufferOperations.writeToTail(Buffer(), 1) { _, _ -> - segmentsCalled = true - 0 - } - assertTrue(segmentsCalled) - } - - @Test - fun bufferCapacity() { - val buffer = Buffer() - - UnsafeBufferOperations.writeToTail(buffer, 1) { data, startIndex, endIndex -> - // Unsafe check, head is not committed yet - assertSame(buffer.head!!.dataAsByteArray(true), data) - assertEquals(0, startIndex) - assertEquals(buffer.head!!.remainingCapacity, endIndex) - 0 - } - - UnsafeBufferOperations.writeToTail(buffer, 1) { _, segment -> - // Unsafe check, head is not committed yet - assertSame(buffer.head, segment) - 0 - } - } - - @Test - fun writeByteByByte() { - val buffer = Buffer() - val data = "hello world".encodeToByteArray() - - for (idx in data.indices) { - val written = UnsafeBufferOperations.writeToTail(buffer, 1) { writeable, pos, _ -> - writeable[pos] = data[idx] - 1 - } - assertEquals(1, written) - assertEquals(idx + 1, buffer.size.toInt()) - } - assertEquals("hello world", buffer.readString()) - } - - @Test - fun writeNothing() { - val buffer = Buffer() - - val write1 = UnsafeBufferOperations.writeToTail(buffer, 1) { _, _, _ -> 0 } - assertEquals(0, write1) - assertTrue(buffer.exhausted()) - - val write2 = UnsafeBufferOperations.writeToTail(buffer, 1) { _, _ -> 0 } - assertEquals(0, write2) - assertTrue(buffer.exhausted()) - - buffer.writeInt(42) - UnsafeBufferOperations.writeToTail(buffer, 1) { _, _, _ -> 0 } - assertEquals(4, buffer.size) - - UnsafeBufferOperations.writeToTail(buffer, 1) { _, _ -> 0 } - assertEquals(4, buffer.size) - - buffer.write(ByteArray(Segment.SIZE - 4)) - UnsafeBufferOperations.writeToTail(buffer, 1) { _, _, _ -> 0 } - assertEquals(Segment.SIZE.toLong(), buffer.size) - - UnsafeBufferOperations.writeToTail(buffer, 1) { _, _ -> 0 } - assertEquals(Segment.SIZE.toLong(), buffer.size) - } - - @Test - fun writeWholeBuffer() { - val buffer = Buffer() - val written = UnsafeBufferOperations.writeToTail(buffer, 1) { data, from, to -> - for (idx in from.. - ctx.setUnchecked(segment, 0, 1) - ctx.setUnchecked(segment, 1, 2) - 2 - } - - assertEquals(2, written) - assertArrayEquals(byteArrayOf(1, 2), buffer.readByteArray()) - } - - @Test - fun requireToManyBytes() { - val buffer = Buffer() - assertFailsWith { - UnsafeBufferOperations.writeToTail(buffer, 100500) { _, _, _ -> fail() } - } - assertTrue(buffer.exhausted()) - - assertFailsWith { - UnsafeBufferOperations.writeToTail(buffer, 100500) { _, _ -> fail() } - } - assertTrue(buffer.exhausted()) - } - - @Test - fun writeToTheEndOfABuffer() { - val buffer = Buffer().apply { write(ByteArray(Segment.SIZE - 1)) } - UnsafeBufferOperations.writeToTail(buffer, 1) { data, pos, limit -> - assertEquals(1, limit - pos) - data[pos] = 42 - 1 - } - assertEquals(Segment.SIZE, buffer.size.toInt()) - UnsafeBufferOperations.writeToTail(buffer, 1) { data, pos, _ -> - data[pos] = 43 - 1 - } - assertEquals(Segment.SIZE + 1, buffer.size.toInt()) - - buffer.skip(Segment.SIZE - 1L) - assertArrayEquals(byteArrayOf(42, 43), buffer.readByteArray()) - } - - @Test - fun writeToTheEndOfABufferUsingCtx() { - val buffer = Buffer().apply { write(ByteArray(Segment.SIZE - 1)) } - UnsafeBufferOperations.writeToTail(buffer, 1) { ctx, seg -> - assertEquals(1, seg.remainingCapacity) - ctx.setUnchecked(seg, 0, 42) - 1 - } - assertEquals(Segment.SIZE, buffer.size.toInt()) - UnsafeBufferOperations.writeToTail(buffer, 1) { ctx, seg -> - ctx.setUnchecked(seg, 0, 43) - 1 - } - assertEquals(Segment.SIZE + 1, buffer.size.toInt()) - - buffer.skip(Segment.SIZE - 1L) - assertArrayEquals(byteArrayOf(42, 43), buffer.readByteArray()) - } - - @Test - fun returnIllegalWriteCount() { - val buffer = Buffer() - assertFailsWith { - UnsafeBufferOperations.writeToTail(buffer, 1) { _, _, _ -> - -1 - } - } - assertTrue(buffer.exhausted()) - - assertFailsWith { - UnsafeBufferOperations.writeToTail(buffer, 1) { _, _, _ -> - 100500 - } - } - assertTrue(buffer.exhausted()) - } - - @Test - fun writeMultipleBytes() { - val buffer = Buffer() - - UnsafeBufferOperations.writeToTail(buffer, 10) { ctx, tail -> - ctx.setUnchecked(tail, 0, 1) - ctx.setUnchecked(tail, 1, 2, 3) - ctx.setUnchecked(tail, 3, 4, 5, 6) - ctx.setUnchecked(tail, 6, 7, 8, 9, 10) - 10 - } - - assertArrayEquals(ByteArray(10) { (it + 1).toByte() }, buffer.readByteArray()) - - // check overlapping writes - UnsafeBufferOperations.writeToTail(buffer, 4) { ctx, tail -> - ctx.setUnchecked(tail, 0, 11, 11, 11, 11) - ctx.setUnchecked(tail, 1, 10, 10, 10) - ctx.setUnchecked(tail, 2, 9, 9) - ctx.setUnchecked(tail, 3, 8) - 4 - } - assertArrayEquals(byteArrayOf(11, 10, 9, 8), buffer.readByteArray()) - } - - @Test - fun resetWriteOnException() { - val buffer = Buffer() - - assertFailsWith { - UnsafeBufferOperations.writeToTail(buffer, 2) { _, _, _ -> - throw TestException() - } - } - - assertTrue(buffer.exhausted()) - - assertFailsWith { - UnsafeBufferOperations.writeToTail(buffer, 2) { _, _ -> - throw TestException() - } - } - - assertTrue(buffer.exhausted()) - } - - @Test - fun returnLessBytesThanItWasActuallyWritten() { - val buffer = Buffer() - - UnsafeBufferOperations.writeToTail(buffer, 42) { data, pos, limit -> - data.fill(0xab.toByte(), pos, limit) - 4 - } - assertEquals(4, buffer.size) - assertEquals(0xababababu, buffer.readUInt()) - } -} diff --git a/core/common/test/util.kt b/core/common/test/util.kt deleted file mode 100644 index 3d1570511..000000000 --- a/core/common/test/util.kt +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package kotlinx.io - -import kotlinx.io.internal.REPLACEMENT_BYTE -import kotlin.test.assertEquals -import kotlin.test.assertTrue - -fun segmentSizes(buffer: Buffer): List { - var segment: Segment? = buffer.head ?: return emptyList() - - val sizes = mutableListOf(segment!!.limit - segment.pos) - segment = segment.next - while (segment != null) { - sizes.add(segment.limit - segment.pos) - segment = segment.next - } - return sizes -} - -fun assertNoEmptySegments(buffer: Buffer) { - assertTrue(segmentSizes(buffer).all { it != 0 }, "Expected all segments to be non-empty") -} - -expect fun tempFileName(): String - -private fun fromHexChar(char: Char): Int { - val code = char.code - return when (code) { - in '0'.code..'9'.code -> code - '0'.code - in 'a'.code..'f'.code -> code - 'a'.code + 10 - in 'A'.code..'F'.code -> code - 'A'.code + 10 - else -> throw NumberFormatException("Not a hexadecimal digit: $char") - } -} - -fun String.decodeHex(): ByteArray { - if (length % 2 != 0) throw IllegalArgumentException("Even number of bytes is expected.") - - val result = ByteArray(length / 2) - - for (idx in result.indices) { - val byte = fromHexChar(this[idx * 2]).shl(4).or(fromHexChar(this[idx * 2 + 1])) - result[idx] = byte.toByte() - } - - return result -} - -fun Char.repeat(count: Int): String { - return toString().repeat(count) -} - -fun assertArrayEquals(a: ByteArray, b: ByteArray) { - assertEquals(a.contentToString(), b.contentToString()) -} - -internal fun String.commonAsUtf8ToByteArray(): ByteArray { - val bytes = ByteArray(4 * length) - - // Assume ASCII until a UTF-8 code point is observed. This is ugly but yields - // about a 2x performance increase for pure ASCII. - for (index in indices) { - val b0 = this[index] - if (b0 >= '\u0080') { - var size = index - processUtf8Bytes(index, length) { c -> - bytes[size++] = c - } - return bytes.copyOf(size) - } - bytes[index] = b0.code.toByte() - } - - return bytes.copyOf(length) -} - -internal inline fun String.processUtf8Bytes( - beginIndex: Int, - endIndex: Int, - yield: (Byte) -> Unit -) { - // Transcode a UTF-16 String to UTF-8 bytes. - var index = beginIndex - while (index < endIndex) { - val c = this[index] - - when { - c < '\u0080' -> { - // Emit a 7-bit character with 1 byte. - yield(c.code.toByte()) // 0xxxxxxx - index++ - - // Assume there is going to be more ASCII - while (index < endIndex && this[index] < '\u0080') { - yield(this[index++].code.toByte()) - } - } - - c < '\u0800' -> { - // Emit a 11-bit character with 2 bytes. - /* ktlint-disable no-multi-spaces */ - yield((c.code shr 6 or 0xc0).toByte()) // 110xxxxx - yield((c.code and 0x3f or 0x80).toByte()) // 10xxxxxx - /* ktlint-enable no-multi-spaces */ - index++ - } - - c !in '\ud800'..'\udfff' -> { - // Emit a 16-bit character with 3 bytes. - /* ktlint-disable no-multi-spaces */ - yield((c.code shr 12 or 0xe0).toByte()) // 1110xxxx - yield((c.code shr 6 and 0x3f or 0x80).toByte()) // 10xxxxxx - yield((c.code and 0x3f or 0x80).toByte()) // 10xxxxxx - /* ktlint-enable no-multi-spaces */ - index++ - } - - else -> { - // c is a surrogate. Make sure it is a high surrogate & that its successor is a low - // surrogate. If not, the UTF-16 is invalid, in which case we emit a replacement - // byte. - if (c > '\udbff' || - endIndex <= index + 1 || - this[index + 1] !in '\udc00'..'\udfff' - ) { - yield(REPLACEMENT_BYTE) - index++ - } else { - // UTF-16 high surrogate: 110110xxxxxxxxxx (10 bits) - // UTF-16 low surrogate: 110111yyyyyyyyyy (10 bits) - // Unicode code point: 00010000000000000000 + xxxxxxxxxxyyyyyyyyyy (21 bits) - val codePoint = (((c.code shl 10) + this[index + 1].code) + (0x010000 - (0xd800 shl 10) - 0xdc00)) - - // Emit a 21-bit character with 4 bytes. - /* ktlint-disable no-multi-spaces */ - yield((codePoint shr 18 or 0xf0).toByte()) // 11110xxx - yield((codePoint shr 12 and 0x3f or 0x80).toByte()) // 10xxxxxx - yield((codePoint shr 6 and 0x3f or 0x80).toByte()) // 10xxyyyy - yield((codePoint and 0x3f or 0x80).toByte()) // 10yyyyyy - /* ktlint-enable no-multi-spaces */ - index += 2 - } - } - } - } -} - -internal expect fun String.asUtf8ToByteArray(): ByteArray diff --git a/core/js/src/-PlatformJs.kt b/core/js/src/-PlatformJs.kt deleted file mode 100644 index 51eb04768..000000000 --- a/core/js/src/-PlatformJs.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io - -public actual open class IOException : Exception { - public actual constructor() : super() - - public actual constructor(message: String?) : super(message) - - public actual constructor(cause: Throwable?) : super(cause) - - public actual constructor(message: String?, cause: Throwable?) : super(message, cause) -} - -public actual open class EOFException : IOException { - public actual constructor() : super() - - public actual constructor(message: String?) : super(message) -} - -internal actual fun withCaughtException(block: () -> Unit): Throwable? { - try { - block() - return null - } catch (t: Throwable) { - return t - } -} diff --git a/core/js/src/RawSink.kt b/core/js/src/RawSink.kt deleted file mode 100644 index 1f611f465..000000000 --- a/core/js/src/RawSink.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io - -public actual interface RawSink : AutoCloseable { - public actual fun write(source: Buffer, byteCount: Long) - - public actual fun flush() - - actual override fun close() -} diff --git a/core/js/src/SegmentPool.kt b/core/js/src/SegmentPool.kt deleted file mode 100644 index c61fbeffa..000000000 --- a/core/js/src/SegmentPool.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io - -internal actual object SegmentPool { - actual val MAX_SIZE: Int = 0 - - actual val byteCount: Int = 0 - - actual fun take(): Segment = Segment.new() - - actual fun recycle(segment: Segment) { - } - - actual fun tracker(): SegmentCopyTracker = AlwaysSharedCopyTracker -} diff --git a/core/js/src/node/nodeModulesJs.kt b/core/js/src/node/nodeModulesJs.kt deleted file mode 100644 index 83fe3f2f4..000000000 --- a/core/js/src/node/nodeModulesJs.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2010-2024 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.node - -internal actual val path: Path by lazy { - try { - js("eval('require')('path')") - } catch (e: Throwable) { - throw UnsupportedOperationException("Module 'path' could not be imported", e) - } -} - -internal actual val fs: Fs by lazy { - try { - js("eval('require')('fs')") - } catch (e: Throwable) { - throw UnsupportedOperationException("Module 'fs' could not be imported", e) - } -} - -internal actual val os: Os by lazy { - try { - js("eval('require')('os')") - } catch (e: Throwable) { - throw UnsupportedOperationException("Module 'os' could not be imported", e) - } -} - -internal actual val buffer: BufferModule by lazy { - try { - js("eval('require')('buffer')") - } catch (e: Throwable) { - throw UnsupportedOperationException("Module 'buffer' could not be imported", e) - } -} - diff --git a/core/js/test/utils.kt b/core/js/test/utils.kt deleted file mode 100644 index d37230b56..000000000 --- a/core/js/test/utils.kt +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright 2010-2024 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 - -internal actual fun String.asUtf8ToByteArray(): ByteArray = commonAsUtf8ToByteArray() diff --git a/core/jvm/src/-JvmPlatform.kt b/core/jvm/src/-JvmPlatform.kt deleted file mode 100644 index 92e17424a..000000000 --- a/core/jvm/src/-JvmPlatform.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2018 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kotlinx.io - -public actual typealias IOException = java.io.IOException - -public actual typealias EOFException = java.io.EOFException diff --git a/core/jvm/src/BuffersJvm.kt b/core/jvm/src/BuffersJvm.kt deleted file mode 100644 index 4b1364a79..000000000 --- a/core/jvm/src/BuffersJvm.kt +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2014 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package kotlinx.io - -import kotlinx.io.unsafe.UnsafeBufferOperations -import kotlinx.io.unsafe.withData -import java.io.EOFException -import java.io.IOException -import java.io.InputStream -import java.io.OutputStream -import java.nio.ByteBuffer -import java.nio.channels.ByteChannel - -/** - * Reads and exhausts 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) - return this -} - -/** - * Reads [byteCount] bytes from [input] into this buffer. Throws an exception when [input] is - * exhausted before reading [byteCount] bytes. - * - * @param input the stream to read data from. - * @param byteCount the number of bytes read from [input]. - * - * @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) - write(input, byteCount, false) - return this -} - -@OptIn(UnsafeIoApi::class) -private fun Buffer.write(input: InputStream, byteCount: Long, forever: Boolean) { - var remainingByteCount = byteCount - var exchaused = false - while (!exchaused && (remainingByteCount > 0L || forever)) { - UnsafeBufferOperations.writeToTail(this, 1) { data, pos, limit -> - val maxToCopy = minOf(remainingByteCount, limit - pos).toInt() - val bytesRead = input.read(data, pos, maxToCopy) - if (bytesRead == -1) { - if (!forever) { - throw EOFException("Stream exhausted before $byteCount bytes were read.") - } - exchaused = true - 0 - } else { - remainingByteCount -= bytesRead - bytesRead - } - } - } -} - -/** - * Consumes [byteCount] bytes from this buffer and writes it to [out]. - * - * @param out the [OutputStream] to write to. - * @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 - */ -@OptIn(UnsafeIoApi::class) -public fun Buffer.readTo(out: OutputStream, byteCount: Long = size) { - checkOffsetAndCount(size, 0, byteCount) - var remainingByteCount = byteCount - - while (remainingByteCount > 0L) { - UnsafeBufferOperations.readFromHead(this) { data, pos, limit -> - val toCopy = minOf(remainingByteCount, limit - pos).toInt() - out.write(data, pos, toCopy) - remainingByteCount -= toCopy - toCopy - } - } -} - -/** - * Copies bytes from this buffer's subrange, starting at [startIndex] and ending at [endIndex], to [out]. This method - * does not consume data from the buffer. - * - * @param out the destination to copy data into. - * @param startIndex the index (inclusive) of the first byte to copy, `0` by default. - * @param endIndex the index (exclusive) of the last byte to copy, `buffer.size` by default. - * - * @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 - */ -@OptIn(UnsafeIoApi::class) -public fun Buffer.copyTo( - out: OutputStream, - startIndex: Long = 0L, - endIndex: Long = size -) { - checkBounds(size, startIndex, endIndex) - if (startIndex == endIndex) return - - var remainingByteCount = endIndex - startIndex - - UnsafeBufferOperations.iterate(this, startIndex) { ctx, seg, segOffset -> - var curr = seg!! - var currentOffset = (startIndex - segOffset).toInt() - while (remainingByteCount > 0) { - ctx.withData(curr) { data, pos, limit -> - val toCopy = minOf(limit - pos - currentOffset, remainingByteCount).toInt() - out.write(data, pos + currentOffset, toCopy) - remainingByteCount -= toCopy - } - curr = ctx.next(curr) ?: break - currentOffset = 0 - } - } -} - -/** - * Writes up to [ByteBuffer.remaining] bytes from this buffer to the sink. - * Return the number of bytes written. - * - * @param sink the sink to write data to. - * - * @sample kotlinx.io.samples.KotlinxIoSamplesJvm.readWriteByteBuffer - */ -@OptIn(UnsafeIoApi::class) -public fun Buffer.readAtMostTo(sink: ByteBuffer): Int { - if (exhausted()) return -1 - var toCopy = 0 - UnsafeBufferOperations.readFromHead(this) { data, pos, limit -> - toCopy = minOf(sink.remaining(), limit - pos) - sink.put(data, pos, toCopy) - toCopy - } - - return toCopy -} - -/** - * Reads all data from [source] into this buffer. - * - * @sample kotlinx.io.samples.KotlinxIoSamplesJvm.transferBufferFromByteBuffer - */ -@OptIn(UnsafeIoApi::class) -public fun Buffer.transferFrom(source: ByteBuffer): Buffer { - val byteCount = source.remaining() - var remaining = byteCount - - while (remaining > 0) { - UnsafeBufferOperations.writeToTail(this, 1) { data, pos, limit -> - val toCopy = minOf(remaining, limit - pos) - source.get(data, pos, toCopy) - remaining -= toCopy - toCopy - } - } - - return this -} - -/** - * Returns a new [ByteChannel] instance representing this buffer. - */ -public fun Buffer.asByteChannel(): ByteChannel = object : ByteChannel { - override fun read(sink: ByteBuffer): Int = readAtMostTo(sink) - - override fun write(source: ByteBuffer): Int { - val sizeBefore = size - transferFrom(source) - return (size - sizeBefore).toInt() - } - - override fun close() {} - - override fun isOpen(): Boolean = true -} diff --git a/core/jvm/src/JvmCore.kt b/core/jvm/src/JvmCore.kt deleted file mode 100644 index e714f7740..000000000 --- a/core/jvm/src/JvmCore.kt +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2014 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kotlinx.io - -import kotlinx.io.unsafe.UnsafeBufferOperations -import java.io.IOException -import java.io.InputStream -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) - -private open class OutputStreamSink( - private val out: OutputStream, -) : RawSink { - - @OptIn(UnsafeIoApi::class) - override fun write(source: Buffer, byteCount: Long) { - checkOffsetAndCount(source.size, 0, byteCount) - var remaining = byteCount - while (remaining > 0) { - // kotlinx.io TODO: detect Interruption. - UnsafeBufferOperations.readFromHead(source) { data, pos, limit -> - val toCopy = minOf(remaining, limit - pos).toInt() - out.write(data, pos, toCopy) - remaining -= toCopy - toCopy - } - } - } - - override fun flush() = out.flush() - - override fun close() = out.close() - - override fun toString() = "RawSink($out)" -} - -/** - * 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) - -private open class InputStreamSource( - private val input: InputStream, -) : RawSource { - - @OptIn(UnsafeIoApi::class) - override fun readAtMostTo(sink: Buffer, byteCount: Long): Long { - if (byteCount == 0L) return 0L - checkByteCount(byteCount) - try { - var readTotal = 0L - UnsafeBufferOperations.writeToTail(sink, 1) { data, pos, limit -> - val maxToCopy = minOf(byteCount, limit - pos).toInt() - readTotal = input.read(data, pos, maxToCopy).toLong() - if (readTotal == -1L) { - 0 - } else { - readTotal.toInt() - } - } - return readTotal - } catch (e: AssertionError) { - if (e.isAndroidGetsocknameError) throw IOException(e) - throw e - } - } - - override fun close() = input.close() - - override fun toString() = "RawSource($input)" -} - -/** - * Returns true if this error is due to a firmware bug fixed after Android 4.2.2. - * https://code.google.com/p/android/issues/detail?id=54072 - */ -internal val AssertionError.isAndroidGetsocknameError: Boolean - get() { - return cause != null && message?.contains("getsockname failed") ?: false - } diff --git a/core/jvm/src/RawSink.kt b/core/jvm/src/RawSink.kt deleted file mode 100644 index 326a2e28b..000000000 --- a/core/jvm/src/RawSink.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2014 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package kotlinx.io - -import java.io.Flushable - -public actual interface RawSink : AutoCloseable, Flushable { - public actual fun write(source: Buffer, byteCount: Long) - - actual override fun flush() - - actual override fun close() -} diff --git a/core/jvm/src/SegmentPool.kt b/core/jvm/src/SegmentPool.kt deleted file mode 100644 index 5190f27bb..000000000 --- a/core/jvm/src/SegmentPool.kt +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2014 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package kotlinx.io - -import kotlinx.io.SegmentPool.HASH_BUCKET_COUNT -import kotlinx.io.SegmentPool.LOCK -import kotlinx.io.SegmentPool.MAX_SIZE -import kotlinx.io.SegmentPool.recycle -import kotlinx.io.SegmentPool.take -import java.util.concurrent.atomic.AtomicIntegerFieldUpdater -import java.util.concurrent.atomic.AtomicReferenceArray - -/** - * Precise [SegmentCopyTracker] implementation tracking a number of shared segment copies. - * Every [addCopy] call increments the counter, every [removeCopy] decrements it. - * - * After calling [removeCopy] the same number of time [addCopy] was called, tracker returns to the unshared state. - * - * The class is internal for testing only. - */ -internal class RefCountingCopyTracker : SegmentCopyTracker() { - companion object { - @JvmStatic - private val fieldUpdater = AtomicIntegerFieldUpdater.newUpdater(RefCountingCopyTracker::class.java, "copyCount") - } - - @Volatile - private var copyCount: Int = 0 - - override val shared: Boolean - get() { - return copyCount > 0 - } - - override fun addCopy() { - fieldUpdater.incrementAndGet(this) - } - - override fun removeCopy(): Boolean { - // The value could not be incremented from `0` under the race, - // so once it zero, it remains zero in the scope of this call. - if (copyCount == 0) return false - - val updatedValue = fieldUpdater.decrementAndGet(this) - // If there are several copies, the last decrement will update copyCount from 0 to -1. - // That would be the last standing copy, and we can recycle it. - // If, however, the decremented value falls below -1, it's an error as there were more - // `removeCopy` than `addCopy` calls. - if (updatedValue >= 0) return true - check(updatedValue == -1) { "Shared copies count is negative: ${updatedValue + 1}" } - copyCount = 0 - return false - } -} - -/** - * This class pools segments in a lock-free singly-linked stack. Though this code is lock-free it - * does use a sentinel [LOCK] value to defend against races. To reduce the contention, the pool consists - * of several buckets (see [HASH_BUCKET_COUNT]), each holding a reference to its own segments stack. - * Every [take] or [recycle] choose one of the buckets depending on a [Thread.currentThread]'s [Thread.getId]. - * - * On [take], a caller swaps the stack's next pointer with the [LOCK] sentinel. If the stack was - * not already locked, the caller replaces the head node with its successor. - * - * On [recycle], a caller swaps the head with a new node whose successor is the replaced head. - * - * On conflict, operations are retried until they succeed. - * - * This tracks the number of bytes in each linked list in its [Segment.limit] property. Each element - * has a limit that's one segment size greater than its successor element. The maximum size of the - * pool is a product of [MAX_SIZE] and [HASH_BUCKET_COUNT]. - * - * [MAX_SIZE] is kept relatively small to avoid excessive memory consumption in case of a large [HASH_BUCKET_COUNT]. - * For better handling of scenarios with high segments demand, an optional second-level pool could be enabled - * by setting up a value of `kotlinx.io.pool.size.bytes` system property. - * - * The second-level pool use half of the [HASH_BUCKET_COUNT] and if an initially selected bucket if empty on [take] or - * full or [recycle], all other buckets will be inspected before finally giving up (which means allocating a new segment - * on [take], or loosing a reference to a segment on [recycle]). - * That pool is used as a backup in case when [take] or [recycle] failed due to - * an empty or exhausted segments chain in a corresponding bucket (one of [HASH_BUCKET_COUNT] buckets). - */ -internal actual object SegmentPool { - /** The maximum number of bytes to pool per hash bucket. */ - // TODO: Is 64 KiB a good maximum size? Do we ever have that many idle segments? - actual val MAX_SIZE = 64 * 1024 // 64 KiB. - - /** A sentinel segment to indicate that the linked list is currently being modified. */ - private val LOCK = - Segment.new(ByteArray(0), pos = 0, limit = 0, copyTracker = null, owner = false) - - /** - * The number of hash buckets. This number needs to balance keeping the pool small and contention - * low. We use the number of processors rounded up to the nearest power of two. For example a - * machine with 6 cores will have 8 hash buckets. - * - * Visible for testing only. - */ - internal val HASH_BUCKET_COUNT = - Integer.highestOneBit(Runtime.getRuntime().availableProcessors() * 2 - 1) - - private val HASH_BUCKET_COUNT_L2 = (HASH_BUCKET_COUNT / 2).coerceAtLeast(1) - - // For now, keep things on Android as they were before, but on JVM - use second level cache. - // See https://developer.android.com/reference/java/lang/System#getProperties() for property name. - private val DEFAULT_SECOND_LEVEL_POOL_TOTAL_SIZE = when (System.getProperty("java.vm.name")) { - "Dalvik" -> "0" - else -> "4194304" // 4MB - } - - // visible for testing - internal val SECOND_LEVEL_POOL_TOTAL_SIZE = - System.getProperty("kotlinx.io.pool.size.bytes", DEFAULT_SECOND_LEVEL_POOL_TOTAL_SIZE) - .toIntOrNull()?.coerceAtLeast(0) ?: 0 - - private val SECOND_LEVEL_POOL_BUCKET_SIZE = - (SECOND_LEVEL_POOL_TOTAL_SIZE / HASH_BUCKET_COUNT_L2).coerceAtLeast(Segment.SIZE) - - /** - * Hash buckets each contain a singly-linked list of segments. The index/key is a hash function of - * thread ID because it may reduce contention or increase locality. - * - * We don't use [ThreadLocal] because we don't know how many threads the host process has and we - * don't want to leak memory for the duration of a thread's life. - */ - private val hashBuckets: AtomicReferenceArray = AtomicReferenceArray(HASH_BUCKET_COUNT) - private val hashBucketsL2: AtomicReferenceArray = AtomicReferenceArray(HASH_BUCKET_COUNT_L2) - - actual val byteCount: Int - get() { - val first = hashBuckets[l1BucketId()] ?: return 0 - return first.limit - } - - @JvmStatic - actual fun take(): Segment { - val buckets = hashBuckets - val bucketId = l1BucketId() - - while (true) { - when (val first = buckets.getAndSet(bucketId, LOCK)) { - LOCK -> { - // We didn't acquire the lock. Let's try again - continue - } - - null -> { - // We acquired the lock but the pool was empty. - // Unlock the bucket and either try to acquire a segment from the second level cache, - // or, if the second level cache is disabled, allocate a brand-new segment. - buckets.set(bucketId, null) - - if (SECOND_LEVEL_POOL_TOTAL_SIZE > 0) { - return takeL2() - } - - return Segment.new() - } - - else -> { - // We acquired the lock and the pool was not empty. Pop the first element and return it. - buckets.set(bucketId, first.next) - first.next = null - first.limit = 0 - return first - } - } - } - } - - @JvmStatic - private fun takeL2(): Segment { - val buckets = hashBucketsL2 - var bucket = l2BucketId() - var attempts = 0 - while (true) { - when (val first = buckets.getAndSet(bucket, LOCK)) { - LOCK -> { - // We didn't acquire the lock, retry - continue - } - - null -> { - // We acquired the lock but the pool was empty. - // Unlock the current bucket and select a new one. - // If all buckets were already scanned, allocate a new segment. - buckets.set(bucket, null) - - if (attempts < HASH_BUCKET_COUNT_L2) { - bucket = (bucket + 1) and (HASH_BUCKET_COUNT_L2 - 1) - attempts++ - continue - } - - return Segment.new() - } - - else -> { - // We acquired the lock and the pool was not empty. Pop the first element and return it. - buckets.set(bucket, first.next) - first.next = null - first.limit = 0 - return first - } - } - } - } - - @JvmStatic - actual fun recycle(segment: Segment) { - require(segment.next == null && segment.prev == null) - if (segment.copyTracker?.removeCopy() == true) return // This segment cannot be recycled. - - val buckets = hashBuckets - val bucketId = l1BucketId() - - segment.pos = 0 - segment.owner = true - - while (true) { - val first = buckets[bucketId] - if (first === LOCK) continue // A take() is currently in progress. - val firstLimit = first?.limit ?: 0 - if (firstLimit >= MAX_SIZE) { - // L1 pool is full. - if (SECOND_LEVEL_POOL_TOTAL_SIZE > 0) { - recycleL2(segment) - } - return - } - - segment.next = first - segment.limit = firstLimit + Segment.SIZE - - if (buckets.compareAndSet(bucketId, first, segment)) { - return - } - } - } - - @JvmStatic - private fun recycleL2(segment: Segment) { - segment.pos = 0 - segment.owner = true - - var bucket = l2BucketId() - val buckets = hashBucketsL2 - var attempts = 0 - - while (true) { - val first = buckets[bucket] - if (first === LOCK) continue // A take() is currently in progress. - val firstLimit = first?.limit ?: 0 - if (firstLimit + Segment.SIZE > SECOND_LEVEL_POOL_BUCKET_SIZE) { - // The current bucket is full, try to find another one and return the segment there. - if (attempts < HASH_BUCKET_COUNT_L2) { - attempts++ - bucket = (bucket + 1) and (HASH_BUCKET_COUNT_L2 - 1) - continue - } - // L2 pool is full. - return - } - - segment.next = first - segment.limit = firstLimit + Segment.SIZE - - if (buckets.compareAndSet(bucket, first, segment)) { - return - } - } - } - - @JvmStatic - actual fun tracker(): SegmentCopyTracker = RefCountingCopyTracker() - - private fun l1BucketId() = bucketId (HASH_BUCKET_COUNT - 1L) - - private fun l2BucketId() = bucketId (HASH_BUCKET_COUNT_L2 - 1L) - - private fun bucketId(mask: Long): Int { - // Get a value in [0..HASH_BUCKET_COUNT_L2) based on the current thread. - @Suppress("DEPRECATION") // TODO: switch to threadId after JDK19 - return (Thread.currentThread().id and mask).toInt() - } -} diff --git a/core/jvm/src/SinksJvm.kt b/core/jvm/src/SinksJvm.kt deleted file mode 100644 index eb09283e1..000000000 --- a/core/jvm/src/SinksJvm.kt +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2014 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package kotlinx.io - -import java.io.IOException -import java.io.OutputStream -import java.nio.ByteBuffer -import java.nio.channels.WritableByteChannel -import java.nio.charset.Charset - -/** - * Encodes substring of [string] starting at [startIndex] and ending at [endIndex] using [charset] - * and writes into this sink. - * - * @param string the string to encode into this sink. - * @param charset the [Charset] to use for encoding. - * @param startIndex the index of the first character to encode, inclusive, 0 by default. - * @param endIndex the index of the last character to encode, exclusive, `string.length` by default. - * - * @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. - * @throws IOException when some I/O error occurs. - * - * @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) - if (charset == Charsets.UTF_8) return writeString(string, startIndex, endIndex) - val data = string.substring(startIndex, endIndex).toByteArray(charset) - write(data, 0, data.size) -} - -/** - * 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 { - val isClosed: () -> Boolean = when (this) { - is RealSink -> this::closed - is Buffer -> { - { false } - } - } - - return object : OutputStream() { - override fun write(byte: Int) { - if (isClosed()) throw IOException("Underlying sink is closed.") - writeToInternalBuffer { it.writeByte(byte.toByte()) } - } - - override fun write(data: ByteArray, offset: Int, byteCount: Int) { - if (isClosed()) throw IOException("Underlying sink is closed.") - writeToInternalBuffer { it.write(data, offset, offset + byteCount) } - } - - override fun flush() { - // For backwards compatibility, a flush() on a closed stream is a no-op. - if (!isClosed()) { - this@asOutputStream.flush() - } - } - - override fun close() = this@asOutputStream.close() - - override fun toString() = "${this@asOutputStream}.asOutputStream()" - } -} - -/** - * Writes data from the [source] into this sink and returns the number of bytes written. - * - * @param source the source to read from. - * - * @throws IllegalStateException when the sink is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoSamplesJvm.readWriteByteBuffer - */ -@OptIn(InternalIoApi::class) -public fun Sink.write(source: ByteBuffer): Int { - val sizeBefore = buffer.size - buffer.transferFrom(source) - val bytesRead = buffer.size - sizeBefore - hintEmit() - return bytesRead.toInt() -} - -/** - * Returns [WritableByteChannel] backed by this sink. Closing the channel will also close the sink. - */ -public fun Sink.asByteChannel(): WritableByteChannel { - val isClosed: () -> Boolean = when (this) { - is RealSink -> this::closed - is Buffer -> { - { false } - } - } - - return object : WritableByteChannel { - override fun close() { - this@asByteChannel.close() - } - - override fun isOpen(): Boolean = !isClosed() - - override fun write(source: ByteBuffer): Int { - check(!isClosed()) { "Underlying sink is closed." } - return this@asByteChannel.write(source) - } - } -} diff --git a/core/jvm/src/SourcesJvm.kt b/core/jvm/src/SourcesJvm.kt deleted file mode 100644 index 39f604d50..000000000 --- a/core/jvm/src/SourcesJvm.kt +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2014 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package kotlinx.io - -import kotlinx.io.unsafe.UnsafeBufferOperations -import java.io.EOFException -import java.io.InputStream -import java.nio.ByteBuffer -import java.nio.channels.ReadableByteChannel -import java.nio.charset.Charset - -@OptIn(UnsafeIoApi::class) -private fun Buffer.readStringImpl(byteCount: Long, charset: Charset): String { - require(byteCount >= 0 && byteCount <= Int.MAX_VALUE) { - "byteCount ($byteCount) is not within the range [0..${Int.MAX_VALUE})" - } - if (size < byteCount) { - throw EOFException("Buffer contains less bytes then required (byteCount: $byteCount, size: $size)") - } - if (byteCount == 0L) return "" - - var result: String? = null - UnsafeBufferOperations.readFromHead(this) { data, pos, limit -> - val len = limit - pos - if (len >= byteCount) { - result = String(data, pos, byteCount.toInt(), charset) - byteCount.toInt() - } else { - 0 - } - } - return if (result == null) { - String(readByteArray(byteCount.toInt()), charset) - } else { - result!! - } -} - -/** - * Decodes whole content of this stream into a string using [charset]. Returns empty string if the source is exhausted. - * - * @param charset the [Charset] to use for string decoding. - * - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoSamplesJvm.readWriteStrings - */ -@OptIn(InternalIoApi::class) -public fun Source.readString(charset: Charset): String { - var req = 1L - while (request(req)) { - req *= 2 - } - return buffer.readStringImpl(buffer.size, charset) -} - -/** - * Decodes [byteCount] bytes of this stream into a string using [charset]. - * - * @param byteCount the number of bytes to read from the source for decoding. - * @param charset the [Charset] to use for string decoding. - * - * @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]. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoSamplesJvm.readStringBounded - */ -@OptIn(InternalIoApi::class) -public fun Source.readString(byteCount: Long, charset: Charset): String { - require(byteCount) - return buffer.readStringImpl(byteCount, charset) -} - -/** - * 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 { - val isClosed: () -> Boolean = when (this) { - is RealSource -> this::closed - is Buffer -> { - { false } - } - } - - return object : InputStream() { - override fun read(): Int { - if (isClosed()) throw IOException("Underlying source is closed.") - if (exhausted()) { - return -1 - } - return readByte() and 0xff - } - - override fun read(data: ByteArray, offset: Int, byteCount: Int): Int { - if (isClosed()) throw IOException("Underlying source is closed.") - checkOffsetAndCount(data.size.toLong(), offset.toLong(), byteCount.toLong()) - - return this@asInputStream.readAtMostTo(data, offset, offset + byteCount) - } - - override fun available(): Int { - if (isClosed()) throw IOException("Underlying source is closed.") - return minOf(buffer.size, Integer.MAX_VALUE).toInt() - } - - override fun close() = this@asInputStream.close() - - override fun toString() = "${this@asInputStream}.asInputStream()" - } -} - -/** - * Reads at most [ByteBuffer.remaining] bytes from this source into [sink] and returns the number of bytes read. - * - * @param sink the sink to write the data to. - * - * @throws IllegalStateException when the source is closed. - * @throws IOException when some I/O error occurs. - * - * @sample kotlinx.io.samples.KotlinxIoSamplesJvm.readWriteByteBuffer - */ -@OptIn(InternalIoApi::class) -public fun Source.readAtMostTo(sink: ByteBuffer): Int { - if (buffer.size == 0L) { - request(Segment.SIZE.toLong()) - if (buffer.size == 0L) return -1 - } - - return buffer.readAtMostTo(sink) -} - -/** - * Returns [ReadableByteChannel] backed by this source. Closing the source will close the source. - */ -public fun Source.asByteChannel(): ReadableByteChannel { - val isClosed: () -> Boolean = when (this) { - is RealSource -> this::closed - is Buffer -> { - { false } - } - } - - return object : ReadableByteChannel { - override fun close() { - this@asByteChannel.close() - } - - override fun isOpen(): Boolean = !isClosed() - - override fun read(sink: ByteBuffer): Int = this@asByteChannel.readAtMostTo(sink) - } -} diff --git a/core/jvm/src/files/FileSystemJvm.kt b/core/jvm/src/files/FileSystemJvm.kt deleted file mode 100644 index b85ebdb7e..000000000 --- a/core/jvm/src/files/FileSystemJvm.kt +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.files - -import kotlinx.io.* -import java.io.FileInputStream -import java.io.FileOutputStream -import java.nio.file.Files -import java.nio.file.StandardCopyOption - -internal annotation class AnimalSnifferIgnore() - -private interface Mover { - fun move(source: Path, destination: Path) -} - -private class NioMover : Mover { - @AnimalSnifferIgnore - override fun move(source: Path, destination: Path) { - if (!source.file.exists()) { - throw FileNotFoundException("Source file does not exist: ${source.file}") - } - try { - Files.move( - source.file.toPath(), destination.file.toPath(), - StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING - ) - } catch (e: Throwable) { - if (e is IOException) throw e - throw IOException("Move failed", e) - } - } -} - -private val mover: Mover by lazy { - try { - Class.forName("java.nio.file.Files") - NioMover() - } catch (e: ClassNotFoundException) { - object : Mover { - override fun move(source: Path, destination: Path) { - throw UnsupportedOperationException("Atomic move not supported") - } - } - } -} - -@JvmField -public actual val SystemFileSystem: FileSystem = object : SystemFileSystemImpl() { - - override fun exists(path: Path): Boolean { - return path.file.exists() - } - - override fun delete(path: Path, mustExist: Boolean) { - if (!exists(path)) { - if (mustExist) { - throw FileNotFoundException("File does not exist: ${path.file}") - } - return - } - if (!path.file.delete()) { - throw IOException("Deletion failed") - } - } - - override fun createDirectories(path: Path, mustCreate: Boolean) { - if (!path.file.mkdirs()) { - if (mustCreate) { - throw IOException("Path already exist: $path") - } - if (path.file.isFile) { - throw IOException("Path already exists and it's a file: $path") - } - } - } - - override fun atomicMove(source: Path, destination: Path) { - mover.move(source, destination) - } - - override fun metadataOrNull(path: Path): FileMetadata? { - if (!path.file.exists()) return null - return FileMetadata(path.file.isFile, path.file.isDirectory, - if (path.file.isFile) path.file.length() else -1L) - } - - override fun source(path: Path): RawSource = FileInputStream(path.file).asSource() - - override fun sink(path: Path, append: Boolean): RawSink = FileOutputStream(path.file, append).asSink() - - override fun resolve(path: Path): Path { - if (!path.file.exists()) throw FileNotFoundException(path.file.absolutePath) - return Path(path.file.canonicalFile) - } - - override fun list(directory: Path): Collection { - val file = directory.file - if (!file.exists()) throw FileNotFoundException(file.absolutePath) - if (!file.isDirectory) throw IOException("Not a directory: ${file.absolutePath}") - return buildList { - file.list()?.forEach { childName -> - add(Path(directory, childName)) - } - } - } -} - -@JvmField -public actual val SystemTemporaryDirectory: Path = Path(System.getProperty("java.io.tmpdir")) - -public actual typealias FileNotFoundException = java.io.FileNotFoundException - -internal actual val isWindows: Boolean = System.getProperty("os.name")?.startsWith("Windows") ?: false diff --git a/core/jvm/src/files/PathsJvm.kt b/core/jvm/src/files/PathsJvm.kt deleted file mode 100644 index 2d26281e1..000000000 --- a/core/jvm/src/files/PathsJvm.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -package kotlinx.io.files - -import kotlinx.io.Sink -import kotlinx.io.Source -import kotlinx.io.buffered -import java.io.File - -public actual class Path internal constructor(internal val file: File) { - public actual val parent: Path? - get() { - val parentFile = file.parentFile ?: return null - return Path(parentFile) - } - - public actual override fun toString(): String = file.toString() - - actual override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is Path) return false - - return toString() == other.toString() - } - - actual override fun hashCode(): Int { - return toString().hashCode() - } - - public actual val isAbsolute: Boolean - get() = file.isAbsolute - public actual val name: String - get() = file.name -} - -@JvmField -public actual val SystemPathSeparator: Char = File.separatorChar - -public actual fun Path(path: String): Path = Path(File(path)) - -// Function only exists to provide binary compatibility with the earlier releases -@JvmName("source") -@PublishedApi -@Suppress("UNUSED") -internal fun Path.sourceHack(): Source = SystemFileSystem.source(this).buffered() - -// Function only exists to provide binary compatibility with the earlier releases -@JvmName("sink") -@PublishedApi -@Suppress("UNUSED") -internal fun Path.sinkHack(): Sink = SystemFileSystem.sink(this).buffered() diff --git a/core/jvm/src/unsafe/UnsafeBufferOperationsJvm.kt b/core/jvm/src/unsafe/UnsafeBufferOperationsJvm.kt deleted file mode 100644 index 683f6af75..000000000 --- a/core/jvm/src/unsafe/UnsafeBufferOperationsJvm.kt +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2010-2024 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.unsafe - -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind.EXACTLY_ONCE -import kotlin.contracts.contract -import kotlinx.io.Buffer -import kotlinx.io.Segment -import kotlinx.io.UnsafeIoApi -import kotlinx.io.unsafe.UnsafeBufferOperations.maxSafeWriteCapacity -import java.nio.ByteBuffer - -/** - * Provides read-only access to the data from the head of a [buffer] by calling the [readAction] on head's data and - * optionally consumes the data at the end of the action. - * - * The [readAction] receives a read-only [ByteBuffer] with buffer head's data. - * - * After exiting the [readAction], all data consumed from the [ByteBuffer] will be also consumed from the [buffer]. - * Consumed bytes determined as a difference between [ByteBuffer.capacity] and [ByteBuffer.remaining]. - * This value will also be propagated as the function return value. - * - * If [readAction] ends execution by throwing an exception, no data will be consumed from the buffer. - * - * If the [buffer] is empty, [IllegalArgumentException] will be thrown. - * - * The data is passed to the [readAction] directly from the buffer's internal storage without copying on - * the best effort basis, meaning that there are no strong zero-copy guarantees - * and the copy will be created if it could not be omitted. - * - * @param buffer a buffer to read from - * @param readAction an action that will be invoked on a [ByteBuffer] containing data from [buffer]'s head - * - * @return Number of bytes read by [readAction]. - * - * @throws IllegalArgumentException when the [buffer] is empty. - * - * @sample kotlinx.io.samples.unsafe.UnsafeReadWriteSamplesJvm.writeToByteChannel - */ -@UnsafeIoApi -@OptIn(ExperimentalContracts::class) -public inline fun UnsafeBufferOperations.readFromHead(buffer: Buffer, readAction: (ByteBuffer) -> Unit): Int { - contract { - callsInPlace(readAction, EXACTLY_ONCE) - } - return readFromHead(buffer) { rawData, pos, limit -> - val bb = ByteBuffer.wrap(rawData, pos, limit - pos).slice().asReadOnlyBuffer() - readAction(bb) - bb.position() - } -} - -/** - * Provides write access to the buffer, allowing to write data - * into a not yet committed portion of the buffer's tail using a [writeAction]. - * - * The [writeAction] receives a [ByteBuffer] representing uncommitted portion of [buffer]'s tail - * - * It's guaranteed that the size of the [ByteBuffer] is at least [minimumCapacity], - * but if the [minimumCapacity] bytes could not be provided for writing, - * the method will throw [IllegalStateException]. - * It is safe to use any [minimumCapacity] value below [maxSafeWriteCapacity], but unless exact minimum number of - * available bytes is required, it's recommended to use `1` as [minimumCapacity] value. - * - * After exiting [writeAction], bytes written to the [ByteBuffer] will be committed to the buffer. - * The number of bytes written is determined as a difference between [ByteBuffer.capacity] and [ByteBuffer.remaining]. - * This value will also be propagated as the function return value. - * - * If [writeAction] ends execution by throwing an exception, no data will be written to the buffer. - * - * The data array is passed to the [writeAction] directly from the buffer's internal storage without copying - * on the best-effort basis, meaning that there are no strong zero-copy guarantees - * and the copy will be created if it could not be omitted. - * - * @param buffer a buffer to read from - * @param minimumCapacity the minimum amount of writable space - * @param writeAction an action that will be invoked on a [ByteBuffer] that will be added to a [buffer] by the end of - * the call - * - * @return Number of bytes written by [writeAction]. - * - * @throws IllegalStateException when [minimumCapacity] is too large and could not be fulfilled. - * - * @sample kotlinx.io.samples.unsafe.UnsafeReadWriteSamplesJvm.readFromByteChannel - */ -@UnsafeIoApi -@OptIn(ExperimentalContracts::class) -public inline fun UnsafeBufferOperations.writeToTail( - buffer: Buffer, - minimumCapacity: Int, - writeAction: (ByteBuffer) -> Unit -): Int { - contract { - callsInPlace(writeAction, EXACTLY_ONCE) - } - return writeToTail(buffer, minimumCapacity) { rawData, pos, limit -> - val bb = ByteBuffer.wrap(rawData, pos, limit - pos).slice() - writeAction(bb) - bb.position() - } -} - -/** - * Provides read-only access to [buffer]'s data by filling provided [iovec] array with [ByteBuffer]'s representing - * [buffer]'s data, supplying it to [readAction] and consuming number of bytes returned by the [readAction]. - * - * If there's not enough space in [iovec] to fit all byte buffers, only a prefix of [buffer]'s data will be supplied to - * [readAction]. - * If the number of byte buffers available for read is less than [iovec]'s size, - * only a prefix of [iovec] will be filled. - * - * The second [readAction]'s parameter denotes the number of buffers supplied. - * - * The value returned by the [readAction] is interpreted as the number of consumed bytes. - * The size of the buffer will be reduced by that value, - * and the corresponding number of bytes from buffer's prefix will be no longer available for read. - * If data was not consumed, the [readAction] should return `0`. - * This value will also be propagated as the function return value. - * - * If [readAction] ends execution by throwing an exception, no data will be consumed from the buffer. - * - * If the [iovec] contains any references, it will be overridden during the call. - * - * If the [buffer] is empty, [IllegalArgumentException] will be thrown. - * - * The data is passed to the [readAction] directly from the buffer's internal storage without copying on - * the best effort basis, meaning that there are no strong zero-copy guarantees - * and the copy will be created if it could not be omitted. - * - * @param buffer a buffer to read from - * @param iovec a temporary array to store [ByteBuffer]s with data from [buffer]'s prefix - * @param readAction an action that will be invoked on an array filled with [ByteBuffer]s holding data from [buffer]'s - * prefix - * - * @return Number of bytes read as returned by [readAction]. - * - * @throws IllegalArgumentException when the [buffer] is empty. - * @throws IllegalArgumentException when the [iovec] is empty. - * - * @sample kotlinx.io.samples.unsafe.UnsafeReadWriteSamplesJvm.gatheringWrite - * - */ -@UnsafeIoApi -@OptIn(ExperimentalContracts::class) -public inline fun UnsafeBufferOperations.readBulk( - buffer: Buffer, - iovec: Array, - readAction: (iovec: Array, iovecSize: Int) -> Long -): Long { - contract { - callsInPlace(readAction, EXACTLY_ONCE) - } - - val head = buffer.head ?: throw IllegalArgumentException("buffer is empty.") - if (iovec.isEmpty()) throw IllegalArgumentException("iovec is empty.") - - var currentSegment: Segment = head - var idx = 0 - var capacity = 0L - do { - val pos = currentSegment.pos - val limit = currentSegment.limit - val len = limit - pos - iovec[idx++] = ByteBuffer.wrap(currentSegment.dataAsByteArray(true), pos, len) - .slice() - .asReadOnlyBuffer() - capacity += len - currentSegment = currentSegment.next ?: break - } while (idx < iovec.size) - - val bytesRead = readAction(iovec, idx) - if (bytesRead != 0L) { - if (bytesRead < 0 || bytesRead > capacity) { - throw IllegalStateException( - "readAction should return a value in range [0, $capacity], but returned: $bytesRead" - ) - } - buffer.skip(bytesRead) - } - return bytesRead -} diff --git a/core/jvm/test/AbstractSinkTestJVM.kt b/core/jvm/test/AbstractSinkTestJVM.kt deleted file mode 100644 index c143c151c..000000000 --- a/core/jvm/test/AbstractSinkTestJVM.kt +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -/* - * Copyright (C) 2014 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kotlinx.io - -import java.io.OutputStream -import java.nio.ByteBuffer -import java.nio.charset.Charset -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.text.Charsets.UTF_8 - -private const val SEGMENT_SIZE = Segment.SIZE - -class BufferSinkTestJVM : AbstractSinkTestJVM(SinkFactory.BUFFER) -class RealSinkTestJVM : AbstractSinkTestJVM(SinkFactory.REAL_BUFFERED_SINK) - -abstract class AbstractSinkTestJVM internal constructor(factory: SinkFactory) { - private val data: Buffer = Buffer() - private val sink: Sink = factory.create(data) - - @Test - fun outputStream() { - val out: OutputStream = sink.asOutputStream() - out.write('a'.code) - out.write("b".repeat(9998).toByteArray(UTF_8)) - out.write('c'.code) - out.flush() - assertEquals(("a" + "b".repeat(9998)) + "c", data.readString()) - } - - @Test - fun outputStreamBounds() { - val out: OutputStream = sink.asOutputStream() - assertFailsWith { - out.write(ByteArray(100), 50, 51) - } - } - - @Test - fun writeToClosedOutputStream() { - if (sink is Buffer) { - return - } - val out = sink.asOutputStream() - sink.close() - assertFailsWith { out.write(0) } - assertFailsWith { out.write(ByteArray(1)) } - assertFailsWith { out.write(ByteArray(42), 0, 1) } - } - - @Test - fun outputStreamClosesSink() { - if (sink is Buffer) { - return - } - - val out = sink.asOutputStream() - out.close() - assertFailsWith { sink.writeByte(0) } - } - - @Test - fun writeNioBuffer() { - val expected = "abcdefg" - val nioByteBuffer: ByteBuffer = ByteBuffer.allocate(1024) - nioByteBuffer.put("abcdefg".toByteArray(UTF_8)) - nioByteBuffer.flip() // Cast necessary for Java 8. - val byteCount: Int = sink.write(nioByteBuffer) - assertEquals(expected.length, byteCount) - assertEquals(expected.length, nioByteBuffer.position()) - assertEquals(expected.length, nioByteBuffer.limit()) - sink.flush() - assertEquals(expected, data.readString()) - } - - @Test - fun writeLargeNioBufferWritesAllData() { - val expected: String = "a".repeat(SEGMENT_SIZE * 3) - val nioByteBuffer: ByteBuffer = ByteBuffer.allocate(SEGMENT_SIZE * 4) - nioByteBuffer.put("a".repeat(SEGMENT_SIZE * 3).toByteArray(UTF_8)) - nioByteBuffer.flip() // Cast necessary for Java 8. - val byteCount: Int = sink.write(nioByteBuffer) - assertEquals(expected.length, byteCount) - assertEquals(expected.length, nioByteBuffer.position()) - assertEquals(expected.length, nioByteBuffer.limit()) - sink.flush() - assertEquals(expected, data.readString()) - } - - @Test - fun writeNioBufferToClosedSink() { - if (sink is Buffer) { - return - } - sink.close() - assertFailsWith { - sink.write(ByteBuffer.allocate(10)) - } - } - - @Test - fun writeStringWithCharset() { - sink.writeString("təˈranəˌsôr", Charset.forName("utf-32be")) - sink.flush() - val expected = "0000007400000259000002c800000072000000610000006e00000259000002cc00000073000000f400000072" - assertArrayEquals(expected.decodeHex(), data.readByteArray()) - } - - @Test - fun writeSubstringWithCharset() { - sink.writeString("təˈranəˌsôr", Charset.forName("utf-32be"), 3, 7) - sink.flush() - assertArrayEquals("00000072000000610000006e00000259".decodeHex(), data.readByteArray()) - } - - @Test - fun writeUtf8SubstringWithCharset() { - sink.writeString("təˈranəˌsôr", Charset.forName("utf-8"), 3, 7) - sink.flush() - assertArrayEquals("ranə".toByteArray(UTF_8), data.readByteArray()) - } -} diff --git a/core/jvm/test/AbstractSourceTestJVM.kt b/core/jvm/test/AbstractSourceTestJVM.kt deleted file mode 100644 index dec531d66..000000000 --- a/core/jvm/test/AbstractSourceTestJVM.kt +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -/* - * Copyright (C) 2014 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kotlinx.io - -import java.io.InputStream -import java.nio.Buffer -import java.nio.ByteBuffer -import java.nio.charset.Charset -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertTrue - -private const val SEGMENT_SIZE = Segment.SIZE - -class BufferSourceTestJVM : AbstractSourceTestJVM(SourceFactory.BUFFER) -class RealBufferedSourceTestJVM : AbstractSourceTestJVM(SourceFactory.REAL_BUFFERED_SOURCE) -class OneByteAtATimeBufferedSourceTestJVM : AbstractSourceTestJVM(SourceFactory.ONE_BYTE_AT_A_TIME_BUFFERED_SOURCE) -class OneByteAtATimeBufferTestJVM : AbstractSourceTestJVM(SourceFactory.ONE_BYTE_AT_A_TIME_BUFFER) -class PeekBufferTestJVM : AbstractSourceTestJVM(SourceFactory.PEEK_BUFFER) -class PeekBufferedSourceTestJVM : AbstractSourceTestJVM(SourceFactory.PEEK_BUFFERED_SOURCE) -abstract class AbstractSourceTestJVM(private val factory: SourceFactory) { - private var sink: Sink - private var source: Source - - init { - val pipe: SourceFactory.Pipe = factory.pipe() - sink = pipe.sink - source = pipe.source - } - - @Test - fun inputStream() { - sink.writeString("abc") - sink.emit() - val input: InputStream = source.asInputStream() - val bytes = byteArrayOf('z'.code.toByte(), 'z'.code.toByte(), 'z'.code.toByte()) - var read: Int = input.read(bytes) - if (factory.isOneByteAtATime) { - assertEquals(1, read) - assertByteArrayEquals("azz", bytes) - read = input.read(bytes) - assertEquals(1, read) - assertByteArrayEquals("bzz", bytes) - read = input.read(bytes) - assertEquals(1, read) - assertByteArrayEquals("czz", bytes) - } else { - assertEquals(3, read) - assertByteArrayEquals("abc", bytes) - } - assertEquals(-1, input.read()) - } - - @Test - fun inputStreamOffsetCount() { - sink.writeString("abcde") - sink.emit() - val input: InputStream = source.asInputStream() - val bytes = - byteArrayOf('z'.code.toByte(), 'z'.code.toByte(), 'z'.code.toByte(), 'z'.code.toByte(), 'z'.code.toByte()) - val read: Int = input.read(bytes, 1, 3) - if (factory.isOneByteAtATime) { - assertEquals(1, read) - assertByteArrayEquals("zazzz", bytes) - } else { - assertEquals(3, read) - assertByteArrayEquals("zabcz", bytes) - } - } - - @Test - fun inputStreamSkip() { - sink.writeString("abcde") - sink.emit() - val input: InputStream = source.asInputStream() - assertEquals(4, input.skip(4)) - assertEquals('e'.code, input.read()) - sink.writeString("abcde") - sink.emit() - assertEquals(5, input.skip(10)) // Try to skip too much. - assertEquals(0, input.skip(1)) // Try to skip when exhausted. - } - - @Test - fun inputStreamCharByChar() { - sink.writeString("abc") - sink.emit() - val input: InputStream = source.asInputStream() - assertEquals('a'.code, input.read()) - assertEquals('b'.code, input.read()) - assertEquals('c'.code, input.read()) - assertEquals(-1, input.read()) - } - - @Test - fun inputStreamBounds() { - sink.writeString("a".repeat(100)) - sink.emit() - val input: InputStream = source.asInputStream() - assertFailsWith { - input.read(ByteArray(100), 50, 51) - } - } - - @Test - fun inputStreamForClosedSource() { - if (source is kotlinx.io.Buffer) { - return - } - - sink.writeByte(0) - sink.emit() - - val input = source.asInputStream() - source.close() - assertFailsWith { input.read() } - assertFailsWith { input.read(ByteArray(1)) } - assertFailsWith { input.read(ByteArray(10), 0, 1) } - } - - @Test - fun inputStreamClosesSource() { - if (source is kotlinx.io.Buffer) { - return - } - - sink.writeByte(0) - sink.emit() - - val input = source.asInputStream() - input.close() - - assertFailsWith { source.readByte() } - } - - @Test - fun inputStreamAvailable() { - val input = source.asInputStream() - assertEquals(0, input.available()) - - sink.writeInt(42) - sink.emit() - assertTrue(source.request(4)) // fill the buffer - - assertEquals(4, input.available()) - - input.read() - assertEquals(3, input.available()) - - source.readByte() - assertEquals(2, input.available()) - - sink.writeByte(0) - sink.emit() - - val expectedBytes = if (source is kotlinx.io.Buffer) { - 3 - } else { - 2 - } - assertEquals(expectedBytes, input.available()) - } - - @Test - fun inputStreamAvailableForClosedSource() { - if (source is kotlinx.io.Buffer) { - return - } - - val input = source.asInputStream() - source.close() - - assertFailsWith { input.available() } - } - - @Test - fun readNioBuffer() { - val expected = if (factory.isOneByteAtATime) "a" else "abcdefg" - sink.writeString("abcdefg") - sink.emit() - val nioByteBuffer: ByteBuffer = ByteBuffer.allocate(1024) - val byteCount: Int = source.readAtMostTo(nioByteBuffer) - assertEquals(expected.length, byteCount) - assertEquals(expected.length, nioByteBuffer.position()) - assertEquals(nioByteBuffer.capacity(), nioByteBuffer.limit()) - (nioByteBuffer as Buffer).flip() // Cast necessary for Java 8. - val data = ByteArray(expected.length) - nioByteBuffer.get(data) - assertEquals(expected, String(data)) - } - - /** Note that this test crashes the VM on Android. */ - @Test - fun readLargeNioBufferOnlyReadsOneSegment() { - val expected: String = if (factory.isOneByteAtATime) "a" else "a".repeat(SEGMENT_SIZE) - sink.writeString("a".repeat(SEGMENT_SIZE * 4)) - sink.emit() - val nioByteBuffer: ByteBuffer = ByteBuffer.allocate(SEGMENT_SIZE * 3) - val byteCount: Int = source.readAtMostTo(nioByteBuffer) - assertEquals(expected.length, byteCount) - assertEquals(expected.length, nioByteBuffer.position()) - assertEquals(nioByteBuffer.capacity(), nioByteBuffer.limit()) - (nioByteBuffer as Buffer).flip() // Cast necessary for Java 8. - val data = ByteArray(expected.length) - nioByteBuffer.get(data) - assertEquals(expected, String(data)) - } - - @Test - fun readNioBufferFromEmptySource() { - assertEquals(-1, source.readAtMostTo(ByteBuffer.allocate(10))) - } - - @Test - fun readSpecificCharsetPartial() { - sink.write( - ("0000007600000259000002c80000006c000000e40000007300000259000002" + - "cc000000720000006100000070000000740000025900000072").decodeHex() - ) - sink.emit() - assertEquals("vəˈläsə", source.readString(7 * 4, Charset.forName("utf-32"))) - } - - @Test - fun readSpecificCharset() { - sink.write( - ("0000007600000259000002c80000006c000000e40000007300000259000002" + - "cc000000720000006100000070000000740000025900000072").decodeHex() - ) - - sink.emit() - assertEquals("vəˈläsəˌraptər", source.readString(Charset.forName("utf-32"))) - } - - @Test - fun readStringTooShortThrows() { - sink.writeString("abc", Charsets.US_ASCII) - sink.emit() - assertFailsWith { - source.readString(4, Charsets.US_ASCII) - } - assertEquals("abc", source.readString()) // The read shouldn't consume any data. - } -} diff --git a/core/jvm/test/BufferTest.kt b/core/jvm/test/BufferTest.kt deleted file mode 100644 index 693cf67ca..000000000 --- a/core/jvm/test/BufferTest.kt +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -/* - * Copyright (C) 2014 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package kotlinx.io - -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream -import java.io.InputStream -import java.nio.ByteBuffer -import java.util.* -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.text.Charsets.UTF_8 - -private const val SEGMENT_SIZE = Segment.SIZE - -class BufferTest { - @Test - fun copyToSpanningSegments() { - val source = Buffer() - source.writeString("a".repeat(SEGMENT_SIZE * 2)) - source.writeString("b".repeat(SEGMENT_SIZE * 2)) - val out = ByteArrayOutputStream() - source.copyTo(out, startIndex = 10L, endIndex = 10L + SEGMENT_SIZE * 3L) - assertEquals( - "a".repeat(SEGMENT_SIZE * 2 - 10) + "b".repeat(SEGMENT_SIZE + 10), - out.toString() - ) - assertEquals( - "a".repeat(SEGMENT_SIZE * 2) + "b".repeat(SEGMENT_SIZE * 2), - source.readString(SEGMENT_SIZE * 4L) - ) - } - - @Test - fun copyToSkippingSegments() { - val source = Buffer() - source.writeString("a".repeat(SEGMENT_SIZE * 2)) - source.writeString("b".repeat(SEGMENT_SIZE * 2)) - val out = ByteArrayOutputStream() - source.copyTo(out, startIndex = SEGMENT_SIZE * 2 + 1L, endIndex = SEGMENT_SIZE * 2 + 4L) - assertEquals("bbb", out.toString()) - assertEquals( - "a".repeat(SEGMENT_SIZE * 2) + "b".repeat(SEGMENT_SIZE * 2), - source.readString(SEGMENT_SIZE * 4L) - ) - } - - @Test - fun copyToStream() { - val buffer = Buffer().also { it.writeString("hello, world!") } - val out = ByteArrayOutputStream() - buffer.copyTo(out) - val outString = String(out.toByteArray(), UTF_8) - assertEquals("hello, world!", outString) - assertEquals("hello, world!", buffer.readString()) - } - - @Test - fun writeToSpanningSegments() { - val buffer = Buffer() - buffer.writeString("a".repeat(SEGMENT_SIZE * 2)) - buffer.writeString("b".repeat(SEGMENT_SIZE * 2)) - val out = ByteArrayOutputStream() - buffer.skip(10) - buffer.readTo(out, SEGMENT_SIZE * 3L) - assertEquals("a".repeat(SEGMENT_SIZE * 2 - 10) + "b".repeat(SEGMENT_SIZE + 10), out.toString()) - assertEquals("b".repeat(SEGMENT_SIZE - 10), buffer.readString(buffer.size)) - } - - @Test - fun writeToStream() { - val buffer = Buffer().also { it.writeString("hello, world!") } - val out = ByteArrayOutputStream() - buffer.readTo(out) - val outString = String(out.toByteArray(), UTF_8) - assertEquals("hello, world!", outString) - assertEquals(0, buffer.size) - } - - @Test - fun readFromStream() { - val input: InputStream = ByteArrayInputStream("hello, world!".toByteArray(UTF_8)) - val buffer = Buffer() - buffer.transferFrom(input) - val out = buffer.readString() - assertEquals("hello, world!", out) - } - - @Test - fun readFromSpanningSegments() { - val input: InputStream = ByteArrayInputStream("hello, world!".toByteArray(UTF_8)) - val buffer = Buffer().also { it.writeString("a".repeat(SEGMENT_SIZE - 10)) } - buffer.transferFrom(input) - val out = buffer.readString() - assertEquals("a".repeat(SEGMENT_SIZE - 10) + "hello, world!", out) - } - - @Test - fun readFromStreamWithCount() { - val input: InputStream = ByteArrayInputStream("hello, world!".toByteArray(UTF_8)) - val buffer = Buffer() - buffer.write(input, 10) - val out = buffer.readString() - assertEquals("hello, wor", out) - } - - @Test - fun readFromStreamThrowsEOFOnExhaustion() { - val input = ByteArrayInputStream("hello, world!".toByteArray(UTF_8)) - val buffer = Buffer() - assertFailsWith { - buffer.write(input, input.available() + 1L) - } - } - - @Test - fun readFromStreamWithNegativeBytesCount() { - assertFailsWith { - Buffer().write(ByteArrayInputStream(ByteArray(1)), -1) - } - } - - @Test - fun readFromDoesNotLeaveEmptyTailSegment() { - val buffer = Buffer() - buffer.transferFrom(ByteArrayInputStream(ByteArray(SEGMENT_SIZE))) - assertNoEmptySegments(buffer) - } - - @Test - fun bufferInputStreamByteByByte() { - val source = Buffer() - source.writeString("abc") - val input: InputStream = source.asInputStream() - assertEquals(3, input.available()) - assertEquals('a'.code, input.read()) - assertEquals('b'.code, input.read()) - assertEquals('c'.code, input.read()) - assertEquals(-1, input.read()) - assertEquals(0, input.available()) - } - - @Test - fun bufferInputStreamBulkReads() { - val source = Buffer() - source.writeString("abc") - val byteArray = ByteArray(4) - Arrays.fill(byteArray, (-5).toByte()) - val input: InputStream = source.asInputStream() - assertEquals(3, input.read(byteArray)) - assertEquals("[97, 98, 99, -5]", byteArray.contentToString()) - Arrays.fill(byteArray, (-7).toByte()) - assertEquals(-1, input.read(byteArray)) - assertEquals("[-7, -7, -7, -7]", byteArray.contentToString()) - } - - @Test - fun copyToOutputStream() { - val source = Buffer() - source.writeString("party") - - val target = Buffer() - source.copyTo(target.asOutputStream()) - assertEquals("party", target.readString()) - assertEquals("party", source.readString()) - } - - @Test - fun copyToOutputStreamWithStartIndex() { - val source = Buffer() - source.writeString("party") - - val target = Buffer() - source.copyTo(target.asOutputStream(), startIndex = 2) - assertEquals("rty", target.readString()) - assertEquals("party", source.readString()) - } - - @Test - fun copyToOutputStreamWithEndIndex() { - val source = Buffer() - source.writeString("party") - - val target = Buffer() - source.copyTo(target.asOutputStream(), endIndex = 3) - assertEquals("par", target.readString()) - assertEquals("party", source.readString()) - } - - @Test - fun copyToOutputStreamWithIndices() { - val source = Buffer() - source.writeString("party") - - val target = Buffer() - source.copyTo(target.asOutputStream(), startIndex = 1, endIndex = 4) - assertEquals("art", target.readString()) - assertEquals("party", source.readString()) - } - - @Test - fun copyToOutputStreamWithEmptyRange() { - val source = Buffer() - source.writeString("hello") - - val target = Buffer() - source.copyTo(target.asOutputStream(), startIndex = 1, endIndex = 1) - assertEquals("hello", source.readString()) - assertEquals("", target.readString()) - } - - @Test - fun readToOutputStream() { - val source = Buffer() - source.writeString("party") - - val target = Buffer() - source.readTo(target.asOutputStream()) - assertEquals("party", target.readString()) - assertEquals("", source.readString()) - } - - @Test - fun readToOutputStreamWithByteCount() { - val source = Buffer() - source.writeString("party") - - val target = Buffer() - source.readTo(target.asOutputStream(), byteCount = 3) - assertEquals("par", target.readString()) - assertEquals("ty", source.readString()) - } - - @Test - fun readEmptyBufferToByteBuffer() { - val bb = ByteBuffer.allocate(128) - val buffer = Buffer() - - assertEquals(-1, buffer.readAtMostTo(bb)) - } -} diff --git a/core/jvm/test/JvmPlatformTest.kt b/core/jvm/test/JvmPlatformTest.kt deleted file mode 100644 index ea6ee688e..000000000 --- a/core/jvm/test/JvmPlatformTest.kt +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -/* - * Copyright (C) 2018 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kotlinx.io - -import org.junit.jupiter.api.io.TempDir -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream -import java.io.File -import java.io.FileOutputStream -import java.io.IOException -import java.lang.IllegalArgumentException -import java.net.Socket -import java.nio.file.Files -import java.nio.file.LinkOption -import java.nio.file.StandardOpenOption -import kotlin.io.path.inputStream -import kotlin.io.path.outputStream -import kotlin.test.* - -class JvmPlatformTest { - @TempDir - lateinit var tempDir: File - - @Test - fun outputStreamSink() { - val baos = ByteArrayOutputStream() - val sink = baos.asSink() - sink.write(Buffer().also { it.writeString("a") }, 1L) - assertArrayEquals(baos.toByteArray(), byteArrayOf(0x61)) - } - - @Test - fun outputStreamSinkWriteZeroBytes() { - val baos = ByteArrayOutputStream() - val sink = baos.asSink() - sink.write(Buffer().also { it.writeString("a") }, 0L) - assertEquals(0, baos.size()) - } - - @Test - fun outputStreamSinkWriteNegativeNumberOfBytes() { - val baos = ByteArrayOutputStream() - val sink = baos.asSink() - assertFailsWith { - sink.write(Buffer().also { it.writeString("a") }, -1) - } - } - - @Test - fun outputStreamSinkWritePartOfTheBuffer() { - val baos = ByteArrayOutputStream() - val sink = baos.asSink() - val buffer = Buffer().also { it.writeString("hello") } - sink.write(buffer, 2) - assertArrayEquals(baos.toByteArray(), byteArrayOf('h'.code.toByte(), 'e'.code.toByte())) - assertEquals("llo", buffer.readString()) - } - - @Test - fun inputStreamSource() { - val bais = ByteArrayInputStream(byteArrayOf(0x61)) - val source = bais.asSource() - val buffer = Buffer() - source.readAtMostTo(buffer, 1) - assertEquals(buffer.readString(), "a") - } - - @Test - fun inputStreamSourceReadZeroBytes() { - val bais = ByteArrayInputStream(ByteArray(128)) - val source = bais.asSource() - val buffer = Buffer() - source.readAtMostTo(buffer, 0) - assertEquals(0, buffer.size) - } - - @Test - fun inputStreamSourceReadNegativeNumberOfBytes() { - val bais = ByteArrayInputStream(ByteArray(128)) - val source = bais.asSource() - assertFailsWith { source.readAtMostTo(Buffer(), -1) } - } - - @Test - fun fileSink() { - val file = File(tempDir, "test") - file.outputStream().asSink().use { sink -> - sink.write(Buffer().also { it.writeString("a") }, 1L) - } - assertEquals(file.readText(), "a") - } - - @Test - fun fileAppendingSink() { - val file = File(tempDir, "test") - file.writeText("a") - FileOutputStream(file, true).asSink().use { sink -> - sink.write(Buffer().also { it.writeString("b") }, 1L) - } - assertEquals(file.readText(), "ab") - } - - @Test - fun fileSource() { - val file = File(tempDir, "test") - file.writeText("a") - val buffer = Buffer() - file.inputStream().asSource().use { source -> - source.readAtMostTo(buffer, 1L) - } - assertEquals(buffer.readString(), "a") - } - - @Test - fun pathSink() { - val file = File(tempDir, "test") - file.toPath().outputStream().asSink().use { sink -> - sink.write(Buffer().also { it.writeString("a") }, 1L) - } - assertEquals(file.readText(), "a") - } - - @Test - fun pathSinkWithOptions() { - val file = File(tempDir, "test") - file.writeText("a") - file.toPath().outputStream(StandardOpenOption.APPEND).asSink().use { sink -> - sink.write(Buffer().also { it.writeString("b") }, 1L) - } - assertEquals(file.readText(), "ab") - } - - @Test - fun pathSource() { - val file = File(tempDir, "test") - file.writeText("a") - val buffer = Buffer() - file.toPath().inputStream().asSource().use { source -> - source.readAtMostTo(buffer, 1L) - } - assertEquals(buffer.readString(), "a") - } - - @Test - fun pathSourceWithOptions() { - val file = File(tempDir, "new.txt") - assertTrue(file.createNewFile()) - val link = File(tempDir, "link.txt") - try { - Files.createSymbolicLink(link.toPath(), file.toPath()) - } catch (e: UnsupportedOperationException) { - // the FS does not support symlinks - return - } - - assertFailsWith { - link.toPath().inputStream(LinkOption.NOFOLLOW_LINKS).asSource().use { it.buffered().readLine() } - } - assertNull(link.toPath().inputStream().asSource().use { it.buffered().readLine() }) - } - - @Test - fun socketSink() { - val baos = ByteArrayOutputStream() - val socket = object : Socket() { - override fun getOutputStream() = baos - } - val sink = socket.outputStream.asSink() - sink.write(Buffer().also { it.writeString("a") }, 1L) - assertArrayEquals(baos.toByteArray(), byteArrayOf(0x61)) - } - - @Test - fun socketSource() { - val bais = ByteArrayInputStream(byteArrayOf(0x61)) - val socket = object : Socket() { - override fun getInputStream() = bais - } - val source = socket.inputStream.asSource() - val buffer = Buffer() - source.readAtMostTo(buffer, 1L) - assertEquals(buffer.readString(), "a") - } -} diff --git a/core/jvm/test/NioTest.kt b/core/jvm/test/NioTest.kt deleted file mode 100644 index 96c59629d..000000000 --- a/core/jvm/test/NioTest.kt +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -/* - * Copyright (C) 2018 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kotlinx.io - -import org.junit.jupiter.api.io.TempDir -import java.io.File -import java.nio.ByteBuffer -import java.nio.channels.FileChannel -import java.nio.channels.ReadableByteChannel -import java.nio.channels.WritableByteChannel -import java.nio.file.Path -import java.nio.file.Paths -import java.nio.file.StandardOpenOption -import kotlin.io.path.createFile -import kotlin.io.path.inputStream -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertTrue -import kotlin.text.Charsets.UTF_8 - -/** Test interop with java.nio. */ -class NioTest { - @TempDir - lateinit var temporaryFolder: Path - - @Test - fun sourceIsOpen() { - val source = RealSource(Buffer()).asByteChannel() - assertTrue(source.isOpen()) - source.close() - assertFalse(source.isOpen()) - } - - @Test - fun sinkIsOpen() { - val sink = RealSink(Buffer()).asByteChannel() - assertTrue(sink.isOpen()) - sink.close() - assertFalse(sink.isOpen()) - } - - @Test - fun writableChannelNioFile() { - val file = Paths.get(temporaryFolder.toString(), "test").createFile() - val fileChannel: FileChannel = FileChannel.open(file, StandardOpenOption.WRITE) - testWritableByteChannel(false, fileChannel) - val emitted: Source = file.inputStream().asSource().buffered() - assertEquals("defghijklmnopqrstuvw", emitted.readString()) - emitted.close() - } - - @Test - fun writableChannelBuffer() { - val buffer = Buffer() - testWritableByteChannel(true, buffer.asByteChannel()) - assertEquals("defghijklmnopqrstuvw", buffer.readString()) - } - - @Test - fun writableChannelBufferedSink() { - val buffer = Buffer() - val bufferedSink: Sink = buffer - testWritableByteChannel(true, bufferedSink.asByteChannel()) - assertEquals("defghijklmnopqrstuvw", buffer.readString()) - } - - @Test - fun readableChannelNioFile() { - val file: File = Paths.get(temporaryFolder.toString(), "test").toFile() - val initialData: Sink = file.outputStream().asSink().buffered() - initialData.writeString("abcdefghijklmnopqrstuvwxyz") - initialData.close() - val fileChannel: FileChannel = FileChannel.open(file.toPath(), StandardOpenOption.READ) - testReadableByteChannel(false, fileChannel) - } - - @Test - fun readableChannelBuffer() { - val buffer = Buffer() - buffer.writeString("abcdefghijklmnopqrstuvwxyz") - testReadableByteChannel(true, buffer.asByteChannel()) - } - - @Test - fun readableChannelBufferedSource() { - val buffer = Buffer() - val bufferedSource: Source = buffer - buffer.writeString("abcdefghijklmnopqrstuvwxyz") - testReadableByteChannel(true, bufferedSource.asByteChannel()) - } - - /** - * Does some basic writes to `channel`. We execute this against both Okio's channels and - * also a standard implementation from the JDK to confirm that their behavior is consistent. - */ - private fun testWritableByteChannel(isBuffer: Boolean, channel: WritableByteChannel) { - assertTrue(channel.isOpen()) - val byteBuffer = ByteBuffer.allocate(1024) - byteBuffer.put("abcdefghijklmnopqrstuvwxyz".toByteArray(UTF_8)) - byteBuffer.flip() // Cast necessary for Java 8. - byteBuffer.position(3) // Cast necessary for Java 8. - byteBuffer.limit(23) // Cast necessary for Java 8. - val byteCount: Int = channel.write(byteBuffer) - assertEquals(20, byteCount) - assertEquals(23, byteBuffer.position()) - assertEquals(23, byteBuffer.limit()) - channel.close() - assertEquals(isBuffer, channel.isOpen) // Buffer.close() does nothing. - } - - /** - * Does some basic reads from `channel`. We execute this against both Okio's channels and - * also a standard implementation from the JDK to confirm that their behavior is consistent. - */ - private fun testReadableByteChannel(isBuffer: Boolean, channel: ReadableByteChannel) { - assertTrue(channel.isOpen) - val byteBuffer = ByteBuffer.allocate(1024) - byteBuffer.position(3) // Cast necessary for Java 8. - byteBuffer.limit(23) // Cast necessary for Java 8. - val byteCount: Int = channel.read(byteBuffer) - assertEquals(20, byteCount) - assertEquals(23, byteBuffer.position()) - assertEquals(23, byteBuffer.limit()) - channel.close() - assertEquals(isBuffer, channel.isOpen()) // Buffer.close() does nothing. - byteBuffer.flip() // Cast necessary for Java 8. - byteBuffer.position(3) // Cast necessary for Java 8. - val data = ByteArray(byteBuffer.remaining()) - byteBuffer[data] - assertEquals("abcdefghijklmnopqrst", String(data, UTF_8)) - } -} diff --git a/core/jvm/test/PoolingTest.kt b/core/jvm/test/PoolingTest.kt deleted file mode 100644 index 064cb539c..000000000 --- a/core/jvm/test/PoolingTest.kt +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io - -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit -import kotlin.random.Random -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertTrue - -class PoolingTest { - @Test - fun segmentSharing() { - val buffer = Buffer() - - buffer.writeByte(1) - var poolSize = SegmentPool.byteCount - buffer.clear() - // clear should return a segment to a pool, so the pool size should grow - assertTrue(poolSize < SegmentPool.byteCount) - - buffer.writeByte(1) - poolSize = SegmentPool.byteCount - val copy = buffer.copy() - buffer.clear() - copy.clear() - assertTrue(poolSize < SegmentPool.byteCount) - - buffer.writeByte(1) - poolSize = SegmentPool.byteCount - val peek = buffer.peek().buffered() - peek.readByte() - buffer.clear() - assertTrue(poolSize < SegmentPool.byteCount) - - buffer.writeByte(1) - poolSize = SegmentPool.byteCount - val otherBuffer = Buffer() - otherBuffer.write(buffer, buffer.size) - otherBuffer.clear() - assertTrue(poolSize < SegmentPool.byteCount) - } - - @Test - fun segmentAcquisitionAndRelease() { - val secondTierSize = SegmentPool.SECOND_LEVEL_POOL_TOTAL_SIZE - val firstTierSize = SegmentPool.MAX_SIZE - - // fill the pool by requiring max possible segments count and then - // releasing them all - val segments = mutableSetOf() - var size = 0 - while (size < secondTierSize + firstTierSize) { - val segment = SegmentPool.take() - size += segment.dataAsByteArray(false).size - segments.add(segment) - } - segments.forEach(SegmentPool::recycle) - - - // take the same number of segments again and check that nothing new was allocated - val segments2 = mutableSetOf() - size = 0 - while (size < secondTierSize + firstTierSize) { - val segment = SegmentPool.take() - size += segment.remainingCapacity - segments2.add(segment) - } - segments2.forEach(SegmentPool::recycle) - - assertEquals(segments, segments2) - } - - @Test - fun contendedUseTest() { - val threadsCount = Runtime.getRuntime().availableProcessors() * 3 - assertTrue(threadsCount >= SegmentPool.HASH_BUCKET_COUNT) - - val segmentsPerThread = SegmentPool.SECOND_LEVEL_POOL_TOTAL_SIZE / Segment.SIZE / threadsCount - val observedSegments = ConcurrentHashMap() - - val pool = Executors.newFixedThreadPool(threadsCount) - repeat(threadsCount) { - pool.submit { - repeat(10000) { - val segments = mutableListOf() - repeat(segmentsPerThread) { - val seg = SegmentPool.take() - segments.add(seg) - observedSegments[seg] = seg - } - segments.forEach { SegmentPool.recycle(it) } - } - } - } - pool.shutdown() - pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS) - - val maxPoolSize = - (SegmentPool.SECOND_LEVEL_POOL_TOTAL_SIZE + SegmentPool.MAX_SIZE * SegmentPool.HASH_BUCKET_COUNT) / Segment.SIZE - assertTrue(observedSegments.size <= maxPoolSize) - } - - @Test - fun contendedUseWithMixedOperationsTest() { - val threadsCount = Runtime.getRuntime().availableProcessors() * 3 - assertTrue(threadsCount >= SegmentPool.HASH_BUCKET_COUNT) - - val segmentsPerThread = SegmentPool.SECOND_LEVEL_POOL_TOTAL_SIZE / Segment.SIZE / threadsCount - val observedSegments = ConcurrentHashMap() - - val pool = Executors.newFixedThreadPool(threadsCount) - repeat(threadsCount) { - pool.submit { - repeat(10000) { - val segments = mutableListOf() - repeat(segmentsPerThread * 2) { - when (segments.size) { - 0 -> { - val seg = SegmentPool.take() - segments.add(seg) - observedSegments[seg] = seg - } - segmentsPerThread -> SegmentPool.recycle(segments.removeLast()) - else -> { - val rnd = Random.nextDouble() - // More segments we have, higher the probability to return one of them back - if (rnd > segments.size.toDouble() / segmentsPerThread) { - SegmentPool.recycle(segments.removeLast()) - } else { - val seg = SegmentPool.take() - segments.add(seg) - observedSegments[seg] = seg - } - } - } - - } - segments.forEach { SegmentPool.recycle(it) } - } - } - } - pool.shutdown() - pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS) - - val maxPoolSize = - (SegmentPool.SECOND_LEVEL_POOL_TOTAL_SIZE + SegmentPool.MAX_SIZE * SegmentPool.HASH_BUCKET_COUNT) / Segment.SIZE - assertTrue(observedSegments.size <= maxPoolSize) - } -} diff --git a/core/jvm/test/RefCounteringCopyTrackerTest.kt b/core/jvm/test/RefCounteringCopyTrackerTest.kt deleted file mode 100644 index 49192ea3c..000000000 --- a/core/jvm/test/RefCounteringCopyTrackerTest.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io - -import kotlin.test.Test -import kotlin.test.assertFalse -import kotlin.test.assertTrue - -class RefCounteringCopyTrackerTest { - @Test - fun stateTransition() { - val tracker = RefCountingCopyTracker() - assertFalse(tracker.shared) - - assertFalse(tracker.removeCopy()) - assertFalse(tracker.shared) - - tracker.addCopy() - assertTrue(tracker.shared) - assertTrue(tracker.removeCopy()) - assertFalse(tracker.shared) - - tracker.addCopy() - assertTrue(tracker.shared) - tracker.addCopy() - assertTrue(tracker.shared) - assertTrue(tracker.removeCopy()) - assertTrue(tracker.shared) - assertTrue(tracker.removeCopy()) - assertFalse(tracker.shared) - } -} diff --git a/core/jvm/test/files/SmokeFileTestWindowsJVM.kt b/core/jvm/test/files/SmokeFileTestWindowsJVM.kt deleted file mode 100644 index 76d6ec4bc..000000000 --- a/core/jvm/test/files/SmokeFileTestWindowsJVM.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2010-2024 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.files - -import kotlin.test.Test -import kotlin.test.assertEquals - -class SmokeFileTestWindowsJVM { - @Test - fun uncParent() { - if (!isWindows) return - assertEquals(Path("\\\\server"), Path("\\\\server\\share").parent) - assertEquals(Path("\\\\server\\share"), Path("\\\\server\\share\\dir").parent) - } -} diff --git a/core/jvm/test/samples/samplesJvm.kt b/core/jvm/test/samples/samplesJvm.kt deleted file mode 100644 index f139d96fd..000000000 --- a/core/jvm/test/samples/samplesJvm.kt +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.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.* - -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.writeString("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.readString()) - } - - @Test - fun bufferTransferToStream() { - val buffer = Buffer() - buffer.writeString("hello") - - val outputStream = ByteArrayOutputStream() - buffer.readTo(outputStream) - - assertTrue(buffer.exhausted()) - - val inputStream = ByteArrayInputStream(outputStream.toByteArray()) - buffer.transferFrom(inputStream) - - assertEquals("hello", buffer.readString()) - } - - @Test - fun writeInputStreamToBuffer() { - val inputStream = ByteArrayInputStream("hello!".encodeToByteArray()) - val buffer = Buffer() - - buffer.write(inputStream, 5) - assertEquals("hello", buffer.readString()) - } - - @Test - fun copyBufferToOutputStream() { - val buffer = Buffer() - buffer.writeString("string") - - val outputStream = ByteArrayOutputStream() - buffer.copyTo(outputStream, startIndex = 2, endIndex = 6) - - assertEquals("string", buffer.readString()) - 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.readString()) - 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)) - } -} diff --git a/core/jvm/test/samples/unsafeAccessSamplesJvm.kt b/core/jvm/test/samples/unsafeAccessSamplesJvm.kt deleted file mode 100644 index 298b75012..000000000 --- a/core/jvm/test/samples/unsafeAccessSamplesJvm.kt +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2010-2024 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.unsafe - -import kotlinx.io.Buffer -import kotlinx.io.UnsafeIoApi -import kotlinx.io.bytestring.ByteString -import kotlinx.io.bytestring.toHexString -import kotlinx.io.bytestring.unsafe.UnsafeByteStringApi -import kotlinx.io.bytestring.unsafe.UnsafeByteStringOperations -import kotlinx.io.readString -import kotlinx.io.unsafe.* -import kotlinx.io.writeString -import java.nio.ByteBuffer -import java.nio.channels.FileChannel -import java.nio.file.Files -import java.nio.file.StandardOpenOption -import java.security.MessageDigest -import kotlin.random.Random -import kotlin.test.* - -@OptIn(UnsafeIoApi::class) -class UnsafeReadWriteSamplesJvm { - - @Test - fun writeToByteChannel() { - val source = Buffer().apply { writeString("hello world") } - // Open a file channel to write into. - FileChannel.open( - Files.createTempFile(null, null), - StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.DELETE_ON_CLOSE - ).use { channel -> - // Write data into the channel until source buffer exhausted - while (!source.exhausted()) { - // Take a byte buffer holding source's data prefix and send it to the channel - UnsafeBufferOperations.readFromHead(source) { headByteBuffer: ByteBuffer -> - channel.write(headByteBuffer) - } - } - assertEquals(11, channel.size()) - } - } - - @Test - fun readFromByteChannel() { - val destination = Buffer() - - // Open a file channel - FileChannel.open( - Files.createTempFile(null, null), - StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.DELETE_ON_CLOSE - ).use { channel -> - // Write some data into it - channel.write(ByteBuffer.wrap("hello world".encodeToByteArray())) - // And reset a read position to the beginning of a file - channel.position(0) - - // Read data until a channel exhausted - var finished = false - do { - // Require a byte buffer to read data into. - // By the end of the call, - // all data written into that byte buffer will be appended to the destination buffer. - UnsafeBufferOperations.writeToTail(destination, 1) { tailByteBuffer: ByteBuffer -> - val bytesRead = channel.read(tailByteBuffer) - // If we read nothing, it's time to wrap up. - finished = bytesRead <= 0 - } - } while (!finished) - } - assertEquals("hello world", destination.readString()) - } - - @Test - fun gatheringWrite() { - // Pre allocate an array to hold byte buffers during readBulk call. - // Such an array should be reused across multiple readBulk calls to reduce the number of allocations. - val buffers = Array(16) { null } - - // A buffer to read from - val source = Buffer().apply { write(Random.nextBytes(64 * 1024)) } - // Write the source buffer's content into a file using file channel's gathering write - FileChannel.open( - Files.createTempFile(null, null), - StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.DELETE_ON_CLOSE - ).use { channel -> - // Continue writing until the source is exhausted - while (!source.exhausted()) { - // Take as many byte buffers as possible (it depends on the source's size and the length - // buffers array) and send it all to the channel. - UnsafeBufferOperations.readBulk(source, buffers) { bbs: Array, byteBuffersCount: Int -> - val bytesWritten = channel.write(bbs, 0, byteBuffersCount) - // Corresponding number of bytes will be consumed from the buffer by the end of readBulk call - bytesWritten - } - } - assertEquals(64 * 1024, channel.size()) - } - } - - @OptIn(UnsafeByteStringApi::class, ExperimentalStdlibApi::class) - @Test - fun messageDigest() { - fun Buffer.digest(algorithm: String): ByteString { - val md = MessageDigest.getInstance(algorithm) - // iterate over all segment and update data - UnsafeBufferOperations.forEachSegment(this) { ctx, segment -> - // when segment is null, we reached the end of a buffer - // access segment data without copying it - ctx.withData(segment) { data, startIndex, endIndex -> - md.update(data, startIndex, endIndex - startIndex) - } - // advance to the next segment - } - return UnsafeByteStringOperations.wrapUnsafe(md.digest()) - } - - val buffer = Buffer().also { it.writeString("hello world") } - assertEquals("5eb63bbbe01eeed093cb22bb8f5acdc3", buffer.digest("MD5").toHexString()) - } -} diff --git a/core/jvm/test/unsafe/UnsafeBufferOperationsJvmReadBulkTest.kt b/core/jvm/test/unsafe/UnsafeBufferOperationsJvmReadBulkTest.kt deleted file mode 100644 index 73cff0661..000000000 --- a/core/jvm/test/unsafe/UnsafeBufferOperationsJvmReadBulkTest.kt +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright 2010-2024 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.unsafe - -import kotlinx.io.* -import java.nio.ByteBuffer -import kotlin.test.* - -@OptIn(UnsafeIoApi::class) -class UnsafeBufferOperationsJvmReadBulkTest { - private class TestException : RuntimeException() - - @Test - fun callsInPlaceContract() { - val buffer = Buffer().apply { writeString("hello world") } - val array = Array(16) { null } - - val called: Boolean - UnsafeBufferOperations.readBulk(buffer, array) { _, _ -> - called = true - 0 - } - assertTrue(called) - } - - @Test - fun readAllFromEmptyBuffer() { - assertFailsWith { - UnsafeBufferOperations.readBulk(Buffer(), Array(1) { null }) { _, _ -> fail() } - } - } - - @Test - fun readUsingEmptyArray() { - assertFailsWith { - UnsafeBufferOperations.readBulk( - Buffer().apply { writeByte(0) }, - Array(0) { null }) { _, _ -> fail() } - } - } - - @Test - fun readSingleSegment() { - val buffer = Buffer().apply { writeString("hello world") } - val array = Array(16) { null } - - val read = UnsafeBufferOperations.readBulk(buffer, array) { arrayArg, iovecLen -> - assertSame(array, arrayArg) - assertEquals(1, iovecLen) - - val buf = arrayArg[0] - assertNotNull(buf) - assertEquals(11, buf.capacity()) - - val str = ByteArray(11).let { - buf.get(it) - it.decodeToString() - } - assertEquals("hello world", str) - - 11 - } - assertEquals(11L, read) - assertTrue(buffer.exhausted()) - } - - @Test - fun readSingleSegmentWithoutConsumingIt() { - val buffer = Buffer().apply { writeString("hello world") } - val array = Array(16) { null } - - val read = UnsafeBufferOperations.readBulk(buffer, array) { arrayArg, iovecLen -> - assertSame(array, arrayArg) - assertEquals(1, iovecLen) - - val buf = arrayArg[0] - assertNotNull(buf) - assertEquals(11, buf.capacity()) - - val str = ByteArray(11).let { - buf.get(it) - it.decodeToString() - } - assertEquals("hello world", str) - - 0 - } - assertEquals(0L, read) - assertEquals("hello world", buffer.readString()) - } - - @Test - fun readMultipleSegments() { - val buffer = Buffer().apply { - write(ByteArray(Segment.SIZE) { 1 }) - write(ByteArray(Segment.SIZE) { 2 }) - write(ByteArray(Segment.SIZE + 1) { 3 }) - } - val buffers = Array(16) { null } - UnsafeBufferOperations.readBulk(buffer, buffers) { array, iovecLen -> - assertSame(buffers, array) - assertEquals(4, iovecLen) - - assertEquals(Segment.SIZE, array[0]!!.remaining()) - val tmpBuffer = ByteArray(Segment.SIZE) - array[0]!!.get(tmpBuffer) - assertContentEquals(ByteArray(Segment.SIZE) { 1 }, tmpBuffer) - - assertEquals(Segment.SIZE, array[1]!!.remaining()) - array[1]!!.get(tmpBuffer) - assertContentEquals(ByteArray(Segment.SIZE) { 2 }, tmpBuffer) - - assertEquals(Segment.SIZE, array[2]!!.remaining()) - array[2]!!.get(tmpBuffer) - assertContentEquals(ByteArray(Segment.SIZE) { 3 }, tmpBuffer) - - assertEquals(1, array[3]!!.remaining()) - assertEquals(3, array[3]!!.get()) - - buffer.size - } - assertTrue(buffer.exhausted()) - } - - @Test - fun readMultipleSegmentsWithoutConsumingIt() { - val buffer = Buffer().apply { - write(ByteArray(Segment.SIZE) { 1 }) - write(ByteArray(Segment.SIZE) { 2 }) - write(ByteArray(Segment.SIZE + 1) { 3 }) - } - val buffers = Array(16) { null } - UnsafeBufferOperations.readBulk(buffer, buffers) { array, iovecLen -> - assertSame(buffers, array) - assertEquals(4, iovecLen) - - assertEquals(Segment.SIZE, array[0]!!.remaining()) - val tmpBuffer = ByteArray(Segment.SIZE) - array[0]!!.get(tmpBuffer) - assertContentEquals(ByteArray(Segment.SIZE) { 1 }, tmpBuffer) - - assertEquals(Segment.SIZE, array[1]!!.remaining()) - array[1]!!.get(tmpBuffer) - assertContentEquals(ByteArray(Segment.SIZE) { 2 }, tmpBuffer) - - assertEquals(Segment.SIZE, array[2]!!.remaining()) - array[2]!!.get(tmpBuffer) - assertContentEquals(ByteArray(Segment.SIZE) { 3 }, tmpBuffer) - - assertEquals(1, array[3]!!.remaining()) - assertEquals(3, array[3]!!.get()) - - 0 - } - assertEquals(Segment.SIZE * 3 + 1L, buffer.size) - } - - @Test - fun consumeBufferPartially() { - val buffer = Buffer().apply { - writeString("hello world") - } - UnsafeBufferOperations.readBulk(buffer, Array(1) { null }) { _, _ -> - 6 - } - assertEquals("world", buffer.readString()) - } - - @Test - fun consumeMultiSegmentBufferPartially() { - val buffer = Buffer().apply { - write(ByteArray(Segment.SIZE * 3)) - } - UnsafeBufferOperations.readBulk(buffer, Array(3) { null }) { _, _ -> - Segment.SIZE * 3 - 1111L - } - assertEquals(1111, buffer.size) - } - - @Test - fun passShortArray() { - val buffer = Buffer().apply { - write(ByteArray(Segment.SIZE * 2)) - } - UnsafeBufferOperations.readBulk(buffer, Array(1) { null }) { array, _ -> - array[0]!!.remaining().toLong() - } - assertEquals(Segment.SIZE.toLong(), buffer.size) - } - - @Test - fun returnIncorrectReadValue() { - val buffer = Buffer().apply { write(ByteArray(Segment.SIZE + 1)) } - val size = buffer.size - - assertFailsWith { - UnsafeBufferOperations.readBulk(buffer, Array(2) { null }) { _, _ -> -1L} - } - assertFailsWith { - UnsafeBufferOperations.readBulk(buffer, Array(2) { null }) { _, _ -> size + 1L } - } - assertFailsWith { - UnsafeBufferOperations.readBulk(buffer, Array(1) { null }) { _, _ -> size } - } - } - - @Test - fun resetReadOnException() { - val buffer = Buffer().apply { writeString("hello world") } - - val sizeBeforeRead = buffer.size - assertFailsWith { - UnsafeBufferOperations.readBulk(buffer, Array(1) { null }) { _, _ -> - throw TestException() - } - } - assertEquals(buffer.size, sizeBeforeRead) - } -} diff --git a/core/jvm/test/unsafe/UnsafeBufferOperationsJvmReadFromHeadTest.kt b/core/jvm/test/unsafe/UnsafeBufferOperationsJvmReadFromHeadTest.kt deleted file mode 100644 index 959d98ac3..000000000 --- a/core/jvm/test/unsafe/UnsafeBufferOperationsJvmReadFromHeadTest.kt +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2010-2024 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.unsafe - -import kotlinx.io.Buffer -import kotlinx.io.UnsafeIoApi -import kotlinx.io.assertArrayEquals -import kotlinx.io.writeString -import java.nio.ByteBuffer -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertTrue -import kotlin.test.fail - -@OptIn(UnsafeIoApi::class) -class UnsafeBufferOperationsJvmReadFromHeadTest { - private class TestException : RuntimeException() - - @Test - fun callsInPlaceContract() { - val buffer = Buffer().apply { writeString("hello world") } - - val called: Boolean - UnsafeBufferOperations.readFromHead(buffer) { _ -> - called = true - } - assertTrue(called) - } - - @Test - fun bufferCapacity() { - val buffer = Buffer().apply { writeString("hello world") } - - val head = buffer.head!! - UnsafeBufferOperations.readFromHead(buffer) { bb: ByteBuffer -> - assertEquals(head.size, bb.remaining()) - assertEquals(0, bb.position()) - assertEquals(head.size, bb.limit()) - } - } - - @Test - fun consumeByteByByte() { - val expectedData = "hello world".encodeToByteArray() - val actualData = ByteArray(expectedData.size) - - val buffer = Buffer().apply { write(expectedData) } - for (idx in actualData.indices) { - val read = UnsafeBufferOperations.readFromHead(buffer) { bb -> - actualData[idx] = bb.get() - } - assertEquals(1, read) - assertEquals(actualData.size - idx - 1, buffer.size.toInt()) - } - assertTrue(buffer.exhausted()) - assertArrayEquals(expectedData, actualData) - } - - @Test - fun readNothing() { - val buffer = Buffer().apply { writeInt(42) } - val read = UnsafeBufferOperations.readFromHead(buffer) { _ -> /* do nothing */ } - assertEquals(0, read) - assertEquals(42, buffer.readInt()) - } - - @Test - fun readEverything() { - val buffer = Buffer().apply { writeString("hello world") } - val read = UnsafeBufferOperations.readFromHead(buffer) { bb -> - bb.position(bb.limit()) - } - assertEquals(11, read) - assertTrue(buffer.exhausted()) - } - - @Test - fun writeIntoReadOnlyBuffer() { - val buffer = Buffer().apply { writeInt(42) } - UnsafeBufferOperations.readFromHead(buffer) { bb -> - assertFailsWith { - bb.put(42) - } - } - assertEquals(42, buffer.readInt()) - } - - @Test - fun readFromEmptyBuffer() { - val buffer = Buffer() - assertFailsWith { - UnsafeBufferOperations.readFromHead(buffer) { _ -> fail() } - } - } - - @Test - fun readFromTheSegmentEnd() { - val segmentSize = UnsafeBufferOperations.maxSafeWriteCapacity - val extraBytesCount = 128 - val bytesToSkip = segmentSize - 2 - - val buffer = Buffer().apply { write(ByteArray(segmentSize + extraBytesCount) { 0xff.toByte() }) } - buffer.skip(bytesToSkip.toLong()) - val head = buffer.head!! - assertEquals(bytesToSkip, head.pos) - - UnsafeBufferOperations.readFromHead(buffer) { bb -> - assertEquals(segmentSize - bytesToSkip, bb.remaining()) - bb.getShort() - } - - assertEquals(extraBytesCount, buffer.size.toInt()) - } - - @Test - fun changeLimit() { - val buffer = Buffer().apply { writeString("hello world") } - val read = UnsafeBufferOperations.readFromHead(buffer) { bb -> - // read a single byte only - bb.position(1) - bb.limit(2) - } - assertEquals(1, read) - assertEquals(10, buffer.size) - } - - @Test - fun resetReadOnException() { - val buffer = Buffer().apply { writeString("hello world") } - - val sizeBeforeRead = buffer.size - assertFailsWith { - UnsafeBufferOperations.readFromHead(buffer) { bb -> - bb.get() - throw TestException() - } - } - assertEquals(buffer.size, sizeBeforeRead) - } -} diff --git a/core/jvm/test/unsafe/UnsafeBufferOperationsJvmWriteToTailTest.kt b/core/jvm/test/unsafe/UnsafeBufferOperationsJvmWriteToTailTest.kt deleted file mode 100644 index 380264be8..000000000 --- a/core/jvm/test/unsafe/UnsafeBufferOperationsJvmWriteToTailTest.kt +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2010-2024 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.unsafe - -import kotlinx.io.* -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertTrue -import kotlin.test.fail - -@OptIn(UnsafeIoApi::class) -class UnsafeBufferOperationsJvmWriteToTailTest { - private class TestException : RuntimeException() - - @Test - fun callsInPlaceContract() { - val buffer = Buffer().apply { writeString("hello world") } - - val called: Boolean - UnsafeBufferOperations.writeToTail(buffer, 1) { _ -> - called = true - } - assertTrue(called) - } - - @Test - fun bufferCapacity() { - val buffer = Buffer() - - UnsafeBufferOperations.writeToTail(buffer, 1) { bb -> - // Unsafe check, head is not committed yet - assertEquals(buffer.head!!.remainingCapacity, bb.remaining()) - assertEquals(0, bb.position()) - assertEquals(buffer.head!!.remainingCapacity, bb.limit()) - } - } - - @Test - fun writeByteByByte() { - val buffer = Buffer() - val data = "hello world".encodeToByteArray() - - for (idx in data.indices) { - val written = UnsafeBufferOperations.writeToTail(buffer, 1) { bb -> - bb.put(data[idx]) - } - assertEquals(1, written) - assertEquals(idx + 1, buffer.size.toInt()) - } - assertEquals("hello world", buffer.readString()) - } - - @Test - fun writeNothing() { - val buffer = Buffer() - val written = UnsafeBufferOperations.writeToTail(buffer, 1) { _ -> } - assertEquals(0, written) - assertTrue(buffer.exhausted()) - } - - @Test - fun writeWholeBuffer() { - val buffer = Buffer() - val written = UnsafeBufferOperations.writeToTail(buffer, 1) { bb -> - bb.position(bb.limit()) - } - assertEquals(Segment.SIZE, written) - assertEquals(Segment.SIZE, buffer.size.toInt()) - } - - @Test - fun requireToManyBytes() { - val buffer = Buffer() - assertFailsWith { - UnsafeBufferOperations.writeToTail(buffer, 100500) { _ -> fail() } - } - assertTrue(buffer.exhausted()) - } - - @Test - fun writeToTheEndOfABuffer() { - val buffer = Buffer().apply { write(ByteArray(Segment.SIZE - 1)) } - UnsafeBufferOperations.writeToTail(buffer, 1) { bb -> - assertEquals(1, bb.remaining()) - bb.put(42) - } - assertEquals(Segment.SIZE, buffer.size.toInt()) - UnsafeBufferOperations.writeToTail(buffer, 1) { bb -> - bb.put(43) - } - assertEquals(Segment.SIZE + 1, buffer.size.toInt()) - - buffer.skip(Segment.SIZE - 1L) - assertArrayEquals(byteArrayOf(42, 43), buffer.readByteArray()) - } - - @Test - fun changeLimit() { - val buffer = Buffer() - - val written = UnsafeBufferOperations.writeToTail(buffer, 8) { bb -> - // only two bytes written - bb.position(2) - bb.limit(4) - } - assertEquals(2, written) - assertEquals(2, buffer.size) - } - - @Test - fun resetWriteOnException() { - val buffer = Buffer() - - assertFailsWith { - UnsafeBufferOperations.writeToTail(buffer, 2) { bb -> - bb.put(42) - throw TestException() - } - } - - assertTrue(buffer.exhausted()) - } -} diff --git a/core/jvm/test/utilJVM.kt b/core/jvm/test/utilJVM.kt deleted file mode 100644 index a17d5ea4b..000000000 --- a/core/jvm/test/utilJVM.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ -package kotlinx.io - -import kotlinx.io.files.SystemTemporaryDirectory -import java.io.File -import kotlin.random.Random -import kotlin.test.assertEquals - -@OptIn(ExperimentalStdlibApi::class) -actual fun tempFileName(): String { - val tmpDir = SystemTemporaryDirectory.file - while (true) { - val randomString = Random.nextBytes(32).toHexString() - val res = File(tmpDir, randomString) - if (!res.exists()) { - return res.absolutePath - } - } -} - -fun assertByteArrayEquals(expectedUtf8: String, b: ByteArray) { - assertEquals(expectedUtf8, b.toString(Charsets.UTF_8)) -} - -internal actual fun String.asUtf8ToByteArray(): ByteArray = toByteArray(Charsets.UTF_8) diff --git a/core/linux/src/files/FileSystemLinux.kt b/core/linux/src/files/FileSystemLinux.kt deleted file mode 100644 index 5c46e71f4..000000000 --- a/core/linux/src/files/FileSystemLinux.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.files - -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.cstr -import kotlinx.cinterop.memScoped -import kotlinx.cinterop.toKString -import platform.posix.__xpg_basename -import platform.posix.dirname - -@OptIn(ExperimentalForeignApi::class) -internal actual fun dirnameImpl(path: String): String { - if (!path.contains(SystemPathSeparator)) { - return "" - } - memScoped { - return dirname(path.cstr.ptr)?.toKString() ?: "" - } -} - -@OptIn(ExperimentalForeignApi::class) -internal actual fun basenameImpl(path: String): String { - memScoped { - return __xpg_basename(path.cstr.ptr)?.toKString() ?: "" - } -} - -internal actual fun isAbsoluteImpl(path: String): Boolean = path.startsWith('/') diff --git a/core/mingw/src/files/FileSystemMingw.kt b/core/mingw/src/files/FileSystemMingw.kt deleted file mode 100644 index e7ad2cc8a..000000000 --- a/core/mingw/src/files/FileSystemMingw.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -@file:OptIn(ExperimentalForeignApi::class) - -package kotlinx.io.files - -import kotlinx.cinterop.* -import kotlinx.io.IOException -import platform.posix.* -import platform.windows.* - -internal actual fun atomicMoveImpl(source: Path, destination: Path) { - if (MoveFileExA(source.path, destination.path, MOVEFILE_REPLACE_EXISTING.convert()) == 0) { - // TODO: get formatted error message - throw IOException("Move failed with error code: ${GetLastError()}") - } -} - -internal actual fun dirnameImpl(path: String): String { - if (!path.contains(UnixPathSeparator) && !path.contains(WindowsPathSeparator)) { - return "" - } - memScoped { - return dirname(path.cstr.ptr)?.toKString() ?: "" - } -} - -internal actual fun basenameImpl(path: String): String { - memScoped { - return basename(path.cstr.ptr)?.toKString() ?: "" - } -} - -internal actual fun isAbsoluteImpl(path: String): Boolean { - if (path.startsWith(SystemPathSeparator)) return true - if (path.length > 1 && path[1] == ':') { - if (path.length == 2) return false - val next = path[2] - return next == WindowsPathSeparator || next == SystemPathSeparator - } - return PathIsRelativeA(path) == 0 -} - -internal actual fun mkdirImpl(path: String) { - if (mkdir(path) != 0) { - throw IOException("mkdir failed: ${strerror(errno)?.toKString()}") - } -} - -private const val MAX_PATH_LENGTH = 32767 - -internal actual fun realpathImpl(path: String): String { - memScoped { - val buffer = allocArray(MAX_PATH_LENGTH) - val len = GetFullPathNameA(path, MAX_PATH_LENGTH.convert(), buffer, null) - if (len == 0u) throw IllegalStateException() - return buffer.toKString() - } -} diff --git a/core/mingw/test/files/SmokeFileTestWindowsMinGW.kt b/core/mingw/test/files/SmokeFileTestWindowsMinGW.kt deleted file mode 100644 index 2dcf04021..000000000 --- a/core/mingw/test/files/SmokeFileTestWindowsMinGW.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2010-2024 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.files - -import kotlin.test.Test -import kotlin.test.assertEquals - -class SmokeFileTestWindowsMinGW { - @Test - fun uncParent() { - assertEquals(Path("\\\\server"), Path("\\\\server\\share").parent) - assertEquals(Path("\\\\server\\share"), Path("\\\\server\\share\\dir").parent) - } -} diff --git a/core/native/src/-NonJvmPlatform.kt b/core/native/src/-NonJvmPlatform.kt deleted file mode 100644 index 8a06214ff..000000000 --- a/core/native/src/-NonJvmPlatform.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2018 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package kotlinx.io - -public actual open class IOException : Exception { - public actual constructor() : super() - - public actual constructor(message: String?) : super(message) - - public actual constructor(cause: Throwable?) : super(cause) - - public actual constructor(message: String?, cause: Throwable?) : super(message, cause) -} - -public actual open class EOFException : IOException { - public actual constructor() : super() - - public actual constructor(message: String?) : super(message) -} diff --git a/core/native/src/RawSink.kt b/core/native/src/RawSink.kt deleted file mode 100644 index 93ee6dd1d..000000000 --- a/core/native/src/RawSink.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package kotlinx.io - -public actual interface RawSink : AutoCloseable { - public actual fun write(source: Buffer, byteCount: Long) - - public actual fun flush() - - actual override fun close() -} diff --git a/core/native/src/SegmentPool.kt b/core/native/src/SegmentPool.kt deleted file mode 100644 index e27321533..000000000 --- a/core/native/src/SegmentPool.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -/* - * Copyright (C) 2014 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package kotlinx.io - -internal actual object SegmentPool { - actual val MAX_SIZE: Int = 0 - - actual val byteCount: Int = 0 - - actual fun take(): Segment = Segment.new() - - actual fun recycle(segment: Segment) { - } - - actual fun tracker(): SegmentCopyTracker = AlwaysSharedCopyTracker -} diff --git a/core/native/src/files/FileSystemNative.kt b/core/native/src/files/FileSystemNative.kt deleted file mode 100644 index 1fa719b15..000000000 --- a/core/native/src/files/FileSystemNative.kt +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.files - -import kotlinx.cinterop.CPointer -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.toKString -import kotlinx.io.IOException -import kotlinx.io.RawSink -import kotlinx.io.RawSource -import platform.posix.* -import kotlin.experimental.ExperimentalNativeApi - -@OptIn(ExperimentalForeignApi::class) -public actual val SystemFileSystem: FileSystem = object : SystemFileSystemImpl() { - override fun exists(path: Path): Boolean { - return access(path.path, F_OK) == 0 - } - - @OptIn(ExperimentalForeignApi::class) - override fun delete(path: Path, mustExist: Boolean) { - if (!exists(path)) { - if (mustExist) { - throw FileNotFoundException("File does not exist: $path") - } - return - } - if (remove(path.path) != 0) { - if (errno == EACCES) { - if (rmdir(path.path) == 0) return - } - throw IOException("Delete failed for $path: ${strerror(errno)?.toKString()}") - } - } - - override fun createDirectories(path: Path, mustCreate: Boolean) { - val metadata = metadataOrNull(path) - if (metadata != null) { - if (mustCreate) { - throw IOException("Path already exists: $path") - } - if (metadata.isRegularFile) { - throw IOException("Path already exists and it's a file: $path") - } - return - } - val paths = arrayListOf() - var p: Path? = path - while (p != null && !exists(p)) { - paths.add(p.toString()) - p = p.parent - } - paths.asReversed().forEach { - mkdirImpl(it) - } - } - - override fun atomicMove(source: Path, destination: Path) { - if (!exists(source)) { - throw FileNotFoundException("Source does not exist: ${source.path}") - } - atomicMoveImpl(source, destination) - } - - override fun metadataOrNull(path: Path): FileMetadata? = metadataOrNullImpl(path) - - override fun resolve(path: Path): Path { - if (!exists(path)) throw FileNotFoundException(path.path) - return Path(realpathImpl(path.path)) - } - - override fun source(path: Path): RawSource { - val openFile: CPointer? = fopen(path.path, "rb") - if (openFile == null) { - if (errno == ENOENT) { - throw FileNotFoundException("File does not exist: $path") - } - throw IOException("Failed to open $path with ${strerror(errno)?.toKString()}") - } - return FileSource(openFile) - } - - override fun sink(path: Path, append: Boolean): RawSink { - val openFile: CPointer = fopen(path.path, if (append) "ab" else "wb") - ?: throw IOException("Failed to open $path with ${strerror(errno)?.toKString()}") - return FileSink(openFile) - } - - override fun list(directory: Path): Collection { - val metadata = metadataOrNull(directory) ?: throw FileNotFoundException(directory.path) - if (!metadata.isDirectory) throw IOException("Not a directory: ${directory.path}") - return buildList { - opendir(directory.path).use { - var child = it.readdir() - while (child != null) { - if (child != "." && child != "..") { - add(Path(directory, child)) - } - child = it.readdir() - } - } - } - } -} - -internal expect fun metadataOrNullImpl(path: Path): FileMetadata? - -internal expect fun atomicMoveImpl(source: Path, destination: Path) - -internal expect fun mkdirImpl(path: String) - -internal expect fun realpathImpl(path: String): String - -public actual open class FileNotFoundException actual constructor( - message: String? -) : IOException(message) - -// 777 in octal, rwx for all (owner, group and others). -internal const val PermissionAllowAll: UShort = 511u - -@OptIn(ExperimentalNativeApi::class) -internal actual val isWindows: Boolean = Platform.osFamily == OsFamily.WINDOWS - -internal expect class OpaqueDirEntry : AutoCloseable { - fun readdir(): String? - override fun close() -} - -internal expect fun opendir(path: String): OpaqueDirEntry diff --git a/core/native/src/files/PathsNative.kt b/core/native/src/files/PathsNative.kt deleted file mode 100644 index 9e2f4a2a7..000000000 --- a/core/native/src/files/PathsNative.kt +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -@file:OptIn(ExperimentalForeignApi::class) - -package kotlinx.io.files - -import kotlinx.cinterop.* -import kotlinx.io.* -import platform.posix.* - -/* - * The very base skeleton just to play around - */ - -public actual class Path internal constructor( - rawPath: String, - @Suppress("UNUSED_PARAMETER") any: Any? -) { - internal val path = removeTrailingSeparators(rawPath) - - public actual val parent: Path? - get() { - when { - path.isEmpty() -> return null - } - val parentName = dirnameImpl(path) - return when { - parentName.isEmpty() -> return null - parentName == path -> return null - else -> Path(parentName) - } - } - - public actual override fun toString(): String = path - actual override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is Path) return false - - return path == other.path - } - - actual override fun hashCode(): Int { - return path.hashCode() - } - - public actual val isAbsolute: Boolean = isAbsoluteImpl(path) - public actual val name: String - get() { - if (path.isEmpty() || path == SystemPathSeparator.toString()) return "" - return basenameImpl(path) - } -} - -public actual val SystemPathSeparator: Char get() = UnixPathSeparator - -internal expect fun dirnameImpl(path: String): String - -internal expect fun basenameImpl(path: String): String - -internal expect fun isAbsoluteImpl(path: String): Boolean - -public actual fun Path(path: String): Path = Path(path, null) - -internal class FileSource( - private val file: CPointer -) : RawSource { - private var closed = false - - override fun readAtMostTo( - sink: Buffer, - byteCount: Long - ): Long { - val temporaryBuffer = ByteArray(byteCount.toInt()) - - // Copy bytes from the file to the segment. - val bytesRead = temporaryBuffer.usePinned { pinned -> - variantFread(pinned.addressOf(0), byteCount.toUInt(), file).toLong() - } - - sink.write(temporaryBuffer, 0, bytesRead.toInt()) - - return when { - bytesRead == byteCount -> bytesRead - feof(file) != 0 -> if (bytesRead == 0L) -1L else bytesRead - ferror(file) != 0 -> throw IOException(errno.toString()) - else -> bytesRead - } - } - - override fun close() { - if (closed) return - closed = true - fclose(file) - } -} - -@OptIn(UnsafeNumber::class) -internal fun variantFread( - target: CPointer>, - byteCount: UInt, - file: CPointer -): UInt = fread(target, 1u, byteCount.convert(), file).convert() - -@OptIn(UnsafeNumber::class) -internal fun variantFwrite( - source: CPointer, - byteCount: UInt, - file: CPointer -): UInt = fwrite(source, 1u, byteCount.convert(), file).convert() - -internal class FileSink( - private val file: CPointer -) : RawSink { - private var closed = false - - override fun write( - source: Buffer, - byteCount: Long - ) { - require(byteCount >= 0L) { "byteCount: $byteCount" } - require(source.size >= byteCount) { "source.size=${source.size} < byteCount=$byteCount" } - check(!closed) { "closed" } - - val allContent = source.readByteArray(byteCount.toInt()) - // Copy bytes from that segment into the file. - val bytesWritten = allContent.usePinned { pinned -> - variantFwrite(pinned.addressOf(0), byteCount.toUInt(), file).toLong() - } - if (bytesWritten < byteCount) { - throw IOException(errno.toString()) - } - } - - override fun flush() { - if (fflush(file) != 0) { - throw IOException(errno.toString()) - } - } - - override fun close() { - if (closed) return - closed = true - if (fclose(file) != 0) { - throw IOException(errno.toString()) - } - } -} diff --git a/core/native/test/util.kt b/core/native/test/util.kt deleted file mode 100644 index f8f50c5fd..000000000 --- a/core/native/test/util.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -@file:OptIn(kotlinx.cinterop.ExperimentalForeignApi::class) - -package kotlinx.io - -import kotlinx.io.files.SystemTemporaryDirectory -import platform.posix.F_OK -import platform.posix.access -import kotlin.random.Random - -@OptIn(ExperimentalStdlibApi::class) -actual fun tempFileName(): String { - val tmpDir = SystemTemporaryDirectory.path - for (i in 0 until 10) { - val name = Random.nextBytes(32).toHexString() - val path = "$tmpDir/$name" - if (access(path, F_OK) != 0) { - return path - } - } - throw IOException("Failed to generate temp file name") -} - -internal actual fun String.asUtf8ToByteArray(): ByteArray = commonAsUtf8ToByteArray() diff --git a/core/nativeNonAndroid/src/files/FileSystemNativeNonAndroid.kt b/core/nativeNonAndroid/src/files/FileSystemNativeNonAndroid.kt deleted file mode 100644 index f36286345..000000000 --- a/core/nativeNonAndroid/src/files/FileSystemNativeNonAndroid.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2010-2024 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.files - -import kotlinx.cinterop.CPointer -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.get -import kotlinx.cinterop.toKString -import kotlinx.io.IOException -import platform.posix.DIR -import platform.posix.closedir -import platform.posix.errno -import platform.posix.strerror - -@OptIn(ExperimentalForeignApi::class) -internal actual class OpaqueDirEntry(private val dir: CPointer) : AutoCloseable { - actual fun readdir(): String? { - val entry = platform.posix.readdir(dir) ?: return null - return entry[0].d_name.toKString() - } - - actual override fun close() { - closedir(dir) - } -} - -@OptIn(ExperimentalForeignApi::class) -internal actual fun opendir(path: String): OpaqueDirEntry { - val dirent = platform.posix.opendir(path) - if (dirent != null) return OpaqueDirEntry(dirent) - throw IOException("Can't open directory $path: ${strerror(errno)?.toKString() ?: "reason unknown"}") -} diff --git a/core/nativeNonApple/src/files/FileSystemNativeNonApple.kt b/core/nativeNonApple/src/files/FileSystemNativeNonApple.kt deleted file mode 100644 index 631aea1a2..000000000 --- a/core/nativeNonApple/src/files/FileSystemNativeNonApple.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.files - -import kotlinx.cinterop.* -import kotlinx.io.IOException -import platform.posix.* - -@OptIn(ExperimentalForeignApi::class) -public actual val SystemTemporaryDirectory: Path - get() = Path(getenv("TMPDIR")?.toKString() ?: getenv("TMP")?.toKString() ?: "") - -@OptIn(ExperimentalForeignApi::class, UnsafeNumber::class) -internal actual fun metadataOrNullImpl(path: Path): FileMetadata? { - memScoped { - val struct_stat = alloc() - if (stat(path.path, struct_stat.ptr) != 0) { - if (errno == ENOENT) return null - throw IOException("stat failed to ${path.path}: ${strerror(errno)?.toKString()}") - } - val mode = struct_stat.st_mode.toInt() - val isFile = (mode and S_IFMT) == S_IFREG - return FileMetadata( - isRegularFile = isFile, - isDirectory = (mode and S_IFMT) == S_IFDIR, - if (isFile) struct_stat.st_size.toLong() else -1L - ) - } -} diff --git a/core/nodeFilesystemShared/src/files/FileSystemNodeJs.kt b/core/nodeFilesystemShared/src/files/FileSystemNodeJs.kt deleted file mode 100644 index 810b5ba02..000000000 --- a/core/nodeFilesystemShared/src/files/FileSystemNodeJs.kt +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.files - -import kotlinx.io.IOException -import kotlinx.io.RawSink -import kotlinx.io.RawSource -import kotlinx.io.node.fs -import kotlinx.io.node.os -import kotlinx.io.withCaughtException - -public actual val SystemFileSystem: FileSystem = object : SystemFileSystemImpl() { - override fun exists(path: Path): Boolean { - return fs.existsSync(path.path) - } - - override fun delete(path: Path, mustExist: Boolean) { - if (!exists(path)) { - if (mustExist) { - throw FileNotFoundException("File does not exist: $path") - } - return - } - withCaughtException { - val stats = fs.statSync(path.path) ?: throw FileNotFoundException("File does not exist: $path") - if (stats.isDirectory()) { - fs.rmdirSync(path.path) - } else { - fs.rmSync(path.path) - } - }?.also { - throw IOException("Delete failed for $path", it) - } - } - - override fun createDirectories(path: Path, mustCreate: Boolean) { - val metadata = metadataOrNull(path) - if (metadata != null) { - if (mustCreate) { - throw IOException("Path already exists: $path") - } - if (metadata.isRegularFile) { - throw IOException("Path already exists and it's a file: $path") - } - return - } - - val parts = arrayListOf() - var p: Path? = path - while (p != null && !exists(p)) { - parts.add(p.toString()) - p = p.parent - } - parts.asReversed().forEach { - fs.mkdirSync(it) - } - } - - override fun atomicMove(source: Path, destination: Path) { - if (!exists(source)) { - throw FileNotFoundException("Source does not exist: ${source.path}") - } - withCaughtException { - fs.renameSync(source.path, destination.path) - }?.also { - throw IOException("Move failed from $source to $destination", it) - } - } - - override fun metadataOrNull(path: Path): FileMetadata? { - if (!exists(path)) return null - var metadata: FileMetadata? = null - withCaughtException { - val stat = fs.statSync(path.path) ?: return@withCaughtException - val mode = stat.mode - val isFile = (mode and fs.constants.S_IFMT) == fs.constants.S_IFREG - metadata = FileMetadata( - isRegularFile = isFile, - isDirectory = (mode and fs.constants.S_IFMT) == fs.constants.S_IFDIR, - if (isFile) stat.size.toLong() else -1L - ) - }?.also { - throw IOException("Stat failed for $path", it) - } - return metadata - } - - override fun source(path: Path): RawSource { - return FileSource(path) - } - - override fun sink(path: Path, append: Boolean): RawSink { - return FileSink(path, append) - } - - override fun resolve(path: Path): Path { - if (!exists(path)) throw FileNotFoundException(path.path) - return Path(fs.realpathSync.native(path.path)) - } - - override fun list(directory: Path): Collection { - val metadata = metadataOrNull(directory) ?: throw FileNotFoundException(directory.path) - if (!metadata.isDirectory) throw IOException("Not a directory: ${directory.path}") - val dir = fs.opendirSync(directory.path) ?: throw IOException("Unable to read directory: ${directory.path}") - try { - return buildList { - var child = dir.readSync() - while (child != null) { - add(Path(directory, child.name)) - child = dir.readSync() - } - } - } finally { - dir.closeSync() - } - } -} - -public actual val SystemTemporaryDirectory: Path - get() { - return Path(os.tmpdir() ?: "") - } - -public actual open class FileNotFoundException actual constructor( - message: String?, -) : IOException(message) - -internal actual val isWindows = os.platform() == "win32" diff --git a/core/nodeFilesystemShared/src/files/PathsNodeJs.kt b/core/nodeFilesystemShared/src/files/PathsNodeJs.kt deleted file mode 100644 index a612c352a..000000000 --- a/core/nodeFilesystemShared/src/files/PathsNodeJs.kt +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.files - -import kotlinx.io.* -import kotlinx.io.node.buffer -import kotlinx.io.node.fs -import kotlinx.io.unsafe.UnsafeBufferOperations -import kotlinx.io.node.path as nodeJsPath - -public actual class Path internal constructor( - rawPath: String, - @Suppress("UNUSED_PARAMETER") any: Any? -) { - internal val path: String = removeTrailingSeparators(rawPath) - - public actual val parent: Path? - get() { - if (path.isEmpty()) return null - if (isWindows) { - if (!path.contains(UnixPathSeparator) && !path.contains(WindowsPathSeparator)) { - return null - } - } else if (!path.contains(SystemPathSeparator)) { - return null - } - val p = nodeJsPath.dirname(path) - return when { - p.isEmpty() -> null - p == path -> null - else -> Path(p) - } - } - - public actual val isAbsolute: Boolean - get() { - return nodeJsPath.isAbsolute(path) - } - - public actual val name: String - get() { - when { - path.isEmpty() -> return "" - } - val p = nodeJsPath.basename(path) - return when { - p.isEmpty() -> "" - else -> p - } - } - - public actual override fun toString(): String = path - - actual override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is Path) return false - - return path == other.path - } - - actual override fun hashCode(): Int { - return path.hashCode() - } -} - -public actual val SystemPathSeparator: Char by lazy { - val sep = nodeJsPath.sep - check(sep.length == 1) - sep[0] -} - -public actual fun Path(path: String): Path { - return Path(path, null) -} - -internal class FileSource(private val path: Path) : RawSource { - private var buffer: kotlinx.io.node.Buffer? = null - private var closed = false - private var offset = 0 - private val fd = open(path) - - private fun open(path: Path): Int { - if (!fs.existsSync(path.path)) { - throw FileNotFoundException("File does not exist: ${path.path}") - } - var fd: Int = -1 - withCaughtException { - fd = fs.openSync(path.path, "r") - }?.also { - throw IOException("Failed to open a file ${path.path}.", it) - } - if (fd < 0) throw IOException("Failed to open a file ${path.path}.") - return fd - } - - override fun readAtMostTo(sink: Buffer, byteCount: Long): Long { - check(!closed) { "Source is closed." } - if (byteCount == 0L) { - return 0 - } - if (buffer === null) { - withCaughtException { - buffer = fs.readFileSync(fd, null) - }?.also { - throw IOException("Failed to read data from ${path.path}", it) - } - } - val len: Int = buffer!!.length - if (offset >= len) { - return -1L - } - val bytesToRead = minOf(byteCount, (len - offset)) - for (i in 0 until bytesToRead) { - sink.writeByte(buffer!!.readInt8(offset++)) - } - - return bytesToRead - } - - override fun close() { - if (!closed) { - closed = true - fs.closeSync(fd) - } - } -} - -internal class FileSink(path: Path, append: Boolean) : RawSink { - private var closed = false - private val fd = open(path, append) - - private fun open(path: Path, append: Boolean): Int { - val flags = if (append) "a" else "w" - var fd = -1 - withCaughtException { - fd = fs.openSync(path.path, flags) - }?.also { - throw IOException("Failed to open a file ${path.path}.", it) - } - if (fd < 0) throw IOException("Failed to open a file ${path.path}.") - return fd - } - - @OptIn(UnsafeIoApi::class) - override fun write(source: Buffer, byteCount: Long) { - check(!closed) { "Sink is closed." } - if (byteCount == 0L) { - return - } - - var remainingBytes = minOf(byteCount, source.size) - while (remainingBytes > 0) { - var segmentBytes = 0 - UnsafeBufferOperations.readFromHead(source) { headData, headPos, headLimit -> - segmentBytes = headLimit - headPos - val buf = buffer.Buffer.allocUnsafe(segmentBytes) - for (offset in 0 until segmentBytes) { - buf.writeInt8(headData[headPos + offset], offset) - } - withCaughtException { - fs.writeFileSync(fd, buf) - }?.also { - throw IOException("Write failed", it) - } - segmentBytes - } - remainingBytes -= segmentBytes - } - } - - override fun flush() = Unit - - override fun close() { - if (!closed) { - closed = true - fs.closeSync(fd) - } - } -} diff --git a/core/nodeFilesystemShared/src/node/buffer.kt b/core/nodeFilesystemShared/src/node/buffer.kt deleted file mode 100644 index a457094e4..000000000 --- a/core/nodeFilesystemShared/src/node/buffer.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2010-2024 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.node - -internal external interface BufferModule { - val Buffer: BufferObj -} - -internal external object BufferObj { - fun allocUnsafe(bytes: Int): Buffer -} - -/** - * Partial declaration of a class mirroring [node:buffer.Buffer](https://nodejs.org/api/buffer.html#buffer). - */ -internal external interface Buffer { - val length: Int - fun readInt8(offset: Int): Byte - fun writeInt8(value: Byte, offset: Int) -} - -internal expect val buffer: BufferModule diff --git a/core/nodeFilesystemShared/src/node/fs.kt b/core/nodeFilesystemShared/src/node/fs.kt deleted file mode 100644 index 429f5d6d6..000000000 --- a/core/nodeFilesystemShared/src/node/fs.kt +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2010-2024 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.node - -internal external interface Fs { - /** - * See https://nodejs.org/api/fs.html#fsexistssyncpath - */ - fun existsSync(path: String): Boolean - - /** - * See https://nodejs.org/api/fs.html#fsmkdirsyncpath-options - */ - fun mkdirSync(path: String): Boolean - - /** - * See https://nodejs.org/api/fs.html#fsrenamesyncoldpath-newpath - */ - fun renameSync(from: String, to: String) - - /** - * See https://nodejs.org/api/fs.html#fsrmdirsyncpath-options - */ - fun rmdirSync(path: String) - - /** - * See https://nodejs.org/api/fs.html#fsrmsyncpath-options - */ - fun rmSync(path: String) - - /** - * See https://nodejs.org/api/fs.html#fsstatsyncpath-options - */ - fun statSync(path: String): Stats? - - /** - * See https://nodejs.org/api/fs.html#fsopensyncpath-flags-mode - */ - fun openSync(path: String, mode: String): Int - - /** - * See https://nodejs.org/api/fs.html#fsclosesyncfd - */ - fun closeSync(fd: Int) - - /** - * See https://nodejs.org/api/fs.html#fsreadfilesyncpath-options - */ - fun readFileSync(fd: Int, options: String?): Buffer - - /** - * See https://nodejs.org/api/fs.html#fswritefilesyncfile-data-options - */ - fun writeFileSync(fd: Int, buffer: Buffer) - - /** - * See https://nodejs.org/api/fs.html#fsopendirsyncpath-options - */ - fun opendirSync(path: String): Dir? - - val realpathSync: realpathSync - - val constants: constants -} - -/** - * Partial declaration of a class mirroring [node:fs.Stats](https://nodejs.org/api/fs.html#class-fsstats) - */ -internal external interface Stats { - val mode: Int - val size: Int - fun isDirectory(): Boolean -} - -/** - * See https://nodejs.org/api/fs.html#fs-constants - */ -internal external interface constants { - val S_IFREG: Int - val S_IFDIR: Int - val S_IFMT: Int -} - -/** - * See https://nodejs.org/api/fs.html#fsrealpathsyncnativepath-options - */ -internal external interface realpathSync { - fun native(path: String): String -} - -/** - * See https://nodejs.org/api/fs.html#class-fsdir - */ -internal external interface Dir { - fun closeSync() - - fun readSync(): Dirent? -} - -/** - * See https://nodejs.org/api/fs.html#class-fsdirent - */ -internal external interface Dirent { - val name: String -} - -internal expect val fs: Fs diff --git a/core/nodeFilesystemShared/src/node/os.kt b/core/nodeFilesystemShared/src/node/os.kt deleted file mode 100644 index 431d60a8f..000000000 --- a/core/nodeFilesystemShared/src/node/os.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2010-2024 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.node - -internal external interface Os { - /** - * See https://nodejs.org/api/os.html#ostmpdir - */ - fun tmpdir(): String? - - - /** - * See https://nodejs.org/api/os.html#osplatform - */ - fun platform(): String -} - -internal expect val os: Os diff --git a/core/nodeFilesystemShared/src/node/path.kt b/core/nodeFilesystemShared/src/node/path.kt deleted file mode 100644 index 25bc90612..000000000 --- a/core/nodeFilesystemShared/src/node/path.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2010-2024 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.node - -internal external interface Path { - - fun isAbsolute(path: String): Boolean - fun dirname(path: String): String - fun basename(path: String): String - - val sep: String -} - -internal expect val path: Path diff --git a/core/nodeFilesystemShared/src/try.kt b/core/nodeFilesystemShared/src/try.kt deleted file mode 100644 index a306eef9e..000000000 --- a/core/nodeFilesystemShared/src/try.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2010-2024 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 - -/** - * Kotlin/Wasm can't handle exceptions thrown by a JS runtime. - * This function wraps a block that may potentially throw something and returns an exception if it was caught. - */ -internal expect fun withCaughtException(block: () -> Unit): Throwable? diff --git a/core/nodeFilesystemShared/test/files/SmokeFileTestWindowsNodeJs.kt b/core/nodeFilesystemShared/test/files/SmokeFileTestWindowsNodeJs.kt deleted file mode 100644 index bb84a4d03..000000000 --- a/core/nodeFilesystemShared/test/files/SmokeFileTestWindowsNodeJs.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2010-2024 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.files - -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNull - -class SmokeFileTestWindowsNodeJs { - @Test - fun uncParent() { - if (!isWindows) return - // NodeJS, correctly, assume that it's a root path, that's where behavior diverge - assertNull(Path("\\\\server\\share").parent) - assertEquals(Path("\\\\server\\share"), Path("\\\\server\\share\\dir").parent) - } -} diff --git a/core/nodeFilesystemShared/test/files/utils.kt b/core/nodeFilesystemShared/test/files/utils.kt deleted file mode 100644 index 20c6b2178..000000000 --- a/core/nodeFilesystemShared/test/files/utils.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io - -import kotlinx.io.node.fs -import kotlinx.io.node.os -import kotlinx.io.node.path -import kotlin.random.Random - -@OptIn(ExperimentalStdlibApi::class) -actual fun tempFileName(): String { - while (true) { - val tmpdir = os.tmpdir() - val filename = Random.nextBytes(32).toHexString() - val fullpath = "$tmpdir${path.sep}$filename" - - if (fs.existsSync(fullpath)) { - continue - } - return fullpath - } -} diff --git a/core/unix/src/files/FileSystemUnix.kt b/core/unix/src/files/FileSystemUnix.kt deleted file mode 100644 index a26fb4975..000000000 --- a/core/unix/src/files/FileSystemUnix.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -@file:OptIn(UnsafeNumber::class, ExperimentalForeignApi::class) - -package kotlinx.io.files - -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.UnsafeNumber -import kotlinx.cinterop.convert -import kotlinx.cinterop.toKString -import kotlinx.io.IOException -import platform.posix.* - -internal actual fun atomicMoveImpl(source: Path, destination: Path) { - if (rename(source.path, destination.path) != 0) { - throw IOException("Move failed: ${strerror(errno)?.toKString()}") - } -} - -internal actual fun realpathImpl(path: String): String { - val result = realpath(path, null) ?: throw IllegalStateException() - try { - return result.toKString() - } finally { - free(result) - } -} - -internal actual fun mkdirImpl(path: String) { - if (mkdir(path, PermissionAllowAll.convert()) != 0) { - throw IOException("mkdir failed: ${strerror(errno)?.toKString()}") - } -} diff --git a/core/wasm/src/-PlatformWasm.kt b/core/wasm/src/-PlatformWasm.kt deleted file mode 100644 index 031d5513b..000000000 --- a/core/wasm/src/-PlatformWasm.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io - -public actual open class IOException : Exception { - public actual constructor() : super() - - public actual constructor(message: String?) : super(message) - - public actual constructor(cause: Throwable?) : super(cause) - - public actual constructor(message: String?, cause: Throwable?) : super(message, cause) -} - -public actual open class EOFException : IOException { - public actual constructor() : super() - - public actual constructor(message: String?) : super(message) -} diff --git a/core/wasm/src/RawSink.kt b/core/wasm/src/RawSink.kt deleted file mode 100644 index 1f611f465..000000000 --- a/core/wasm/src/RawSink.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io - -public actual interface RawSink : AutoCloseable { - public actual fun write(source: Buffer, byteCount: Long) - - public actual fun flush() - - actual override fun close() -} diff --git a/core/wasm/src/SegmentPool.kt b/core/wasm/src/SegmentPool.kt deleted file mode 100644 index c61fbeffa..000000000 --- a/core/wasm/src/SegmentPool.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io - -internal actual object SegmentPool { - actual val MAX_SIZE: Int = 0 - - actual val byteCount: Int = 0 - - actual fun take(): Segment = Segment.new() - - actual fun recycle(segment: Segment) { - } - - actual fun tracker(): SegmentCopyTracker = AlwaysSharedCopyTracker -} diff --git a/core/wasm/test/utils.kt b/core/wasm/test/utils.kt deleted file mode 100644 index d37230b56..000000000 --- a/core/wasm/test/utils.kt +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright 2010-2024 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 - -internal actual fun String.asUtf8ToByteArray(): ByteArray = commonAsUtf8ToByteArray() diff --git a/core/wasmJs/src/-PlatformWasmJs.kt b/core/wasmJs/src/-PlatformWasmJs.kt deleted file mode 100644 index d93280360..000000000 --- a/core/wasmJs/src/-PlatformWasmJs.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2010-2024 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 - -internal class JsException(message: String) : RuntimeException(message) - -internal actual fun withCaughtException(block: () -> Unit): Throwable? { - val e = catchJsThrowable(block) ?: return null - return JsException(e.toString()) -} - -private fun catchJsThrowable(block: () -> Unit): JsAny? = js("""{ - try { - block(); - return null; - } catch (e) { - if (e.message) return e.message; - if (e && e.toString) return e.toString(); - return e + ""; - } -}""") - diff --git a/core/wasmJs/src/node/nodeModulesWasmJs.kt b/core/wasmJs/src/node/nodeModulesWasmJs.kt deleted file mode 100644 index 9bb913ce3..000000000 --- a/core/wasmJs/src/node/nodeModulesWasmJs.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2010-2024 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.node - - -internal fun requireExists(): Boolean = js("typeof require === 'function'") - -internal fun requireModule(mod: String): JsAny? = js("""{ - try { - let m = require(mod); - if (m) return m; - return null; - } catch (e) { - return null; - } - }""") - -internal fun loadModule(name: String): JsAny { - if (!requireExists()) { - throw UnsupportedOperationException("Module $name could not be loaded") - } - val mod = requireModule(name) ?: throw UnsupportedOperationException("Module '$name' could not be imported") - return mod -} - -internal actual val buffer: BufferModule by lazy { - @Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE") - loadModule("buffer") as BufferModule -} - -internal actual val os: Os by lazy { - @Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE") - loadModule("os") as Os -} - -internal actual val path: Path by lazy { - @Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE") - loadModule("path") as Path -} - -internal actual val fs: Fs by lazy { - @Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE") - loadModule("fs") as Fs -} diff --git a/core/wasmWasi/src/-WasmUtils.kt b/core/wasmWasi/src/-WasmUtils.kt deleted file mode 100644 index 3a644b525..000000000 --- a/core/wasmWasi/src/-WasmUtils.kt +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2010-2024 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. - */ -@file:OptIn(UnsafeWasmMemoryApi::class) - -package kotlinx.io - -import kotlinx.io.unsafe.UnsafeBufferOperations -import kotlin.math.min -import kotlin.wasm.unsafe.MemoryAllocator -import kotlin.wasm.unsafe.Pointer -import kotlin.wasm.unsafe.UnsafeWasmMemoryApi - -internal fun Pointer.loadInt(offset: Int): Int = (this + offset).loadInt() -internal fun Pointer.loadLong(offset: Int): Long = (this + offset).loadLong() -internal fun Pointer.loadShort(offset: Int): Short = (this + offset).loadShort() -internal fun Pointer.loadByte(offset: Int): Byte = (this + offset).loadByte() - -internal fun Pointer.loadBytes(length: Int): ByteArray { - val buffer = ByteArray(length) - for (offset in 0 until length) { - buffer[offset] = this.loadByte(offset) - } - return buffer -} - -internal fun Pointer.storeInt(offset: Int, value: Int): Unit = (this + offset).storeInt(value) -internal fun Pointer.storeLong(offset: Int, value: Long): Unit = (this + offset).storeLong(value) -internal fun Pointer.storeShort(offset: Int, value: Short): Unit = (this + offset).storeShort(value) -internal fun Pointer.storeByte(offset: Int, value: Byte): Unit = (this + offset).storeByte(value) - -internal fun Pointer.storeBytes(bytes: ByteArray) { - for (offset in bytes.indices) { - this.storeByte(offset, bytes[offset]) - } -} - -@OptIn(UnsafeWasmMemoryApi::class, UnsafeIoApi::class) -internal fun Buffer.readToLinearMemory(pointer: Pointer, bytes: Int) { - checkBounds(size, 0L, bytes.toLong()) - var remaining = bytes - var currentPtr = pointer - while (remaining > 0 && !exhausted()) { - UnsafeBufferOperations.readFromHead(this) { ctx, seg -> - val read = minOf(remaining, seg.size) - for (offset in 0.. 0) { - UnsafeBufferOperations.writeToTail(this, 1) { ctx, seg -> - - val cap = min(seg.remainingCapacity, remaining) - for (offset in 0.. { - val bytes = value.encodeToByteArray() - val ptr = allocate(bytes.size + 1) - ptr.storeBytes(bytes) - ptr.storeByte(bytes.size, 0) - return ptr to (bytes.size + 1) -} - -/** - * Encodes [value] into a NULL-terminated byte sequence using UTF-8 encoding, - * stores it in memory starting at the position this pointer points to, - * and returns the length of the stored bytes sequence. - */ -internal fun Pointer.allocateString(value: String): Int { - val bytes = value.encodeToByteArray() - storeBytes(bytes) - storeByte(bytes.size, 0) - return bytes.size + 1 -} - -/** - * Allocates memory to hold a single integer value. - */ -@UnsafeWasmMemoryApi -internal fun MemoryAllocator.allocateInt(): Pointer = allocate(Int.SIZE_BYTES) - -/** - * Decodes zero-terminated string from a sequence of bytes that should not exceed [maxLength] bytes in length. - */ -@UnsafeWasmMemoryApi -internal fun Pointer.loadString(maxLength: Int): String { - val bytes = loadBytes(maxLength) - val firstZeroByte = bytes.indexOf(0) - val length = if (firstZeroByte == -1) maxLength else firstZeroByte - return bytes.decodeToString(0, length) -} diff --git a/core/wasmWasi/src/files/FileSystemWasm.kt b/core/wasmWasi/src/files/FileSystemWasm.kt deleted file mode 100644 index 19810c8b1..000000000 --- a/core/wasmWasi/src/files/FileSystemWasm.kt +++ /dev/null @@ -1,628 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.files - -import kotlinx.io.* -import kotlinx.io.wasi.* -import kotlin.math.min -import kotlin.wasm.unsafe.UnsafeWasmMemoryApi -import kotlin.wasm.unsafe.withScopedMemoryAllocator - -/** - * Path to a directory suitable for creating temporary files. - * - * This path is always `/tmp`, meaning that either `/` or `/tmp` should be pre-opened to use this path. - */ -public actual val SystemTemporaryDirectory: Path = Path("/tmp") - -/** - * An instance of [FileSystem] representing a default system-wide filesystem. - * - * The implementation is built upon Wasm WASI preview 1. - * - * To use files, at least one directory should be pre-opened. - * - * Operations on all absolute paths that are not sub-paths of pre-opened directories will fail. - * - * Relative paths are treated as paths relative to the first pre-opened directory. - * For example, if following directories were pre-opened: `/work`, `/tmp`, `/persistent`, then - * the path `a/b/c/d` will be resolved to `/work/a/b/c/d` as `/work` is the first pre-opened directory. - */ -public actual val SystemFileSystem: FileSystem get() = WasiFileSystem - -@OptIn(UnsafeWasmMemoryApi::class) -internal object WasiFileSystem : SystemFileSystemImpl() { - - override fun exists(path: Path): Boolean { - return metadataOrNull(path) != null - } - - override fun delete(path: Path, mustExist: Boolean) { - val preOpen = PreOpens.findPreopen(path) - withScopedMemoryAllocator { allocator -> - val (stringBuffer, stringLength) = allocator.storeString(path.path) - - val unlinkRes = Errno(path_unlink_file(preOpen.fd, stringBuffer.address.toInt(), stringLength)) - if (unlinkRes == Errno.success) return - if (unlinkRes == Errno.noent) { - if (mustExist) throw FileNotFoundException("File does not exist: $path") - return - } - // In case the path corresponding to a directory, either Error.isdir, or Errno.perm will be returned. - // In all other cases, there's no sense to continue, so we'll bail out with an exception. - if (unlinkRes != Errno.isdir && unlinkRes != Errno.perm) { - throw IOException("Unable to remove file $path: ${unlinkRes.description}") - } - - val removeDirRes = Errno(path_remove_directory(preOpen.fd, stringBuffer.address.toInt(), stringLength)) - if (removeDirRes == Errno.success) return - if (removeDirRes == Errno.noent) { - if (mustExist) throw FileNotFoundException("File does not exist: $path") - return - } - throw IOException("Unable to remove directory ${path.path}: ${unlinkRes.description}") - } - } - - override fun createDirectories(path: Path, mustCreate: Boolean) { - val preOpen = PreOpens.findPreopen(path) - if (path == preOpen.path) { - if (!mustCreate) return - throw IOException("Directory already exists: $path") - } - val segments: List = buildList { - var currentPath: Path? = path - while (currentPath != null && currentPath != preOpen.path) { - add(currentPath.path) - currentPath = currentPath.parent - } - } - var created = false - withScopedMemoryAllocator { allocator -> - // Allocating one extra byte to place the NULL-byte there - val pathBuffer = allocator.allocate(segments.first().encodeToByteArray().size + 1) - - for (idx in segments.size - 1 downTo 0) { - val segment = segments[idx] - val segmentLength = pathBuffer.allocateString(segment) - - val res = Errno(path_create_directory(preOpen.fd, pathBuffer.address.toInt(), segmentLength)) - if (res == Errno.success) { - created = true - } else if (res != Errno.exist) { - throw IOException( - "Can't create directory $path. " + - "Creation of an intermediate directory $segment failed: ${res.description}" - ) - } - } - if (mustCreate && !created) throw IOException("Directory already exists: $path") - if (!created) { - if (!metadataOrNull(path)!!.isDirectory) { - throw IOException("Path already exists and it's not a directory: $path") - } - } - } - } - - /** - * The move is not atomic (well, we don't know what kind of move it is), but there are no - * alternatives. - */ - override fun atomicMove(source: Path, destination: Path) { - val sourcePreOpen = PreOpens.findPreopen(source) - val destPreOpen = PreOpens.findPreopen(destination) - - withScopedMemoryAllocator { allocator -> - val (sourceBuffer, sourceBufferLength) = allocator.storeString(source.path) - val (destBuffer, destBufferLength) = allocator.storeString(destination.path) - - val res = Errno( - path_rename( - oldFd = sourcePreOpen.fd, - oldPathPtr = sourceBuffer.address.toInt(), - oldPathLen = sourceBufferLength, - newFd = destPreOpen.fd, - newPathPtr = destBuffer.address.toInt(), - newPathLen = destBufferLength - ) - ) - when (res) { - Errno.success -> return - Errno.noent -> throw FileNotFoundException( - "Failed to rename $source to $destination as either source file/directory, " + - "or destination's parent directory does not exist." - ) - - else -> throw IOException("Failed to rename $source to $destination: ${res.description}") - } - } - } - - override fun source(path: Path): RawSource { - val preOpen = PreOpens.findPreopen(path) - - val fd = withScopedMemoryAllocator { allocator -> - val fdPtr = allocator.allocateInt() - val (stringBuffer, stringBufferLength) = allocator.storeString(path.path) - - val res = Errno( - path_open( - fd = preOpen.fd, - dirflags = listOf(LookupFlags.symlink_follow).toBitset(), - pathPtr = stringBuffer.address.toInt(), pathLen = stringBufferLength, - oflags = 0, - fsRightsBase = listOf(Rights.fd_read).toBitset(), - fsRightsInheriting = 0, - fdFlags = 0, - resultPtr = fdPtr.address.toInt() - ) - ) - if (res != Errno.success) { - if (res == Errno.noent) throw FileNotFoundException("File does not exist: $path") - throw IOException("Can't open $path for read: ${res.description}") - } - fdPtr.loadInt() - } - return FileSource(fd) - } - - @OptIn(UnsafeWasmMemoryApi::class) - override fun sink(path: Path, append: Boolean): RawSink { - val preOpen = PreOpens.findPreopen(path) - - val fd = withScopedMemoryAllocator { allocator -> - val fdPtr = allocator.allocateInt() - val (stringBuffer, stringBufferLength) = allocator.storeString(path.path) - - val fdFlags = buildList { - if (append) { - add(FdFlags.append) - } - }.toBitset() - - val openFlags = buildList { - add(OpenFlags.creat) - if (!append) { - add(OpenFlags.trunc) - } - }.toBitset() - - val res = Errno( - path_open( - fd = preOpen.fd, - dirflags = listOf(LookupFlags.symlink_follow).toBitset(), - pathPtr = stringBuffer.address.toInt(), pathLen = stringBufferLength, - oflags = openFlags, - fsRightsBase = listOf(Rights.fd_write, Rights.fd_sync).toBitset(), - fsRightsInheriting = 0, - fdFlags = fdFlags, - resultPtr = fdPtr.address.toInt() - ) - ) - if (res != Errno.success) { - throw IOException("Can't open $path for write: ${res.description}") - } - fdPtr.loadInt() - } - return FileSink(fd) - } - - override fun metadataOrNull(path: Path): FileMetadata? { - val preOpen = PreOpens.findPreopenOrNull(path) ?: return null - val md = metadataOrNullInternal(preOpen.fd, path, true) ?: return null - - val filetype = md.filetype - val isDirectory = filetype == FileType.directory - val filesize = if (isDirectory) -1 else md.filesize - return FileMetadata( - isRegularFile = filetype == FileType.regular_file, - isDirectory = isDirectory, - size = filesize - ) - } - - /** - * Returns an absolute path to the same file or directory the [path] is pointing to. - * All symbolic links are solved, extra path separators and references to current (`.`) or - * parent (`..`) directories are removed. - * If the [path] is a relative path then it'll be resolved against current working directory. - * If there is no file or directory to which the [path] is pointing to then [FileNotFoundException] will be thrown. - * - * The behavior of this method differs from other platforms as the resolution - * may not fail if there is no filesystem-node (file, directory, symlink, etc.) corresponding - * to some interior path. This allows successfully resolving paths like `/a/b/c/../../d/e` when - * pre-opened directories are `/a/b/c` and `/a/d/e`. - * - * @param path the path to resolve. - * @return a resolved path. - * @throws FileNotFoundException if there is no file or directory corresponding to the specified path. - */ - override fun resolve(path: Path): Path { - val absolutePath = if (path.isAbsolute) { - path - } else { - Path(PreOpens.roots.first(), path.path) - } - - val resolvedPath = resolvePathImpl(absolutePath, 0) - ?: throw FileNotFoundException("Path does not exist: $path") - check(resolvedPath.isAbsolute) { - "Path is not absolute after symlinks resolution" - } - - val normalizedPath = resolvedPath.normalized() - if (!exists(normalizedPath)) throw FileNotFoundException("Path does not exist: $path") - return normalizedPath - } - - internal fun symlink(linked: Path, target: Path) { - val preOpen = PreOpens.findPreopen(target) - - withScopedMemoryAllocator { allocator -> - val (fromBuffer, fromBufferLength) = allocator.storeString(linked.path) - val (toBuffer, toBufferLength) = allocator.storeString(target.path) - val res = Errno( - path_symlink( - fromBuffer.address.toInt(), fromBufferLength, - preOpen.fd, toBuffer.address.toInt(), toBufferLength - ) - ) - - if (res == Errno.success) return - throw IOException("Can't create symbolic link $target pointing to $linked: ${res.description}") - } - } - - override fun list(directory: Path): Collection { - val preOpen = PreOpens.findPreopen(directory) - - val metadata = metadataOrNullInternal(preOpen.fd, directory, true) - ?: throw FileNotFoundException(directory.path) - if (metadata.filetype != FileType.directory) throw IOException("Not a directory: ${directory.path}") - - val children = mutableListOf() - val dir_fd = withScopedMemoryAllocator { allocator -> - val fdPtr = allocator.allocateInt() - val (stringBuffer, stringBufferLength) = allocator.storeString(directory.path) - - val res = Errno( - path_open( - fd = preOpen.fd, - dirflags = listOf(LookupFlags.symlink_follow).toBitset(), - pathPtr = stringBuffer.address.toInt(), pathLen = stringBufferLength, - oflags = setOf(OpenFlags.directory).toBitset(), - fsRightsBase = listOf(Rights.fd_readdir, Rights.fd_read).toBitset(), - fsRightsInheriting = 0, - fdFlags = 0, - resultPtr = fdPtr.address.toInt() - ) - ) - if (res != Errno.success) throw IOException("Can't open directory ${directory.path}: ${res.description}") - fdPtr.loadInt() - } - try { - withScopedMemoryAllocator { allocator -> - val resultSizePtr = allocator.allocateInt() - // directory's filesize expected to be larger than the actual buffer size required to fit all entries - val bufferSize = metadata.filesize.toInt() - val buffer = allocator.allocate(bufferSize) - val resultSize: Int - // Unsuported on Windows and Android: - // https://github.com/nodejs/node/blob/6f4d6011ea1b448cf21f5d363c44e4a4c56ca34c/deps/uvwasi/src/uvwasi.c#L19 - val res = Errno( - fd_readdir( - fd = dir_fd, - bufPtr = buffer.address.toInt(), - bufLen = bufferSize, - cookie = 0L, - resultPtr = resultSizePtr.address.toInt() - ) - ) - if (res != Errno.success) { - throw IOException("Can't read directory ${directory.path}: ${res.description}") - } - resultSize = resultSizePtr.loadInt() - check(resultSize <= bufferSize) { "Result size: $resultSize, buffer size: $bufferSize" } - var entryPtr = buffer - val endPtr = entryPtr + resultSize - while (entryPtr.address < endPtr.address) { - // read dirent: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#-dirent-record - // Each entry is 24-byte-wide dirent with filename length at offset 16, followed by - // filename length bytes of data. - val entryLen = entryPtr.loadInt(16) - entryPtr += 24 - val name = entryPtr.loadString(entryLen) - entryPtr += entryLen - if (name != "." && name != "..") { - children.add(Path(directory, name)) - } - } - } - return children - } finally { - fd_close(dir_fd) - } - } -} - -private fun Path.normalized(): Path { - require(isAbsolute) - - val parts = path.split(UnixPathSeparator) - val constructedPath = mutableListOf() - // parts[0] is always empty - for (idx in 1 until parts.size) { - when (val part = parts[idx]) { - "." -> continue - ".." -> constructedPath.removeLastOrNull() - else -> constructedPath.add(part) - } - } - return Path(UnixPathSeparator.toString(), *constructedPath.toTypedArray()) -} - -public actual open class FileNotFoundException actual constructor( - message: String?, -) : IOException(message) - -// The property affects only paths processing and in Wasi paths are always '/'-delimited. -internal actual val isWindows: Boolean get() = false - -internal object PreOpens { - data class PreOpen(val path: Path, val fd: Int) - - internal val roots: List by lazy { - preopens.map { it.path } - } - - internal val preopens: List by lazy { - // 0 - stdin, 1 - stdout, 2 - stderr, 3 - if the first preopened directory, if any - val firstPreopenFd = 3 - val rootPaths = mutableListOf() - - for (fd in firstPreopenFd..): Boolean { - return withScopedMemoryAllocator { allocator -> - val prestat = Prestat(allocator) - val res = Errno(fd_prestat_get(fd, prestat.address)) - - if (res == Errno.badf) { - return@withScopedMemoryAllocator false - } - - if (res != Errno.success) { - throw IOException("Unable to process fd=$fd as preopen: ${res.description}") - } - - check(prestat.type == PrestatType.dir) { "Unsupported prestat type" } - - val nameLength = prestat.nameLength - val pathBuffer = allocator.allocate(nameLength) - val dirnameRes = Errno(fd_prestat_dir_name(fd, pathBuffer.address.toInt(), nameLength)) - if (dirnameRes != Errno.success) { - throw IOException("Unable to get preopen dir name for fd=$fd: ${dirnameRes.description}") - } - outputList.add(PreOpen(Path(pathBuffer.loadBytes(nameLength).decodeToString()), fd)) - - true - } - } -} - -private const val TEMP_CIOVEC_BUFFER_LEN = 8192 - -private class FileSink(private val fd: Fd) : RawSink { - private var closed: Boolean = false - - @OptIn(UnsafeWasmMemoryApi::class) - override fun write(source: Buffer, byteCount: Long) { - check(byteCount >= 0) - if (byteCount == 0L) return - withScopedMemoryAllocator { allocator -> - val temporaryWriteBuffer = allocator.allocate(TEMP_CIOVEC_BUFFER_LEN) - var remaining = byteCount - - val ciovec = Ciovec(allocator).also { - it.buffer = temporaryWriteBuffer - } - - val resultPtr = allocator.allocateInt() - - while (remaining > 0) { - val bytesToWrite = minOf(remaining, TEMP_CIOVEC_BUFFER_LEN).toInt() - source.readToLinearMemory(temporaryWriteBuffer, bytesToWrite) - ciovec.length = bytesToWrite - - val res = Errno(fd_write(fd, ciovec.address, 1, resultPtr.address.toInt())) - if (res != Errno.success) { - throw IOException("Write failed: ${res.description}") - } - check(resultPtr.loadInt(0) == bytesToWrite) { - "Expected to write $bytesToWrite, but ${resultPtr.loadInt(0)} bytes were written" - } - remaining -= bytesToWrite - } - } - } - - override fun flush() { - val res = Errno(fd_sync(fd)) - if (res != Errno.success) { - throw IOException("fd_sync failed: ${res.description}") - } - } - - override fun close() { - if (!closed) { - closed = true - fd_close(fd) - } - } -} - -private class FileSource(private val fd: Fd) : RawSource { - private var closed: Boolean = false - - @OptIn(UnsafeWasmMemoryApi::class) - override fun readAtMostTo(sink: Buffer, byteCount: Long): Long { - check(byteCount >= 0) - if (byteCount == 0L) return 0L - var totalRead = 0L - - withScopedMemoryAllocator { allocator -> - val temporaryReadBuffer = allocator.allocate(TEMP_CIOVEC_BUFFER_LEN) - var remaining = byteCount - - val ciovec = Ciovec(allocator).also { - it.buffer = temporaryReadBuffer - } - - val resultPtr = allocator.allocateInt() - while (remaining > 0) { - val bytesToRead = minOf(remaining, TEMP_CIOVEC_BUFFER_LEN).toInt() - ciovec.length = bytesToRead - - val res = Errno(fd_read(fd, ciovec.address, 1, resultPtr.address.toInt())) - if (res != Errno.success) { - throw IOException("Read failed: ${res.description}") - } - val readBytes = resultPtr.loadInt() - // we're done - if (readBytes == 0) { - if (totalRead == 0L) return -1L - break - } - sink.writeFromLinearMemory(temporaryReadBuffer, readBytes) - remaining -= readBytes - totalRead += readBytes - } - } - return totalRead - } - - override fun close() { - if (!closed) { - closed = true - fd_close(fd) - } - } -} - -private data class InternalMetadata(val filetype: FileType, val filesize: Long) - -@OptIn(UnsafeWasmMemoryApi::class) -private fun metadataOrNullInternal(rootFd: Fd, path: Path, followSymlinks: Boolean): InternalMetadata? { - withScopedMemoryAllocator { allocator -> - val (pathBuffer, pathBufferLength) = allocator.storeString(path.path) - - val filestat = FileStat(allocator) - - val res = Errno( - path_filestat_get( - fd = rootFd, - flags = if (followSymlinks) 1 else 0, - pathPtr = pathBuffer.address.toInt(), pathLen = pathBufferLength, - resultPtr = filestat.address - ) - ) - - if (res == Errno.noent || res == Errno.notcapable) return null - if (res != Errno.success) throw IOException(res.description) - - return InternalMetadata(filestat.filetype, filestat.filesize) - } -} - -@OptIn(UnsafeWasmMemoryApi::class) -private fun readlinkInternal(rootFd: Fd, path: Path, linkSize: Int): Path { - withScopedMemoryAllocator { allocator -> - val resultPtr = allocator.allocateInt() - val (pathBuffer, pathBufferLength) = allocator.storeString(path.path) - // Allocating one extra byte to have enough space for the NULL-byte - val resultBuffer = allocator.allocate(linkSize + 1) - - val res = Errno( - path_readlink( - fd = rootFd, - pathPtr = pathBuffer.address.toInt(), pathLen = pathBufferLength, - bufPtr = resultBuffer.address.toInt(), bufLen = linkSize + 1, - resultPtr = resultPtr.address.toInt() - ) - ) - if (res != Errno.success) { - throw IOException("Link resolution failed for path $path: ${res.description}") - } - val resultLength = resultPtr.loadInt() - // resultLength includes the NULL-byte, we don't have to read it - return Path(resultBuffer.loadBytes(min(resultLength, linkSize)).decodeToString()) - } -} - -// The value compatible with current Linux defaults -private const val PATH_RESOLUTION_MAX_LINKS_DEPTH = 40 - -private fun resolvePathImpl(path: Path, recursion: Int): Path? { - if (recursion >= PATH_RESOLUTION_MAX_LINKS_DEPTH) { - throw IOException("Too many levels of symbolic links") - } - val resolvedParent = when (val parent = path.parent) { - null -> null - else -> resolvePathImpl(parent, recursion + 1) - } - val withResolvedParent = when (resolvedParent) { - null -> path - else -> Path(resolvedParent, path.name) - } - // There are cases when we simply can't resolve the intermediate path, but the resulting path should be fine: - // pre-opened directories: [/a/b/c, /a/d/e] - // path to resolve: /a/b/c/../../d/e/f - // The path, after normalization, is /a/d/e/f and its metadata could be fetched using the root /a/d/e. - // However, normalization could not be performed before links resolution, and intermediate non-normalized path - // may point to a directory not belonging to any of pre-opened directories (like /a/b/c/../..). - // So, let's hope for the best and throw an exception after resolution and normalization. - val preOpen = PreOpens.findPreopenOrNull(withResolvedParent) ?: return withResolvedParent - val metadata = metadataOrNullInternal(preOpen.fd, withResolvedParent, false) ?: return withResolvedParent - if (metadata.filetype == FileType.symbolic_link) { - val linkTarget = readlinkInternal(preOpen.fd, withResolvedParent, metadata.filesize.toInt()) - val resolvedPath = if (linkTarget.isAbsolute || resolvedParent == null) { - linkTarget - } else { - Path(resolvedParent, linkTarget.path) - } - return resolvePathImpl(resolvedPath, recursion + 1) - } - return withResolvedParent -} diff --git a/core/wasmWasi/src/files/PathsWasm.kt b/core/wasmWasi/src/files/PathsWasm.kt deleted file mode 100644 index f0b3b3d71..000000000 --- a/core/wasmWasi/src/files/PathsWasm.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io.files - -public actual class Path internal constructor(rawPath: String, @Suppress("UNUSED_PARAMETER") obj: Any?) { - internal val path: String = removeTrailingSeparators(rawPath, false) - - actual override fun toString(): String = path - - actual override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null) return false - if (this::class != other::class) return false - - other as Path - return path == other.path - } - - actual override fun hashCode(): Int { - return path.hashCode() - } - - public actual val parent: Path? - get() { - if (path == SystemPathSeparator.toString()) return null - - val idx = path.lastIndexOf(SystemPathSeparator) - if (idx < 0) return null - // path ends with '/', but as it was normalized there is only one case -> it's "/" - if (idx == path.length - 1) return null - val rawBase = if (idx == 0) "$SystemPathSeparator" else path.substring(0, idx) - val base = removeTrailingSeparators(rawBase, false) - // there was nothing but multiple '/' - return Path(base, null) - } - - public actual val name: String - get() { - val idx = path.lastIndexOf(SystemPathSeparator) - return if (idx < 0) { - path - } else { - path.substring(idx + 1) - } - } - - public actual val isAbsolute: Boolean = path.startsWith(SystemPathSeparator) -} - -// The path separator is always '/'. -// https://github.com/WebAssembly/wasi-filesystem/blob/e79b05803e9ffd3b0cfdc0a8af20ac743abbe36a/wit/types.wit#L13C4-L13C71 -public actual val SystemPathSeparator: Char get() = UnixPathSeparator - -public actual fun Path(path: String): Path = Path(path, null as Any?) diff --git a/core/wasmWasi/src/wasi/functions.kt b/core/wasmWasi/src/wasi/functions.kt deleted file mode 100644 index b4176801d..000000000 --- a/core/wasmWasi/src/wasi/functions.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2010-2024 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.wasi - -import kotlin.wasm.WasmImport - -// https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#-fd_closefd-fd---result-errno -@WasmImport("wasi_snapshot_preview1", "fd_close") -internal external fun fd_close(fd: Fd): Int - -@WasmImport("wasi_snapshot_preview1", "fd_prestat_get") -internal external fun fd_prestat_get(fd: Fd, resultPtr: Int): Int - -@WasmImport("wasi_snapshot_preview1", "fd_prestat_dir_name") -internal external fun fd_prestat_dir_name(fd: Fd, pathPtr: Int, pathLen: Int): Int - -@WasmImport("wasi_snapshot_preview1", "fd_read") -internal external fun fd_read(fd: Fd, iovecPtr: Int, iovecLen: Int, resultPtr: Int): Int - -@WasmImport("wasi_snapshot_preview1", "fd_readdir") -internal external fun fd_readdir(fd: Fd, bufPtr: Int, bufLen: Int, cookie: Long, resultPtr: Int): Int - -@WasmImport("wasi_snapshot_preview1", "fd_sync") -internal external fun fd_sync(fd: Fd): Int - -@WasmImport("wasi_snapshot_preview1", "fd_write") -internal external fun fd_write(fd: Fd, iovecPtr: Int, iovecLen: Int, resultPtr: Int): Int - -@WasmImport("wasi_snapshot_preview1", "path_create_directory") -internal external fun path_create_directory(fd: Fd, pathPtr: Int, pathLen: Int): Int - -// TODO: is flags Int? -@WasmImport("wasi_snapshot_preview1", "path_filestat_get") -internal external fun path_filestat_get(fd: Fd, flags: Int, pathPtr: Int, pathLen: Int, resultPtr: Int): Int - -@WasmImport("wasi_snapshot_preview1", "path_open") -internal external fun path_open( - fd: Fd, - dirflags: Int, // is it? - pathPtr: Int, pathLen: Int, - oflags: Int, // is it? - fsRightsBase: Long, - fsRightsInheriting: Long, - fdFlags: Short, // is it? - resultPtr: Int -): Int - -@WasmImport("wasi_snapshot_preview1", "path_remove_directory") -internal external fun path_remove_directory(fd: Fd, pathPtr: Int, pathLen: Int): Int - -@WasmImport("wasi_snapshot_preview1", "path_rename") -external internal fun path_rename( - oldFd: Fd, oldPathPtr: Int, oldPathLen: Int, - newFd: Fd, newPathPtr: Int, newPathLen: Int -): Int - -@WasmImport("wasi_snapshot_preview1", "path_unlink_file") -external internal fun path_unlink_file( - fd: Fd, pathPtr: Int, pathLen: Int -): Int - -@WasmImport("wasi_snapshot_preview1", "path_readlink") -external internal fun path_readlink( - fd: Fd, pathPtr: Int, pathLen: Int, bufPtr: Int, bufLen: Int, resultPtr: Int -): Int - -@WasmImport("wasi_snapshot_preview1", "path_symlink") -external internal fun path_symlink(oldPathPtr: Int, oldPathLen: Int, fd: Fd, newPathPtr: Int, newPathLen: Int): Int diff --git a/core/wasmWasi/src/wasi/types.kt b/core/wasmWasi/src/wasi/types.kt deleted file mode 100644 index 5c8bf66a5..000000000 --- a/core/wasmWasi/src/wasi/types.kt +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright 2010-2024 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. - */ - -@file:OptIn(UnsafeWasmMemoryApi::class) - -package kotlinx.io.wasi - -import kotlinx.io.loadByte -import kotlinx.io.loadInt -import kotlinx.io.loadLong -import kotlinx.io.storeInt -import kotlin.wasm.unsafe.MemoryAllocator -import kotlin.wasm.unsafe.Pointer -import kotlin.wasm.unsafe.UnsafeWasmMemoryApi - -// https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md -internal enum class Errno(val description: String) { - success("No error occurred. System call completed successfully"), - toobig("Argument list too long"), - acces("Permission denied"), - addrinuse("Address in use"), - addrnotavail("Address not available"), - afnosupport("Address family not supported"), - again("Resource unavailable, or operation would block"), - already("Connection already in progress"), - badf("Bad file descriptor"), - badmsg("Bad message"), - busy("Device or resource busy"), - canceled("Operation canceled"), - child("No child processes"), - connaborted("Connection aborted"), - connrefused("Connection refused"), - connreset("Connection reset"), - deadlk("Resource deadlock would occur"), - destaddrreq("Destination address required"), - dom("Mathematics argument out of domain of function"), - dquot("Reserved"), - exist("File exists"), - fault("Bad address"), - fbig("File too large"), - hostunreach("Host is unreachable"), - idrm("Identifier removed"), - ilseq("Illegal byte sequence"), - inprogress("Operation in progress"), - intr("Interrupted function"), - inval("Invalid argument"), - io("I/O error"), - isconn("Socket is connected"), - isdir("Is a directory"), - loop("Too many levels of symbolic links"), - mfile("File descriptor value too large"), - mlink("Too many links"), - msgsize("Message too large"), - multihop("Reserved"), - nametoolong("Filename too long"), - netdown("Network is down"), - netreset("Connection aborted by network"), - netunreach("Network unreachable"), - nfile("Too many files open in system"), - nobufs("No buffer space available"), - nodev("No such device"), - noent("No such file or directory"), - noexec("Executable file format error"), - nolck("No locks available"), - nolink("Reserved"), - nomem("Not enough space"), - nomsg("No message of the desired type"), - noprotoopt("Protocol not available"), - nospc("No space left on device"), - nosys("Function not supported"), - notconn("The socket is not connected"), - notdir("Not a directory or a symbolic link to a directory"), - notempty("Directory not empty"), - notrecoverable("State not recoverable"), - notsock("Not a socket"), - notsup("Not supported, or operation not supported on socket"), - notty("Inappropriate I/O control operation"), - nxio("No such device or address"), - overflow("Value too large to be stored in data type"), - ownerdead("Previous owner died"), - perm("Operation not permitted"), - pipe("Broken pipe"), - proto("Protocol error"), - protonosupport("Protocol not supported"), - prototype("Protocol wrong type for socket"), - range("Result too large"), - rofs("Read-only file system"), - spipe("Invalid seek"), - srch("No such process"), - stale("Reserved"), - timedout("Connection timed out"), - txtbsy("Text file busy"), - xdev("Cross-device link"), - notcapable("Extension: Capabilities insufficient") -} - -internal fun Errno(errno: Int): Errno { - require(errno in Errno.entries.indices) { "Unknown errno: $errno" } - return Errno.entries[errno] -} - -internal enum class FileType { - unknown, - block_device, - character_device, - directory, - regular_file, - socket_dgram, - socket_stream, - symbolic_link -} - -internal fun FileType(filetype: Byte): FileType { - val value = filetype.toInt() - require(value in FileType.entries.indices) { "Unknown file type: $value" } - return FileType.entries[value] -} - -internal enum class Rights { - fd_datasync, - fd_read, - fd_seek, - fd_fdstat_set_flags, - fd_sync, - fd_tell, - fd_write, - fd_advise, - fd_allocate, - path_create_directory, - path_create_file, - path_link_source, - path_link_target, - path_open, - fd_readdir, - path_readlink, - path_rename_source, - path_rename_target, - path_filestat_get, - path_filestat_set_size, - path_filestat_set_times, - fd_filestat_get, - fd_filestat_set_size, - fd_filestat_set_times, - path_symlink, - path_remove_directory, - path_unlink_file, - poll_fd_readwrite, - sock_shutdown, - sock_accept -} - -internal fun Iterable.toBitset(): Long { - var bitset = 0L - for (right in this) { - bitset = bitset.or(1L shl right.ordinal) - } - return bitset -} - -internal enum class FdFlags { - append, - dsync, - nonblock, - rsync -} - -internal fun Iterable.toBitset(): Short { - var bitset = 0 - for (flag in this) { - bitset = bitset.or(1 shl flag.ordinal) - } - return bitset.toShort() -} - -internal enum class LookupFlags { - symlink_follow -} - -internal fun Iterable.toBitset(): Int { - var bitset = 0 - for (flag in this) { - bitset = bitset.or(1 shl flag.ordinal) - } - return bitset -} - -internal enum class OpenFlags { - creat, - directory, - excl, - trunc, -} - -internal fun Iterable.toBitset(): Int { - var bitset = 0 - for (flag in this) { - bitset = bitset.or(1 shl flag.ordinal) - } - return bitset -} - -internal typealias Fd = Int - -internal enum class PrestatType { - dir, - invalid -} - -internal interface WasmMemoryAllocated { - val address: Int -} - -/** - * See https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#-prestat-variant - */ -internal data class Prestat(val ptr: Pointer) : WasmMemoryAllocated { - val type: PrestatType - get() = if (ptr.loadByte().toInt() == 0) { - PrestatType.dir - } else { - PrestatType.invalid - } - - val nameLength: Int - get() = ptr.loadInt(4) - - override val address: Int - get() = ptr.address.toInt() -} - -internal fun Prestat(allocator: MemoryAllocator): Prestat = Prestat(allocator.allocate(8)) - -/** - * See https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#-filestat-record - */ -internal data class FileStat(val ptr: Pointer) : WasmMemoryAllocated { - val dev: Long - get() = ptr.loadLong() - - val ino: Long - get() = ptr.loadLong(8) - - val filetype: FileType - get() = FileType(ptr.loadByte(16)) - - val linkCount: Long - get() = ptr.loadLong(24) - - val filesize: Long - get() = ptr.loadLong(32) - - val accessTime: Long - get() = ptr.loadLong(40) - - val modificationTime: Long - get() = ptr.loadLong(48) - - val changeTime: Long - get() = ptr.loadLong(56) - - override val address: Int - get() = ptr.address.toInt() -} - -internal fun FileStat(allocator: MemoryAllocator): FileStat = FileStat(allocator.allocate(64)) - -/** - * See https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#-ciovec-record - */ -internal data class Ciovec(val ptr: Pointer) : WasmMemoryAllocated { - var buffer: Pointer - get() = Pointer(ptr.loadInt().toUInt()) - set(value) = ptr.storeInt(value.address.toInt()) - - var length: Int - get() = ptr.loadInt(4) - set(value) = ptr.storeInt(4, value) - - override val address: Int - get() = ptr.address.toInt() -} - -internal fun Ciovec(allocator: MemoryAllocator): Ciovec = Ciovec(allocator.allocate(8)) diff --git a/core/wasmWasi/test/WasiFsTest.kt b/core/wasmWasi/test/WasiFsTest.kt deleted file mode 100644 index 40a835be5..000000000 --- a/core/wasmWasi/test/WasiFsTest.kt +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright 2010-2024 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.files - -import kotlinx.io.IOException -import kotlinx.io.buffered -import kotlinx.io.readLine -import kotlinx.io.writeString -import kotlin.test.* - -class WasiFsTest { - @Test - fun hasTemp() { - val preopen = PreOpens.preopens.first() - assertEquals(3, preopen.fd) - assertEquals(Path("/tmp"), preopen.path) - } - - @Test - fun multiplePreOpens() { - fun checkPreOpen(forPath: String, expected: String?) { - val preOpen = PreOpens.findPreopenOrNull(Path(forPath)) - if (expected == null) { - assertNull(preOpen) - } else { - assertNotNull(preOpen) - assertEquals(Path(expected), preOpen.path) - } - } - - checkPreOpen(forPath = "/data", expected = null) - checkPreOpen(forPath = "/tmp", expected = "/tmp") - checkPreOpen(forPath = "/tmp/a", expected = "/tmp") - checkPreOpen(forPath = "/var", expected = null) - checkPreOpen(forPath = "/var/what", expected = null) - checkPreOpen(forPath = "/var/log", expected = "/var/log") - checkPreOpen(forPath = "/tmp ", expected = null) - checkPreOpen(forPath = "/tmpry", expected = null) - checkPreOpen(forPath = "/var/logging", expected = null) - } - - @Test - fun resolveRelativePath() { - val path = Path("a", "b", "c") - SystemFileSystem.createDirectories(path) - try { - val resolved = SystemFileSystem.resolve(path) - assertEquals(Path(PreOpens.roots.first(), "a", "b", "c"), resolved) - } finally { - SystemFileSystem.delete(path) - SystemFileSystem.delete(path.parent!!) - SystemFileSystem.delete(path.parent!!.parent!!) - } - } - - @Test - fun createDirectoryForRelativePath() { - val path = Path("rel") - SystemFileSystem.createDirectories(path) - try { - val metadata = SystemFileSystem.metadataOrNull(Path(PreOpens.roots.first(), "rel")) - assertNotNull(metadata) - assertTrue(metadata.isDirectory) - } finally { - SystemFileSystem.delete(path, false) - } - } - - @Test - fun resolution() { - val resolved = SystemFileSystem.resolve(Path("/tmp/../../a/../b/../../tmp")) - assertEquals(Path("/tmp"), resolved) - - SystemFileSystem.createDirectories(Path("/tmp/a/b/c/d/e")) - try { - WasiFileSystem.symlink(Path("/tmp/a/b/c/d/e"), Path("/tmp/l")) - WasiFileSystem.symlink(Path("a/b/c/d/e"), Path("/tmp/lr")) - WasiFileSystem.symlink(Path("/blablabla"), Path("/tmp/dangling")) - - assertEquals(Path("/tmp/a/b"), SystemFileSystem.resolve(Path("/tmp/lr/../../.."))) - assertEquals(Path("/tmp/a/b/c"), SystemFileSystem.resolve(Path("/tmp/l/../.."))) - - assertFailsWith { - SystemFileSystem.resolve(Path("/tmp/dangling")) - } - } finally { - SystemFileSystem.delete(Path("/tmp/a/b/c/d/e")) - SystemFileSystem.delete(Path("/tmp/a/b/c/d")) - SystemFileSystem.delete(Path("/tmp/a/b/c")) - SystemFileSystem.delete(Path("/tmp/a/b")) - SystemFileSystem.delete(Path("/tmp/a")) - SystemFileSystem.delete(Path("/tmp/l")) - SystemFileSystem.delete(Path("/tmp/lr")) - SystemFileSystem.delete(Path("/tmp/dangling")) - } - } - - @Test - fun symlinks() { - val src = Path("/tmp/src") - val tgt = Path("/tmp/tgt") - - try { - SystemFileSystem.sink(src).buffered().use { - it.writeString("Hello") - } - WasiFileSystem.symlink(src, tgt) - SystemFileSystem.source(tgt).buffered().use { - assertEquals("Hello", it.readLine()) - } - } finally { - SystemFileSystem.delete(src) - SystemFileSystem.delete(tgt) - } - } - - @Test - fun recursiveSimlinks() { - val src = Path("/tmp/src") - val tgt = Path("/tmp/tgt") - - WasiFileSystem.symlink(src, tgt) - WasiFileSystem.symlink(tgt, src) - - try { - assertFailsWith { - SystemFileSystem.resolve(src) - } - } finally { - SystemFileSystem.delete(src) - SystemFileSystem.delete(tgt) - } - } - - @Test - fun deepSymlinksChain() { - val src = Path("/tmp/src") - - SystemFileSystem.sink(src).close() - - val linksCount = 100 - val safeSymlinksDepth = 30 - val paths = mutableListOf(src) - for (i in 0 ..< linksCount) { - val link = Path("/tmp/link$i") - WasiFileSystem.symlink(paths.last(), link) - paths.add(link) - } - - try { - assertEquals(src, SystemFileSystem.resolve(paths[safeSymlinksDepth])) - val exception = assertFailsWith { - SystemFileSystem.resolve(paths.last()) - } - assertEquals("Too many levels of symbolic links", exception.message) - } finally { - paths.asReversed().forEach { - SystemFileSystem.delete(it) - } - } - } - - @Test - fun multilevelSymlinks() { - val src = Path("/tmp/result/a/z") - - // /tmp/x/y/z -> /tmp/foo/a/z -> /tmp/result/a/z - val finalLink = Path("/tmp/x/y/z") - - try { - SystemFileSystem.createDirectories(Path("/tmp/result/a")) - SystemFileSystem.createDirectories(Path("/tmp/x")) - // create a file - SystemFileSystem.sink(src).close() - - WasiFileSystem.symlink(Path("/tmp/result"), Path("/tmp/foo")) - WasiFileSystem.symlink(Path("/tmp/foo/a"), Path("/tmp/x/y")) - - assertEquals(src, WasiFileSystem.resolve(Path(finalLink))) - } finally { - SystemFileSystem.delete(Path("/tmp/x/y")) - SystemFileSystem.delete(Path("/tmp/x")) - SystemFileSystem.delete(Path("/tmp/foo")) - SystemFileSystem.delete(Path("/tmp/result/a/z")) - SystemFileSystem.delete(Path("/tmp/result/a")) - SystemFileSystem.delete(Path("/tmp/result")) - } - } - - @Test - fun mkdirRoot() { - SystemFileSystem.createDirectories(Path("/tmp")) // should succeed - assertFailsWith { SystemFileSystem.createDirectories(Path("/tmp"), true) } - } - - @Test - fun intermediateDirectoryCreationFailure() { - val targetPath = Path("/tmp/a/b/c/d/e") - val existingFile = Path("/tmp/a/b") - - SystemFileSystem.createDirectories(Path("/tmp/a/")) - try { - SystemFileSystem.sink(existingFile).close() - val exception = assertFailsWith { - SystemFileSystem.createDirectories(targetPath) - } - assertNotNull(exception.message) - assertTrue( - exception.message!!.startsWith( - "Can't create directory $targetPath. Creation of an intermediate directory /tmp/a/b/c failed" - ) - ) - } finally { - SystemFileSystem.delete(Path("/tmp/a/b")) - SystemFileSystem.delete(Path("/tmp/a")) - } - } - - // https://github.com/nodejs/node/blob/6f4d6011ea1b448cf21f5d363c44e4a4c56ca34c/deps/uvwasi/src/uvwasi.c#L19 - @Test - fun readdirUnsupportedOnWindows() { - if (!isWindows) return - - assertFailsWith { - SystemFileSystem.list(PreOpens.roots.first()) - } - } -} diff --git a/core/wasmWasi/test/test-driver.mjs.template b/core/wasmWasi/test/test-driver.mjs.template deleted file mode 100644 index 21003bed2..000000000 --- a/core/wasmWasi/test/test-driver.mjs.template +++ /dev/null @@ -1,41 +0,0 @@ - -import { WASI } from 'wasi'; -import { argv, env } from 'node:process'; - -const wasi = new WASI({ version: 'preview1', args: argv, env: env, preopens: { - '/tmp': '', - '/var/log': '' -}}); - -const module = await import(/* webpackIgnore: true */'node:module'); -const require = module.default.createRequire(import.meta.url); -const fs = require('fs'); -const path = require('path'); -const url = require('url'); -const filepath = url.fileURLToPath(import.meta.url); -const dirpath = path.dirname(filepath); -const wasmBuffer = fs.readFileSync(path.resolve(dirpath, './kotlinx-io-kotlinx-io-core-wasm-wasi-test.wasm')); -const wasmModule = new WebAssembly.Module(wasmBuffer); -const wasmInstance = new WebAssembly.Instance(wasmModule, wasi.getImportObject()); - -wasi.initialize(wasmInstance); - -const exports = wasmInstance.exports - -export default new Proxy(exports, { - _shownError: false, - get(target, prop) { - if (!this._shownError) { - this._shownError = true; - if (typeof console !== "undefined") { - console.error("Do not use default import. Use corresponding named import instead.") - } - } - return target[prop]; - } -}); -export const { - startUnitTests, - _initialize, - memory -} = exports; diff --git a/core/wasmWasi/test/utils.kt b/core/wasmWasi/test/utils.kt deleted file mode 100644 index 5f4b4a254..000000000 --- a/core/wasmWasi/test/utils.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -package kotlinx.io - -import kotlinx.io.files.Path -import kotlinx.io.files.SystemTemporaryDirectory -import kotlin.random.Random - -@OptIn(ExperimentalStdlibApi::class) -actual fun tempFileName(): String = - Path(SystemTemporaryDirectory, Random.nextBytes(32).toHexString()).toString() diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index 3ea8fdde7..000000000 --- a/gradle.properties +++ /dev/null @@ -1,11 +0,0 @@ -# -# Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. -# Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -# - -group=org.jetbrains.kotlinx -version=0.5.4 -kotlin.code.style=official -org.gradle.jvmargs=-Xmx4G -nativeBenchmarksEnabled=true -kotlin.mpp.applyDefaultHierarchyTemplate=false diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml deleted file mode 100644 index a26422f15..000000000 --- a/gradle/libs.versions.toml +++ /dev/null @@ -1,25 +0,0 @@ -[versions] -kotlin = "2.0.0" -java = "8" -dokka = "1.9.20" -kover = "0.8.2" -bcv = "0.16.3" -benchmark = "0.4.11" -jmh = "1.37" -coroutines = "1.7.3" -animalsniffer = "1.7.1" - -[libraries] - -kotlinx-benchmark-runtime = { group = "org.jetbrains.kotlinx", name = "kotlinx-benchmark-runtime", version.ref = "benchmark" } -kotlin-gradle-plugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } -dokka-gradle-plugin = { group = "org.jetbrains.dokka", name = "dokka-gradle-plugin", version.ref = "dokka" } -kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" } -animalsniffer-gradle-plugin = { group = "ru.vyarus", name = "gradle-animalsniffer-plugin", version.ref = "animalsniffer" } - -[plugins] - -dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } -kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" } -bcv = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "bcv" } -kotlinx-benchmark-plugin = { id = "org.jetbrains.kotlinx.benchmark", version.ref = "benchmark"} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index e6441136f3d4ba8a0da8d277868979cfbc8ad796..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43453 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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
NnX)EH+Nua)3Y(c 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 '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index 25da30dbd..000000000 --- a/gradlew.bat +++ /dev/null @@ -1,92 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock deleted file mode 100644 index 6bfdf9ddc..000000000 --- a/kotlin-js-store/yarn.lock +++ /dev/null @@ -1,529 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -ansi-colors@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - -braces@~3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" - integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== - dependencies: - fill-range "^7.1.1" - -browser-stdout@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" - integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -camelcase@^6.0.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== - -chalk@^4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chokidar@3.5.3: - version "3.5.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" - integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -debug@4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - -decamelize@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" - integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== - -diff@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" - integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-string-regexp@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -fill-range@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" - integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== - dependencies: - to-regex-range "^5.0.1" - -find-up@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -flat@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" - integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== - -format-util@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271" - integrity sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg== - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - -fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob@8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -he@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-plain-obj@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== - -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - -js-yaml@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -log-symbols@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - -minimatch@5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" - integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== - dependencies: - brace-expansion "^2.0.1" - -minimatch@^5.0.1: - version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== - dependencies: - brace-expansion "^2.0.1" - -mocha@10.3.0: - version "10.3.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.3.0.tgz#0e185c49e6dccf582035c05fa91084a4ff6e3fe9" - integrity sha512-uF2XJs+7xSLsrmIvn37i/wnc91nw7XjOQB8ccyx5aEgdnohr7n+rEiZP23WkCYHjilR6+EboEnbq/ZQDz4LSbg== - dependencies: - ansi-colors "4.1.1" - browser-stdout "1.3.1" - chokidar "3.5.3" - debug "4.3.4" - diff "5.0.0" - escape-string-regexp "4.0.0" - find-up "5.0.0" - glob "8.1.0" - he "1.2.0" - js-yaml "4.1.0" - log-symbols "4.1.0" - minimatch "5.0.1" - ms "2.1.3" - serialize-javascript "6.0.0" - strip-json-comments "3.1.1" - supports-color "8.1.1" - workerpool "6.2.1" - yargs "16.2.0" - yargs-parser "20.2.4" - yargs-unparser "2.0.0" - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -picomatch@^2.0.4, picomatch@^2.2.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== - -safe-buffer@^5.1.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -serialize-javascript@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" - integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== - dependencies: - randombytes "^2.1.0" - -source-map-support@0.5.21: - version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-json-comments@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -supports-color@8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -typescript@5.4.3: - version "5.4.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.3.tgz#5c6fedd4c87bee01cd7a528a30145521f8e0feff" - integrity sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg== - -workerpool@6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" - integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - -yargs-parser@20.2.4: - version "20.2.4" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" - integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== - -yargs-parser@^20.2.2: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - -yargs-unparser@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" - integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== - dependencies: - camelcase "^6.0.0" - decamelize "^4.0.0" - flat "^5.0.2" - is-plain-obj "^2.1.0" - -yargs@16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/settings.gradle.kts b/settings.gradle.kts deleted file mode 100644 index fa3f32448..000000000 --- a/settings.gradle.kts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. - */ - -pluginManagement { - includeBuild("build-logic") - - repositories { - mavenCentral() - google() - gradlePluginPortal() - } -} - -plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version ("0.8.0") -} - -rootProject.name = "kotlinx-io" - -include(":kotlinx-io-core") -include(":kotlinx-io-benchmarks") -include(":kotlinx-io-bytestring") -include(":kotlinx-io-smoke-tests") - -project(":kotlinx-io-core").projectDir = file("./core") -project(":kotlinx-io-benchmarks").projectDir = file("./benchmarks") -project(":kotlinx-io-bytestring").projectDir = file("./bytestring") -project(":kotlinx-io-smoke-tests").projectDir = file("./smoke-tests") \ No newline at end of file diff --git a/smoke-tests/README.md b/smoke-tests/README.md deleted file mode 100644 index d484cbe82..000000000 --- a/smoke-tests/README.md +++ /dev/null @@ -1,20 +0,0 @@ -Tests aimed to verify that all artifacts are published correctly -and there are no dependency related issues. - -There are two sets of tests: one targeting Gradle projects and another targeting Maven projects. -Each set could be invoked independently via `verifyGradleProjects` and `verifyMavenProjects` tasks -correspondingly. Both tasks are aggregated by the `smokeTest` task. - -Note that `check` and `test` tasks are no-op. - -Tests could be executed against arbitrary `kotlinx-io` version. The version could be set using -`smokeTest.kotlinxIoVersion` property. If the property is unset or has a blank value, current project -version will be used instead. In that case, local maven repo will be used and publication tasks -will be executed before any tests. - -For projects published to a staging Sonatype repository it's possible to specify repository ID -using `smokeTest.stagingRepository` property. - -### How to run - -`./gradlew :kotlinx-io-smoke-tests:smokeTest -PsmokeTest.kotlinxIoVersion=0.5.3-test -PsmokeTest.stagingRepository=orgjetbrainskotlinx-3482` diff --git a/smoke-tests/build.gradle.kts b/smoke-tests/build.gradle.kts deleted file mode 100644 index 8cb81b6e9..000000000 --- a/smoke-tests/build.gradle.kts +++ /dev/null @@ -1,100 +0,0 @@ -import org.apache.tools.ant.taskdefs.condition.Os - -plugins { - kotlin("jvm") -} - -val stagingRepositoryIdRawValue = project.findProperty("smokeTest.stagingRepository")?.toString() - -val stagingRepositoryId: String = if (stagingRepositoryIdRawValue != null) { - stagingRepositoryIdRawValue -} else { - logger.warn("smokeTest.stagingRepository was not set.") - "" -} - -val stagingRepository: String = "https://oss.sonatype.org/content/repositories/$stagingRepositoryId" - -val kotlinxIoVersionRawValue = project.findProperty("smokeTest.kotlinxIoVersion")?.toString() -var useLocalBuild = false - -val kotlinxIoVersion: String = if (kotlinxIoVersionRawValue.isNullOrBlank()) { - useLocalBuild = true - val v = version.toString() - logger.warn("smokeTest.kotlinxIoVersion was not specified, using $v instead.") - v -} else { - kotlinxIoVersionRawValue -} - -repositories { - mavenCentral() -} - -tasks { - val kotlinVersion: String = libs.versions.kotlin.get() - - val verifyMavenProjects by registering(Exec::class) { - workingDir = projectDir.resolve("src").resolve("test").resolve("resources").resolve("maven-projects") - executable = workingDir.resolve(getMavenWrapperName()).absolutePath - args = buildList { - add("-DKOTLIN_VERSION=$kotlinVersion") - add("-DKOTLINX_IO_VERSION=$kotlinxIoVersion") - if (stagingRepository.isNotBlank()) { - add("-DSTAGING_REPOSITORY_URL=$stagingRepository") - add("-Pstaging") - } - add("verify") - } - if (useLocalBuild) { - dependsOn(project(":kotlinx-io-core").tasks.named("publishToMavenLocal")) - dependsOn(project(":kotlinx-io-bytestring").tasks.named("publishToMavenLocal")) - } - } - val cleanMavenProjects by registering(Exec::class) { - workingDir = projectDir.resolve("src").resolve("test").resolve("resources").resolve("maven-projects") - executable = workingDir.resolve(getMavenWrapperName()).absolutePath - args = listOf("-DKOTLIN_VERSION=$kotlinVersion", "-DKOTLINX_IO_VERSION=$kotlinxIoVersion", "clean") - } - - val verifyGradleProjects = create("verifyGradleProjects", Test::class) { - useJUnit() - if (useLocalBuild) { - dependsOn(project(":kotlinx-io-core").tasks.named("publishToMavenLocal")) - dependsOn(project(":kotlinx-io-bytestring").tasks.named("publishToMavenLocal")) - } - - systemProperty("kotlinxIoVersion", kotlinxIoVersion) - systemProperty("stagingRepository", stagingRepository) - systemProperty("useLocalBuild", useLocalBuild) - systemProperty("kotlinVersion", kotlinVersion) - } - - create("smokeTest") { - dependsOn(verifyMavenProjects) - dependsOn(verifyGradleProjects) - } - - named("clean").configure { - dependsOn(cleanMavenProjects) - } - - check.configure { - enabled = false - } - test.configure { - enabled = false - } -} - -dependencies { - testImplementation(kotlin("test")) - testImplementation(gradleTestKit()) -} - -fun getMavenWrapperName(): String = - if (Os.isFamily(Os.FAMILY_WINDOWS)) { - "mvnw.cmd" - } else { - "mvnw" - } diff --git a/smoke-tests/src/test/kotlin/GradleProjectsTest.kt b/smoke-tests/src/test/kotlin/GradleProjectsTest.kt deleted file mode 100644 index 0364a0738..000000000 --- a/smoke-tests/src/test/kotlin/GradleProjectsTest.kt +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -package kotlinx.io - -import org.gradle.testkit.runner.BuildResult -import org.gradle.testkit.runner.GradleRunner -import org.gradle.testkit.runner.TaskOutcome -import org.junit.Rule -import org.junit.rules.TemporaryFolder -import kotlin.io.path.outputStream -import kotlin.test.Test -import kotlin.test.assertEquals - -private const val buildScriptFilename = "build.gradle.kts" -private const val settingsFilename = "settings.gradle.kts" - -public class GradleProjectsTest { - @JvmField - @Rule - public val projectDir = TemporaryFolder() - - private val kotlinxIoVersion: String = System.getProperty("kotlinxIoVersion")!! - private val kotlinVersion: String = System.getProperty("kotlinVersion")!! - private val useLocalBuild: String = System.getProperty("useLocalBuild")!! - private val stagingRepository: String = System.getProperty("stagingRepository")!! - private val bytestringDependency: String = "org.jetbrains.kotlinx:kotlinx-io-bytestring:$kotlinxIoVersion" - private val coreDependency: String = "org.jetbrains.kotlinx:kotlinx-io-core:$kotlinxIoVersion" - - private fun generateBuildScript(multiplatform: Boolean, dependencyName: String) { - val templateFile = (if (multiplatform) "kmp" else "jvm") + "." + buildScriptFilename - var template = GradleProjectsTest::class.java.getResourceAsStream( - "/templates/$templateFile")!!.reader().readText() - - template = template.replace("%DEPENDENCY%", dependencyName) - .replace("%KOTLIN_VERSION%", kotlinVersion) - .replace("%USE_LOCAL_REPO%", useLocalBuild) - .replace("%STAGING_REPOSITORY%", stagingRepository) - - val outFile = projectDir.newFile(buildScriptFilename) - outFile.writeText(template) - } - - private fun setupTest(testCase: String, multiplatform: Boolean, dependencyName: String) { - copySrcFile(testCase, multiplatform) - - projectDir.newFile(settingsFilename).outputStream().use { - GradleProjectsTest::class.java.getResourceAsStream("/templates/$settingsFilename")!!.copyTo(it) - } - - generateBuildScript(multiplatform, dependencyName) - } - - private fun copySrcFile(testCase: String, multiplatform: Boolean) { - val testSubdir = if (multiplatform) "commonTest" else "test" - val srcDir = projectDir.newFolder("src", testSubdir, "kotlin") - val resource = GradleProjectsTest::class.java.getResourceAsStream("/gradle-projects/$testCase/SmokeTest.kt")!! - val outFile = srcDir.toPath().resolve("SmokeTest.kt") - outFile.outputStream().use { - resource.copyTo(it) - } - } - - private fun assertTestPassed(buildResult: BuildResult, taskName: String = ":test") { - assertEquals(TaskOutcome.SUCCESS, buildResult.task(taskName)?.outcome, - "Task \"$taskName\" should pass.\n" + buildResult.output) - } - - @Test - fun bytestringJvm() { - setupTest("bytestring-jvm", false, bytestringDependency) - val results = GradleRunner.create() - .withProjectDir(projectDir.root) - .withArguments(":test") - .run() - - assertTestPassed(results) - } - - @Test - fun coreJvm() { - setupTest("core-jvm", false, coreDependency) - val results = GradleRunner.create() - .withProjectDir(projectDir.root) - .withArguments(":test") - .run() - - assertTestPassed(results) - } - - @Test - fun bytestringMultiplatform() { - setupTest("bytestring-multiplatform", true, bytestringDependency) - val results = GradleRunner.create() - .withProjectDir(projectDir.root) - .withArguments(":allTests") - .run() - - assertTestPassed(results, ":allTests") - } - - @Test - fun coreMultiplatform() { - setupTest("core-multiplatform", true, coreDependency) - val results = GradleRunner.create() - .withProjectDir(projectDir.root) - .withArguments(":allTests") - .run() - - assertTestPassed(results, ":allTests") - } -} diff --git a/smoke-tests/src/test/resources/gradle-projects/bytestring-jvm/SmokeTest.kt b/smoke-tests/src/test/resources/gradle-projects/bytestring-jvm/SmokeTest.kt deleted file mode 100644 index 1292914c3..000000000 --- a/smoke-tests/src/test/resources/gradle-projects/bytestring-jvm/SmokeTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -package org.example - -import kotlinx.io.bytestring.ByteString -import kotlin.test.Test -import kotlin.test.assertEquals - -class SmokeTest { - @Test - fun test() { - assertEquals("ByteString(size=1 hex=42)", ByteString(0x42).toString()) - } -} diff --git a/smoke-tests/src/test/resources/gradle-projects/bytestring-multiplatform/SmokeTest.kt b/smoke-tests/src/test/resources/gradle-projects/bytestring-multiplatform/SmokeTest.kt deleted file mode 100644 index 6379f1e2a..000000000 --- a/smoke-tests/src/test/resources/gradle-projects/bytestring-multiplatform/SmokeTest.kt +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -import kotlinx.io.bytestring.ByteString -import kotlin.test.Test -import kotlin.test.assertEquals - -class SmokeTest { - @Test - fun test() { - assertEquals("ByteString(size=1 hex=42)", ByteString(0x42).toString()) - } -} diff --git a/smoke-tests/src/test/resources/gradle-projects/core-jvm/SmokeTest.kt b/smoke-tests/src/test/resources/gradle-projects/core-jvm/SmokeTest.kt deleted file mode 100644 index b9aab8b87..000000000 --- a/smoke-tests/src/test/resources/gradle-projects/core-jvm/SmokeTest.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -package org.example - -import kotlinx.io.Buffer -import kotlinx.io.bytestring.ByteString -import kotlinx.io.readByteArray -import kotlinx.io.readByteString -import kotlinx.io.write -import kotlin.test.Test -import kotlin.test.assertContentEquals -import kotlin.test.assertEquals - -class SmokeTest { - @Test - fun testCore() { - val buffer = Buffer() - buffer.writeLong(0) - assertContentEquals(ByteArray(8), buffer.readByteArray()) - } - - @Test - fun testByteString() { - val byteString = ByteString(0x42) - val buffer = Buffer() - buffer.write(byteString) - - assertEquals(ByteString(0x42), buffer.readByteString()) - } -} diff --git a/smoke-tests/src/test/resources/gradle-projects/core-multiplatform/SmokeTest.kt b/smoke-tests/src/test/resources/gradle-projects/core-multiplatform/SmokeTest.kt deleted file mode 100644 index c8546e00c..000000000 --- a/smoke-tests/src/test/resources/gradle-projects/core-multiplatform/SmokeTest.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -import kotlinx.io.Buffer -import kotlinx.io.bytestring.ByteString -import kotlinx.io.files.Path -import kotlinx.io.files.SystemFileSystem -import kotlinx.io.readByteArray -import kotlinx.io.readByteString -import kotlinx.io.write -import kotlin.test.Test -import kotlin.test.assertContentEquals -import kotlin.test.assertEquals - -class SmokeTest { - @Test - fun testCore() { - val buffer = Buffer() - buffer.writeLong(0) - assertContentEquals(ByteArray(8), buffer.readByteArray()) - } - - @Test - fun testByteString() { - val byteString = ByteString(0x42) - val buffer = Buffer() - buffer.write(byteString) - - assertEquals(ByteString(0x42), buffer.readByteString()) - } - - @Test - fun testUseFiles() { - try { - SystemFileSystem.exists(Path(".")) - } catch (t: Throwable) { - // that's fine - } - } -} diff --git a/smoke-tests/src/test/resources/maven-projects/.mvn/wrapper/maven-wrapper.jar b/smoke-tests/src/test/resources/maven-projects/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index cb28b0e37c7d206feb564310fdeec0927af4123a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62547 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 A?*IS* diff --git a/smoke-tests/src/test/resources/maven-projects/.mvn/wrapper/maven-wrapper.properties b/smoke-tests/src/test/resources/maven-projects/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index d8b2495a1..000000000 --- a/smoke-tests/src/test/resources/maven-projects/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,18 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.1/apache-maven-3.9.1-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar diff --git a/smoke-tests/src/test/resources/maven-projects/bytestring-jvm/pom.xml b/smoke-tests/src/test/resources/maven-projects/bytestring-jvm/pom.xml deleted file mode 100644 index f956622e8..000000000 --- a/smoke-tests/src/test/resources/maven-projects/bytestring-jvm/pom.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - 4.0.0 - - - org.jetbrains.kotlinx - kotlinx-maven-projects - 1.0-SNAPSHOT - - - bytestring-jvm - ${project.parent.version} - - bytestring-jvm - - - UTF-8 - 1.8 - 1.8 - - - - - org.jetbrains.kotlinx - kotlinx-io-bytestring-jvm - ${kotlinx.io.version} - - - - - - - org.jetbrains.kotlin - kotlin-maven-plugin - ${kotlin.version} - - - - compile - - compile - - - - ${project.basedir}/src/main/kotlin - - - - - - test-compile - - test-compile - - - - ${project.basedir}/src/test/kotlin - - - - - - - - diff --git a/smoke-tests/src/test/resources/maven-projects/bytestring-jvm/src/test/kotlin/SmokeTest.kt b/smoke-tests/src/test/resources/maven-projects/bytestring-jvm/src/test/kotlin/SmokeTest.kt deleted file mode 100644 index 1292914c3..000000000 --- a/smoke-tests/src/test/resources/maven-projects/bytestring-jvm/src/test/kotlin/SmokeTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -package org.example - -import kotlinx.io.bytestring.ByteString -import kotlin.test.Test -import kotlin.test.assertEquals - -class SmokeTest { - @Test - fun test() { - assertEquals("ByteString(size=1 hex=42)", ByteString(0x42).toString()) - } -} diff --git a/smoke-tests/src/test/resources/maven-projects/core-jvm/pom.xml b/smoke-tests/src/test/resources/maven-projects/core-jvm/pom.xml deleted file mode 100644 index 57d4d8a1b..000000000 --- a/smoke-tests/src/test/resources/maven-projects/core-jvm/pom.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - 4.0.0 - - org.jetbrains.kotlinx - kotlinx-maven-projects - 1.0-SNAPSHOT - - - core-jvm - ${project.parent.version} - - core-jvm - - - UTF-8 - 1.8 - 1.8 - - - - - org.jetbrains.kotlinx - kotlinx-io-core-jvm - ${kotlinx.io.version} - - - - - - - org.jetbrains.kotlin - kotlin-maven-plugin - ${kotlin.version} - - - - compile - - compile - - - - ${project.basedir}/src/main/kotlin - - - - - - test-compile - - test-compile - - - - ${project.basedir}/src/test/kotlin - - - - - - - - diff --git a/smoke-tests/src/test/resources/maven-projects/core-jvm/src/test/kotlin/SmokeTest.kt b/smoke-tests/src/test/resources/maven-projects/core-jvm/src/test/kotlin/SmokeTest.kt deleted file mode 100644 index cdeb01c22..000000000 --- a/smoke-tests/src/test/resources/maven-projects/core-jvm/src/test/kotlin/SmokeTest.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -package org.example - -import kotlinx.io.Buffer -import kotlinx.io.bytestring.ByteString -import kotlinx.io.readByteArray -import kotlinx.io.readByteString -import kotlinx.io.write -import java.lang.RuntimeException -import kotlin.test.Test -import kotlin.test.assertContentEquals -import kotlin.test.assertEquals - -class SmokeTest { - @Test - fun testCore() { - val buffer = Buffer() - buffer.writeLong(0) - assertContentEquals(ByteArray(8), buffer.readByteArray()) - } - - @Test - fun testByteString() { - val byteString = ByteString(0x42) - val buffer = Buffer() - buffer.write(byteString) - - assertEquals(ByteString(0x42), buffer.readByteString()) - } -} diff --git a/smoke-tests/src/test/resources/maven-projects/mvnw b/smoke-tests/src/test/resources/maven-projects/mvnw deleted file mode 100755 index 8d937f4c1..000000000 --- a/smoke-tests/src/test/resources/maven-projects/mvnw +++ /dev/null @@ -1,308 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Apache Maven Wrapper startup batch script, version 3.2.0 -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /usr/local/etc/mavenrc ] ; then - . /usr/local/etc/mavenrc - fi - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "$(uname)" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME - else - JAVA_HOME="/Library/Java/Home"; export JAVA_HOME - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=$(java-config --jre-home) - fi -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$JAVA_HOME" ] && - JAVA_HOME=$(cygpath --unix "$JAVA_HOME") - [ -n "$CLASSPATH" ] && - CLASSPATH=$(cygpath --path --unix "$CLASSPATH") -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && - JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="$(which javac)" - if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=$(which readlink) - if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then - if $darwin ; then - javaHome="$(dirname "\"$javaExecutable\"")" - javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" - else - javaExecutable="$(readlink -f "\"$javaExecutable\"")" - fi - javaHome="$(dirname "\"$javaExecutable\"")" - javaHome=$(expr "$javaHome" : '\(.*\)/bin') - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=$(cd "$wdir/.." || exit 1; pwd) - fi - # end of workaround - done - printf '%s' "$(cd "$basedir" || exit 1; pwd)" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - # Remove \r in case we run on Windows within Git Bash - # and check out the repository with auto CRLF management - # enabled. Otherwise, we may read lines that are delimited with - # \r\n and produce $'-Xarg\r' rather than -Xarg due to word - # splitting rules. - tr -s '\r\n' ' ' < "$1" - fi -} - -log() { - if [ "$MVNW_VERBOSE" = true ]; then - printf '%s\n' "$1" - fi -} - -BASE_DIR=$(find_maven_basedir "$(dirname "$0")") -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR -log "$MAVEN_PROJECTBASEDIR" - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" -if [ -r "$wrapperJarPath" ]; then - log "Found $wrapperJarPath" -else - log "Couldn't find $wrapperJarPath, downloading it ..." - - if [ -n "$MVNW_REPOURL" ]; then - wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" - else - wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" - fi - while IFS="=" read -r key value; do - # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) - safeValue=$(echo "$value" | tr -d '\r') - case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; - esac - done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" - log "Downloading from: $wrapperUrl" - - if $cygwin; then - wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") - fi - - if command -v wget > /dev/null; then - log "Found wget ... using wget" - [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" - else - wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - log "Found curl ... using curl" - [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" - else - curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" - fi - else - log "Falling back to using Java to download" - javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" - javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaSource=$(cygpath --path --windows "$javaSource") - javaClass=$(cygpath --path --windows "$javaClass") - fi - if [ -e "$javaSource" ]; then - if [ ! -e "$javaClass" ]; then - log " - Compiling MavenWrapperDownloader.java ..." - ("$JAVA_HOME/bin/javac" "$javaSource") - fi - if [ -e "$javaClass" ]; then - log " - Running MavenWrapperDownloader.java ..." - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -# If specified, validate the SHA-256 sum of the Maven wrapper jar file -wrapperSha256Sum="" -while IFS="=" read -r key value; do - case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; - esac -done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" -if [ -n "$wrapperSha256Sum" ]; then - wrapperSha256Result=false - if command -v sha256sum > /dev/null; then - if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then - wrapperSha256Result=true - fi - elif command -v shasum > /dev/null; then - if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then - wrapperSha256Result=true - fi - else - echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." - echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." - exit 1 - fi - if [ $wrapperSha256Result = false ]; then - echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 - echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 - echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 - exit 1 - fi -fi - -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$JAVA_HOME" ] && - JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") - [ -n "$CLASSPATH" ] && - CLASSPATH=$(cygpath --path --windows "$CLASSPATH") - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -# shellcheck disable=SC2086 # safe args -exec "$JAVACMD" \ - $MAVEN_OPTS \ - $MAVEN_DEBUG_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/smoke-tests/src/test/resources/maven-projects/mvnw.cmd b/smoke-tests/src/test/resources/maven-projects/mvnw.cmd deleted file mode 100644 index c4586b564..000000000 --- a/smoke-tests/src/test/resources/maven-projects/mvnw.cmd +++ /dev/null @@ -1,205 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Apache Maven Wrapper startup batch script, version 3.2.0 -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* -if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" - -FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %WRAPPER_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file -SET WRAPPER_SHA_256_SUM="" -FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B -) -IF NOT %WRAPPER_SHA_256_SUM%=="" ( - powershell -Command "&{"^ - "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ - "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ - " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ - " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ - " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ - " exit 1;"^ - "}"^ - "}" - if ERRORLEVEL 1 goto error -) - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% ^ - %JVM_CONFIG_MAVEN_PROPS% ^ - %MAVEN_OPTS% ^ - %MAVEN_DEBUG_OPTS% ^ - -classpath %WRAPPER_JAR% ^ - "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ - %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" -if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%"=="on" pause - -if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% - -cmd /C exit /B %ERROR_CODE% diff --git a/smoke-tests/src/test/resources/maven-projects/pom.xml b/smoke-tests/src/test/resources/maven-projects/pom.xml deleted file mode 100644 index eac3a17b0..000000000 --- a/smoke-tests/src/test/resources/maven-projects/pom.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - 4.0.0 - pom - - org.jetbrains.kotlinx - kotlinx-maven-projects - 1.0-SNAPSHOT - - maven-projects - - - UTF-8 - 1.8 - 1.8 - ${KOTLIN_VERSION} - ${KOTLINX_IO_VERSION} - ${STAGING_REPOSITORY_URL} - - - - - org.jetbrains.kotlin - kotlin-stdlib - ${kotlin.version} - - - org.jetbrains.kotlin - kotlin-test-junit - ${kotlin.version} - test - - - - - bytestring-jvm - core-jvm - - - - - staging - - - nexus - ${staring.repository.url} - - - - - diff --git a/smoke-tests/src/test/resources/templates/jvm.build.gradle.kts b/smoke-tests/src/test/resources/templates/jvm.build.gradle.kts deleted file mode 100644 index 21ed87399..000000000 --- a/smoke-tests/src/test/resources/templates/jvm.build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -plugins { - kotlin("jvm") version "%KOTLIN_VERSION%" -} - -val stagingRepository: String = "%STAGING_REPOSITORY%" -val useLocalRepo: Boolean = "%USE_LOCAL_REPO%".toBoolean() - -repositories { - mavenCentral() - if (stagingRepository.isNotBlank()) { - maven(url = stagingRepository) - } - if (useLocalRepo) { - mavenLocal() - } -} - -dependencies { - implementation("%DEPENDENCY%") - testImplementation(kotlin("test")) -} diff --git a/smoke-tests/src/test/resources/templates/kmp.build.gradle.kts b/smoke-tests/src/test/resources/templates/kmp.build.gradle.kts deleted file mode 100644 index 882aebb1d..000000000 --- a/smoke-tests/src/test/resources/templates/kmp.build.gradle.kts +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl -import org.gradle.api.Project - -plugins { - kotlin("multiplatform") version "%KOTLIN_VERSION%" -} - -val stagingRepository: String = "%STAGING_REPOSITORY%" -val useLocalRepo: Boolean = "%USE_LOCAL_REPO%".toBoolean() - -repositories { - mavenCentral() - if (stagingRepository.isNotBlank()) { - maven(url = stagingRepository) - } - if (useLocalRepo) { - mavenLocal() - } -} - -@OptIn(ExperimentalWasmDsl::class) -kotlin { - jvm() - js(IR) { - browser { - testTask { - useMocha() - } - } - nodejs() - } - wasmWasi { - nodejs() - } - wasmJs { - nodejs() - browser { - testTask { - useMocha() - } - } - } - - androidNativeArm32() - androidNativeArm64() - androidNativeX64() - androidNativeX86() - - iosArm64() - iosX64() - iosSimulatorArm64() - - watchosX64() - watchosArm32() - watchosDeviceArm64() - watchosSimulatorArm64() - watchosArm64() - - tvosArm64() - tvosX64() - tvosSimulatorArm64() - - iosSimulatorArm64() - watchosSimulatorArm64() - - linuxArm64() - linuxX64() - - @Suppress("DEPRECATION") - linuxArm32Hfp() - - macosArm64() - macosX64() - - mingwX64() - - sourceSets { - commonMain { - dependencies { - implementation("%DEPENDENCY%") - } - } - commonTest { - dependencies { - implementation(kotlin("test")) - } - } - } -} diff --git a/smoke-tests/src/test/resources/templates/settings.gradle.kts b/smoke-tests/src/test/resources/templates/settings.gradle.kts deleted file mode 100644 index a7cdf09a1..000000000 --- a/smoke-tests/src/test/resources/templates/settings.gradle.kts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers. - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. - */ - -pluginManagement { - repositories { - mavenCentral() - google() - gradlePluginPortal() - } -}