Skip to content

Commit

Permalink
Refactor decoding numbers from byte arrays
Browse files Browse the repository at this point in the history
Fixes #123
  • Loading branch information
nodh committed Sep 16, 2024
1 parent 2d52fc2 commit ae082e0
Showing 1 changed file with 63 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -211,12 +211,16 @@ object Asn1 {

/** Adds an INTEGER [Asn1Primitive] to this ASN.1 structure */
fun Int(value: Int) = value.encodeToTlv()

/** Adds an INTEGER [Asn1Primitive] to this ASN.1 structure */
fun Int(value: Long) = value.encodeToTlv()

/** Adds an INTEGER [Asn1Primitive] to this ASN.1 structure */
fun Int(value: UInt) = value.encodeToTlv()

/** Adds an INTEGER [Asn1Primitive] to this ASN.1 structure */
fun Int(value: ULong) = value.encodeToTlv()

/** Adds an INTEGER [Asn1Primitive] to this ASN.1 structure */
fun Int(value: BigInteger) = value.encodeToTlv()

Expand Down Expand Up @@ -301,12 +305,16 @@ fun Boolean.encodeToTlv() = Asn1Primitive(Asn1Element.Tag.BOOL, byteArrayOf(if (

/** Produces an INTEGER as [Asn1Primitive] */
fun Int.encodeToTlv() = Asn1Primitive(Asn1Element.Tag.INT, encodeToDer())

/** Produces an INTEGER as [Asn1Primitive] */
fun Long.encodeToTlv() = Asn1Primitive(Asn1Element.Tag.INT, encodeToDer())

/** Produces an INTEGER as [Asn1Primitive] */
fun UInt.encodeToTlv() = Asn1Primitive(Asn1Element.Tag.INT, encodeToDer())

/** Produces an INTEGER as [Asn1Primitive] */
fun ULong.encodeToTlv() = Asn1Primitive(Asn1Element.Tag.INT, encodeToDer())

/** Produces an INTEGER as [Asn1Primitive] */
fun BigInteger.encodeToTlv() = Asn1Primitive(Asn1Element.Tag.INT, encodeToDer())

Expand Down Expand Up @@ -401,7 +409,9 @@ fun ULong.toTwosComplementByteArray() = when {
(this shr 24).toByte(),
(this shr 16).toByte(),
(this shr 8).toByte(),
this.toByte())
this.toByte()
)

else -> this.toLong().toTwosComplementByteArray()
}

Expand All @@ -412,37 +422,49 @@ fun UInt.toTwosComplementByteArray() = toLong().toTwosComplementByteArray()
fun Long.toTwosComplementByteArray() = when {
(this >= -0x80L && this <= 0x7FL) ->
byteArrayOf(
this.toByte())
this.toByte()
)

(this >= -0x8000L && this <= 0x7FFFL) ->
byteArrayOf(
(this ushr 8).toByte(),
this.toByte())
this.toByte()
)

(this >= -0x800000L && this <= 0x7FFFFFL) ->
byteArrayOf(
(this ushr 16).toByte(),
(this ushr 8).toByte(),
this.toByte())
this.toByte()
)

(this >= -0x80000000L && this <= 0x7FFFFFFFL) ->
byteArrayOf(
(this ushr 24).toByte(),
(this ushr 16).toByte(),
(this ushr 8).toByte(),
this.toByte())
this.toByte()
)

(this >= -0x8000000000L && this <= 0x7FFFFFFFFFL) ->
byteArrayOf(
(this ushr 32).toByte(),
(this ushr 24).toByte(),
(this ushr 16).toByte(),
(this ushr 8).toByte(),
this.toByte())
this.toByte()
)

(this >= -0x800000000000L && this <= 0x7FFFFFFFFFFFL) ->
byteArrayOf(
(this ushr 40).toByte(),
(this ushr 32).toByte(),
(this ushr 24).toByte(),
(this ushr 16).toByte(),
(this ushr 8).toByte(),
this.toByte())
this.toByte()
)

(this >= -0x80000000000000L && this <= 0x7FFFFFFFFFFFFFL) ->
byteArrayOf(
(this ushr 48).toByte(),
Expand All @@ -451,7 +473,9 @@ fun Long.toTwosComplementByteArray() = when {
(this ushr 24).toByte(),
(this ushr 16).toByte(),
(this ushr 8).toByte(),
this.toByte())
this.toByte()
)

else ->
byteArrayOf(
(this ushr 56).toByte(),
Expand All @@ -461,53 +485,51 @@ fun Long.toTwosComplementByteArray() = when {
(this ushr 24).toByte(),
(this ushr 16).toByte(),
(this ushr 8).toByte(),
this.toByte())
this.toByte()
)
}

/** Encodes a signed Int to a minimum-size twos-complement byte array */
fun Int.toTwosComplementByteArray() = toLong().toTwosComplementByteArray()

fun Int.Companion.fromTwosComplementByteArray(it: ByteArray) = when (it.size) {
4 -> (it[0].toInt() shl 24) or (it[1].toUByte().toInt() shl 16) or (it[2].toUByte().toInt() shl 8) or (it[3].toUByte().toInt())
3 -> (it[0].toInt() shl 16) or (it[1].toUByte().toInt() shl 8) or (it[2].toUByte().toInt())
2 -> (it[0].toInt() shl 8) or (it[1].toUByte().toInt() shl 0)
1 -> (it[0].toInt())
else -> throw IllegalArgumentException("Input with size $it is out of bounds for Int")
}
fun Int.Companion.fromTwosComplementByteArray(it: ByteArray) =
if (it.isEmpty() || it.size > Int.SIZE_BYTES) {
throw IllegalArgumentException("Input with size $it is out of bounds for Int")
} else {
(0..<it.size).fold(0) { acc, idx ->
acc or (it.getInt(idx) shl (idx.shiftBytes(it) * Byte.SIZE_BITS))
}
}

private fun ByteArray.getInt(idx: Int) = if (idx == 0) this[idx].toInt() else this[idx].toUByte().toInt()

fun UInt.Companion.fromTwosComplementByteArray(it: ByteArray) =
Long.fromTwosComplementByteArray(it).let {
require ((0 <= it) && (it <= 0xFFFFFFFFL)) { "Value $it is out of bounds for UInt" }
require((0 <= it) && (it <= 0xFFFFFFFFL)) { "Value $it is out of bounds for UInt" }
it.toUInt()
}

fun Long.Companion.fromTwosComplementByteArray(it: ByteArray) = when (it.size) {
8 -> (it[0].toLong() shl 56) or (it[1].toUByte().toLong() shl 48) or (it[2].toUByte().toLong() shl 40) or
(it[3].toUByte().toLong() shl 32) or (it[4].toUByte().toLong() shl 24) or
(it[5].toUByte().toLong() shl 16) or (it[6].toUByte().toLong() shl 8) or (it[7].toUByte().toLong())
7 -> (it[0].toLong() shl 48) or (it[1].toUByte().toLong() shl 40) or (it[2].toUByte().toLong() shl 32) or
(it[3].toUByte().toLong() shl 24) or (it[4].toUByte().toLong() shl 16) or
(it[5].toUByte().toLong() shl 8) or (it[6].toUByte().toLong())
6 -> (it[0].toLong() shl 40) or (it[1].toUByte().toLong() shl 32) or (it[2].toUByte().toLong() shl 24) or
(it[3].toUByte().toLong() shl 16) or (it[4].toUByte().toLong() shl 8) or (it[5].toUByte().toLong())
5 -> (it[0].toLong() shl 32) or (it[1].toUByte().toLong() shl 24) or (it[2].toUByte().toLong() shl 16) or
(it[3].toUByte().toLong() shl 8) or (it[4].toUByte().toLong())
4 -> (it[0].toLong() shl 24) or (it[1].toUByte().toLong() shl 16) or (it[2].toUByte().toLong() shl 8) or
(it[3].toUByte().toLong())
3 -> (it[0].toLong() shl 16) or (it[1].toUByte().toLong() shl 8) or (it[2].toUByte().toLong())
2 -> (it[0].toLong() shl 8) or (it[1].toUByte().toLong() shl 0)
1 -> (it[0].toLong())
else -> throw IllegalArgumentException("Input with size $it is out of bounds for Long")
}
fun Long.Companion.fromTwosComplementByteArray(it: ByteArray) =
if (it.isEmpty() || it.size > Long.SIZE_BYTES) {
throw IllegalArgumentException("Input with size $it is out of bounds for Long")
} else {
(0..<it.size).fold(0L) { acc, idx ->
acc or (it.getLong(idx) shl (idx.shiftBytes(it) * Byte.SIZE_BITS))
}
}

private fun ByteArray.getLong(idx: Int) = if (idx == 0) this[idx].toLong() else this[idx].toUByte().toLong()

private fun Int.shiftBytes(it: ByteArray) = (it.size - 1 - this)

fun ULong.Companion.fromTwosComplementByteArray(it: ByteArray) = when {
((it.size == 9) && (it[0] == 0.toByte())) ->
(it[1].toUByte().toULong() shl 56) or (it[2].toUByte().toULong() shl 48) or (it[3].toUByte().toULong() shl 40) or
(it[4].toUByte().toULong() shl 32) or (it[5].toUByte().toULong() shl 24) or
(it[6].toUByte().toULong() shl 16) or (it[7].toUByte().toULong() shl 8) or
(it[8].toUByte().toULong())
((it.size == ULong.SIZE_BYTES + 1) && (it[0] == 0.toByte())) ->
(1..<it.size).fold(0uL) { acc, idx ->
acc or (it[idx].toUByte().toULong() shl (idx.shiftBytes(it) * Byte.SIZE_BITS))
}

else -> Long.fromTwosComplementByteArray(it).let {
require (it >= 0) { "Value $it is out of bounds for ULong" }
require(it >= 0) { "Value $it is out of bounds for ULong" }
it.toULong()
}
}
Expand Down

0 comments on commit ae082e0

Please sign in to comment.