From 05aa0df532b263bdfca1ac3f8493db7022950239 Mon Sep 17 00:00:00 2001 From: Brendan Ribera Date: Tue, 20 Sep 2016 11:07:42 -0700 Subject: Fix hash computation for JRuby's RubyMessage `System.identityHashCode` returns a hash that does not consider a Message's values. This means two Messages with identical values will not have identical hashCodes. This patch uses the pattern from RubyMap to combine the hashCodes from all values in a given message and produce a unique, consistent, value-based hash. --- .../java/com/google/protobuf/jruby/RubyMessage.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'ruby/src') diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java index 462f8a69..733ccfbc 100644 --- a/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java @@ -41,6 +41,8 @@ import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.util.ByteList; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.Map; @@ -164,8 +166,21 @@ public class RubyMessage extends RubyObject { */ @JRubyMethod public IRubyObject hash(ThreadContext context) { - int hashCode = System.identityHashCode(this); - return context.runtime.newFixnum(hashCode); + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + for (RubyMap map : maps.values()) { + digest.update((byte) map.hashCode()); + } + for (RubyRepeatedField repeatedField : repeatedFields.values()) { + digest.update((byte) repeatedFields.hashCode()); + } + for (IRubyObject field : fields.values()) { + digest.update((byte) field.hashCode()); + } + return context.runtime.newString(new ByteList(digest.digest())); + } catch (NoSuchAlgorithmException ignore) { + return context.runtime.newFixnum(System.identityHashCode(this)); + } } /* -- cgit v1.2.3