What is the difference between two conversions from Long to ByteArray? - kotlin

import java.lang.Long as JLong
import java.lang.Byte as JByte
import java.nio.ByteBuffer
fun Long.toByteArray1() =
ByteBuffer.allocate(JLong.SIZE / JByte.SIZE)
.putLong(this)
.array()
fun Long.toByteArray2() =
this.toString()
.toByteArray(Charsets.UTF_8)
fun main(args: Array<String>) {
val a1: ByteArray = 10L.toByteArray1()
val a2: ByteArray = 10L.toByteArray2()
println("a1 = ${a1.toString()}")
println("a1 = ${ByteBuffer.wrap(a1).getLong()}")
println("a2 = ${a2.toString()}")
println("a2 = ${String(a2, Charsets.UTF_8)}")
}
What is the difference between toByteArray1() and toByteArray2()?
If I send the bytes into the outputstream what receiver will get?

They are completely different.
.toByteArray1() returns bytes of the signed long number (therefore it should contain exactly 8 bytes = 64 bits):
println(0.toByteArray1().size) // 8
println(1234.toByteArray1().size) // 8
.toByteArray2() first converts the long to String and then to the byte array with the String characters encoded in UTF-8 (since there are only digits, they are encoded one byte each), and the byte array in this case contains the same number of bytes as the length of the string representation:
println(0.toByteArray2().size) // 1
println(-1234.toByteArray2().size) // 5
println(123456789012.toByteArray2().size) // 12
And, of course, the decoders should be different, as in your example where you decode a1 and a2.

Related

Kotlin convert hex string to ByteArray

I have this string:
val s = "00b44a0bc6303782b729a7f9b44a3611b247ddf1e544f8b1420e2aae976003219175461d2bd7" +
"6e64ba657d7c9dff6ed7b17980778ec0cbf75fc16e52463e2d784f5f20c1691f17cdc597d7a514108" +
"0809a38c635b2a62082e310aa963ca15953894221ad54c6b30aea10f4dd88a66c55ab9c413eae49c0b" +
"28e6a3981e0021a7dcb0759af34b095ce3efce78938f2a2bed70939ba47591b88f908db1eadf237a7a" +
"7100ac87130b6119d7ae41b35fd27ff6021ac928273c20f0b3a01df1e6a070b8e2e93b5220ad0210400" +
"0c0c1e82e17fd00f6ac16ef37c3b6153d348e470843a84f25473a51f040a42671cd94ffc989eb27fd42" +
"b817f8173bfa95bdfa17a2ae22fd5c89dab2822bcc973b5b90f8fadc9b074cca8f9365b1e8994ff0bda48" + "b1f7498cce02d4e794915f8a4208de3eaf9fbff5"
Which is hexadecimal notation of bytes, hardcoded in as string format.
I need this thing as a bytearray, importantly, not the ASCII representation of it, actually the hexadecimal values that is represents.
All the kotlin methods I can find, such as:
val b = s.encodeToByteArray()
Seem to be taking the actual ASCII value of the string, and converting it to a bytearray.
How do I create a bytearray directly from the values in this string?
You can handle it like this:
fun String.decodeHex(): ByteArray {
check(length % 2 == 0) { "Must have an even length" }
return chunked(2)
.map { it.toInt(16).toByte() }
.toByteArray()
}
Split the string into 2-character pairs, representing each byte.
Parse each hex pair to their integer values.
Convert the parsed Ints to Bytes.
My other answer is the simplest way, but it creates two intermediate lists - a list of strings and a list of bytes - before it creates the byte array. Here are two slightly more complex versions that are more efficient.
This version uses sequences to take advantage of lazy evaluation. It still produces a string for every byte, but uses no intermediate lists.
fun String.decodeHex(): ByteArray {
check(length % 2 == 0) { "Must have an even length" }
val byteIterator = chunkedSequence(2)
.map { it.toInt(16).toByte() }
.iterator()
return ByteArray(length / 2) { byteIterator.next() }
}
This version uses the JDK's java.lang.Integer.parseInt function. It creates the ByteArray directly with no intermediate data-structures.
fun String.decodeHex(): ByteArray {
check(length % 2 == 0) { "Must have an even length" }
return ByteArray(length / 2) {
Integer.parseInt(this, it * 2, (it + 1) * 2, 16).toByte()
}
}

Convert Long to Base64 in Kotlin

