skip to Main Content

I have 12 bytes saved as 24 HEX chars array, for example: "0123456789abcdef12345678" and I must storage this hex string as a lot of double variables (in time-series database). Yes, this is crazy, but in SaaS aplications, time to time it is normal that we need to do ridiculous workaround.
But… how to do it?

var input = "0123456789abcdef12345678"; // always 12 bytes input, but converted to 24 hex chars string by SaaS software on beginning of this script
var output1 = 0.0; // double - probably 8 bytes
var output2 = 0.0; // double - probably 8 bytes
var output3 = 0.0; // double - probably 8 bytes
// how to divide input variable into outputX variables?

I think it is possible to do it in 2 output's variables, but I could use as many I need – if it helps but I think, 12 variables is not the most optimal choice.

And another, easier question: how to rollback this, and transform output's variables back to input variable?
Of course it has to be a lossless way. And of course, it should work in pure JavaScript – dynamic typing does not make this task any easier.

So… what is the most effective way to code and decode this?

2

Answers


  1. You could put the bytes in an Uint8Array (of length 12), then use a Float64Array (of length 1.5) as a view on the same buffer, or a Float32Array (of length 3). However, interpreting arbitrary raw bytes as floats might end up with NaN, which will probably not serialise nicely into your database and may return as a different NaN value.

    It’s much easier to use Uint32 values instead, for which you don’t even need typed arrays. To get your hex string into three integers, just slice and parse them:

    var input = "0123456789abcdef12345678"; // always 12 bytes input, but converted to 24 hex chars string by SaaS software on beginning of this script
    var output1 = parseInt(input.slice( 0,  8), 16);
    var output2 = parseInt(input.slice( 8, 16), 16);
    var output3 = parseInt(input.slice(16, 24), 16);
    console.log(output1, output2, output3);

    Reversal is similarly simple, just convert the integers back to hex strings and concatenate them:

    const input = [19088743, 2309737967, 305419896];
    const parts = input.map(i => i.toString(16).padStart(8, '0'));
    console.log(parts.join(''));
    Login or Signup to reply.
  2. The format of a double involves a sign (1 bit), an exponent (11 bits) and a significand (53 bits, but the first is always a 1, so only 52 bits of data).

    A byte is 8 bits. You want to save 12 bytes, so you need 96 bits. You want to do this in 2 doubles, so you have to find 48 bits per double. And you’d like it to be easy to work with.

    One answer is that numbers that require at at most 53 binary digits of precision are represented exactly. So let x = 1/2**48 then 0*x, 1*x, ..., 2**48 * x are all represented exactly. Which allows us to encode and decode 6 bytes per double using those numbers. Here is code to do that with an easy test so that you can verify that it actually works correctly.

    function bytes2doubles(bytes) {
        let doubles = [0, 0];
        for (let i = 0; i < 2; i++) {
            for (let j = 0; j < 6; j++) {
                doubles[i] = (doubles[i] + bytes[6*i+j])/256;
            }
        }
        return doubles;
    }
    
    function doubles2bytes(doubles) {
        let bytes = [];
        for (i = 0; i < 2; i++) {
            let d = doubles[i];
            for (j = 5;-1 < j; j--) {
                d = 256*d;
                bytes[6*i + j] = Math.floor(d);
                d -= Math.floor(d);
            }
        }
        return bytes;
    }
    
    let b = [];
    for (let i = 0; i < 12; i++) {
        b.push(Math.floor(Math.random()*256));
    }
    console.log([b, bytes2doubles(b), doubles2bytes(bytes2doubles(b))]);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search