How do I swap endian-ness (byte order) of a variable in javascript
Asked Answered
N

4

26

I am receiving and sending a decimal representation of two little endian numbers. I would like to:

  • shift one variable 8 bits left
  • OR them
  • shift a variable number of bits
  • create 2 8 bit numbers representing the first and second half of the 16 bit number.

javascript (according to https://developer.mozilla.org/en/JavaScript/Reference/Operators/Bitwise_Operators) uses big endian representation when shifting...

endianness is a bit foreign to me (I am only 90 percent sure that my outlined steps are what i want.) so swapping is a bit dizzying. please help! I only really need to know how to swap the order in an efficient manner. (I can only think of using a for loop on a toString() return value)

Neuropath answered 16/3, 2011 at 2:37 Comment(0)
G
44
function swap16(val) {
    return ((val & 0xFF) << 8)
           | ((val >> 8) & 0xFF);
}

Explanation:

  1. Let's say that val is, for example, 0xAABB.
  2. Mask val to get the LSB by &ing with 0xFF: result is 0xBB.
  3. Shift that result 8 bits to the left: result is 0xBB00.
  4. Shift val 8 bits to the right: result is 0xAA (the LSB has "dropped off" the right-hand side).
  5. Mask that result to get the LSB by &ing with 0xFF: result is 0xAA.
  6. Combine the results from steps 3 and step 5 by |ing them together:
    0xBB00 | 0xAA is 0xBBAA.

function swap32(val) {
    return ((val & 0xFF) << 24)
           | ((val & 0xFF00) << 8)
           | ((val >> 8) & 0xFF00)
           | ((val >> 24) & 0xFF);
}

Explanation:

  1. Let's say that val is, for example, 0xAABBCCDD.
  2. Mask val to get the LSB by &ing with 0xFF: result is 0xDD.
  3. Shift that result 24 bits to the left: result is 0xDD000000.
  4. Mask val to get the second byte by &ing with 0xFF00: result is 0xCC00.
  5. Shift that result 8 bits to the left: result is 0xCC0000.
  6. Shift val 8 bits to the right: result is 0xAABBCC (the LSB has "dropped off" the right-hand side).
  7. Mask that result to get the second byte by &ing with 0xFF00: result is 0xBB00.
  8. Shift val 24 bits to the right: result is 0xAA (everything except the MSB has "dropped off" the right-hand side).
  9. Mask that result to get the LSB by &ing with 0xFF: result is 0xAA.
  10. Combine the results from steps 3, 5, 7 and 9 by |ing them together:
    0xDD000000 | 0xCC0000 | 0xBB00 | 0xAA is 0xDDCCBBAA.
Gayton answered 16/3, 2011 at 3:9 Comment(6)
so, this seems to work...is there a plain spoken explanation or should i just take it and run?Neuropath
@griotspeak: I've edited to attempt an explanation, although I suspect that it would make more sense for you to read the wikipedia articles on endianness and bitwise operations instead.Gayton
ok, this wasn't actually what i wanted, but i think i am simply being unclear. I need a literal reversal of the first 8(or 16?) bits. I think this answer gets me close enough to work it out.Neuropath
function swap8(val) { return ((val & 0x1) << 7) | ((val & 0x2) << 5) | ((val & 0x4) << 3) | ((val & 0x8) << 1) | ((val >> 1) & 0x8) | ((val >> 3) & 0x4) | ((val >> 5) & 0x2) | ((val >> 7) & 0x1); } was my final solutionNeuropath
In case this helps anyone, the swap16() function alone was not handling signed integers correctly. I had to combine it with this in order to get everything working correctly: ((((val & 0xFF) << 8) | ((val >> 8) & 0xFF)) << 16) >> 16Tailorbird
@whiskeyspider mentioned the sign problem already. Here's what I ended up with instead: function swap16(val) { var x = ((val & 0xFF) << 8) | ((val >> 8) & 0xFF); if (x < 0) x = (x & 0x7FFF) + 0x8000; return x; } function swap32(val) { var x = ((val & 0xFF) << 24) | ((val & 0xFF00) << 8) | ((val >> 8) & 0xFF00) | ((val >> 24) & 0xFF); if (x < 0) x = (x & 0x7FFFFFFF) + 0x80000000; return x; }Credential
S
10

Such function can be used to change endianness in js:

const changeEndianness = (string) => {
        const result = [];
        let len = string.length - 2;
        while (len >= 0) {
          result.push(string.substr(len, 2));
          len -= 2;
        }
        return result.join('');
}

changeEndianness('AA00FF1234'); /// '3412FF00AA'
Socialize answered 6/12, 2017 at 6:56 Comment(1)
Wonderful job using strings!Detent
M
1

Use the << (bit shift) operator. Ex: 1 << 2 == 4.

I really think that the underlying implementation of JavaScript will use whatever endianess the platform it is running on is using. Since you cannot directly access memory in JavaScript you won't ever have to worry about how numbers are represented physically in memory. Bit shifting integer values always yield the same result no matter the endianess. You only see a difference when looking at individual bytes in memory using pointers.

Metallize answered 16/3, 2011 at 3:7 Comment(2)
I am literally being given a decimal representation of a little endian number and need to give back a decimal representation of a little ending number. A display is being driven by the binary representation... so it matters.Neuropath
i think i see what you were getting at, and i think that in many cases you would be correct. the number i get and give is meant to turn 8 lights on from left to right so i will, essentially, be looking at the individual bytes.Neuropath
M
0

Here is a oneliner for arrays to swap between big and little endian (and vise versa). The swapping is done using reverse on byte level. I guess for large arrays, it is more efficient than looping over scalar swap function.

function swapbyte(x) {
    return new Float64Array(new Int8Array(x.buffer).reverse().buffer).reverse()
}

// Example
buf = new ArrayBuffer(16);        // for 2 float64 numbers
enBig = new Float64Array(buf);
enBig[0] = 3.2073756306779606e-192;
enBig[1] = 2.7604354232023903e+199;

enLittle = swapbyte(enBig)

// two famous numbers are revealed
console.log(enLittle)
// Float64Array [ 6.283185307179586, 2.718281828459045 ]

// swapping again yields the original input
console.log(swapbyte(enLittle))
// Float64Array [ 3.2073756306779606e-192, 2.7604354232023903e+199 ]
Mame answered 16/9, 2022 at 22:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.