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

org.kin.stellarfork.codec.BinaryCodec.kt Maven / Gradle / Ivy

/*
 * 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.
 */
package org.kin.stellarfork.codec

import kotlin.experimental.and
import kotlin.experimental.or

/*
 * 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.
 */ /**
 * Converts between byte arrays and strings of "0"s and "1"s.
 *
 *
 * TODO: may want to add more bit vector functions like and/or/xor/nand
 * TODO: also might be good to generate boolean[] from byte[] et cetera.
 *
 * @author Apache Software Foundation
 * @version $Id$
 * @since 1.3
 */
class BinaryCodec : BinaryDecoder, BinaryEncoder {
    /**
     * Converts an array of raw binary data into an array of ASCII 0 and 1 characters.
     *
     * @param source the raw binary data to convert
     * @return 0 and 1 ASCII character bytes one for each bit of the argument
     */
    override fun encode(source: ByteArray?): ByteArray? {
        return toAsciiBytes(source)
    }

    /**
     * Converts an array of raw binary data into an array of ASCII 0 and 1 chars.
     *
     * @param source the raw binary data to convert
     * @return 0 and 1 ASCII character chars one for each bit of the argument
     * @throws EncoderException if the argument is not a byte[]
     */
    @Throws(EncoderException::class)
    override fun encode(source: Any?): Any? {
        if (source !is ByteArray) {
            throw EncoderException("argument not a byte array")
        }
        return toAsciiChars(source)
    }

    /**
     * Decodes a byte array where each byte represents an ASCII '0' or '1'.
     *
     * @param source each byte represents an ASCII '0' or '1'
     * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
     * @throws DecoderException if argument is not a byte[], char[] or String
     */
    @Throws(DecoderException::class)
    override fun decode(source: Any?): Any? {
        if (source == null) {
            return EMPTY_BYTE_ARRAY
        }
        if (source is ByteArray) {
            return fromAscii(source)
        }
        if (source is CharArray) {
            return fromAscii(source)
        }
        if (source is String) {
            return fromAscii(source.toCharArray())
        }
        throw DecoderException("argument not a byte array")
    }

    /**
     * Decodes a byte array where each byte represents an ASCII '0' or '1'.
     *
     * @param source each byte represents an ASCII '0' or '1'
     * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
     */
    override fun decode(source: ByteArray?): ByteArray? {
        return fromAscii(source)
    }

    /**
     * Decodes a String where each char of the String represents an ASCII '0' or '1'.
     *
     * @param ascii String of '0' and '1' characters
     * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
     */
    fun toByteArray(ascii: String?): ByteArray {
        return if (ascii == null) {
            EMPTY_BYTE_ARRAY
        } else fromAscii(ascii.toCharArray())
    }

    companion object {
        /*
         * tried to avoid using ArrayUtils to minimize dependencies
         * while using these empty arrays - dep is just not worth it.
         */

        /**
         * Empty char array.
         */
        private val EMPTY_CHAR_ARRAY = CharArray(0)
        /**
         * Empty byte array.
         */
        private val EMPTY_BYTE_ARRAY = ByteArray(0)
        /**
         * Mask for bit 0 of a byte.
         */
        private const val BIT_0 = 1
        /**
         * Mask for bit 1 of a byte.
         */
        private const val BIT_1 = 0x02
        /**
         * Mask for bit 2 of a byte.
         */
        private const val BIT_2 = 0x04
        /**
         * Mask for bit 3 of a byte.
         */
        private const val BIT_3 = 0x08
        /**
         * Mask for bit 4 of a byte.
         */
        private const val BIT_4 = 0x10
        /**
         * Mask for bit 5 of a byte.
         */
        private const val BIT_5 = 0x20
        /**
         * Mask for bit 6 of a byte.
         */
        private const val BIT_6 = 0x40
        /**
         * Mask for bit 7 of a byte.
         */
        private const val BIT_7 = 0x80
        private val BITS = intArrayOf(
            BIT_0,
            BIT_1,
            BIT_2,
            BIT_3,
            BIT_4,
            BIT_5,
            BIT_6,
            BIT_7
        )
        // ------------------------------------------------------------------------
        //
        // static codec operations
        //
        // ------------------------------------------------------------------------
        /**
         * Decodes a char array where each char represents an ASCII '0' or '1'.
         *
         * @param ascii each char represents an ASCII '0' or '1'
         * @return the raw encoded binary where each bit corresponds to a char in the char array argument
         */
        @JvmStatic
        fun fromAscii(ascii: CharArray?): ByteArray {
            if (ascii == null || ascii.size == 0) {
                return EMPTY_BYTE_ARRAY
            }
            // get length/8 times bytes with 3 bit shifts to the right of the length
            val l_raw = ByteArray(ascii.size shr 3)
            /*
         * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the
         * loop.
         */
            var ii = 0
            var jj = ascii.size - 1
            while (ii < l_raw.size) {
                for (bits in BITS.indices) {
                    if (ascii[jj - bits] == '1') {
                        l_raw[ii] = l_raw[ii] or BITS[bits].toByte()
                    }
                }
                ii++
                jj -= 8
            }
            return l_raw
        }

        /**
         * Decodes a byte array where each byte represents an ASCII '0' or '1'.
         *
         * @param ascii each byte represents an ASCII '0' or '1'
         * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
         */
        @JvmStatic
        fun fromAscii(ascii: ByteArray?): ByteArray? {
            if (isEmpty(ascii)) {
                return EMPTY_BYTE_ARRAY
            }
            // get length/8 times bytes with 3 bit shifts to the right of the length
            val l_raw = ByteArray(ascii!!.size shr 3)
            /*
         * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the
         * loop.
         */
            var ii = 0
            var jj = ascii.size - 1
            while (ii < l_raw.size) {
                for (bits in BITS.indices) {
                    if (ascii[jj - bits] == '1'.toByte()) {
                        l_raw[ii] = l_raw[ii] or BITS[bits].toByte()
                    }
                }
                ii++
                jj -= 8
            }
            return l_raw
        }

        /**
         * Returns `true` if the given array is `null` or empty (size 0.)
         *
         * @param array the source array
         * @return `true` if the given array is `null` or empty (size 0.)
         */
        private fun isEmpty(array: ByteArray?): Boolean {
            return array == null || array.size == 0
        }

        /**
         * Converts an array of raw binary data into an array of ASCII 0 and 1 character bytes - each byte is a truncated
         * char.
         *
         * @param raw the raw binary data to convert
         * @return an array of 0 and 1 character bytes for each bit of the argument
         */
        @JvmStatic
        fun toAsciiBytes(raw: ByteArray?): ByteArray? {
            if (isEmpty(raw)) {
                return EMPTY_BYTE_ARRAY
            }
            // get 8 times the bytes with 3 bit shifts to the left of the length
            val l_ascii = ByteArray(raw!!.size shl 3)
            /*
         * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the
         * loop.
         */
            var ii = 0
            var jj = l_ascii.size - 1
            while (ii < raw.size) {
                for (bits in BITS.indices) {
                    if ((raw[ii] and BITS[bits].toByte()).toInt() == 0) {
                        l_ascii[jj - bits] = '0'.toByte()
                    } else {
                        l_ascii[jj - bits] = '1'.toByte()
                    }
                }
                ii++
                jj -= 8
            }
            return l_ascii
        }

        /**
         * Converts an array of raw binary data into an array of ASCII 0 and 1 characters.
         *
         * @param raw the raw binary data to convert
         * @return an array of 0 and 1 characters for each bit of the argument
         */
        @JvmStatic
        fun toAsciiChars(raw: ByteArray?): CharArray? {
            if (isEmpty(raw)) {
                return EMPTY_CHAR_ARRAY
            }
            // get 8 times the bytes with 3 bit shifts to the left of the length
            val l_ascii = CharArray(raw!!.size shl 3)
            /*
         * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the
         * loop.
         */
            var ii = 0
            var jj = l_ascii.size - 1
            while (ii < raw.size) {
                for (bits in BITS.indices) {
                    if ((raw[ii] and BITS[bits].toByte()).toInt() == 0) {
                        l_ascii[jj - bits] = '0'
                    } else {
                        l_ascii[jj - bits] = '1'
                    }
                }
                ii++
                jj -= 8
            }
            return l_ascii
        }

        /**
         * Converts an array of raw binary data into a String of ASCII 0 and 1 characters.
         *
         * @param raw the raw binary data to convert
         * @return a String of 0 and 1 characters representing the binary data
         */
        @JvmStatic
        fun toAsciiString(raw: ByteArray?): String? {
            return toAsciiChars(raw)?.let { String(it) }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy