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

ru.inforion.lab403.common.extensions.arrays.kt Maven / Gradle / Ivy

There is a newer version: 0.3.5
Show newest version
@file:Suppress("NOTHING_TO_INLINE", "unused")

package ru.inforion.lab403.common.extensions

import java.io.DataInputStream
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.ByteOrder.BIG_ENDIAN
import java.nio.ByteOrder.LITTLE_ENDIAN
import java.nio.charset.Charset
import kotlin.math.abs

/**
 * Safely convert byte array to string without loss any information (even negative bytes)
 *
 * Безопасно конвертирует массив байт в строку с возможностью обратного преобразования
 */
inline fun ByteArray.convertToString() = toString(Charsets.ISO_8859_1)

/**
 * Safely convert string to byte array without loss any information (even negative bytes)
 *
 * Безопасно конвертирует строку в массив байт с возможностью обратного преобразования
 */
inline fun String.convertToBytes() = toByteArray(Charsets.ISO_8859_1)

fun ByteArray.fromPDP11(start: Int = 0, end: Int = 0): ByteArray {
    val result = this.slice(start until end).toByteArray()
    for (k in result.indices step 2) {
        val tmp = result[k]
        result[k] = result[k + 1]
        result[k + 1] = tmp
    }
    return result
}

fun  Array.bisectLeft(key: T): Int where T : Comparable {
    var idx = abs(sorted().binarySearch(key)).coerceAtMost(size - 1)
    while (idx > 0 && this[idx - 1] >= key) idx--
    return idx
}

inline operator fun  Array.get(range: IntRange): Array {
    val size = if (range.last <= 0) {
        this.size - range.first - range.last
    } else {
        range.last - range.first + 1
    }
    return Array(size) { this[(it + range.first) % this.size] }
}

operator fun ByteArray.get(range: IntRange): ByteArray {
    val size = if (range.last <= 0) {
        this.size - range.first - range.last
    } else {
        range.last - range.first + 1
    }

    val result = ByteArray(size)

    if (range.last < 0) {
        val s1 = this.size - range.first
        val s2 = -range.last
        System.arraycopy(this, range.first, result, 0, s1)
        System.arraycopy(this, 0, result, s1, s2)
    } else {
        System.arraycopy(this, range.first, result, 0, result.size)
    }

    return result
}

fun ByteArray.startswith(data: ByteArray, size: Int = -1): Boolean {
    require(size <= data.size) { "size must be <= data.size" }
    if (size != -1) {
        if (size > this.size)
            return false
    } else {
        if (data.size > this.size)
            return false
    }
    val last = (if (size != -1) size else data.size) - 1
    for (k in 0..last) {
        if (this[k] != data[k])
            return false
    }
    return true
}

fun ByteArray.fuzzyStartswith(data: ByteArray, size: Int = -1, fuzzy: List): Boolean {
    require(size <= data.size) { "size must be <= data.size" }
    if (size != -1) {
        if (size > this.size)
            return false
    } else {
        if (data.size > this.size)
            return false
    }
    val last = (if (size != -1) size else data.size) - 1
    for (k in 0..last)
        if (!fuzzy[k] && this[k] != data[k])
            return false
    return true
}

inline fun ByteArray.indexOf(element: Byte, fromIndex: Int): Int {
    for (index in fromIndex..size)
        if (element == this[index])
            return index
    return -1
}

inline fun ByteArray.copyOfRange(start: Int) = copyOfRange(start, size)

inline funByteArray.split(byte: Byte, limit: Int = 0, modify: (ByteArray) -> T): List {
    require(limit >= 0) { "Limit must be non-negative, but was $limit." }

    var currentOffset = 0
    var nextIndex = indexOf(byte, currentOffset)
    if (nextIndex == -1 || limit == 1) {
        return listOf(modify(this.copyOf()))
    }

    val isLimited = limit > 0
    val result = ArrayList(if (isLimited) limit.coerceAtMost(10) else 10)
    do {
        result.add(modify(copyOfRange(currentOffset, nextIndex)))
        currentOffset = nextIndex + 1
        // Do not search for next occurrence if we're reaching limit
        if (isLimited && result.size == limit - 1) break
        nextIndex = indexOf(byte, currentOffset)
    } while (nextIndex != -1)

    result.add(modify(copyOfRange(currentOffset)))
    return result
}

inline funByteArray.split(char: Char, limit: Int = 0, modify: (ByteArray) -> T) = split(char.toByte(), limit, modify)

inline fun ByteArray.split(char: Char, limit: Int = 0) = split(char.toByte(), limit) { it }

fun ByteArray.chunks(length: Int): List {
    var offset = 0
    val dis = DataInputStream(this.inputStream())
    return List(this.size / length + 1) {
        val size = Math.min(dis.available(), length)
        val buffer = ByteArray(size)
        offset += dis.read(buffer, 0, size)
        buffer
    }
}

inline val ByteBuffer.byte: Byte get() = get()
inline fun ByteBuffer.byte(value: Byte): ByteBuffer = put(value)
inline fun ByteBuffer.short(value: Short): ByteBuffer = putShort(value)
inline fun ByteBuffer.int(value: Int): ByteBuffer = putInt(value)
inline fun ByteBuffer.long(value: Long): ByteBuffer = putLong(value)

operator fun >Array.get(e: Enum): T = this[e.ordinal]
operator fun >Array.set(e: Enum, value: T) {
    this[e.ordinal] = value
}

fun ByteArray.putInt64(index: Int, value: Long, order: ByteOrder = LITTLE_ENDIAN) {
    when (order) {
        BIG_ENDIAN -> {
            this[index + 0] = value[63..56].asByte
            this[index + 1] = value[55..48].asByte
            this[index + 2] = value[47..40].asByte
            this[index + 3] = value[39..32].asByte
            this[index + 4] = value[31..24].asByte
            this[index + 5] = value[23..16].asByte
            this[index + 6] = value[15..8].asByte
            this[index + 7] = value[7..0].asByte
        }
        LITTLE_ENDIAN -> {
            this[index + 7] = value[63..56].asByte
            this[index + 6] = value[55..48].asByte
            this[index + 5] = value[47..40].asByte
            this[index + 4] = value[39..32].asByte
            this[index + 3] = value[31..24].asByte
            this[index + 2] = value[23..16].asByte
            this[index + 1] = value[15..8].asByte
            this[index + 0] = value[7..0].asByte
        }
        else -> throw IllegalArgumentException("WRONG BYTE ORDER")
    }
}

fun ByteArray.putInt56(index: Int, value: Long, order: ByteOrder = LITTLE_ENDIAN) {
    when (order) {
        BIG_ENDIAN -> {
            this[index + 0] = value[55..48].asByte
            this[index + 1] = value[47..40].asByte
            this[index + 2] = value[39..32].asByte
            this[index + 3] = value[31..24].asByte
            this[index + 4] = value[23..16].asByte
            this[index + 5] = value[15..8].asByte
            this[index + 6] = value[7..0].asByte
        }
        LITTLE_ENDIAN -> {
            this[index + 6] = value[55..48].asByte
            this[index + 5] = value[47..40].asByte
            this[index + 4] = value[39..32].asByte
            this[index + 3] = value[31..24].asByte
            this[index + 2] = value[23..16].asByte
            this[index + 1] = value[15..8].asByte
            this[index + 0] = value[7..0].asByte
        }
        else -> throw IllegalArgumentException("WRONG BYTE ORDER")
    }
}

fun ByteArray.putInt48(index: Int, value: Long, order: ByteOrder = LITTLE_ENDIAN) {
    when (order) {
        BIG_ENDIAN -> {
            this[index + 0] = value[47..40].asByte
            this[index + 1] = value[39..32].asByte
            this[index + 2] = value[31..24].asByte
            this[index + 3] = value[23..16].asByte
            this[index + 4] = value[15..8].asByte
            this[index + 5] = value[7..0].asByte
        }
        LITTLE_ENDIAN -> {
            this[index + 5] = value[47..40].asByte
            this[index + 4] = value[39..32].asByte
            this[index + 3] = value[31..24].asByte
            this[index + 2] = value[23..16].asByte
            this[index + 1] = value[15..8].asByte
            this[index + 0] = value[7..0].asByte
        }
        else -> throw IllegalArgumentException("WRONG BYTE ORDER")
    }
}

fun ByteArray.putInt40(index: Int, value: Long, order: ByteOrder = LITTLE_ENDIAN) {
    when (order) {
        BIG_ENDIAN -> {
            this[index + 0] = value[39..32].asByte
            this[index + 1] = value[31..24].asByte
            this[index + 2] = value[23..16].asByte
            this[index + 3] = value[15..8].asByte
            this[index + 4] = value[7..0].asByte
        }
        LITTLE_ENDIAN -> {
            this[index + 4] = value[39..32].asByte
            this[index + 3] = value[31..24].asByte
            this[index + 2] = value[23..16].asByte
            this[index + 1] = value[15..8].asByte
            this[index + 0] = value[7..0].asByte
        }
        else -> throw IllegalArgumentException("WRONG BYTE ORDER")
    }
}

fun ByteArray.putInt32(index: Int, value: Int, order: ByteOrder = LITTLE_ENDIAN) {
    when (order) {
        BIG_ENDIAN -> {
            this[index + 0] = value[31..24].asByte
            this[index + 1] = value[23..16].asByte
            this[index + 2] = value[15..8].asByte
            this[index + 3] = value[7..0].asByte
        }
        LITTLE_ENDIAN -> {
            this[index + 3] = value[31..24].asByte
            this[index + 2] = value[23..16].asByte
            this[index + 1] = value[15..8].asByte
            this[index + 0] = value[7..0].asByte
        }
        else -> throw IllegalArgumentException("WRONG BYTE ORDER")
    }
}

fun ByteArray.putInt24(index: Int, value: Int, order: ByteOrder = LITTLE_ENDIAN) {
    when (order) {
        BIG_ENDIAN -> {
            this[index + 0] = value[23..16].asByte
            this[index + 1] = value[15..8].asByte
            this[index + 2] = value[7..0].asByte
        }
        LITTLE_ENDIAN -> {
            this[index + 2] = value[23..16].asByte
            this[index + 1] = value[15..8].asByte
            this[index + 0] = value[7..0].asByte
        }
        else -> throw IllegalArgumentException("WRONG BYTE ORDER")
    }
}

fun ByteArray.putInt16(index: Int, value: Int, order: ByteOrder = LITTLE_ENDIAN) {
    when (order) {
        BIG_ENDIAN -> {
            this[index + 0] = value[15..8].asByte
            this[index + 1] = value[7..0].asByte
        }
        LITTLE_ENDIAN -> {
            this[index + 1] = value[15..8].asByte
            this[index + 0] = value[7..0].asByte
        }
        else -> throw IllegalArgumentException("WRONG BYTE ORDER")
    }
}

fun ByteArray.putInt8(index: Int, value: Int) {
    this[index + 0] = value[7..0].asByte
}

fun ByteArray.putInt(index: Int, value: Long, size: Int, order: ByteOrder = LITTLE_ENDIAN) {
    when (size) {
        1 -> putInt8(index, value.asInt)
        2 -> putInt16(index, value.asInt, order)
        3 -> putInt24(index, value.asInt, order)
        4 -> putInt32(index, value.asInt, order)
        5 -> putInt40(index, value, order)
        6 -> putInt48(index, value, order)
        7 -> putInt56(index, value, order)
        8 -> putInt64(index, value, order)
        else -> throw IllegalArgumentException("Wrong int size!")
    }
}

fun ByteArray.getInt64(index: Int, order: ByteOrder = LITTLE_ENDIAN): Long {
    return when (order) {
        BIG_ENDIAN -> {
            insert(this[index + 7].asULong, 7..0)
                    .insert(this[index + 6].asULong, 15..8)
                    .insert(this[index + 5].asULong, 23..16)
                    .insert(this[index + 4].asULong, 31..24)
                    .insert(this[index + 3].asULong, 39..32)
                    .insert(this[index + 2].asULong, 47..40)
                    .insert(this[index + 1].asULong, 55..48)
                    .insert(this[index + 0].asULong, 63..56)
        }
        LITTLE_ENDIAN -> {
            insert(this[index + 0].asULong, 7..0)
                    .insert(this[index + 1].asULong, 15..8)
                    .insert(this[index + 2].asULong, 23..16)
                    .insert(this[index + 3].asULong, 31..24)
                    .insert(this[index + 4].asULong, 39..32)
                    .insert(this[index + 5].asULong, 47..40)
                    .insert(this[index + 6].asULong, 55..48)
                    .insert(this[index + 7].asULong, 63..56)
        }
        else -> throw IllegalArgumentException("WRONG BYTE ORDER")
    }
}

fun ByteArray.getInt56(index: Int, order: ByteOrder = LITTLE_ENDIAN): Long {
    return when (order) {
        BIG_ENDIAN -> {
            insert(this[index + 6].asULong, 7..0)
                .insert(this[index + 5].asULong, 15..8)
                .insert(this[index + 4].asULong, 23..16)
                .insert(this[index + 3].asULong, 31..24)
                .insert(this[index + 2].asULong, 39..32)
                .insert(this[index + 1].asULong, 47..40)
                .insert(this[index + 0].asULong, 55..48)
        }
        LITTLE_ENDIAN -> {
            insert(this[index + 0].asULong, 7..0)
                .insert(this[index + 1].asULong, 15..8)
                .insert(this[index + 2].asULong, 23..16)
                .insert(this[index + 3].asULong, 31..24)
                .insert(this[index + 4].asULong, 39..32)
                .insert(this[index + 5].asULong, 47..40)
                .insert(this[index + 6].asULong, 55..48)
        }
        else -> throw IllegalArgumentException("WRONG BYTE ORDER")
    }
}

fun ByteArray.getInt48(index: Int, order: ByteOrder = LITTLE_ENDIAN): Long {
    return when (order) {
        BIG_ENDIAN -> {
            insert(this[index + 5].asULong, 7..0)
                .insert(this[index + 4].asULong, 15..8)
                .insert(this[index + 3].asULong, 23..16)
                .insert(this[index + 2].asULong, 31..24)
                .insert(this[index + 1].asULong, 39..32)
                .insert(this[index + 0].asULong, 47..40)
        }
        LITTLE_ENDIAN -> {
            insert(this[index + 0].asULong, 7..0)
                .insert(this[index + 1].asULong, 15..8)
                .insert(this[index + 2].asULong, 23..16)
                .insert(this[index + 3].asULong, 31..24)
                .insert(this[index + 4].asULong, 39..32)
                .insert(this[index + 5].asULong, 47..40)
        }
        else -> throw IllegalArgumentException("WRONG BYTE ORDER")
    }
}

fun ByteArray.getInt40(index: Int, order: ByteOrder = LITTLE_ENDIAN): Long {
    return when (order) {
        BIG_ENDIAN -> {
            insert(this[index + 4].asULong, 7..0)
                .insert(this[index + 3].asULong, 15..8)
                .insert(this[index + 2].asULong, 23..16)
                .insert(this[index + 1].asULong, 31..24)
                .insert(this[index + 0].asULong, 39..32)
        }
        LITTLE_ENDIAN -> {
            insert(this[index + 0].asULong, 7..0)
                .insert(this[index + 1].asULong, 15..8)
                .insert(this[index + 2].asULong, 23..16)
                .insert(this[index + 3].asULong, 31..24)
                .insert(this[index + 4].asULong, 39..32)
        }
        else -> throw IllegalArgumentException("WRONG BYTE ORDER")
    }
}


fun ByteArray.getInt32(index: Int, order: ByteOrder = LITTLE_ENDIAN): Long {
    return when (order) {
        BIG_ENDIAN -> {
            insert(this[index + 3].asULong, 7..0)
                    .insert(this[index + 2].asULong, 15..8)
                    .insert(this[index + 1].asULong, 23..16)
                    .insert(this[index + 0].asULong, 31..24)
        }
        LITTLE_ENDIAN -> {
            insert(this[index + 0].asULong, 7..0)
                    .insert(this[index + 1].asULong, 15..8)
                    .insert(this[index + 2].asULong, 23..16)
                    .insert(this[index + 3].asULong, 31..24)
        }
        else -> throw IllegalArgumentException("WRONG BYTE ORDER")
    }
}

fun ByteArray.getInt24(index: Int, order: ByteOrder = LITTLE_ENDIAN): Long {
    return when (order) {
        BIG_ENDIAN -> {
            insert(this[index + 2].asULong, 7..0)
                    .insert(this[index + 1].asULong, 15..8)
                    .insert(this[index + 0].asULong, 23..16)
        }
        LITTLE_ENDIAN -> {
            insert(this[index + 0].asULong, 7..0)
                    .insert(this[index + 1].asULong, 15..8)
                    .insert(this[index + 2].asULong, 23..16)
        }
        else -> throw IllegalArgumentException("WRONG BYTE ORDER")
    }
}

fun ByteArray.getInt16(index: Int, order: ByteOrder = LITTLE_ENDIAN): Long {
    return when (order) {
        BIG_ENDIAN -> {
            insert(this[index + 1].asULong, 7..0)
                    .insert(this[index + 0].asULong, 15..8)
        }
        LITTLE_ENDIAN -> {
            insert(this[index + 0].asULong, 7..0)
                    .insert(this[index + 1].asULong, 15..8)
        }
        else -> throw IllegalArgumentException("WRONG BYTE ORDER")
    }
}

fun ByteArray.getInt8(index: Int): Int {
    return this[index].asUInt
}

@Deprecated("getValue is deprecated use getInt instead", ReplaceWith("getInt"))
fun ByteArray.getValue(index: Int, bytesNumber: Int, order: ByteOrder = LITTLE_ENDIAN): Long {
    return when(bytesNumber){
        1 -> getInt8(index).toULong()
        2 -> getInt16(index, order)
        3 -> getInt24(index, order)
        4 -> getInt32(index, order)
        8 -> getInt64(index, order)
        else -> throw IllegalArgumentException("Available bytesNumber is 1, 2, 3, 4, 8")
    }
}

fun ByteArray.getDouble(index: Int, order: ByteOrder = LITTLE_ENDIAN): Double {
    return when (order) {
        LITTLE_ENDIAN -> {
            val tmp = insert(this[index + 7].asULong, 7..0)
                    .insert(this[index + 6].asULong, 15..8)
                    .insert(this[index + 5].asULong, 23..16)
                    .insert(this[index + 4].asULong, 31..24)
                    .insert(this[index + 3].asULong, 39..32)
                    .insert(this[index + 2].asULong, 47..40)
                    .insert(this[index + 1].asULong, 55..48)
                    .insert(this[index + 0].asULong, 63..56)
            tmp.ieee754()
        }
        BIG_ENDIAN -> {
            val tmp = insert(this[index + 0].asULong, 7..0)
                    .insert(this[index + 1].asULong, 15..8)
                    .insert(this[index + 2].asULong, 23..16)
                    .insert(this[index + 3].asULong, 31..24)
                    .insert(this[index + 4].asULong, 39..32)
                    .insert(this[index + 5].asULong, 47..40)
                    .insert(this[index + 6].asULong, 55..48)
                    .insert(this[index + 7].asULong, 63..56)
            tmp.ieee754()
        }
        else -> throw IllegalArgumentException("WRONG BYTE ORDER")
    }
}

fun ByteArray.getInt(index: Int, size: Int, order: ByteOrder = LITTLE_ENDIAN): Long {
    return when (size) {
        1 -> getInt8(index).asULong
        2 -> getInt16(index, order)
        3 -> getInt24(index, order)
        4 -> getInt32(index, order)
        5 -> getInt40(index, order)
        6 -> getInt48(index, order)
        7 -> getInt56(index, order)
        8 -> getInt64(index, order)
        else -> throw IllegalArgumentException("Wrong int size!")
    }
}

fun ByteArray.putDouble(index: Int, value: Double, order: ByteOrder = LITTLE_ENDIAN) {
    val tmp = value.ieee754()
    when (order) {
        BIG_ENDIAN -> {
            this[index + 0] = tmp[63..56].asByte
            this[index + 1] = tmp[55..48].asByte
            this[index + 2] = tmp[47..40].asByte
            this[index + 3] = tmp[39..32].asByte
            this[index + 4] = tmp[31..24].asByte
            this[index + 5] = tmp[23..16].asByte
            this[index + 6] = tmp[15..8].asByte
            this[index + 7] = tmp[7..0].asByte
        }
        LITTLE_ENDIAN -> {
            this[index + 7] = tmp[63..56].asByte
            this[index + 6] = tmp[55..48].asByte
            this[index + 5] = tmp[47..40].asByte
            this[index + 4] = tmp[39..32].asByte
            this[index + 3] = tmp[31..24].asByte
            this[index + 2] = tmp[23..16].asByte
            this[index + 1] = tmp[15..8].asByte
            this[index + 0] = tmp[7..0].asByte
        }
        else -> throw IllegalArgumentException("WRONG BYTE ORDER")
    }
}

fun ByteArray.getArray(index: Int, size: Int): ByteArray = copyOfRange(index, index + size)
fun ByteArray.putArray(index: Int, data: ByteArray) = System.arraycopy(data, 0, this, index, data.size)

fun ByteArray.getString(index: Int, size: Int, charset: Charset = Charsets.US_ASCII): String = getArray(index, size).toString(charset)
fun ByteArray.putString(index: Int, string: String, charset: Charset = Charsets.US_ASCII) = putArray(index, string.toByteArray(charset))

fun Byte.pack(size: Int, order: ByteOrder = LITTLE_ENDIAN): ByteArray =
        ByteArray(size).also { it.putInt(0, this.asULong, size, order) }
fun Short.pack(size: Int, order: ByteOrder = LITTLE_ENDIAN): ByteArray =
        ByteArray(size).also { it.putInt(0, this.asULong, size, order) }
fun Int.pack(size: Int, order: ByteOrder = LITTLE_ENDIAN): ByteArray =
        ByteArray(size).also { it.putInt(0, this.asULong, size, order) }
fun Long.pack(size: Int, order: ByteOrder = LITTLE_ENDIAN): ByteArray =
        ByteArray(size).also { it.putInt(0, this, size, order) }

inline fun ByteArray.sumByLong(selector: (Byte) -> Long): Long {
    var sum: Long = 0
    for (element in this) {
        sum += selector(element)
    }
    return sum
}

inline fun ShortArray.sumByLong(selector: (Short) -> Long): Long {
    var sum: Long = 0
    for (element in this) {
        sum += selector(element)
    }
    return sum
}

inline fun IntArray.sumByLong(selector: (Int) -> Long): Long {
    var sum: Long = 0
    for (element in this) {
        sum += selector(element)
    }
    return sum
}

inline fun LongArray.sumByLong(selector: (Long) -> Long): Long {
    var sum: Long = 0
    for (element in this) {
        sum += selector(element)
    }
    return sum
}

inline fun  Array.sumByLong(selector: (T) -> Long): Long {
    var sum: Long = 0
    for (element in this) {
        sum += selector(element)
    }
    return sum
}

inline fun  Iterable.sumByLong(selector: (T) -> Long): Long {
    var sum: Long = 0
    for (element in this) {
        sum += selector(element)
    }
    return sum
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy