Bitwise

Context

I have a legacy identifier with a reference implementation is JS. I need to generate IDs in Java.

I rarely have to think about bitwise operations, so this problem started out frustrating, but I developed an appreciation for the elegance of binary, hex and bitwise operations as I warmed up, and was reluctant to trade them in for the relatively self-documenting Java syntax at the end.

JS

We use an array of 8-bit, unsigned ints:

var ints = new Uint8Array(8)

Presumably, we’re using unsigned ints so we can encode

We populate this array with random values:

window.crypto.getRandomValues(ints)

We get the last (random) 4 bits of the first byte using the AND operator against 1111 (0xF in hex):

var rand = 0xF & ints[0]

We prepend 0111 by adding 011100000 (0x70 in hex):

ints[0] = 0x70 + rand

I think this may be due to max long in Java, ie Long.MAX_VALUE == 0x7FFF_FFFF_FFFF_FFFFL.

We convert this to an array of chars using and then base64 encode:

btoa(String.fromCharCode(...ints))

I think the base64 encoding is to retain the integrity of the “bytes”.

Sanity:

> atob("ekMJT1sVNBc=").split('').map(c=>c.charCodeAt(0))
< [122, 67, 9, 79, 91, 21, 52, 23]

Java

Java makes this easier with native 64 bit long, byte primitives and convenience classes. Java REPL makes it easy to hack around.

Decode

For sanity, can we decode the value produced by JS?

import java.nio.ByteBuffer
import java.util.Base64
String encoded = "ekMJT1sVNBc="
byte[] bytes = Base64.getDecoder().decode(encoded)
long id = ByteBuffer.wrap(bytes).getLong()

Encode

Generate a random long within a range 0x7…:

import java.util.Random
long id = new Random().longs(0x7000_0000_0000_0000L, Long.MAX_VALUE).limit(1).findFirst().getAsLong()

Convert to bytes:

byte[] bytes = ByteBuffer.allocate(Long.BYTES).putLong(id).array()

Note this method produces values outside the range 0-255, eg:

< byte[] bytes = [116, 91, -54, -35, -92, -103, 127, -28]
> Integer.toHexString(bytes[2])
< "ffffffca"

I suspect ID generation originated in Java as random unsigned longs, was replicated in JS

Encode:

Base64.getEncoder().encodeToString(bytes)

Sanity:

Long.toHexString(id)
Long.toBinaryString(id)

Feedback

Thoughts? Suggestions? Hit me up @erikeldridge

License

Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 International License, and code samples are licensed under the MIT license.