Module: Lucerna::Gates::Hashing

Defined in:
lib/lucerna/gates/hashing.rb

Overview

The frozen bucketing contract (never change this):

bucket = murmur3_x86_32(utf8("#{salt}:#{unit_id}"), seed 0) % 10000

Buckets are basis points in [0, 10000). Salts are stored in the runtime, never derived from keys. Every SDK in every language must reproduce these values bit-for-bit — the golden vectors in sdks/core/src/vectors.json enforce it.

Constant Summary collapse

MASK =
0xFFFFFFFF
C1 =
0xcc9e2d51
C2 =
0x1b873593

Class Method Summary collapse

Class Method Details

.bucket(salt, unit_id) ⇒ Object

The unit's bucket for a salt, in basis points [0, 10000).



66
67
68
# File 'lib/lucerna/gates/hashing.rb', line 66

def bucket(salt, unit_id)
  murmur3("#{salt}:#{unit_id}") % 10_000
end

.murmur3(input, seed = 0) ⇒ Object

MurmurHash3 x86 32-bit over the UTF-8 bytes of input, as uint32. 32-bit wraparound is emulated by masking after every multiply/add; Ruby's right shift on the masked non-negative value matches the reference implementation's unsigned shift.



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
# File 'lib/lucerna/gates/hashing.rb', line 24

def murmur3(input, seed = 0)
  data = input.encode(Encoding::UTF_8).bytes
  length = data.length
  h1 = seed & MASK

  block_end = length - (length % 4)
  index = 0
  while index < block_end
    k1 = data[index] |
      (data[index + 1] << 8) |
      (data[index + 2] << 16) |
      (data[index + 3] << 24)
    k1 = (k1 * C1) & MASK
    k1 = ((k1 << 15) | (k1 >> 17)) & MASK
    k1 = (k1 * C2) & MASK
    h1 ^= k1
    h1 = ((h1 << 13) | (h1 >> 19)) & MASK
    h1 = ((h1 * 5) + 0xe6546b64) & MASK
    index += 4
  end

  k1 = 0
  remaining = length % 4
  k1 ^= data[index + 2] << 16 if remaining == 3
  k1 ^= data[index + 1] << 8 if remaining >= 2
  if remaining >= 1
    k1 ^= data[index]
    k1 = (k1 * C1) & MASK
    k1 = ((k1 << 15) | (k1 >> 17)) & MASK
    k1 = (k1 * C2) & MASK
    h1 ^= k1
  end

  h1 ^= length
  h1 ^= h1 >> 16
  h1 = (h1 * 0x85ebca6b) & MASK
  h1 ^= h1 >> 13
  h1 = (h1 * 0xc2b2ae35) & MASK
  h1 ^ (h1 >> 16)
end