-
-
Save bouk/2eef1fffaa88262c364da830dbabc65c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
require 'digest/sha2' | |
require 'benchmark' | |
require 'set' | |
module HashDigestUtils | |
extend self | |
ADD_VALUE_TO_DIGEST = { | |
String => ->(val, digest) { digest << val }, | |
FalseClass => ->(val, digest) { digest << 'FalseClass'.freeze }, | |
TrueClass => ->(val, digest) { digest << 'TrueClass'.freeze }, | |
NilClass => ->(val, digest) { digest << 'NilClass'.freeze }, | |
Symbol => ->(val, digest) { | |
digest << 'Symbol'.freeze | |
digest << val.to_s | |
}, | |
Fixnum => ->(val, digest) { | |
digest << 'Fixnum'.freeze | |
digest << val.to_s | |
}, | |
Bignum => ->(val, digest) { | |
digest << 'Bignum'.freeze | |
digest << val.to_s | |
}, | |
Array => ->(val, digest) { | |
digest << 'Array'.freeze | |
val.each do |element| | |
ADD_VALUE_TO_DIGEST[element.class].call(element, digest) | |
end | |
}, | |
Hash => ->(val, digest) { | |
digest << 'Hash'.freeze | |
val.sort.each do |array| | |
ADD_VALUE_TO_DIGEST[Array].call(array, digest) | |
end | |
}, | |
Set => ->(val, digest) { | |
digest << 'Set'.freeze | |
ADD_VALUE_TO_DIGEST[Array].call(val, digest) | |
}, | |
Encoding => ->(val, digest) { | |
digest << 'Encoding'.freeze | |
digest << val.name | |
}, | |
} | |
ADD_VALUE_TO_DIGEST.default_proc = ->(_, val) { | |
raise TypeError, "couldn't digest #{ val }" | |
} | |
def digest(obj) | |
digest = Digest::SHA256.new | |
ADD_VALUE_TO_DIGEST[obj.class].call(obj, digest) | |
digest.hexdigest | |
end | |
end | |
module RefinementDigestUtils | |
extend self | |
module AddValueToDigest | |
refine Object do | |
def add_value_to_digest(_) | |
raise TypeError, "couldn't digest #{self}" | |
end | |
end | |
refine String do | |
def add_value_to_digest(digest) | |
digest << self | |
end | |
end | |
refine FalseClass do | |
def add_value_to_digest(digest) | |
digest << 'FalseClass'.freeze | |
end | |
end | |
refine TrueClass do | |
def add_value_to_digest(digest) | |
digest << 'TrueClass'.freeze | |
end | |
end | |
refine NilClass do | |
def add_value_to_digest(digest) | |
digest << 'NilClass'.freeze | |
end | |
end | |
refine Symbol do | |
def add_value_to_digest(digest) | |
digest << 'Symbol'.freeze | |
digest << to_s | |
end | |
end | |
refine Fixnum do | |
def add_value_to_digest(digest) | |
digest << 'Fixnum'.freeze | |
digest << to_s | |
end | |
end | |
refine Bignum do | |
def add_value_to_digest(digest) | |
digest << 'Bignum'.freeze | |
digest << to_s | |
end | |
end | |
refine Array do | |
def add_value_to_digest(digest) | |
digest << 'Array'.freeze | |
each do |element| | |
element.add_value_to_digest(digest) | |
end | |
end | |
end | |
refine Hash do | |
def add_value_to_digest(digest) | |
digest << 'Hash'.freeze | |
sort.each do |array| | |
digest << 'Array'.freeze | |
array.each do |element| | |
element.add_value_to_digest(digest) | |
end | |
end | |
end | |
end | |
refine Set do | |
def add_value_to_digest(digest) | |
digest << 'Set'.freeze | |
digest << 'Array'.freeze | |
each do |element| | |
element.add_value_to_digest(digest) | |
end | |
end | |
end | |
refine Encoding do | |
def add_value_to_digest(digest) | |
digest << 'Encoding'.freeze | |
digest << self.name | |
end | |
end | |
end | |
using AddValueToDigest | |
def digest(obj) | |
digest = Digest::SHA256.new | |
obj.add_value_to_digest(digest) | |
digest.hexdigest | |
end | |
end | |
module SwitchDigestUtils | |
extend self | |
def add_value_to_digest(val, digest) | |
case val | |
when String | |
digest << val | |
when Array | |
digest << 'Array'.freeze | |
val.each do |element| | |
add_value_to_digest(element, digest) | |
end | |
when FalseClass | |
digest << 'FalseClass'.freeze | |
when TrueClass | |
digest << 'TrueClass'.freeze | |
when NilClass | |
digest << 'NilClass'.freeze | |
when Symbol | |
digest << 'Symbol'.freeze | |
digest << val.to_s | |
when Fixnum | |
digest << 'Fixnum'.freeze | |
digest << val.to_s | |
when Bignum | |
digest << 'Bignum'.freeze | |
digest << val.to_s | |
when Hash | |
digest << 'Hash'.freeze | |
val.sort.each do |array| | |
digest << 'Array'.freeze | |
array.each do |element| | |
add_value_to_digest(element, digest) | |
end | |
end | |
when Set | |
digest << 'Set'.freeze | |
digest << 'Array'.freeze | |
val.each do |element| | |
add_value_to_digest(element, digest) | |
end | |
when Encoding | |
digest << 'Encoding'.freeze | |
digest << val.name | |
else | |
raise TypeError, "couldn't digest #{val}" | |
end | |
end | |
def digest(obj) | |
digest = Digest::SHA256.new | |
add_value_to_digest(obj, digest) | |
digest.hexdigest | |
end | |
end | |
n = 100_000 | |
def digestit(mod) | |
digested = mod.digest(["a", 1, :c, [1012312311513453452345, true, false, nil, "wow!!"], "c", [1, 2, Set.new([{"b" => 1}, 2, 3])], "a", "b", "arqj4jqo3i4gj3oi"]) | |
end | |
Benchmark.bmbm do |x| | |
x.report("Hash") { n.times { | |
digestit(HashDigestUtils) | |
} } | |
x.report("Switch") { n.times { | |
digestit(SwitchDigestUtils) | |
} } | |
x.report("Refinement") { n.times { | |
digestit(RefinementDigestUtils) | |
} } | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment