RawSource

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.

Thread-safety guarantees

RawSource implementations are not required to be thread safe. However, if an implementation provides some thread safety guarantees, it is recommended to explicitly document them.

Samples

import kotlinx.io.*
import kotlin.test.*

fun main() { 
   //sampleStart 
   /**
 * 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()) 
   //sampleEnd
}

Inheritors

Functions

Link copied to clipboard

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.

Link copied to clipboard
abstract override fun close()

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.

Link copied to clipboard
abstract fun readAtMostTo(sink: Buffer, byteCount: Long): Long

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.