Reputation: 17538
Given an array of four 8-bit unsigned ints, for example a 32-bit RGBA color
rgba = [255, 255, 255, 255] # white
what is the most efficient way to combine these four Fixnums into a single 32-bit Integer?
Currently, I'm using pack and unpack:
rgba.pack('C*').unpack('L')[0] #=> 4294967295
# Check work: same value as corresponding hex literal
0xffffffff #=> 4294967295
but I wonder if there's a more efficient way. I found this C++ question:
Cleanest way to combine two shorts to an int
and I think <<
is a "shift" and |
is a "mask", but I don't know enough about those operators, and have never used them in ruby.
Upvotes: 4
Views: 3958
Reputation: 12568
Shift each element by 24 - (index * 8), then OR them all together:
rgba = [255, 255, 255, 255]
rgba[0] << 24 | rgba[1] << 16 | rgba[2] << 8 | rgba[3]
I didn't shift rgba[3]
because shifting by zero has no effect.
Some measurements:
Using unpack
Benchmark.measure do
100_000.times do
rgba = [255, 255, 255, 255]
rgba.pack('C*').unpack('L')[0]
end
end
Result:
=> #<Benchmark::Tms:0x007fe137005350
@cstime=0.0,
@cutime=0.0,
@label="",
@real=0.146899,
@stime=0.0,
@total=0.1399999999999999,
@utime=0.1399999999999999>
Using bitwise operators
Benchmark.measure do
100_100.times do
rgba = [255, 255, 255, 255]
rgba[0] << 24 | rgba[1] << 16 | rgba[2] << 8 | rgba[3]
end
end
Result:
=> #<Benchmark::Tms:0x007fd66438fd28
@cstime=0.0,
@cutime=0.0,
@label="",
@real=0.088995,
@stime=0.0,
@total=0.08000000000000007,
@utime=0.08000000000000007>
Upvotes: 3