mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8323746: Add PathElement hashCode and equals
Reviewed-by: mcimadamore
This commit is contained in:
parent
917838e0a5
commit
b58d73b915
4 changed files with 165 additions and 86 deletions
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -32,7 +32,6 @@ import java.util.Optional;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import jdk.internal.foreign.LayoutPath;
|
import jdk.internal.foreign.LayoutPath;
|
||||||
import jdk.internal.foreign.LayoutPath.PathElementImpl.PathKind;
|
|
||||||
import jdk.internal.foreign.Utils;
|
import jdk.internal.foreign.Utils;
|
||||||
import jdk.internal.foreign.layout.MemoryLayoutUtil;
|
import jdk.internal.foreign.layout.MemoryLayoutUtil;
|
||||||
import jdk.internal.foreign.layout.PaddingLayoutImpl;
|
import jdk.internal.foreign.layout.PaddingLayoutImpl;
|
||||||
|
@ -847,7 +846,13 @@ public sealed interface MemoryLayout
|
||||||
*
|
*
|
||||||
* @since 22
|
* @since 22
|
||||||
*/
|
*/
|
||||||
sealed interface PathElement permits LayoutPath.PathElementImpl {
|
sealed interface PathElement
|
||||||
|
permits LayoutPath.DereferenceElement,
|
||||||
|
LayoutPath.GroupElementByIndex,
|
||||||
|
LayoutPath.GroupElementByName,
|
||||||
|
LayoutPath.SequenceElement,
|
||||||
|
LayoutPath.SequenceElementByIndex,
|
||||||
|
LayoutPath.SequenceElementByRange {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@return a path element which selects a member layout with the given name in a
|
* {@return a path element which selects a member layout with the given name in a
|
||||||
|
@ -862,10 +867,7 @@ public sealed interface MemoryLayout
|
||||||
* @param name the name of the member layout to be selected
|
* @param name the name of the member layout to be selected
|
||||||
*/
|
*/
|
||||||
static PathElement groupElement(String name) {
|
static PathElement groupElement(String name) {
|
||||||
Objects.requireNonNull(name);
|
return new LayoutPath.GroupElementByName(name);
|
||||||
return new LayoutPath.PathElementImpl(PathKind.GROUP_ELEMENT,
|
|
||||||
path -> path.groupElement(name),
|
|
||||||
"groupElement(\"" + name + "\")");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -876,12 +878,7 @@ public sealed interface MemoryLayout
|
||||||
* @throws IllegalArgumentException if {@code index < 0}
|
* @throws IllegalArgumentException if {@code index < 0}
|
||||||
*/
|
*/
|
||||||
static PathElement groupElement(long index) {
|
static PathElement groupElement(long index) {
|
||||||
if (index < 0) {
|
return new LayoutPath.GroupElementByIndex(index);
|
||||||
throw new IllegalArgumentException("Index < 0");
|
|
||||||
}
|
|
||||||
return new LayoutPath.PathElementImpl(PathKind.GROUP_ELEMENT,
|
|
||||||
path -> path.groupElement(index),
|
|
||||||
"groupElement(" + index + ")");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -892,12 +889,7 @@ public sealed interface MemoryLayout
|
||||||
* @throws IllegalArgumentException if {@code index < 0}
|
* @throws IllegalArgumentException if {@code index < 0}
|
||||||
*/
|
*/
|
||||||
static PathElement sequenceElement(long index) {
|
static PathElement sequenceElement(long index) {
|
||||||
if (index < 0) {
|
return new LayoutPath.SequenceElementByIndex(index);
|
||||||
throw new IllegalArgumentException("Index must be positive: " + index);
|
|
||||||
}
|
|
||||||
return new LayoutPath.PathElementImpl(PathKind.SEQUENCE_ELEMENT_INDEX,
|
|
||||||
path -> path.sequenceElement(index),
|
|
||||||
"sequenceElement(" + index + ")");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -923,15 +915,7 @@ public sealed interface MemoryLayout
|
||||||
* @throws IllegalArgumentException if {@code start < 0}, or {@code step == 0}
|
* @throws IllegalArgumentException if {@code start < 0}, or {@code step == 0}
|
||||||
*/
|
*/
|
||||||
static PathElement sequenceElement(long start, long step) {
|
static PathElement sequenceElement(long start, long step) {
|
||||||
if (start < 0) {
|
return new LayoutPath.SequenceElementByRange(start, step);
|
||||||
throw new IllegalArgumentException("Start index must be positive: " + start);
|
|
||||||
}
|
|
||||||
if (step == 0) {
|
|
||||||
throw new IllegalArgumentException("Step must be != 0: " + step);
|
|
||||||
}
|
|
||||||
return new LayoutPath.PathElementImpl(PathKind.SEQUENCE_RANGE,
|
|
||||||
path -> path.sequenceElement(start, step),
|
|
||||||
"sequenceElement(" + start + ", " + step + ")");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -943,9 +927,7 @@ public sealed interface MemoryLayout
|
||||||
* {@code 0 <= I < C}.
|
* {@code 0 <= I < C}.
|
||||||
*/
|
*/
|
||||||
static PathElement sequenceElement() {
|
static PathElement sequenceElement() {
|
||||||
return new LayoutPath.PathElementImpl(PathKind.SEQUENCE_ELEMENT,
|
return LayoutPath.SequenceElement.instance();
|
||||||
LayoutPath::sequenceElement,
|
|
||||||
"sequenceElement()");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -953,9 +935,7 @@ public sealed interface MemoryLayout
|
||||||
* {@linkplain AddressLayout#targetLayout() target layout} (where set)}
|
* {@linkplain AddressLayout#targetLayout() target layout} (where set)}
|
||||||
*/
|
*/
|
||||||
static PathElement dereferenceElement() {
|
static PathElement dereferenceElement() {
|
||||||
return new LayoutPath.PathElementImpl(PathKind.DEREF_ELEMENT,
|
return LayoutPath.DereferenceElement.instance();
|
||||||
LayoutPath::derefElement,
|
|
||||||
"dereferenceElement()");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -363,54 +363,141 @@ public class LayoutPath {
|
||||||
.collect(joining(", selected from: "));
|
.collect(joining(", selected from: "));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public record GroupElementByName(String name)
|
||||||
* This class provides an immutable implementation for the {@code PathElement} interface. A path element implementation
|
implements MemoryLayout.PathElement, UnaryOperator<LayoutPath> {
|
||||||
* is simply a pointer to one of the selector methods provided by the {@code LayoutPath} class.
|
|
||||||
*/
|
|
||||||
public static final class PathElementImpl implements MemoryLayout.PathElement, UnaryOperator<LayoutPath> {
|
|
||||||
|
|
||||||
public enum PathKind {
|
// Assert invariants
|
||||||
SEQUENCE_ELEMENT("unbound sequence element"),
|
public GroupElementByName {
|
||||||
SEQUENCE_ELEMENT_INDEX("bound sequence element"),
|
Objects.requireNonNull(name);
|
||||||
SEQUENCE_RANGE("sequence range"),
|
|
||||||
GROUP_ELEMENT("group element"),
|
|
||||||
DEREF_ELEMENT("dereference element");
|
|
||||||
|
|
||||||
final String description;
|
|
||||||
|
|
||||||
PathKind(String description) {
|
|
||||||
this.description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String description() {
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final PathKind kind;
|
|
||||||
final UnaryOperator<LayoutPath> pathOp;
|
|
||||||
final String stringRepresentation;
|
|
||||||
|
|
||||||
public PathElementImpl(PathKind kind,
|
|
||||||
UnaryOperator<LayoutPath> pathOp,
|
|
||||||
String stringRepresentation) {
|
|
||||||
this.kind = kind;
|
|
||||||
this.pathOp = pathOp;
|
|
||||||
this.stringRepresentation = stringRepresentation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LayoutPath apply(LayoutPath layoutPath) {
|
public LayoutPath apply(LayoutPath layoutPath) {
|
||||||
return pathOp.apply(layoutPath);
|
return layoutPath.groupElement(name);
|
||||||
}
|
|
||||||
|
|
||||||
public PathKind kind() {
|
|
||||||
return kind;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return stringRepresentation;
|
return "groupElement(\"" + name + "\")";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public record GroupElementByIndex(long index)
|
||||||
|
implements MemoryLayout.PathElement, UnaryOperator<LayoutPath> {
|
||||||
|
|
||||||
|
// Assert invariants
|
||||||
|
public GroupElementByIndex {
|
||||||
|
if (index < 0) {
|
||||||
|
throw new IllegalArgumentException("Index < 0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LayoutPath apply(LayoutPath layoutPath) {
|
||||||
|
return layoutPath.groupElement(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "groupElement(" + index + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public record SequenceElementByIndex(long index)
|
||||||
|
implements MemoryLayout.PathElement, UnaryOperator<LayoutPath> {
|
||||||
|
|
||||||
|
// Assert invariants
|
||||||
|
public SequenceElementByIndex {
|
||||||
|
if (index < 0) {
|
||||||
|
throw new IllegalArgumentException("Index < 0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LayoutPath apply(LayoutPath layoutPath) {
|
||||||
|
return layoutPath.sequenceElement(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "sequenceElement(" + index + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public record SequenceElementByRange(long start, long step)
|
||||||
|
implements MemoryLayout.PathElement, UnaryOperator<LayoutPath> {
|
||||||
|
|
||||||
|
// Assert invariants
|
||||||
|
public SequenceElementByRange {
|
||||||
|
if (start < 0) {
|
||||||
|
throw new IllegalArgumentException("Start index must be positive: " + start);
|
||||||
|
}
|
||||||
|
if (step == 0) {
|
||||||
|
throw new IllegalArgumentException("Step must be != 0: " + step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LayoutPath apply(LayoutPath layoutPath) {
|
||||||
|
return layoutPath.sequenceElement(start, step);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "sequenceElement(" + start + ", " + step + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public record SequenceElement()
|
||||||
|
implements MemoryLayout.PathElement, UnaryOperator<LayoutPath> {
|
||||||
|
|
||||||
|
private static final SequenceElement INSTANCE = new SequenceElement();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LayoutPath apply(LayoutPath layoutPath) {
|
||||||
|
return layoutPath.sequenceElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "sequenceElement()";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MemoryLayout.PathElement instance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public record DereferenceElement()
|
||||||
|
implements MemoryLayout.PathElement, UnaryOperator<LayoutPath> {
|
||||||
|
|
||||||
|
private static final DereferenceElement INSTANCE = new DereferenceElement();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LayoutPath apply(LayoutPath layoutPath) {
|
||||||
|
return layoutPath.derefElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overriding here will ensure DereferenceElement will have a hash code
|
||||||
|
// that is different from the hash code of SequenceElement.
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 31;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "dereferenceElement()";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MemoryLayout.PathElement instance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -26,7 +26,6 @@
|
||||||
package jdk.internal.foreign.layout;
|
package jdk.internal.foreign.layout;
|
||||||
|
|
||||||
import jdk.internal.foreign.LayoutPath;
|
import jdk.internal.foreign.LayoutPath;
|
||||||
import jdk.internal.foreign.LayoutPath.PathElementImpl.PathKind;
|
|
||||||
import jdk.internal.foreign.Utils;
|
import jdk.internal.foreign.Utils;
|
||||||
|
|
||||||
import java.lang.foreign.GroupLayout;
|
import java.lang.foreign.GroupLayout;
|
||||||
|
@ -40,11 +39,11 @@ import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.MethodType;
|
import java.lang.invoke.MethodType;
|
||||||
import java.lang.invoke.VarHandle;
|
import java.lang.invoke.VarHandle;
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
|
||||||
public abstract sealed class AbstractLayout<L extends AbstractLayout<L> & MemoryLayout>
|
public abstract sealed class AbstractLayout<L extends AbstractLayout<L> & MemoryLayout>
|
||||||
permits AbstractGroupLayout, PaddingLayoutImpl, SequenceLayoutImpl, ValueLayouts.AbstractValueLayout {
|
permits AbstractGroupLayout, PaddingLayoutImpl, SequenceLayoutImpl, ValueLayouts.AbstractValueLayout {
|
||||||
|
@ -174,12 +173,14 @@ public abstract sealed class AbstractLayout<L extends AbstractLayout<L> & Memory
|
||||||
|
|
||||||
public long byteOffset(PathElement... elements) {
|
public long byteOffset(PathElement... elements) {
|
||||||
return computePathOp(LayoutPath.rootPath((MemoryLayout) this), LayoutPath::offset,
|
return computePathOp(LayoutPath.rootPath((MemoryLayout) this), LayoutPath::offset,
|
||||||
EnumSet.of(PathKind.SEQUENCE_ELEMENT, PathKind.SEQUENCE_RANGE, PathKind.DEREF_ELEMENT), elements);
|
Set.of(LayoutPath.SequenceElement.class, LayoutPath.SequenceElementByRange.class, LayoutPath.DereferenceElement.class),
|
||||||
|
elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MethodHandle byteOffsetHandle(PathElement... elements) {
|
public MethodHandle byteOffsetHandle(PathElement... elements) {
|
||||||
return computePathOp(LayoutPath.rootPath((MemoryLayout) this), LayoutPath::offsetHandle,
|
return computePathOp(LayoutPath.rootPath((MemoryLayout) this), LayoutPath::offsetHandle,
|
||||||
EnumSet.of(PathKind.DEREF_ELEMENT), elements);
|
Set.of(LayoutPath.DereferenceElement.class),
|
||||||
|
elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
public VarHandle varHandle(PathElement... elements) {
|
public VarHandle varHandle(PathElement... elements) {
|
||||||
|
@ -197,23 +198,27 @@ public abstract sealed class AbstractLayout<L extends AbstractLayout<L> & Memory
|
||||||
|
|
||||||
public MethodHandle sliceHandle(PathElement... elements) {
|
public MethodHandle sliceHandle(PathElement... elements) {
|
||||||
return computePathOp(LayoutPath.rootPath((MemoryLayout) this), LayoutPath::sliceHandle,
|
return computePathOp(LayoutPath.rootPath((MemoryLayout) this), LayoutPath::sliceHandle,
|
||||||
Set.of(PathKind.DEREF_ELEMENT), elements);
|
Set.of(LayoutPath.DereferenceElement.class),
|
||||||
|
elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemoryLayout select(PathElement... elements) {
|
public MemoryLayout select(PathElement... elements) {
|
||||||
return computePathOp(LayoutPath.rootPath((MemoryLayout) this), LayoutPath::layout,
|
return computePathOp(LayoutPath.rootPath((MemoryLayout) this), LayoutPath::layout,
|
||||||
EnumSet.of(PathKind.SEQUENCE_ELEMENT_INDEX, PathKind.SEQUENCE_RANGE, PathKind.DEREF_ELEMENT), elements);
|
Set.of(LayoutPath.SequenceElementByIndex.class, LayoutPath.SequenceElementByRange.class, LayoutPath.DereferenceElement.class),
|
||||||
|
elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <Z> Z computePathOp(LayoutPath path, Function<LayoutPath, Z> finalizer,
|
private static <Z> Z computePathOp(LayoutPath path, Function<LayoutPath, Z> finalizer,
|
||||||
Set<PathKind> badKinds, PathElement... elements) {
|
Set<Class<?>> badTypes, PathElement... elements) {
|
||||||
Objects.requireNonNull(elements);
|
Objects.requireNonNull(elements);
|
||||||
for (PathElement e : elements) {
|
for (PathElement e : elements) {
|
||||||
LayoutPath.PathElementImpl pathElem = (LayoutPath.PathElementImpl)Objects.requireNonNull(e);
|
Objects.requireNonNull(e);
|
||||||
if (badKinds.contains(pathElem.kind())) {
|
if (badTypes.contains(e.getClass())) {
|
||||||
throw new IllegalArgumentException(String.format("Invalid %s selection in layout path", pathElem.kind().description()));
|
throw new IllegalArgumentException("Invalid selection in layout path: " + e);
|
||||||
}
|
}
|
||||||
path = pathElem.apply(path);
|
@SuppressWarnings("unchecked")
|
||||||
|
UnaryOperator<LayoutPath> pathOp = (UnaryOperator<LayoutPath>) e;
|
||||||
|
path = pathOp.apply(path);
|
||||||
}
|
}
|
||||||
return finalizer.apply(path);
|
return finalizer.apply(path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -311,6 +311,13 @@ public class TestLayoutPaths {
|
||||||
assertEquals(actualByteOffset, expectedByteOffset);
|
assertEquals(actualByteOffset, expectedByteOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHashCodeCollision() {
|
||||||
|
PathElement sequenceElement = PathElement.sequenceElement();
|
||||||
|
PathElement dereferenceElement = PathElement.dereferenceElement();
|
||||||
|
assertNotEquals(sequenceElement.hashCode(), dereferenceElement.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGroupElementIndexToString() {
|
public void testGroupElementIndexToString() {
|
||||||
PathElement e = PathElement.groupElement(2);
|
PathElement e = PathElement.groupElement(2);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue