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

io.dahgan.stream.Streams.kt Maven / Gradle / Ivy

The newest version!
package io.dahgan.stream

import java.util.*

/**
 * Wraps the given byte arrays, detects its encoding and finally converts it to characters.
 */
class ByteStream(private val input: ByteArray, private val offset: Int = 0) : Stream {
    companion object {
        private val X00 = 0x00.toByte()
        private val XFE = 0xFE.toByte()
        private val XFF = 0xFF.toByte()
        private val XEF = 0xEF.toByte()
        private val XBB = 0xBB.toByte()
        private val XBF = 0xBF.toByte()

        private val decoders = mapOf(
                Encoding.UTF8 to UTF8Decoder(),
                Encoding.UTF16LE to UTF16Decoder.le(),
                Encoding.UTF16BE to UTF16Decoder.be(),
                Encoding.UTF32LE to UTF32Decoder.le(),
                Encoding.UTF32BE to UTF32Decoder.be()
        )
    }

    private val encoding: Encoding by lazy {
        when {
            input.size >= 4 && input[0] == X00 && input[1] == X00 && input[2] == XFE && input[3] == XFF -> Encoding.UTF32BE
            input.size >= 4 && input[0] == X00 && input[1] == X00 && input[2] == X00 -> Encoding.UTF32BE
            input.size >= 4 && input[0] == XFF && input[1] == XFE && input[2] == X00 && input[3] == X00 -> Encoding.UTF32LE
            input.size >= 4 && input[1] == X00 && input[2] == X00 && input[3] == X00 -> Encoding.UTF32LE
            input.size >= 2 && input[0] == XFE && input[1] == XFF -> Encoding.UTF16BE
            input.size >= 1 && input[0] == X00 -> Encoding.UTF16BE
            input.size >= 2 && input[0] == XFF && input[1] == XFE -> Encoding.UTF16LE
            input.size >= 2 && input[1] == X00 -> Encoding.UTF16LE
            input.size >= 4 && input[0] == XEF && input[1] == XBB && input[2] == XBF -> Encoding.UTF8
            else -> Encoding.UTF8
        }
    }

    private val head: UniChar by lazy {
        decoders[encoding]!!.decode(input, offset)
    }

    /**
     * @see Stream#encoding()
     */
    override fun encoding(): Encoding = encoding

    /**
     * @see Stream#head()
     */
    override fun head(): UniChar = head

    /**
     * @see Stream#tail()
     */
    override fun tail(): ByteStream = ByteStream(this.input, head.offset)

    /**
     * @see Stream#isEmpty()
     */
    override fun isEmpty(): Boolean = input.size <= offset

    /**
     * @see Stream#isNotEmpty()
     */
    override fun isNotEmpty(): Boolean = !isEmpty()

    /**
     * @see Stream#push(head)
     */
    override fun push(head: UniChar): Stream = PushedStream(head, this)

    /**
     * @see Stream#codes()
     */
    override fun codes(): IntArray {
        val destination = ArrayList()

        var current = this
        while (current.isNotEmpty()) {
            destination.add(current.head.code)
            current = current.tail()
        }

        return destination.toIntArray()
    }
}

/**
 * A PushedStream adds the given decoded character as head to the given Stream.
 */
class PushedStream(private val head: UniChar, private val tail: Stream) : Stream {
    /**
     * @see Stream#encoding()
     */
    override fun encoding(): Encoding = tail.encoding()

    /**
     * @see Stream#head()
     */
    override fun head(): UniChar = head

    /**
     * @see Stream#tail()
     */
    override fun tail(): Stream = tail

    /**
     * @see Stream#isEmpty()
     */
    override fun isEmpty(): Boolean = false

    /**
     * @see Stream#isNotEmpty()
     */
    override fun isNotEmpty(): Boolean = true

    /**
     * @see Stream#push(head)
     */
    override fun push(head: UniChar): Stream = PushedStream(head, this)

    /**
     * @see Stream#codes()
     */
    override fun codes(): IntArray = intArrayOf(head.code, *tail.codes())
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy