Module: PseudoRandom::Seed
- Defined in:
- lib/pseudo_random/seed.rb
Overview
Internal seed canonicalization & hashing (FNV-1a 64-bit) Uses C++ implementation if available, otherwise pure Ruby
Constant Summary collapse
- FNV_OFFSET =
0xcbf29ce484222325- FNV_PRIME =
0x100000001b3- MASK64 =
0xffff_ffff_ffff_ffff
Class Method Summary collapse
-
.canonical_each_byte(obj) ⇒ Object
Depth-first canonical serialization streamed as bytes.
-
.encode_varint(num) ⇒ Object
Varint (7-bit continuation) encoding.
-
.to_seed_int(obj) ⇒ Object
Public: Convert arbitrary Ruby object to a deterministic 31-bit Integer for Random.new.
-
.zigzag(num) ⇒ Object
ZigZag encode signed -> unsigned integer.
Class Method Details
.canonical_each_byte(obj) ⇒ Object
Depth-first canonical serialization streamed as bytes
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 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/pseudo_random/seed.rb', line 31 def canonical_each_byte(obj, ...) case obj when NilClass yield 'n'.ord when TrueClass yield 't'.ord when FalseClass yield 'f'.ord when Integer yield 'i'.ord encode_varint(zigzag(obj), ...) when Float yield 'd'.ord [obj].pack('G').each_byte(...) when String str = obj.encode(Encoding::UTF_8) yield 's'.ord encode_varint(str.bytesize, ...) str.each_byte(...) when Symbol str = obj.to_s.encode(Encoding::UTF_8) yield 'y'.ord encode_varint(str.bytesize, ...) str.each_byte(...) when Array yield 'a'.ord encode_varint(obj.length, ...) obj.each { |e| canonical_each_byte(e, ...) } when Hash yield 'h'.ord encode_varint(obj.length, ...) # Canonical order by key string representation to avoid insertion order dependence obj.keys.map(&:to_s).sort.each do |ks| canonical_each_byte(ks, ...) original_key = if obj.key?(ks) ks elsif obj.key?(ks.to_sym) ks.to_sym else # Fallback (should not usually happen) obj.keys.find { |k| k.to_s == ks } end canonical_each_byte(obj[original_key], ...) end when Time yield 'T'.ord encode_varint(obj.to_i, ...) encode_varint(obj.nsec, ...) else # Fallback: class name + ':' + to_s (could cause collisions if to_s not stable) rep = "#{obj.class.name}:#{obj}" rep = rep.encode(Encoding::UTF_8) yield 'o'.ord encode_varint(rep.bytesize, ...) rep.each_byte(...) end end |
.encode_varint(num) ⇒ Object
Varint (7-bit continuation) encoding
95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/pseudo_random/seed.rb', line 95 def encode_varint(num) raise ArgumentError, 'negative varint' if num < 0 loop do byte = num & 0x7f num >>= 7 if num.zero? yield byte break else yield(byte | 0x80) end end end |
.to_seed_int(obj) ⇒ Object
Public: Convert arbitrary Ruby object to a deterministic 31-bit Integer for Random.new
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/pseudo_random/seed.rb', line 14 def to_seed_int(obj) if PseudoRandom.native_extension_loaded? # Use C++ implementation for better performance PseudoRandom::SeedNative.to_seed_int(obj) else # Fall back to Ruby implementation h = FNV_OFFSET canonical_each_byte(obj) do |byte| h ^= byte h = (h * FNV_PRIME) & MASK64 end s = h ^ (h >> 32) s & 0x7fff_ffff end end |
.zigzag(num) ⇒ Object
ZigZag encode signed -> unsigned integer
90 91 92 |
# File 'lib/pseudo_random/seed.rb', line 90 def zigzag(num) num >= 0 ? (num << 1) : ((-num << 1) - 1) end |