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

Implement stable hashCode for records. #102

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions pbj-integration-tests/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ jmh {
// includes.add("AccountDetailsBench")
// includes.add("JsonBench")
// includes.add("VarIntBench")
// includes.add("HashBench")
// includes.add("EqualsHashCodeBench");

jmhVersion.set("1.35")
includeTests.set(true)
// jvmArgsAppend.add("-XX:MaxInlineSize=100 -XX:MaxInlineLevel=20")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.hedera.pbj.intergration.jmh;

import com.google.protobuf.CodedInputStream;
import com.google.protobuf.CodedOutputStream;
import com.hedera.pbj.intergration.test.TestHashFunctions;
import com.hedera.pbj.runtime.MalformedProtobufException;

Check notice on line 6 in pbj-integration-tests/src/jmh/java/com/hedera/pbj/intergration/jmh/EqualsHashCodeBench.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

pbj-integration-tests/src/jmh/java/com/hedera/pbj/intergration/jmh/EqualsHashCodeBench.java#L6

Unused import - com.hedera.pbj.runtime.MalformedProtobufException.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Codacy found a minor Code Style issue: Unused import - com.hedera.pbj.runtime.MalformedProtobufException.

The issue highlighted by Checkstyle is that the MalformedProtobufException class is imported but not used anywhere in the code. Unused imports can lead to unnecessary clutter in the codebase, making it harder to read and maintain. It's considered good practice to remove any imports that are not being used.

To fix this issue, you should simply remove the import statement for MalformedProtobufException from the code. Here's the suggested change:

Suggested change
import com.hedera.pbj.runtime.MalformedProtobufException;
// Remove the unused import statement
// import com.hedera.pbj.runtime.MalformedProtobufException;

After making this change, ensure that the rest of your code does not actually use the MalformedProtobufException class. If it's used, you should not remove the import statement. Otherwise, removing it will clean up the code and resolve the Checkstyle warning.


This comment was generated by an experimental AI tool.

import com.hedera.pbj.runtime.io.buffer.BufferedData;
import com.hedera.pbj.runtime.io.buffer.Bytes;

Check notice on line 8 in pbj-integration-tests/src/jmh/java/com/hedera/pbj/intergration/jmh/EqualsHashCodeBench.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

pbj-integration-tests/src/jmh/java/com/hedera/pbj/intergration/jmh/EqualsHashCodeBench.java#L8

Unused import - com.hedera.pbj.runtime.io.buffer.Bytes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Codacy found a minor Code Style issue: Unused import - com.hedera.pbj.runtime.io.buffer.Bytes.

The code provided includes an import statement for com.hedera.pbj.runtime.io.buffer.Bytes which is flagged by Checkstyle as an unused import. Unused imports are problematic because they can:

  1. Lead to unnecessary compilation overhead.
  2. Make the code less readable and maintainable, as they can cause confusion about which classes are actually being used in the code.
  3. Potentially cause name conflicts and compilation errors if there are other classes with the same name in different packages.

To fix the issue, you should remove the unused import statement from the code. This will clean up the codebase, making it more maintainable and avoiding any unnecessary compilation overhead.

Here's the code suggestion to fix the problem:

Suggested change
import com.hedera.pbj.runtime.io.buffer.Bytes;
// Remove the unused import statement
// import com.hedera.pbj.runtime.io.buffer.Bytes;

This comment was generated by an experimental AI tool.

import com.hedera.pbj.runtime.io.stream.ReadableStreamingData;

Check notice on line 9 in pbj-integration-tests/src/jmh/java/com/hedera/pbj/intergration/jmh/EqualsHashCodeBench.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

pbj-integration-tests/src/jmh/java/com/hedera/pbj/intergration/jmh/EqualsHashCodeBench.java#L9

Unused import - com.hedera.pbj.runtime.io.stream.ReadableStreamingData.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Codacy found a minor Code Style issue: Unused import - com.hedera.pbj.runtime.io.stream.ReadableStreamingData.

The code issue identified by Checkstyle is an unused import statement. Unused imports are problematic because they clutter the codebase, make the code less readable, and can sometimes lead to confusion about class dependencies. Additionally, they can slightly increase the size of the compiled bytecode, although the Java compiler and JVM are quite good at optimizing this.

In Java, it is considered good practice to remove any import statements that are not used by the class. This helps to keep the code clean and maintainable. Most modern IDEs have the ability to automatically identify and remove unused imports, and there are also build tools and plugins that can do this as part of the build process.

To fix this issue, simply remove the unused import statement from the code:

Suggested change
import com.hedera.pbj.runtime.io.stream.ReadableStreamingData;
// Remove the unused import statement

This comment was generated by an experimental AI tool.

import com.hedera.pbj.test.proto.pbj.Hasheval;
import com.hedera.pbj.test.proto.pbj.Suit;
import com.hedera.pbj.test.proto.pbj.TimestampTest;
import com.hedera.pbj.test.proto.pbj.tests.HashevalTest;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.*;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@SuppressWarnings("unused")
@State(Scope.Benchmark)
@Fork(1)
@Warmup(iterations = 4, time = 2)
@Measurement(iterations = 5, time = 2)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@BenchmarkMode(Mode.AverageTime)
public class EqualsHashCodeBench {
private TimestampTest testStamp;
private TimestampTest testStamp1;

public EqualsHashCodeBench() {
testStamp = new TimestampTest(987L, 123);
testStamp1 = new TimestampTest(987L, 122);
}

@Benchmark
@OperationsPerInvocation(1050)
public void benchHashCode(Blackhole blackhole) throws IOException {
for (int i = 0; i < 1050; i++) {
testStamp.hashCode();
}
}

@Benchmark
@OperationsPerInvocation(1050)
public void benchEquals(Blackhole blackhole) throws IOException {
for (int i = 0; i < 1050; i++) {
testStamp.equals(testStamp);
}
}

@Benchmark
@OperationsPerInvocation(1050)
public void benchNotEquals(Blackhole blackhole) throws IOException {
for (int i = 0; i < 1050; i++) {
testStamp.equals(testStamp1);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.hedera.pbj.intergration.jmh;

import com.google.protobuf.CodedInputStream;
import com.google.protobuf.CodedOutputStream;
import com.hedera.pbj.intergration.test.TestHashFunctions;
import com.hedera.pbj.runtime.MalformedProtobufException;
import com.hedera.pbj.runtime.io.buffer.BufferedData;
import com.hedera.pbj.runtime.io.buffer.Bytes;
import com.hedera.pbj.runtime.io.stream.ReadableStreamingData;

Check notice on line 9 in pbj-integration-tests/src/jmh/java/com/hedera/pbj/intergration/jmh/HashBench.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

pbj-integration-tests/src/jmh/java/com/hedera/pbj/intergration/jmh/HashBench.java#L9

Unused import - com.hedera.pbj.runtime.io.stream.ReadableStreamingData.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Codacy found a minor Code Style issue: Unused import - com.hedera.pbj.runtime.io.stream.ReadableStreamingData.

The issue highlighted by Checkstyle is that the code contains an import statement for com.hedera.pbj.runtime.io.stream.ReadableStreamingData, but this class is not being used anywhere in the code snippet provided. Unused imports can lead to unnecessary clutter in the codebase, making it harder to read and understand, and can potentially cause confusion about the code's dependencies.

To fix this issue, you should remove the unused import statement from your code. This will clean up the code and ensure that only necessary dependencies are included, which is a good practice for maintaining a clean and efficient codebase.

Here's the code suggestion to remove the unused import:

Suggested change
import com.hedera.pbj.runtime.io.stream.ReadableStreamingData;
// Remove the unused import statement
// import com.hedera.pbj.runtime.io.stream.ReadableStreamingData;

This comment was generated by an experimental AI tool.

import com.hedera.pbj.test.proto.pbj.Hasheval;
import com.hedera.pbj.test.proto.pbj.Suit;
import com.hedera.pbj.test.proto.pbj.TimestampTest;
import com.hedera.pbj.test.proto.pbj.tests.HashevalTest;

Check notice on line 13 in pbj-integration-tests/src/jmh/java/com/hedera/pbj/intergration/jmh/HashBench.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

pbj-integration-tests/src/jmh/java/com/hedera/pbj/intergration/jmh/HashBench.java#L13

Unused import - com.hedera.pbj.test.proto.pbj.tests.HashevalTest.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Codacy found a minor Code Style issue: Unused import - com.hedera.pbj.test.proto.pbj.tests.HashevalTest.

The issue highlighted by Checkstyle is about an unused import statement. In Java, it's considered good practice to remove any import statements that are not used in the code, as they can lead to unnecessary clutter and can sometimes cause confusion or even name conflicts if there are classes with the same name in different packages.

Unused imports can occur for several reasons, such as after refactoring code where a class is no longer used, or if an import was added automatically by an IDE during auto-completion but ended up not being used.

To fix this issue, simply remove the unused import statement from the code. This will clean up the code and ensure that it adheres to the code style guidelines enforced by Checkstyle.

Here's the suggestion to remove the unused import:

Suggested change
import com.hedera.pbj.test.proto.pbj.tests.HashevalTest;
// Remove the following unused import statement
// import com.hedera.pbj.test.proto.pbj.tests.HashevalTest;

By applying this change, the code will be cleaner and comply with the code style rules. Remember to check if the import is indeed unused in the entire file before removing it to avoid accidentally causing compilation errors.


This comment was generated by an experimental AI tool.

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;

import java.io.ByteArrayInputStream;

Check notice on line 17 in pbj-integration-tests/src/jmh/java/com/hedera/pbj/intergration/jmh/HashBench.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

pbj-integration-tests/src/jmh/java/com/hedera/pbj/intergration/jmh/HashBench.java#L17

Unused import - java.io.ByteArrayInputStream.
import java.io.IOException;
import java.nio.*;

Check notice on line 19 in pbj-integration-tests/src/jmh/java/com/hedera/pbj/intergration/jmh/HashBench.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

pbj-integration-tests/src/jmh/java/com/hedera/pbj/intergration/jmh/HashBench.java#L19

Using the '.*' form of import should be avoided - java.nio.*.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Codacy found a minor Code Style issue: Using the '.' form of import should be avoided - java.nio..

The code provided includes a wildcard import statement for the java.nio package:

import java.nio.*;

Wildcard imports, indicated by the .*, import all classes from a package. While this can make it easier to access all classes within a package without listing them individually, it can also lead to several issues:

  1. Namespace Clarity: It becomes unclear which classes are actually being used from the package, making the code less readable and maintainable.
  2. Potential Conflicts: If two packages have classes with the same name, wildcard imports could cause name conflicts, leading to compilation errors or the need for fully qualified class names.
  3. Compile Time: Wildcard imports can slightly increase compile time because the compiler has to resolve all the classes in the package, although this impact is usually negligible.
  4. Code Style and Best Practices: Many style guides and linters, like Checkstyle, discourage the use of wildcard imports in favor of explicit class imports.

To fix the issue, you should replace the wildcard import with specific class imports for only the classes you are actually using from the java.nio package. For instance, if you are only using ByteBuffer and CharBuffer from java.nio, you would import them explicitly:

import java.nio.ByteBuffer;
import java.nio.CharBuffer;

Without knowing which specific classes from java.nio are being used in your code, I can't provide the exact imports you need. However, you should look through your code, identify which classes from java.nio are used, and import them individually.

Here is a code suggestion to replace the wildcard import with placeholder individual imports:

Suggested change
import java.nio.*;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
// Add other specific imports from java.nio as needed

This comment was generated by an experimental AI tool.

import java.util.Random;
import java.util.concurrent.TimeUnit;

@SuppressWarnings("unused")
@State(Scope.Benchmark)
@Fork(1)
@Warmup(iterations = 4, time = 2)
@Measurement(iterations = 5, time = 2)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@BenchmarkMode(Mode.AverageTime)
public class HashBench {
private Hasheval hasheval;

public HashBench() {
TimestampTest tst = new TimestampTest(987L, 123);
hasheval = new Hasheval(1, -1, 2, 3, -2,
123f, 7L, -7L, 123L, 234L,
-345L, 456.789D, true, Suit.ACES, tst, "FooBarKKKKHHHHOIOIOI",
Bytes.wrap(new byte[]{1, 2, 3, 4, 5, 6, 7, (byte)255}));
}

@Benchmark
@OperationsPerInvocation(1050)
public void hashBenchSHA256(Blackhole blackhole) throws IOException {
for (int i = 0; i < 1050; i++) {
TestHashFunctions.hash1(hasheval);
}
}

@Benchmark
@OperationsPerInvocation(1050)
public void hashBenchFieldWise(Blackhole blackhole) throws IOException {
for (int i = 0; i < 1050; i++) {
TestHashFunctions.hash2(hasheval);
}
}
}
34 changes: 34 additions & 0 deletions pbj-integration-tests/src/main/proto/hasheval.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
syntax = "proto3";

package proto;

option java_package = "com.hedera.pbj.test.proto.java";
option java_multiple_files = true;
// <<<pbj.java_package = "com.hedera.pbj.test.proto.pbj">>> This comment is special code for setting PBJ Compiler java package

import "timestampTest.proto";
import "google/protobuf/wrappers.proto";
import "everything.proto";

/**
* Example protobuf containing examples of all types
*/
message Hasheval {
int32 int32Number = 1;
sint32 sint32Number = 2;
uint32 uint32Number = 3;
fixed32 fixed32Number = 4;
sfixed32 sfixed32Number = 5;
float floatNumber = 6;
int64 int64Number = 7;
sint64 sint64Number = 8;
uint64 uint64Number = 9;
fixed64 fixed64Number = 10;
sfixed64 sfixed64Number = 11;
double doubleNumber = 12;
bool booleanField = 13;
Suit enumSuit = 14;
TimestampTest subObject = 15;
string text = 16;
bytes bytesField = 17;
}
62 changes: 62 additions & 0 deletions pbj-integration-tests/src/main/proto/timestampTest2.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
syntax = "proto3";

package proto;

/** Test issue 87 */
/** Test issue 87 */

/*-
* ‌
* Hedera Network Services Protobuf
* ​
* Copyright (C) 2018 - 2021 Hedera Hashgraph, LLC
* ​
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ‍
*/

option java_package = "com.hedera.pbj.test.proto.java";
option java_multiple_files = true;
// <<<pbj.java_package = "com.hedera.pbj.test.proto.pbj">>> This comment is special code for setting PBJ Compiler java package

/**
* An exact date and time. This is the same data structure as the protobuf Timestamp.proto (see the
* comments in https://github.com/google/protobuf/blob/master/src/google/protobuf/timestamp.proto)
*/
message TimestampTest2 {
/**
* Number of complete seconds since the start of the epoch
*/
int64 seconds = 1;

/**
* Number of nanoseconds since the start of the last second
*/
int32 nanos = 2;

/**
* Number of picoseconds since the start of the last nanosecond
*/
int32 pico = 3;
}

/**
* An exact date and time, with a resolution of one second (no nanoseconds).
*/
message TimestampTestSeconds2 {
/**
* Number of complete seconds since the start of the epoch
*/
int64 seconds = 1;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.hedera.pbj.intergration.test;

import org.junit.jupiter.api.Test;

import com.hedera.pbj.test.proto.pbj.TimestampTest;
import com.hedera.pbj.test.proto.pbj.TimestampTest2;

import static org.junit.jupiter.api.Assertions.*;

class HasjhEqualsTest {
@Test
void differentObjectsWithDefaulEquals() {
TimestampTest tst = new TimestampTest(1, 2);
TimestampTest2 tst2 = new TimestampTest2(1, 2, 0);

assertFalse(tst.equals(tst2));
}

@Test
void sameObjectsWithNoDefaulEquals() {
TimestampTest tst = new TimestampTest(3, 4);
TimestampTest tst1 = new TimestampTest(3, 4);

assertEquals(tst, tst1);
}

@Test
void sameObjectsWithDefaulNoEquals() {
TimestampTest tst = new TimestampTest(3, 4);
TimestampTest tst1 = new TimestampTest(3, 5);

assertNotEquals(tst, tst1);
}

@Test
void sameObjectsWithDefaulEquals() {
TimestampTest tst = new TimestampTest(0, 0);
TimestampTest tst1 = new TimestampTest(0, 0);

assertEquals(tst, tst1);
}

@Test
void differentObjectsWithDefaulHashCode() {
TimestampTest tst = new TimestampTest(0, 0);
TimestampTest2 tst2 = new TimestampTest2(0, 0, 0);

assertEquals(tst.hashCode(), tst2.hashCode());
}

@Test
void differentObjectsWithNoDefaulHashCode() {
TimestampTest tst = new TimestampTest(1, 0);
TimestampTest2 tst2 = new TimestampTest2(1, 0, 0);

assertEquals(tst.hashCode(), tst2.hashCode());
}

@Test
void differentObjectsWithNoDefaulHashCode1() {
TimestampTest tst = new TimestampTest(0, 0);
TimestampTest2 tst2 = new TimestampTest2(0, 0, 3);

assertNotEquals(tst.hashCode(), tst2.hashCode());
}

@Test
void differentObjectsWithNoDefaulHashCode2() {
TimestampTest2 tst = new TimestampTest2(0, 0, 0);
TimestampTest2 tst2 = new TimestampTest2(0, 0, 0);

assertEquals(tst.hashCode(), tst2.hashCode());
}

@Test
void differentObjectsWithNoDefaulHashCode3() {
TimestampTest2 tst = new TimestampTest2(1, 2, 3);
TimestampTest2 tst2 = new TimestampTest2(1, 2, 3);

assertEquals(tst.hashCode(), tst2.hashCode());
}

@Test
void differentObjectsWithNoDefaulHashCode4() {
TimestampTest2 tst = new TimestampTest2(1, 4, 3);
TimestampTest2 tst2 = new TimestampTest2(1, 2, 3);

assertNotEquals(tst.hashCode(), tst2.hashCode());
}
}
Loading