I need to create, in Kotlin, a byte array with mixed types in it.
Specifically, I need to write something like:
0, 1, 0x02, 1, "me#emailaddr.com"
to a characteristic over BLE using Kotlin.
Clearly I'm new to Kotlin. The convenient ways to do it in Swift don't seem to have similar functions in Kotlin.
I've tried:
data class BlePayload(val protoVer: Int, val transId: Int, val cmdType: Int, val valueType: Int, val value: String)
fun packagePayload(): BlePayload {
val checkinInfo: BlePayload = BlePayload(0, 1, 0x02, 1, "me#emailaddr.com")
return checkinInfo
}
...
private fun writeCharacteristic(characteristic: BluetoothGattCharacteristic) {
val payload = BluetoothUtils.packagePayload()
val strByteArray = payload.toString()
val byteArray = strByteArray.toByteArray()
mClientActionListener.log(TAG_GATT_CLIENT_CALLBACK, "Writing to characteristic ${characteristic.uuid}")
mClientActionListener.log(TAG_GATT_CLIENT_CALLBACK, "** strByteArray = $strByteArray")
mClientActionListener.log(TAG_GATT_CLIENT_CALLBACK, "** byteArray = $byteArray")
mClientActionListener.log(TAG_GATT_CLIENT_CALLBACK, "** Writing the Payload: $payload")
characteristic.setValue(byteArray)
}
But that seems to be cumbersome and wrong. However, I can't see another way around it.
It needs to be a byte array for the Peripheral.
I can't find a way to make a byte array without making it a string first.
I don't see a way to go directly to a byte array with mixed types.
I am sure I must be wrong - I just seem to be too new to understand what the Kotlin docs are telling me.
Thanks for looking.
If you're OK pulling in a library to help with this task, I'd consider using Square's Okio for this one. I've used it for this kind of bit-fiddling in the past. Your example would look something like this with Okio:
fun packagePayload(): ByteArray =
Buffer()
.writeInt(0)
.writeInt(1)
.writeInt(2)
.writeInt(1)
.writeString("me#addr.com", Charsets.US_ASCII)
.readByteArray()
Okio also offers little-endian versions of the above methods (i.e. writeIntLe()).
So I ended up abandoning the idea of a data object in favor of:
fun packagePayload(): ByteArray {
var payloadArray: ByteArray = ByteArray(0)
payloadArray = payloadArray.plus(0.toByte())
payloadArray = payloadArray.plus(1.toByte())
payloadArray = payloadArray.plus(2.toByte())
payloadArray = payloadArray.plus(1.toByte())
payloadArray = payloadArray.plus("me#addr.com".toByteArray(Charsets.US_ASCII))
Log.i(TAG_BLEUTIL, "** output of array: " + payloadArray.contentToString())
// [0, 1, 2, 1, 109, 101, 64, 97, 100, 100, 114, 46, 99, 111, 109]
return payloadArray
}
That ended up being a lot more simple.
Here's the catch: if you want 4 byte integers, there is not Kotlin helper fun to make that easy for you.
So you need to do something like this to get the 4 byte Int:
fun Int.toBytes() : ByteArray {
var retArray = ByteArray(0)
for(i in 0..3) {
retArray = retArray.plus((this shr (i * 8) and 0xFF).toByte())
}
return retArray//.reversedArray()
}
In which case you would use it like so:
payloadArray = payloadArray.plus(0.toBytes())
Notice the "//.reversedArray()" at the end of the function. That's in case you need to switch between little endian and big endian.
Hope that helps someone in the future.
Let me know if you have a better solution.
Thanks
Related
I'm teaching myself coding with kotlin and AS and as a self-assigned project I'm making a little game. I have 6 var integers that are used to get some random numbers and to modify those numbers in certain ways. In order to reduce the amount of code and to simplify passing those integers around to functions I have all 6 stored in a single mutable map. Most of the time when I pass them from one function to the other (or one class to the other) I need all 6, but there is one case where I only need 3 of them.
My question is this: Is it more efficient to pass just the 3 values I need? Or is that actually LESS efficient because I'm passing new copies of already existing values? I'm really not sure how that works internally
example: Would you...
fun main() {
val gameArgs = mutableMapOf("numDice" to 3,
"minNum" to 1,
"maxNum" to 20,
"modNum" to 5,
"targetNum" to 17,
"bonusDice" to -1,)
val rollNumbers = rollDice(gameArgs)
// and then send the rollNumbers and the gameArgs elsewhere to evaluate what
// the numbers mean
}
fun rollDice(args: MutableMap<String, Int>):List<Int>{
val numList = mutableListOf<Int>()
repeat(args["numDice"]!!){
numList.add((args["minNum"]!!..args["maxNum"]!!).random())
}
return numList
}
OR, would you...
fun main() {
val gameArgs = mutableMapOf("numDice" to 3,
"minNum" to 1,
"maxNum" to 20,
"modNum" to 5,
"targetNum" to 17,
"bonusDice" to -1,)
val rollNumbers = rollDice(gameArgs["numDice"]!!, gameArgs["minNum"]!!, gameArgs["maxNum"]!!)
// and then send the number list and the other 3 values from the map elsewhere
}
fun rollDice(numDice: Int, minNum: Int, maxNum: Int):List<Int>{
val numList = mutableListOf<Int>()
repeat(numDice){
numList.add((minNum..maxNum).random())
}
return numList
}
My instincts say the latter is less memory intensive, but my anxiety whispers that the latter might be MORE memory intensive because I'm making copies of 3 values that already exist.
So which is more efficient? And does it make a difference if rollDice() is in another class?
Thanks in advance
[edited to fix one type and one copy/paste error]
You are over-concerned about performance. You could pass ten strings or a hundred copies of short strings like this and it's completely insignificant to performance. Think about when a web page opens on a computer 20 years ago, and thousands of words of text appears on your screen near-instantly. But when you pass a String, it is not copied. Only its memory address is copied, which is even more trivial. Regardless, it is both more efficient and cleaner code to pass the whole thing, not the individual components. Focus primarily on the cleanest-looking code and only try to optimize when you have a performance-critical section of code, like a piece of algorithm that's repeated thousands of times while the user is waiting for the result.
You should not use Maps for this. That's the sort of thing you do in weakly-typed languages like JavaScript. Maps are never used in this way in a strongly-typed language. When you have multiple parameters with different meanings, you use a class to represent them. This makes your code robust. The compiler will complain about and red-underline most of your errors before you even have to install your code to test it. With a map, you won't find your errors until after the program is running and you start having weird bugs that you have to go through the debugging process to find. Replace your map with a class like this:
data class GameSession(
val numDice: Int = 1,
val minNumber: Int = 1,
val maxNumber: Int = 20,
val modNumber: Int = 5,
val targetNumber: Int = 17,
val bonusDice: Int = -1
)
fun main() {
val game = GameSession()
val rollNumbers = rollDice(game)
// ...
}
fun rollDice(game: GameSession): List<Int>{
val numList = mutableListOf<Int>()
repeat(game.numDice){
numList.add((game.minNumber..game.maxNumber).random())
}
return numList
}
To take it a step further, it would make more design sence for rollDice() to be a function of the GameSession class. Also, you can skip making a temporary MutableList and just return a List directly using the List() function. So it could look like this:
data class GameSession(
val numDice: Int = 1,
val minNumber: Int = 1,
val maxNumber: Int = 20,
val modNumber: Int = 5,
val targetNumber: Int = 17,
val bonusDice: Int = -1
) {
fun rollDice(): List<Int> {
return List<Int>(numDice) { (minNumber..maxNumber).random() }
}
}
fun main() {
val game = GameSession()
val rollNumbers = game.rollDice()
// ...
}
Any RxJava experts that can help me figure this one out?
I have a function that returns a Single<ByteArray> based on some position parameter.
Basically, I want to keep calling that function, until it returns an empty ByteArray.
NOTE: The position parameter is supposed to indicate how many bytes where already read. E.g. first time it will be 0, next time it will be how many bytes where retrieved the first time, and so on.
This is what I have now:
fun readBytes(position: Int) : Single<ByteArray>{ ... }
fun readAllLogs() : Single<LogEntry> {
val oldPosition = AtomicInteger(0)
val position = AtomicInteger(0)
Observable.defer {
readBytes(position.get())
}.reduceWith({ LogEntry() }, { log, bytes ->
position.addAndGet(bytes.size)
log += bytes
log
}).repeatUntil {
val canWeStop = position.get() == oldPosition.get()
oldPosition.set(position.get())
canWeStop
}.toObservable()
}
For completeness, here's my LogEntry accumulator
data class LogEntry {
var data: ByteArray = byteArrayOf()
private set
operator fun plusAssign(bytes: ByteArray) {
data += bytes
}
}
Question 1:
How can I exit this stream elegantly? E.g. not keeping track of separate variables outside the stream?
Question 2:
Any optimizations I can make? I'm going from Single to Observable twice to be able to chain these operators. I'm sure that can be more efficient
I have a library that has default ways to encrypt stuff that it uses, which means user just need to provide a string as a key.
lib.encryption("key")
lib.doEncryptedStuff() // use default encryption
I want to allow users use custom encryption, including those that implement custom java crypto providers. My question is, what should be the correct way to ask user for such encryption methods? For now I came with idea to request two ciphers - one for encryption and one for decryption.
// create and init() ciphers here
lib.encryption(cipherEncrypt, cipherDecrypt)
lib.doEncryptedStuff() // use custom ciphers
But I'm unsure whether this is the correct way. Are Ciphers enough? Or should I provide interface for encryption for user to implement? I remember there being issues with reusing IV, which means cipher re-initialization?
Another issue that I'm facing is that it's really hard for me to generalize interface. Like AES with ECB will work with encrypt()/decrypt() methods, but with CBC it requires IV, which could also be stored inside encrypted data. What a mess.
Seems like I've nailed it. I used Stream-like approach, but not tied to streams. It allows to use any encryption user needs by passing interface as parameter.
My interface:
interface Encryption
{
fun encrypt(
read: (buffer: ByteArray, count: Int) -> Int,
write: (buffer: ByteArray, count: Int) -> Unit
)
fun decrypt(
read: (buffer: ByteArray, count: Int) -> Int,
write: (buffer: ByteArray, count: Int) -> Unit
)
}
Example implementation:
// init cipher and buffer
val cipher = Cipher.getInstance(MODE)
val buffer = ByteArray(BLOCK_SIZE)
cipher.init(Cipher.ENCRYPT_MODE, keystore.getKey(KEY_ALIAS, null))
// write iv (when decrypting read IV from beginning)
val iv = cipher.iv
write(iv, iv.size)
// write data
var count = 0
while (true)
{
count = read(buffer, BLOCK_SIZE)
if (count < BLOCK_SIZE)
break
val encrypted = cipher.update(buffer, 0, count)
write(encrypted, encrypted.size)
}
// flush remains
if (count > 0)
{
val final = cipher.doFinal(buffer, 0, count)
write(final, final.size)
}
Example usage (input and output are streams):
encryption.encrypt(
read = { buffer, count ->
input.read(buffer, 0, count)
},
write = { buffer, count ->
output.write(buffer, 0, count)
}
)
I'm trying to convert a String to a ByteArray, and convert the ByteArray to a String in Kotlin.
My program was crashing, so I decided to debug a little, and my surprise was that when I convert a String into a ByteArray and I convert the same ByteArray to a String, the value isn't the same.
Like, if I do this:
val ivEnc = "[B#9a7d34b"
val dataEnc = "[B#1125ac5"
passTextEncrypted.setText(ivEnc.toByteArray().toString())
passTextDecrypted.setText(dataEnc.toByteArray().toString())
I expect to recive "[B#9a7d34b" and "[B#1125ac5". But every time I run the program, I recive different "random" values (all start with "[B#").
Is there something I'm doing wrong?
When you print out a ByteArray just using toString(), you are effectively getting its object ID (it's a bit more complicated, but that definition will work for now), rather than the contents of the ByteArray itself.
Solution 1
Pass a Charset as an argument to toString():
val bytes = "Hello!".toByteArray()
val string = bytes.toString(Charset.defaultCharset())
println(string) // Prints: "Hello!"
Solution 2
Pass the ByteArray as an argument to the String constructor:
val bytes = "Hello!".toByteArray()
val string = String(bytes)
println(string) // Prints: "Hello!"
On each encoding the result changes so if you specify the encoding you would not lose any data, you can do that by using Charset
val s = "this is an example"
val b = s.toByteArray(Charset.defaultCharset())
println(b)
val ss = String(b, Charset.defaultCharset())
println(ss)
I have raw PCM files generated from a base64 string. Here is the link to the API response that returns in..
I then create a PCM file from this string, after which I convert it to MP3 using the LAME library.
Here is the code that deals with the conversion:
companion object {
init {
System.loadLibrary("mp3lame")
}
}
private external fun initEncoder(numChannels: Int, sampleRate: Int, bitRate: Int, mode: Int, quality: Int)
private external fun destroyEncoder()
private external fun encodeFile(sourcePath: String, targetPath: String): Int
val NUM_CHANNELS = 1
val SAMPLE_RATE = 16000
val BITRATE = 128
val MODE = 3
val QUALITY = 0
fun createAudioFromBase64AndGetPath(inputBase64: String, outputFileName: String) {
initEncoder(NUM_CHANNELS, SAMPLE_RATE, BITRATE, MODE, QUALITY)
val path: String = "newFile.wav"
try {
val decoded = Base64.decode(inputBase64, Base64.NO_WRAP)
try {
val fileRaw = File(Environment.getExternalStorageDirectory().toString() + "/$outputFileName.pcm")
val fileEncoded = File(Environment.getExternalStorageDirectory().toString() + "/$outputFileName.mp3")
val os = FileOutputStream(fileRaw, true)
os.write(decoded)
os.close()
val result = encodeFile(fileRaw!!.absolutePath, fileEncoded!!.absolutePath)
if (result == 0) {
Log.d ("encoded to ", fileEncoded!!.name)
}
destroyEncoder()
} catch (e: Exception) {
Log.e ("decode ", "first catch", e)
e.printStackTrace()
}
} catch (e: Exception) {
e.printStackTrace()
Log.d ("decode ", "2nd catch", e)
}
}
The audio comes out sounding like this.
I've tried navigating the C library files that explain what the different variable options for initEncoder mean and I've fiddled around but nothing changes.
To try to troubleshoot this without the lag of compiling the app each time, I took the base64 string and converted it to a PCM file using an online converter (Motobit). I then used a very nifty (and free) converter for mac called XLD to test these conversions without having to compile the app each time to see if I could figure out what was going on, and if perhaps I was just using the wrong combination of variables for initEncoder.
The first thing I noticed was that I had to select the 'Open raw PCM (bin+cue)...' option in order to open the pcm filed downloaded from motobit.
Here is the selection window.
The next piece of the puzzle, which seems to be the crucial piece, was that I was only able to convert the audio properly (without the noise) when selecting "Little" in the Endian box. The problem is that back in my app, I am unable to find out how or if I even can access and change this property in the LAME library.
For clarity, I'm using the wrapper from here: https://developer.samsung.com/technical-doc/view.do?v=T000000090
If you get desperate, you can always switch the endianness yourself:
for (i in decoded.indices step 2)
{
val swap = decoded[i]
decoded[i] = decoded[i + 1]
decoded[i + 1] = swap
}
...since Base64.decode() returns a byte array, and I'm assuming you're using 16-bit audio.
I couldn't find the initEncoder() docs, myself.