Ver Fonte

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.
Brendan Ribera há 9 anos atrás
pai
commit
525c6327ab

+ 17 - 2
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));
+        }
     }
 
     /*