Skip to content

Binaries #109

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions benchmarks/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
apply plugin: 'kotlin-multiplatform'
apply plugin: 'kotlin-allopen'
apply plugin: 'kotlinx.benchmark'
plugins{
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.allopen"
id "kotlinx.benchmark"
}


// how to apply plugin to a specific source set?
allOpen {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package kotlinx.io

import kotlinx.benchmark.*
import kotlinx.io.bytes.*
import kotlinx.io.bytes.buildInput

/**
* Benchmark with prebuilt ByteInput
Expand All @@ -13,7 +13,7 @@ class BytesInputReadBenchmark {
lateinit var input: Input

@Param("2097152")
open var size: Int = 42
var size: Int = 42

@Setup
fun setup() {
Expand Down
26 changes: 4 additions & 22 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,28 +1,10 @@
buildscript {
repositories {
jcenter()
gradlePluginPortal()
maven { url "https://dl.bintray.com/kotlin/kotlin-eap" }
maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
maven { url "https://dl.bintray.com/orangy/maven" }
maven { url "https://dl.bintray.com/kotlin/kotlinx" }
}

dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
classpath "org.jetbrains.kotlinx:kotlinx.benchmark.gradle:$benchmarks_version"
classpath "com.vanniktech:gradle-android-junit-jacoco-plugin:0.15.0"
}
}

allprojects {
repositories {
mavenLocal()
jcenter()
maven { url "https://dl.bintray.com/kotlin/kotlinx" }
maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
maven { url "https://dl.bintray.com/kotlin/kotlin-eap" }
maven { url "https://dl.bintray.com/orangy/maven" }
maven{url = "https://dl.bintray.com/kotlin/kotlinx"}
maven{url = "https://dl.bintray.com/kotlin/kotlin-dev"}
maven{url = "https://dl.bintray.com/kotlin/kotlin-eap"}
maven{url = "https://dl.bintray.com/orangy/maven"}
}
}
32 changes: 26 additions & 6 deletions core/build.gradle
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
buildscript {
repositories {
jcenter()
mavenLocal()
maven { url "https://dl.bintray.com/mipt-npm/kscience" }
}

dependencies {
classpath 'ru.mipt.npm:gradle-tools:0.6.0'
}
}

plugins {
id "kotlin-multiplatform"
id "org.jetbrains.kotlin.multiplatform"
id "maven-publish"
id "jacoco"
id 'org.jetbrains.dokka' version '0.10.1'
id 'org.jetbrains.dokka' version '1.4.0'
id "ru.mipt.npm.publish" version "0.6.0"
}

def ideaActive = System.getProperty('idea.active') == 'true'
Expand All @@ -21,7 +34,7 @@ kotlin {
macosX64("native")
}

js {
js(BOTH) {
nodejs {
testTask {
debug = false
Expand All @@ -30,6 +43,14 @@ kotlin {
}
}
}
browser {
testTask {
debug = false
useMocha {
timeout = "0"
}
}
}
}

sourceSets {
Expand Down Expand Up @@ -142,9 +163,8 @@ kotlin {
}
}

dokka {
outputFormat = 'gfm'
outputDirectory = "$buildDir/dokka"
dokkaGfm.configure {
outputDirectory.set(file("$buildDir/dokka"))
}

jacoco {
Expand Down
69 changes: 69 additions & 0 deletions core/commonMain/src/kotlinx/io/Binary.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package kotlinx.io

import kotlinx.io.internal.ByteArrayInput
import kotlin.math.min

/**
* [Binary] represents a fixed-size multi-read byte block, which is not attached to the Input which was used to create it.
* The binary could be associated with a resource, but it should guarantee that when someone is trying to read the binary,
* this resource is re-acquired.
*/
public interface Binary {

public val size: Int

/**
* Read maximum of [atMost] bytes as input from the binary, starting at [offset]. The generated input is always closed
* when leaving scope, so it could not be leaked outside of scope of [block].
*/
public fun <R> read(offset: Int = 0, atMost: Int = size - offset, block: Input.() -> R): R

public companion object {
public val EMPTY: Binary = ByteArrayBinary(ByteArray(0))
}
}

internal class ByteArrayBinary(
internal val array: ByteArray,
internal val start: Int = 0,
override val size: Int = array.size - start
) : Binary {

override fun <R> read(offset: Int, atMost: Int, block: Input.() -> R): R {
require(offset >= 0) { "Offset must be positive" }
require(offset < array.size) { "Offset $offset is larger than array size" }
val input = ByteArrayInput(
array,
offset + start,
min(offset + start + atMost, start + size)
)
return input.use(block)
}
}

public fun ByteArray.asBinary(): Binary = ByteArrayBinary(this)

/**
* Produce a [ByteArray] representing an exact copy of this [Binary]
*/
public fun Binary.toByteArray(): ByteArray = if (this is ByteArrayBinary) {
array.copyOf() // TODO do we need to ensure data safety here?
} else {
read {
readByteArray()
}
}

/**
* Direct write of binary to the output. Returns the number of bytes written
*/
public fun Output.writeBinary(binary: Binary): Int {
return if (binary is ByteArrayBinary) {
writeByteArray(binary.array, binary.start, binary.start + binary.size)
binary.size
} else {
binary.read {
copyTo(this@writeBinary)
}
}
}
2 changes: 1 addition & 1 deletion core/commonMain/src/kotlinx/io/Closeable.common.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package kotlinx.io
* Closeable resource.
*/
public expect interface Closeable {
fun close(): Unit
public fun close(): Unit
}

/**
Expand Down
38 changes: 38 additions & 0 deletions core/commonMain/src/kotlinx/io/CountingInput.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package kotlinx.io

import kotlinx.io.buffer.Buffer
import kotlinx.io.pool.ObjectPool

/**
* An input that can track its current reader position relative to input start
*/
public abstract class CountingInput(pool: ObjectPool<Buffer>) : Input(pool) {
private var bufferIndex: Int = 0
private var lastBufferSize: Int = 0

final override fun fill(buffer: Buffer, startIndex: Int, endIndex: Int): Int {
bufferIndex += lastBufferSize
lastBufferSize = fillCounting(buffer, startIndex, endIndex, bufferIndex)
return lastBufferSize
}

/**
* Fill buffer appending counter
* @param absoluteBufferIndex is the absolute start index of buffer start in the input
*/
public abstract fun fillCounting(buffer: Buffer, startIndex: Int, endIndex: Int, absoluteBufferIndex: Int): Int

/**
* Absolute read position relative to input start or last reset position
*/
public fun absolutePosition(): Int = bufferIndex + bufferPosition()

/**
* Reset counter and read position if it is applicable
*/
protected open fun position(startIndex: Int) {
bufferIndex = startIndex
lastBufferSize = 0
//TODO discard current buffer to avoid misuse
}
}
8 changes: 4 additions & 4 deletions core/commonMain/src/kotlinx/io/Exceptions.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package kotlinx.io

import kotlinx.io.buffer.*
import kotlinx.io.buffer.Buffer

expect open class IOException(message: String, cause: Throwable?) : Exception {
constructor(message: String)
public expect open class IOException(message: String, cause: Throwable?) : Exception {
public constructor(message: String)
}

expect open class EOFException(message: String) : IOException
public expect open class EOFException(message: String) : IOException

internal fun checkBufferAndIndexes(buffer: Buffer, startIndex: Int, endIndex: Int) {
require(startIndex >= 0) { "Start index ($startIndex) should be positive." }
Expand Down
2 changes: 1 addition & 1 deletion core/commonMain/src/kotlinx/io/ExperimentalIoApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package kotlinx.io
/**
* API marked with this annotation is experimental and could be changed
*/
@Experimental(Experimental.Level.WARNING)
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
public annotation class ExperimentalIoApi
18 changes: 15 additions & 3 deletions core/commonMain/src/kotlinx/io/Input.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package kotlinx.io

import kotlinx.io.buffer.*
import kotlinx.io.bytes.*
import kotlinx.io.pool.*
import kotlin.math.*
import kotlinx.io.bytes.Bytes
import kotlinx.io.bytes.pointed
import kotlinx.io.pool.ObjectPool
import kotlin.math.min

/**
* [Input] is an abstract base class for synchronous byte readers.
Expand Down Expand Up @@ -557,4 +558,15 @@ public abstract class Input : Closeable {
val previewSize = previewBytes?.size(previewIndex) ?: limit
return previewSize - position
}

/**
* Read a [Binary] of given [size] if possible. The resulting binary is independent from this input and could be used
* separately.
*/
public open fun readBinary(size: Int): Binary = readByteArray(size).asBinary()

/**
* Current read position in a current buffer.
*/
protected fun bufferPosition(): Int = position
}
4 changes: 2 additions & 2 deletions core/commonMain/src/kotlinx/io/InputOperations.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package kotlinx.io

import kotlinx.io.buffer.*
import kotlin.math.*
import kotlin.math.min

/**
* Copies the [Input] content to [destination].
Expand Down Expand Up @@ -61,7 +61,7 @@ public fun Input.readByteArray(size: Int): ByteArray {
checkSize(size)

val result = ByteArray(size)
var position = size
var position = 0
while (position < size) {
val count = readBufferRange { buffer, startOffset, endOffset ->
val length = min(endOffset - startOffset, size - position)
Expand Down
4 changes: 2 additions & 2 deletions core/commonMain/src/kotlinx/io/Inputs.common.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package kotlinx.io

import kotlinx.io.internal.*
import kotlinx.io.internal.LimitingInput

/**
* Creates an input from the given byte array, starting from inclusively [startIndex] and until [endIndex] exclusively.
Expand Down Expand Up @@ -28,4 +28,4 @@ public fun Input.limit(limit: Long): Input {
* The resulting input will be closed as soon as either the original input is exhausted
* or [limit] bytes is read.
*/
public fun Input.limit(limit: Int) = limit(limit.toLong())
public fun Input.limit(limit: Int): Input = limit(limit.toLong())
9 changes: 6 additions & 3 deletions core/commonMain/src/kotlinx/io/Output.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package kotlinx.io

import kotlinx.io.buffer.*
import kotlinx.io.pool.*
import kotlinx.io.buffer.Buffer
import kotlinx.io.buffer.DEFAULT_BUFFER_SIZE
import kotlinx.io.buffer.DefaultBufferPool
import kotlinx.io.buffer.fill
import kotlinx.io.pool.ObjectPool

/**
* [Output] is an abstract base for writable streams of bytes over some sink.
Expand All @@ -17,7 +20,7 @@ public abstract class Output(
/**
* Buffer pool used by this [Input] for copy and cache operations.
*/
val bufferPool: ObjectPool<Buffer> get() = pool
public val bufferPool: ObjectPool<Buffer> get() = pool

/**
* Current buffer.
Expand Down
6 changes: 4 additions & 2 deletions core/commonMain/src/kotlinx/io/Outputs.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package kotlinx.io

import kotlinx.io.buffer.*
import kotlinx.io.buffer.Buffer
import kotlinx.io.buffer.UnmanagedBufferPool
import kotlinx.io.buffer.get

/**
* Output which uses a byte array as its data storage.
Expand Down Expand Up @@ -50,6 +52,6 @@ public class ByteArrayOutput(initialCapacity: Int = 16) : Output(UnmanagedBuffer
}
}

@UseExperimental(ExperimentalStdlibApi::class)
@OptIn(ExperimentalStdlibApi::class)
private fun powOf2(minCapacity: Int) = if (minCapacity >= 1 shl 30) minCapacity else (1 shl (32 - (minCapacity - 1).countLeadingZeroBits()))
}
5 changes: 2 additions & 3 deletions core/commonMain/src/kotlinx/io/buffer/BufferPools.common.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package kotlinx.io.buffer

import kotlinx.io.pool.*
import kotlin.native.concurrent.*
import kotlinx.io.pool.DefaultPool
import kotlin.native.concurrent.ThreadLocal

internal const val DEFAULT_BUFFER_SIZE: Int = 1024
internal const val DEFAULT_POOL_CAPACITY: Int = 16
Expand All @@ -25,7 +25,6 @@ internal class DefaultBufferPool(

@ThreadLocal
companion object {
@ThreadLocal
val Instance = DefaultBufferPool()
}
}
Loading