I need to convert a value of type Long to Base64 encoding.
I tried converting to a byte[] and then passing that to Base64.encodeToString but I get the wrong answer.
I looked through many of the examples here but none helped.
Maybe I am misunderstanding how encodeToString works?
Using this answer to the question "How do I convert Long to byte[] and back in Java":
import java.nio.ByteBuffer
import java.util.Base64
fun main() {
val number: Long = 12345678
val encodedNumberString = Base64.getEncoder().encodeToString(longToBytes(number))
println("Number: $number.")
println("Encoded number: \"$encodedNumberString\".")
val decodedNumberBytes = Base64.getDecoder().decode(encodedNumberString)
val decodedNumber = bytesToLong(decodedNumberBytes)
println("Decoded number: $decodedNumber.")
}
private fun longToBytes(number: Long): ByteArray {
val buffer = ByteBuffer.allocate(java.lang.Long.BYTES)
buffer.putLong(number)
return buffer.array()
}
private fun bytesToLong(bytes: ByteArray): Long {
val buffer = ByteBuffer.allocate(java.lang.Long.BYTES)
buffer.put(bytes)
// Prepare the byte buffer to enable reading from it.
buffer.flip()
return buffer.long
}
This is the output on my system:
Number: 12345678.
Encoded number: "AAAAAAC8YU4=".
Decoded number: 12345678.
Update: Base64
Base64 is a way to convert binary data to text and uses a safe subset of 64 characters that can be transferred in for example an e-mail attachment. Not all 256 values in a byte can be sent without problems, so only 6 bits (2^6 = 64) are encoded in each character. This means that 3 bytes can be transferred in 4 characters (the overhead is 33%).
Update: extension functions
As Alex.T already mentioned in the comment, Kotlin enables you to make the Long <-> ByteArray conversions a lot shorter using extension functions (assuming the ByteArray implementation has an accessible backing array):
fun Long.toByteArray(): ByteArray = ByteBuffer.allocate(java.lang.Long.BYTES).apply { putLong(this#toByteArray) }.array()
fun ByteArray.toLong(): Long = ByteBuffer.allocate(java.lang.Long.BYTES).apply { put(this#toLong); flip() }.long
fun main() {
val number = 12345678L
val encodedNumberString = Base64.getEncoder().encodeToString(number.toByteArray())
println("Encoded number: \"$encodedNumberString\".")
val decodedNumber = Base64.getDecoder().decode(encodedNumberString).toLong()
println("Decoded number: $decodedNumber.")
}

scanner in kotlin to read in 2 lines

Here is the code. n continually outputs 50 and not 2:
import java.util.*
fun main(args: Array<String>) {
val scanner = Scanner(System.`in`)
val n = scanner.next().first().toInt()
val array1 = readLine()!!.split(" ").map { it.toInt() }
var product:Int=0
println(n)
println(array1[0])
println(array1[1])
if (n ==2) {
product = array1[0] * array1[1]
}
println(product)
}
Sample Input:
2
5 3
Output:
2
5 3
50
5
3
0
How do I use scanner in kotlin to read in 2 lines?
Short form
Use scanner.nextInt() instead of scanner.next().first().toInt().
Explanation
By calling scanner.next() you receive the next complete token of the Scanner as a String. Then you take the first character using first(). The problem is, that calling toInt() on a Char will not parse the string as an integer value but return the ASCII char code of the char.
Example: '2'.toInt() returns 50 and not 2 since the ASCII char code of 2 is 50. (See https://www.cs.cmu.edu/~pattis/15-1XX/common/handouts/ascii.html)
Conclusion: In your case, directly read the integer value from the Scanner using scanner.nextInt(), but if you want to convert a char to an integer by "parsing", convert it to a string first: '2'.toString().toInt() will return an integer with the value 2
Addition
It is probably nice to know that you can also use this the other way round: 50.toChar() returns a character with the value '2'.
👍

Convert Hex value to Base64 using Kotlin

I have this value:
263e5df7a93ec5f5ea6ac215ed957c30
When I fill this in on: https://8gwifi.org/base64Hex.jsp (Hex to Base64)
It gives me back:
Jj5d96k+xfXqasIV7ZV8MA==
This is the expected value. However, when I try this in Kotlin,
val encodedHexB64 = Base64.encodeToString("263e5df7a93ec5f5ea6ac215ed957c30".toByteArray(UTF_8), Base64.NO_WRAP)
It gives me back:
MjYzZTVkZjdhOTNlYzVmNWVhNmFjMjE1ZWQ5NTdjMzA=
How to get the correct value in Kotlin?
To complete the previous:
val input = "263e5df7a93ec5f5ea6ac215ed957c30"
val bytes = input.chunked(2).map { it.toInt(16).toByte() }.toByteArray()
val encodeBase64 = Base64.encodeToString(bytes, Base64.DEFAULT)
Now you have: Jj5d96k+xfXqasIV7ZV8MA==
It looks like the input string represents 16 bytes, where each byte is coded with two hex digit chars of that string.
On the contrary toByteArray(UTF_8) encodes the string in UTF-8 encoding turning each char into one or more bytes. When you convert these bytes to base64, first you get the longer result and second — these are completely different bytes.
I suppose the correct way to convert the input hex string into byte array would be:
val input = "263e5df7a93ec5f5ea6ac215ed957c30"
val bytes = input.chunked(2).map { it.toInt(16).toByte() }.toByteArray()
Then you encode these bytes to base64 as usual.

NumberFormatException in converting string to byte

I am trying to get the MD5 format of string
Code:
fun getEncodedData(data: String): String? {
val MD5 = "MD5"
// Create MD5 Hash
val digest = java.security.MessageDigest
.getInstance(MD5)
digest.update(data.toByte())
val messageDigest = digest.digest()
// Create Hex String
val hexString = StringBuilder()
for (aMessageDigest in messageDigest) {
var h = Integer.toHexString(0xFF and aMessageDigest.toInt())
while (h.length < 2)
h = "0$h"
hexString.append(h)
}
return hexString.toString()
}
There is a crash at: digest.update(data.toByte()). I get number format Exception
Input I am passing for data: oEXm43
There is no crash if I pass ex: 11 as a string for input data
Should the input always should be integer in the string or can it be a mixture of number and characters.
You're trying to call the update method that takes a single byte parameter, and using toByte which converts the entire string's numerical value to a single byte. This conversion method is what fails on non-numerical values inside a String.
Instead, you can use the variant of update with a byte[] parameter, and convert your String to an array of bytes (one per character) with toByteArray:
digest.update(data.toByteArray())