Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
8283175: Add equals/hashcode to MemorySegment
Reviewed-by: sundar
  • Loading branch information
mcimadamore committed Mar 15, 2022
1 parent a8a519d commit 5b15ef7
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 16 deletions.
Expand Up @@ -35,6 +35,7 @@
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import jdk.internal.javac.PreviewFeature;

Expand Down Expand Up @@ -177,8 +178,9 @@ public FunctionDescriptor dropReturnLayout() {
@Override
public String toString() {
return String.format("(%s)%s",
Stream.of(argLayouts)
.map(Object::toString)
IntStream.range(0, argLayouts.size())
.mapToObj(i -> (i == firstVariadicArgumentIndex() ?
"..." : "") + argLayouts.get(i))
.collect(Collectors.joining()),
returnLayout().map(Object::toString).orElse("v"));
}
Expand All @@ -187,31 +189,28 @@ public String toString() {
* Compares the specified object with this function descriptor for equality. Returns {@code true} if and only if the specified
* object is also a function descriptor, and all the following conditions are met:
* <ul>
* <li>the two function descriptors have equals return layouts (see {@link MemoryLayout#equals(Object)}), or both have no return layout</li>
* <li>the two function descriptors have argument layouts that are pair-wise equal (see {@link MemoryLayout#equals(Object)})
* <li>the two function descriptors have equals return layouts (see {@link MemoryLayout#equals(Object)}), or both have no return layout;</li>
* <li>the two function descriptors have argument layouts that are pair-wise {@linkplain MemoryLayout#equals(Object) equal}; and</li>
* <li>the two function descriptors have the same leading {@linkplain #firstVariadicArgumentIndex() variadic argument index}</li>
* </ul>
*
* @param other the object to be compared for equality with this function descriptor.
* @return {@code true} if the specified object is equal to this function descriptor.
*/
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof FunctionDescriptor f)) {
return false;
}
return Objects.equals(resLayout, f.resLayout) && Objects.equals(argLayouts, f.argLayouts);
return other instanceof FunctionDescriptor f &&
Objects.equals(resLayout, f.resLayout) &&
Objects.equals(argLayouts, f.argLayouts) &&
firstVariadicArgumentIndex() == f.firstVariadicArgumentIndex();
}

/**
* {@return the hash code value for this function descriptor}
*/
@Override
public int hashCode() {
int hashCode = Objects.hashCode(argLayouts);
return resLayout == null ? hashCode : resLayout.hashCode() ^ hashCode;
return Objects.hash(argLayouts, resLayout, firstVariadicArgumentIndex());
}

/**
Expand Down
30 changes: 30 additions & 0 deletions src/java.base/share/classes/java/lang/foreign/MemorySegment.java
Expand Up @@ -1694,6 +1694,36 @@ default void setAtIndex(ValueLayout.OfAddress layout, long index, Addressable va
layout.accessHandle().set(this, index * layout.byteSize(), value.address());
}

/**
* Compares the specified object with this memory segment for equality. Returns {@code true} if and only if the specified
* object is also a memory segment, and if that segment refers to the same memory region as this segment. More specifically,
* for two segments to be considered equals, all the following must be true:
* <ul>
* <li>the two segments must be of the same kind; either both are {@linkplain #isNative() native segments},
* backed by off-heap memory, or both are backed by on-heap memory;
* <li>if the two segments are {@linkplain #isNative() native segments}, their {@link #address() base address}
* must be {@linkplain MemoryAddress#equals(Object) equal}. Otherwise, the two segments must wrap the
* same Java array instance, at the same starting offset;</li>
* <li>the two segments must have the same {@linkplain #byteSize() size}; and</li>
* <li>the two segments must have the {@linkplain MemorySession#equals(Object) same} {@linkplain #session() temporal bounds}.
* </ul>
* @apiNote This method does not perform a structural comparison of the contents of the two memory segments. Clients can
* compare memory segments structurally by using the {@link #mismatch(MemorySegment)} method instead.
*
* @param that the object to be compared for equality with this memory segment.
* @return {@code true} if the specified object is equal to this memory segment.
* @see #mismatch(MemorySegment)
* @see #asOverlappingSlice(MemorySegment)
*/
@Override
boolean equals(Object that);

/**
* {@return the hash code value for this memory segment}
*/
@Override
int hashCode();


/**
* Copies a number of elements from a source memory segment to a destination array. The elements, whose size and alignment
Expand Down
Expand Up @@ -495,6 +495,27 @@ public String toString() {
return "MemorySegment{ id=0x" + Long.toHexString(id()) + " limit: " + length + " }";
}

@Override
public boolean equals(Object o) {
return o instanceof AbstractMemorySegmentImpl that &&
isNative() == that.isNative() &&
unsafeGetOffset() == that.unsafeGetOffset() &&
unsafeGetBase() == that.unsafeGetBase() &&
length == that.length &&
session.equals(that.session);
}

@Override
public int hashCode() {
return Objects.hash(
isNative(),
unsafeGetOffset(),
unsafeGetBase(),
length,
session
);
}

public static AbstractMemorySegmentImpl ofBuffer(ByteBuffer bb) {
Objects.requireNonNull(bb);
long bbAddress = nioAccess.getBufferAddress(bb);
Expand Down
11 changes: 11 additions & 0 deletions test/jdk/java/foreign/TestFunctionDescriptor.java
Expand Up @@ -37,6 +37,7 @@

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotEquals;
import static org.testng.Assert.assertTrue;

public class TestFunctionDescriptor extends NativeTestHelper {
Expand Down Expand Up @@ -93,4 +94,14 @@ public void testDropReturnLayout() {
Optional<MemoryLayout> returnLayoutOp = fd.returnLayout();
assertFalse(returnLayoutOp.isPresent());
}

@Test
public void testEquals() {
FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_INT, C_INT);
FunctionDescriptor fd_va1 = FunctionDescriptor.of(C_INT).asVariadic(C_INT, C_INT);
FunctionDescriptor fd_va2 = FunctionDescriptor.of(C_INT, C_INT).asVariadic(C_INT);
assertEquals(fd, fd);
assertNotEquals(fd, fd_va1);
assertNotEquals(fd, fd_va2);
}
}
26 changes: 26 additions & 0 deletions test/jdk/java/foreign/TestSegments.java
Expand Up @@ -114,6 +114,32 @@ public void testSlices() {
}
}

@Test
public void testEqualsOffHeap() {
try (MemorySession session = MemorySession.openConfined()) {
MemorySegment segment = MemorySegment.allocateNative(100, session);
assertEquals(segment, segment.asReadOnly());
assertEquals(segment, segment.asSlice(0, 100));
assertNotEquals(segment, segment.asSlice(10, 90));
assertNotEquals(segment, segment.asSlice(0, 90));
assertEquals(segment, MemorySegment.ofAddress(segment.address(), 100, session.asNonCloseable()));
assertNotEquals(segment, MemorySegment.ofAddress(segment.address(), 100, MemorySession.global()));
MemorySegment segment2 = MemorySegment.allocateNative(100, session);
assertNotEquals(segment, segment2);
}
}

@Test
public void testEqualsOnHeap() {
MemorySegment segment = MemorySegment.ofArray(new byte[100]);
assertEquals(segment, segment.asReadOnly());
assertEquals(segment, segment.asSlice(0, 100));
assertNotEquals(segment, segment.asSlice(10, 90));
assertNotEquals(segment, segment.asSlice(0, 90));
MemorySegment segment2 = MemorySegment.ofArray(new byte[100]);
assertNotEquals(segment, segment2);
}

@Test(expectedExceptions = IndexOutOfBoundsException.class)
public void testSmallSegmentMax() {
long offset = (long)Integer.MAX_VALUE + (long)Integer.MAX_VALUE + 2L + 6L; // overflows to 6 when casted to int
Expand Down
Expand Up @@ -399,7 +399,7 @@ public void testStructStackSpill() {
public void testVarArgsInRegs() {
MethodType mt = MethodType.methodType(void.class, int.class, int.class, float.class);
FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT).asVariadic(C_INT, C_FLOAT);
FunctionDescriptor fdExpected = FunctionDescriptor.ofVoid(ADDRESS, C_INT).asVariadic(C_INT, C_FLOAT);
FunctionDescriptor fdExpected = FunctionDescriptor.ofVoid(ADDRESS, C_INT, C_INT, C_FLOAT);
CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false);

assertFalse(bindings.isInMemoryReturn);
Expand All @@ -422,7 +422,7 @@ public void testVarArgsInRegs() {
public void testVarArgsOnStack() {
MethodType mt = MethodType.methodType(void.class, int.class, int.class, float.class);
FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT).asVariadic(C_INT, C_FLOAT);
FunctionDescriptor fdExpected = FunctionDescriptor.ofVoid(ADDRESS, C_INT).asVariadic(C_INT, C_FLOAT);
FunctionDescriptor fdExpected = FunctionDescriptor.ofVoid(ADDRESS, C_INT, C_INT, C_FLOAT);
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);

assertFalse(bindings.isInMemoryReturn);
Expand Down
Expand Up @@ -188,7 +188,7 @@ public void testAbiExampleVarargs() {
FunctionDescriptor fd = FunctionDescriptor.ofVoid(
C_INT, C_DOUBLE).asVariadic(C_INT, C_DOUBLE, C_DOUBLE);
FunctionDescriptor fdExpected = FunctionDescriptor.ofVoid(
ADDRESS, C_INT, C_DOUBLE).asVariadic(C_INT, C_DOUBLE, C_DOUBLE);
ADDRESS, C_INT, C_DOUBLE, C_INT, C_DOUBLE, C_DOUBLE);
CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false);

assertFalse(bindings.isInMemoryReturn);
Expand Down

0 comments on commit 5b15ef7

Please sign in to comment.