All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.RealSink.kt Maven / Gradle / Ivy

/*
 * 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." }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy