Skip to content

Instantly share code, notes, and snippets.

@bouk
Last active September 19, 2016 15:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bouk/2eef1fffaa88262c364da830dbabc65c to your computer and use it in GitHub Desktop.
Save bouk/2eef1fffaa88262c364da830dbabc65c to your computer and use it in GitHub Desktop.
# 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