Module: DJB2
- Defined in:
- lib/djb2.rb,
lib/djb2/version.rb
Defined Under Namespace
Classes: Error
Constant Summary collapse
- VERSION =
"0.2.0"
Class Method Summary collapse
-
.digest(string) ⇒ Integer
Computes the djb2 hash (xor variant) of the given string.
Class Method Details
.digest(string) ⇒ Integer
Computes the djb2 hash (xor variant) of the given string.
The hash is computed using 64-bit arithmetic split into two 32-bit halves to keep all intermediate values within Ruby's Fixnum range, avoiding Bignum allocation in the hot loop. This makes the implementation YJIT-friendly: the JIT can emit efficient native code for the entire loop without any object allocations.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/djb2.rb', line 19 def self.digest(string) raise TypeError, "no implicit conversion of #{string.class} into String" unless string.is_a?(String) hi = 0 # upper 32 bits of the hash lo = 5381 # lower 32 bits of the hash i = 0 len = string.bytesize # Process 4 bytes at a time to reduce loop overhead. stop = len - (len & 3) while i < stop # Multiply (hi:lo) by 33 using: x * 33 = (x << 5) + x # lo half: must mask (lo << 5) to 32 bits BEFORE adding lo, so that # the carry into the hi half is correct (0 or 1, never more). t = ((lo << 5) & 0xFFFFFFFF) + lo # hi half: (hi << 5) can be up to 37 bits, but the total expression # still fits in a Fixnum (< 62 bits), so we only mask at the end. hi = ((hi << 5) + (lo >> 27) + hi + (t >> 32)) & 0xFFFFFFFF # XOR the current byte into the lo half. lo = (t & 0xFFFFFFFF) ^ string.getbyte(i) t = ((lo << 5) & 0xFFFFFFFF) + lo hi = ((hi << 5) + (lo >> 27) + hi + (t >> 32)) & 0xFFFFFFFF lo = (t & 0xFFFFFFFF) ^ string.getbyte(i + 1) t = ((lo << 5) & 0xFFFFFFFF) + lo hi = ((hi << 5) + (lo >> 27) + hi + (t >> 32)) & 0xFFFFFFFF lo = (t & 0xFFFFFFFF) ^ string.getbyte(i + 2) t = ((lo << 5) & 0xFFFFFFFF) + lo hi = ((hi << 5) + (lo >> 27) + hi + (t >> 32)) & 0xFFFFFFFF lo = (t & 0xFFFFFFFF) ^ string.getbyte(i + 3) i += 4 end # Handle remaining 0-3 bytes. while i < len t = ((lo << 5) & 0xFFFFFFFF) + lo hi = ((hi << 5) + (lo >> 27) + hi + (t >> 32)) & 0xFFFFFFFF lo = (t & 0xFFFFFFFF) ^ string.getbyte(i) i += 1 end # Combine the two halves into a 64-bit result. Using multiply instead # of (hi << 32) | lo avoids a YJIT side exit caused by left shift overflow. hi * 0x100000000 + lo end |