4

I'm trying to understand a piece of code shared here: https://stackoverflow.com/a/2117523/2586761

// returns a valid GUID
'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random() * 16 | 0;
    var v = c === 'x' ? r : (r & 0x3 | 0x8);
    return v.toString(16);
});
// "3bce4931-6c75-41ab-afe0-2ec108a30860"

I understand from the question Using bitwise OR 0 to floor a number that a 3.142 | 0 will return an integer by truncating the fraction, but I'm guessing when it comes to applying this to hex numbers, and lost when we AND then OR like: randomNumber & 0x3 | 0x8.

I've discovered that a hex value has a toString method (a number doesn't!):

0x3.toString() // returns '3'

And I'm guessing that maybe randomNumber & 0x3 | 0x8 is returning a two bit value which is an ascii character (not just a number) in the desired range... but I'm guessing, and I can't find a nice reference which gives me the whole picture.

Can anyone step me through it / suggest a reference?

This question seems related, but doesn't deal with JS specifically, or match my example: bitwise and logical AND/OR in terms of hex result


Update:

I've since notice that there are a bunch of great answers on the original question which explore the question, in particular https://stackoverflow.com/a/28921801/2586761, but none of them answer all my questions..

In particular, how does a number get converted to a character without using something like String.fromCharCode()?

Community
  • 1
  • 1
ptim
  • 14,902
  • 10
  • 83
  • 103
  • 2
    Great question! Also for the record, numbers do have a `toString` method: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString – SimianAngel Jul 23 '16 at 14:27
  • gold, thanks @SimianAngel! I was missing the parens and radix: `(42).toString(16) // "2a"` – ptim Jul 23 '16 at 14:55

1 Answers1

4

No, neither & nor | performs conversion to an ASCII character, this conversion is performed by the last v.toString(16). & and | just do bitwise operations.

Before the second line of the function, r contains a random integer number between 0x0 and 0xf. It can be represented with four bits: r == abcd, where a, b, c and d are 0 or 1.

r & 0x3 nullifies everything except for two latter bits (since 3 is binary 11). So, r & 0x3 == 00cd.

| 0x8 sets the first bit (since 8 is binary 1000). So, r & 0x3 | 0x8 == 10cd.

As a result, the whole expression r & 0x3 | 0x8 takes two least significant bits of r and appends them to binary 10. Before this operation we could have any number from 0x0 to 0xf, but after it only four variants are possible: 0x8 (binary 1000), 0x9 (binary 1001), 0xa (binary 1010) or 0xb (binary 1011).

Ruslan Batdalov
  • 793
  • 1
  • 8
  • 21
  • fantastic, thanks.. I've nearly got it.. can you explain how the four variations `1000, 1001, 1010, 1011` get translated to a random alphanumeric? `(1011).toString(16) // '3f3'` – ptim Jul 23 '16 at 14:49
  • 2
    Each of these four variations is just one hexadecimal symbol: 1000 is '8', 1001 is '9', 1010 is 'a', and 1011 is 'b'. The whole random string is generated by the starting string with a lot of x's and some y's. The algorithm substitutes a random digit (in the whole range from 0 to f) for any x and one of these four options in place of y. – Ruslan Batdalov Jul 23 '16 at 15:24
  • ah, now I understand, in the Y position, only those four characters are allowed by the spec.. also.. I was under the mistaken impression that the characters produced were `[a-z-0-9]`, but now I understand that only hex values are produced.. mystery solved! Many thanks! – ptim Jul 25 '16 at 01:51