jing
jing

Reputation: 2021

How to create ruby uuid from bytes?

C# contains method to generate Guid from bytes:

byte[] bytes = {107, 97, 155, 242, 36, 52, 182, 87, 67, 223, 163, 166, 7, 175, 123, 223};
Guid guid = new Guid(bytes); // => {f29b616b-3424-57b6-43df-a3a607af7bdf}

How to write ruby code which will generate the same uuid as C#? The uuid defined in Ruby:SecureRandom doesn't accept any parameters.

Upvotes: 0

Views: 1221

Answers (5)

user229044
user229044

Reputation: 239240

This can be done in a really straightforward way by converting the bytes to an array of hex strings, inserting -'s in the right locations, and then joining the array.

def to_uuid(bytes)
  hex = bytes.map { |b| b.to_s(16).rjust(2, '0') }
  [4, 7, 10, 13].inject(hex) { |hex, n| hex.insert(n, '-') }.join
end

to_uuid([107, 97, 155, 242, 36, 52, 182, 87, 67, 223, 163, 166, 7, 175, 123, 223])
# => "6b619bf2-2434-b657-43df-a3a607af7bdf"

Given a byte array...

bytes = [107, 97, 155, 242, 36, 52, 182, 87, 67, 223, 163, 166, 7, 175, 123, 223];

You can convert the bytes to hexadecimal and then pad the resulting strings with leading 0s...

hex = bytes.map { |b| b.to_s(16).rjust(2, '0') }
# => ["6b", "61", "9b", "f2", "24", "34", "b6", "57", "43", "df", "a3", "a6", "07", "af", "7b", "df"]

Then insert hyphens at the right places...

[4, 7, 10, 13].inject(hex) { |hex, n| hex.insert(n, '-') }
# => ["6b", "61", "9b", "f2", "-", "24", "34", "-", "b6", "57", "-", "43", "df", "-", "a3", "a6", "07", "af", "7b", "df"]

Then join the array:

hex.join
# => "6b619bf2-2434-b657-43df-a3a607af7bdf"

Upvotes: 0

Harsh Gupta
Harsh Gupta

Reputation: 4538

Another way to skin a cat, simple and easy to understand:

a = [107, 97, 155, 242, 36, 52, 182, 87, 67, 223, 163, 166, 7, 175, 123, 223]

def _guid(ints, reverse=false)
  hexes = ints.map { |b| b.to_s(16).rjust(2, '0') }
  return hexes.reverse.join if reverse
  hexes.join
end

def guid(ints)
  '%s-%s-%s-%s-%s' % [
    _guid(ints[0...4], true),
    _guid(ints[4...6], true),
    _guid(ints[6...8], true),
    _guid(ints[8...10]),
    _guid(ints[10..-1]),
  ]
end

puts guid a # => f29b616b-3424-57b6-43df-a3a607af7bdf

Upvotes: 1

Jordan Running
Jordan Running

Reputation: 106027

Here's one way, using nothing but sprintf. I'm not sure if I love it or hate it.

arr = [107, 97, 155, 242, 36, 52, 182, 87, 67, 223, 163, 166, 7, 175, 123, 223]

fmt = "%4$02x%3$02x%2$02x%1$02x-" \
      "%6$02x%5$02x-%8$02x%7$02x-%9$02x%10$02x-" \
      "%11$02x%12$02x%13$02x%14$x%15$x%16$x"

str = sprintf(fmt, *arr)
# => "f29b616b-3424-57b6-43df-a3a607af7bdf"

This uses sprintf's $ flag to explicitly specify the order for the hex digits, e.g. %4$02x means "print the fourth octet in the arguments as two hex digits."

We could, of course, generate the format string:

positions = [[4, 3, 2, 1], [6, 5], [8, 7], [9, 10], 11..16]
fmt = positions.map {|a| a.map {|d| "%#{d}$02x" }.join }.join("-")
# => "%4$02x%3$02x%2$02x%1$02x-%6$02x%5$02x-%8$02x%7$02x-%9$02x%10$02x-%11$02x%12$02x%13$02x%14$02x%15$02x%16$02x"

str = sprintf(fmt, *arr)
# => "f29b616b-3424-57b6-43df-a3a607af7bdf"

...but at that point you might as well just do this:

positions = [ [ 3, 2, 1, 0 ], [ 5, 4 ], [ 7, 6 ], [ 8, 9 ], 10..15 ]
str = positions.map {|a| a.map {|n| "%02x" % arr[n] }.join }.join("-")
# => f29b616b-3424-57b6-43df-a3a607af7bdf

You can see all of these in action on repl.it: https://repl.it/@jrunning/FamousRewardingApplescript

Upvotes: 2

mcfinnigan
mcfinnigan

Reputation: 11638

First approximation answer:

a = [107, 97, 155, 242, 36, 52, 182, 87, 67, 223, 163, 166, 7, 175, 123, 223]
ah = a.map{ |i| i.to_s(16) }

puts [4,2,2,2,6].inject([]) { |result, idx| result << ah.slice!(0, idx).reverse.join }.join("-")

f29b616b-3424-57b6-df43-df7baf7a6a3
=> nil

There is almost certainly a cleaner way to do it, but this gives you something to work with. It uses inject to accumulate the generated uuid string sections into an array, then joins those into the guid.

Each block of the guid is a subarray of the byte array, seemingly ordered lsb to msb.

Upvotes: -1

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

Sometimes development process involves writing code, not only calling existing libraries:

bytes.each_with_object([]) do |b, acc|
  acc << [] if acc.size == 0 ||
               acc.size == 1 && acc.last.size == 4 ||
               acc.size > 1 && acc.size < 5 && acc.last.size == 2
  acc.last << b.to_s(16).rjust(2, '0')
end.map.with_index do |e, idx|
  idx < 3 ? e.reverse : e
end.map(&:join).join('-')
#⇒ "f29b616b-3424-57b6-43df-a3a607af7bdf"

Upvotes: 4

Related Questions