Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Speed up digest with recursive hash #320

Merged
merged 1 commit into from Jun 21, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
84 changes: 45 additions & 39 deletions lib/sprockets/digest_utils.rb
Expand Up @@ -35,6 +35,50 @@ def detect_digest_class(bytes)
DIGEST_SIZES[bytes.bytesize]
end

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.to_a, digest)
},
Encoding => ->(val, digest) {
digest << 'Encoding'.freeze
digest << val.name
},
}
ADD_VALUE_TO_DIGEST.default_proc = ->(_, val) {
raise TypeError, "couldn't digest #{ val }"
}
private_constant :ADD_VALUE_TO_DIGEST

# Internal: Generate a hexdigest for a nested JSON serializable object.
#
# This is used for generating cache keys, so its pretty important its
Expand All @@ -45,46 +89,8 @@ def detect_digest_class(bytes)
# Returns a String digest of the object.
def digest(obj)
digest = digest_class.new
queue = [obj]

while queue.length > 0
obj = queue.shift
klass = obj.class

if klass == String
digest << obj
elsif klass == Symbol
digest << 'Symbol'
digest << obj.to_s
elsif klass == Fixnum
digest << 'Fixnum'
digest << obj.to_s
elsif klass == Bignum
digest << 'Bignum'
digest << obj.to_s
elsif klass == TrueClass
digest << 'TrueClass'
elsif klass == FalseClass
digest << 'FalseClass'
elsif klass == NilClass
digest << 'NilClass'.freeze
elsif klass == Array
digest << 'Array'
queue.concat(obj)
elsif klass == Hash
digest << 'Hash'
queue.concat(obj.sort)
elsif klass == Set
digest << 'Set'
queue.concat(obj.to_a)
elsif klass == Encoding
digest << 'Encoding'
digest << obj.name
else
raise TypeError, "couldn't digest #{klass}"
end
end

ADD_VALUE_TO_DIGEST[obj.class].call(obj, digest)
digest.digest
end

Expand Down