Question

I have a function that is attempting to convert a time in milliseconds obtained from the currentTimeMillis method available on the JVM to a vector of signed bytes. It looks good most of the time, except for one small problem -- it doesn't always appear to increment the nth byte position correctly after reaching the maximum byte value for the preceding 8 bits. Here is an example sequence of bytes:

[0 0 1 69 74 87 123 56]
[0 0 1 69 74 87 123 56]
[0 0 1 69 74 87 123 56]
[0 0 1 69 74 87 123 56]
[0 0 1 69 74 87 123 57]
[0 0 1 69 74 87 123 57]
[0 0 1 69 74 87 123 57]
[0 0 1 69 74 87 123 57]
[0 0 1 69 74 87 123 57]
[0 0 1 69 74 87 123 57]
[0 0 1 69 74 87 1 64]
[0 0 1 69 74 87 1 64]
[0 0 1 69 74 87 1 64]
[0 0 1 69 74 87 1 64]
[0 0 1 69 74 87 1 65]
[0 0 1 69 74 87 1 65]
[0 0 1 69 74 87 1 65]
[0 0 1 69 74 87 1 65]
[0 0 1 69 74 87 1 65]
[0 0 1 69 74 87 1 66]
[0 0 1 69 74 87 1 66]

Oops. As you can see the sixth byte from the right is not getting incremented to 88. This doesn't appear to happen all the time -- other times it will increment the byte correctly. This will be a problem because I am sending this value to a server which expects a time interval as a sequence of byte values that must constantly increment.

Here is the code i used to generate this:

(def max-signed-bytes 128)

(defn byte-at
  "Shifts x by a given number of bits"
  [x offset]
  (mod (bit-shift-right x offset) max-signed-bytes))

(defn Long->bytes
  "Convert a 64-bit integer into a byte vector"
  [^Long x]
  (conj []
        (byte-at x 56)
        (byte-at x 48)
        (byte-at x 40)
        (byte-at x 32)
        (byte-at x 24)
        (byte-at x 16)
        (byte-at x 8)
        (mod x max-signed-bytes)))

(defn time->bytes
  "Returns the current time in milliseconds as a byte vector"
  []
  (Long->bytes (System/currentTimeMillis)))

I tried modding against 256 for an unsigned byte value and it doesn't appear to run into this problem. However, I will need a signed byte value in order to stuff the contents into a byte array, since I ultimately need to write a byte array using DataOutputStream's write method.

Was it helpful?

Solution

Easiest way to get a byte array from a long is probably

(defn num->bytes [n] (.toByteArray (biginteger n)))

But, since your edit specifies you are using DataOutputStream, just use the writeLong method, directly.

so.core=>(def buffer (java.io.ByteArrayOutputStream.))
#'so.core/buffer
so.core=> (def data-stream (java.io.DataOutputStream. buffer))
#'so.core/data-stream
so.core=> (.writeLong data-stream 257)
nil
so.core=> (.toByteArray buffer)
#<byte[] [B@6ea4b78b>
so.core=> (vec *1)
[0 0 0 0 0 0 1 1]

Problem with your code is as you diagnosed in your edit. You need the modulus over number of possible byte values, 256, rather than the maximum signed value, 128. Otherwise, you are mapping two byte-values to the same number (= (mod 127 128) (mod -1 128)) ;=> true. Conversion to bytes will be signed (.byteValue (mod 255 256)) ;=> -1.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top