--- old/src/java.base/share/classes/sun/nio/ch/NativeSocketAddress.java 2020-05-07 19:46:17.000000000 +0100 +++ new/src/java.base/share/classes/sun/nio/ch/NativeSocketAddress.java 2020-05-07 19:46:17.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,11 +34,14 @@ import java.net.StandardProtocolFamily; import java.net.UnknownHostException; import java.nio.channels.UnsupportedAddressTypeException; - +import jdk.incubator.foreign.MemoryAddress; +import jdk.incubator.foreign.MemorySegment; import jdk.internal.access.JavaNetInetAddressAccess; import jdk.internal.access.SharedSecrets; -import jdk.internal.misc.Unsafe; -import jdk.internal.util.ArraysSupport; +import static sun.nio.ch.NativeSocketLayout.AF_INET; +import static sun.nio.ch.NativeSocketLayout.AF_INET6; +import static sun.nio.ch.NativeSocketLayout.SOCKADDR_IN_SIZE; +import static sun.nio.ch.NativeSocketLayout.SOCKADDR_IN6_SIZE; /** * A native socket address that is the union of struct sockaddr, struct sockaddr_in, @@ -48,39 +51,23 @@ */ class NativeSocketAddress { private static final JavaNetInetAddressAccess JNINA = SharedSecrets.getJavaNetInetAddressAccess(); - private static final Unsafe UNSAFE = Unsafe.getUnsafe(); - private static final long ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class); - - private static final int AF_INET = AFINET(); - private static final int AF_INET6 = AFINET6(); - - private static final int SIZEOF_SOCKADDR4 = sizeofSockAddr4(); - private static final int SIZEOF_SOCKADDR6 = sizeofSockAddr6(); - private static final int SIZEOF_SOCKETADDRESS = Math.max(SIZEOF_SOCKADDR4, SIZEOF_SOCKADDR6); - private static final int SIZEOF_FAMILY = sizeofFamily(); - private static final int OFFSET_FAMILY = offsetFamily(); - private static final int OFFSET_SIN4_PORT = offsetSin4Port(); - private static final int OFFSET_SIN4_ADDR = offsetSin4Addr(); - private static final int OFFSET_SIN6_PORT = offsetSin6Port(); - private static final int OFFSET_SIN6_ADDR = offsetSin6Addr(); - private static final int OFFSET_SIN6_SCOPE_ID = offsetSin6ScopeId(); - private static final int OFFSET_SIN6_FLOWINFO = offsetSin6FlowInfo(); - // SOCKETADDRESS - private final long address; + private final MemorySegment segment; + private final MemoryAddress memoryAddress; + private final long rawAddress; long address() { - return address; + return rawAddress; } NativeSocketAddress() { - long base = UNSAFE.allocateMemory(SIZEOF_SOCKETADDRESS); - UNSAFE.setMemory(base, SIZEOF_SOCKETADDRESS, (byte) 0); - this.address = base; + segment = NativeSocketLayout.allocate(); + memoryAddress = segment.baseAddress(); + rawAddress = memoryAddress.toRawLongValue(); } /** - * Allocate an array of native socket addresses. + * Allocates an array of native socket addresses. */ static NativeSocketAddress[] allocate(int count) { NativeSocketAddress[] array = new NativeSocketAddress[count]; @@ -96,13 +83,13 @@ } /** - * Free all non-null native socket addresses in the given array. + * Frees all non-null native socket addresses in the given array. */ static void freeAll(NativeSocketAddress[] array) { for (int i = 0; i < array.length; i++) { NativeSocketAddress sa = array[i]; if (sa != null) { - UNSAFE.freeMemory(sa.address); + sa.segment.close(); } } } @@ -116,164 +103,110 @@ */ int encode(ProtocolFamily protocolFamily, InetSocketAddress isa) { if (protocolFamily == StandardProtocolFamily.INET) { - // struct sockaddr + // struct sockaddr_in InetAddress ia = isa.getAddress(); if (!(ia instanceof Inet4Address)) throw new UnsupportedAddressTypeException(); - putFamily(AF_INET); - putAddress(AF_INET, ia); - putPort(AF_INET, isa.getPort()); - return SIZEOF_SOCKADDR4; + setFamily(AF_INET); + setAddress(AF_INET, ia); + setPort(AF_INET, isa.getPort()); + return SOCKADDR_IN_SIZE; } else { - // struct sockaddr6 - putFamily(AF_INET6); - putAddress(AF_INET6, isa.getAddress()); - putPort(AF_INET6, isa.getPort()); - UNSAFE.putInt(address + OFFSET_SIN6_FLOWINFO, 0); - return SIZEOF_SOCKADDR6; + // struct sockaddr_in6 + setFamily(AF_INET6); + setAddress(AF_INET6, isa.getAddress()); + setPort(AF_INET6, isa.getPort()); + setFlowInfo(0); + return SOCKADDR_IN6_SIZE; } } /** - * Return an InetSocketAddress to represent the socket address in this buffer. + * Returns an InetSocketAddress to represent the socket address in this segment. * @throws SocketException if the socket address is not AF_INET or AF_INET6 */ InetSocketAddress decode() throws SocketException { - int family = family(); + int family = getFamily(); if (family != AF_INET && family != AF_INET6) throw new SocketException("Socket family not recognized"); - return new InetSocketAddress(address(family), port(family)); - } - - /** - * Find a mismatch between this and another socket address - * @return the byte offset of the first mismatch or -1 if no mismatch - */ - private int mismatch(NativeSocketAddress other) { - int i = ArraysSupport.vectorizedMismatch(null, - this.address, - null, - other.address, - SIZEOF_SOCKETADDRESS, - ArraysSupport.LOG2_ARRAY_BYTE_INDEX_SCALE); - if (i >= 0) - return i; - i = SIZEOF_SOCKETADDRESS - ~i; - for (; i < SIZEOF_SOCKETADDRESS; i++) { - if (UNSAFE.getByte(this.address + i) != UNSAFE.getByte(other.address + i)) { - return i; - } - } - return -1; + return new InetSocketAddress(getAddress(family), getPort(family)); } @Override public boolean equals(Object other) { - if (other instanceof NativeSocketAddress) { - return mismatch((NativeSocketAddress) other) < 0; - } else { + if (!(other instanceof NativeSocketAddress)) { return false; } + return NativeSocketLayout.mismatch(this.memoryAddress, ((NativeSocketAddress)other).memoryAddress) < 0; } @Override public int hashCode() { - int h = 0; - for (int offset = 0; offset < SIZEOF_SOCKETADDRESS; offset++) { - h = 31 * h + UNSAFE.getByte(address + offset); - } - return h; + return NativeSocketLayout.hash(memoryAddress, (x,y) -> 31 * x + y); } @Override public String toString() { - int family = family(); + int family = getFamily(); if (family == AF_INET || family == AF_INET6) { return ((family == AF_INET) ? "AF_INET" : "AF_INET6") - + ", address=" + address(family) + ", port=" + port(family); + + ", address=" + getAddress(family) + ", port=" + getPort(family); } else { return ""; } } /** - * Return the value of the sa_family field. + * Returns the value of the sa_family field. */ - private int family() { - if (SIZEOF_FAMILY == 1) { - return UNSAFE.getByte(address + OFFSET_FAMILY); - } else if (SIZEOF_FAMILY == 2) { - return UNSAFE.getShort(address + OFFSET_FAMILY); - } else { - throw new InternalError(); - } + private int getFamily() { + return NativeSocketLayout.family(memoryAddress); } /** * Stores the given family in the sa_family field. */ - private void putFamily(int family) { - if (SIZEOF_FAMILY == 1) { - UNSAFE.putByte(address + OFFSET_FAMILY, (byte) family); - } else if (SIZEOF_FAMILY == 2) { - UNSAFE.putShort(address + OFFSET_FAMILY, (short) family); - } else { - throw new InternalError(); - } + private void setFamily(int family) { + NativeSocketLayout.setFamily(memoryAddress, family); } /** - * Return the value of the sin_port or sin6_port field. These fields are - * stored in network order. + * Returns the value of the sin_port or sin6_port field. */ - private int port(int family) { - byte b1, b2; - if (family == AF_INET) { - b1 = UNSAFE.getByte(address + OFFSET_SIN4_PORT); - b2 = UNSAFE.getByte(address + OFFSET_SIN4_PORT + 1); - } else { - b1 = UNSAFE.getByte(address + OFFSET_SIN6_PORT); - b2 = UNSAFE.getByte(address + OFFSET_SIN6_PORT + 1); - } - return (Byte.toUnsignedInt(b1) << 8) + Byte.toUnsignedInt(b2); + private int getPort(int family) { + if (family == AF_INET) + return NativeSocketLayout.getIPv4Port(memoryAddress); + else + return NativeSocketLayout.getIPv6Port(memoryAddress); } /** - * Stores the given port number in the sin_port or sin6_port field. The - * port is stored in network order. + * Stores the given port number in the sin_port or sin6_port field. */ - private void putPort(int family, int port) { - byte b1 = (byte) ((port >> 8) & 0xff); - byte b2 = (byte) ((port >> 0) & 0xff); - if (family == AF_INET) { - UNSAFE.putByte(address + OFFSET_SIN4_PORT, b1); - UNSAFE.putByte(address + OFFSET_SIN4_PORT + 1, b2); - } else { - UNSAFE.putByte(address + OFFSET_SIN6_PORT, b1); - UNSAFE.putByte(address + OFFSET_SIN6_PORT + 1, b2); - } + private void setPort(int family, int port) { + if (family == AF_INET) + NativeSocketLayout.setIPv4Port(memoryAddress, port); + else + NativeSocketLayout.setIPv6Port(memoryAddress, port); } /** - * Return an InetAddress to represent the value of the address in the + * Returns an InetAddress to represent the value of the address in the * sin4_addr or sin6_addr fields. For IPv6 addresses, the Inet6Address is * created with the sin6_scope_id in the sockaddr_in6 structure. */ - private InetAddress address(int family) { - int len; - int offset; + private InetAddress getAddress(int family) { + final MemoryAddress address = memoryAddress; int scope_id; + byte[] bytes; + if (family == AF_INET) { - len = 4; - offset = OFFSET_SIN4_ADDR; + bytes = NativeSocketLayout.getIPv4Address(address); scope_id = 0; } else { - len = 16; - offset = OFFSET_SIN6_ADDR; - scope_id = UNSAFE.getInt(address + OFFSET_SIN6_SCOPE_ID); + bytes = NativeSocketLayout.getIPv6Address(address); + scope_id = NativeSocketLayout.getScopeId(address); } - byte[] bytes = new byte[len]; - UNSAFE.copyMemory(null, address + offset, bytes, ARRAY_BASE_OFFSET, len); try { if (scope_id == 0) { return InetAddress.getByAddress(bytes); @@ -290,57 +223,28 @@ * fields. For IPv6 addresses, the sin6_addr will be popluated with an * IPv4-mapped IPv6 address when the given InetAddress is an IPv4 address. */ - private void putAddress(int family, InetAddress ia) { + private void setAddress(int family, InetAddress ia) { + final MemoryAddress address = memoryAddress; if (family == AF_INET) { // IPv4 address - putAddress(address + OFFSET_SIN4_ADDR, (Inet4Address) ia); + NativeSocketLayout.setIPv4Address(address, JNINA.addressValue((Inet4Address) ia)); } else { int scope_id; if (ia instanceof Inet4Address) { // IPv4-mapped IPv6 address - UNSAFE.setMemory(address + OFFSET_SIN6_ADDR, 10, (byte) 0); - UNSAFE.putByte(address + OFFSET_SIN6_ADDR + 10, (byte) 0xff); - UNSAFE.putByte(address + OFFSET_SIN6_ADDR + 11, (byte) 0xff); - putAddress(address + OFFSET_SIN6_ADDR + 12, (Inet4Address) ia); + NativeSocketLayout.setMappedAddress(address, JNINA.addressValue((Inet4Address) ia)); scope_id = 0; } else { // IPv6 address var inet6Address = (Inet6Address) ia; - putAddress(address + OFFSET_SIN6_ADDR, inet6Address); + NativeSocketLayout.setIPv6Address(address, JNINA.addressBytes(inet6Address)); scope_id = inet6Address.getScopeId(); } - UNSAFE.putInt(address + OFFSET_SIN6_SCOPE_ID, scope_id); + NativeSocketLayout.setScopeId(address, scope_id); } } - private static void putAddress(long address, Inet4Address ia) { - int ipAddress = JNINA.addressValue(ia); - // network order - UNSAFE.putByte(address + 0, (byte) ((ipAddress >>> 24) & 0xFF)); - UNSAFE.putByte(address + 1, (byte) ((ipAddress >>> 16) & 0xFF)); - UNSAFE.putByte(address + 2, (byte) ((ipAddress >>> 8) & 0xFF)); - UNSAFE.putByte(address + 3, (byte) (ipAddress & 0xFF)); - } - - private static void putAddress(long address, Inet6Address ia) { - byte[] bytes = JNINA.addressBytes(ia); - UNSAFE.copyMemory(bytes, ARRAY_BASE_OFFSET, null, address, 16); - } - - private static native int AFINET(); - private static native int AFINET6(); - private static native int sizeofSockAddr4(); - private static native int sizeofSockAddr6(); - private static native int sizeofFamily(); - private static native int offsetFamily(); - private static native int offsetSin4Port(); - private static native int offsetSin4Addr(); - private static native int offsetSin6Port(); - private static native int offsetSin6Addr(); - private static native int offsetSin6ScopeId(); - private static native int offsetSin6FlowInfo(); - - static { - IOUtil.load(); + private void setFlowInfo(int flowInfo) { + NativeSocketLayout.setFlowInfo(memoryAddress, flowInfo); } } --- /dev/null 2020-05-07 19:46:20.000000000 +0100 +++ new/src/java.base/share/classes/sun/nio/ch/NativeSocketLayout.java 2020-05-07 19:46:19.000000000 +0100 @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; +import java.util.function.IntBinaryOperator; +import jdk.incubator.foreign.MemoryAddress; +import jdk.incubator.foreign.MemoryHandles; +import jdk.incubator.foreign.MemoryLayout; +import jdk.incubator.foreign.MemoryLayout.PathElement; +import jdk.incubator.foreign.MemoryLayouts; +import jdk.incubator.foreign.MemorySegment; +import jdk.internal.misc.Unsafe; +import jdk.internal.util.ArraysSupport; +import static jdk.incubator.foreign.MemoryLayout.PathElement.*; + +/** + * A native socket address .... + */ +class NativeSocketLayout { + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + public static final int AF_INET = AFINET(); + public static final int AF_INET6 = AFINET6(); + + private static final int SIZEOF_SOCKADDR4 = sizeofSockAddr4(); + private static final int SIZEOF_SOCKADDR6 = sizeofSockAddr6(); + private static final int SIZEOF_SOCKETADDRESS = Math.max(SIZEOF_SOCKADDR4, SIZEOF_SOCKADDR6); + private static final int SIZEOF_FAMILY = sizeofFamily(); + private static final int OFFSET_FAMILY = offsetFamily(); + private static final int OFFSET_SIN4_PORT = offsetSin4Port(); + private static final int OFFSET_SIN4_ADDR = offsetSin4Addr(); + private static final int OFFSET_SIN6_PORT = offsetSin6Port(); + private static final int OFFSET_SIN6_ADDR = offsetSin6Addr(); + private static final int OFFSET_SIN6_SCOPE_ID = offsetSin6ScopeId(); + private static final int OFFSET_SIN6_FLOWINFO = offsetSin6FlowInfo(); + + private static final ByteOrder NETWORK_BYTE_ORDER = ByteOrder.BIG_ENDIAN; + private static final ByteOrder HOST_BYTE_ORDER = ByteOrder.nativeOrder(); + + private static final MemoryLayout SOCKADDR; + + public static final int SOCKADDR_IN_SIZE; + public static final int SOCKADDR_IN6_SIZE; + + private static final VarHandle SA_FAMILY_HANDLE; + private static final VarHandle SIN_PORT_HANDLE; + private static final VarHandle SIN_ADDR_HANDLE; + private static final VarHandle SIN6_PORT_HANDLE; + private static final VarHandle SIN6_FLOWINFO_HANDLE; + private static final VarHandle SIN6_ADDR_HANDLE; + private static final VarHandle SIN6_SCOPE_HANDLE; + private static final VarHandle BYTEWISE_HANDLE; + private static final VarHandle IPv4_MAPPED_HANDLE; + + private static final long SIN6_ADDR_OFFSET; + private static final long IPv4_MAPPED_OFFSET; + + // byte/short to/from int adapter handles + private static final MethodHandle INT_TO_SHORT; + private static final MethodHandle SHORT_TO_UNSIGNED_INT; + private static final MethodHandle INT_TO_BYTE; + private static final MethodHandle BYTE_TO_UNSIGNED_INT; + + private static final boolean VERIFY = true; + private static final boolean DEBUG = true; + + static { + try { + var lookup = MethodHandles.lookup(); + BYTE_TO_UNSIGNED_INT = lookup.findStatic(Byte.class, "toUnsignedInt", MethodType.methodType(int.class, byte.class)); + INT_TO_BYTE = lookup.findStatic(NativeSocketLayout.class, "byteValue", MethodType.methodType(byte.class, int.class)); + SHORT_TO_UNSIGNED_INT = lookup.findStatic(Short.class, "toUnsignedInt", MethodType.methodType(int.class, short.class)); + INT_TO_SHORT = lookup.findStatic(NativeSocketLayout.class, "shortValue", MethodType.methodType(short.class, int.class)); + } catch (Throwable throwable) { + throw new AssertionError(throwable); + } + + MemoryLayout familyLayout; + if (OFFSET_FAMILY == 1 && SIZEOF_FAMILY == 1) { + familyLayout = MemoryLayout.ofStruct(MemoryLayout.ofPaddingBits(8), MemoryLayouts.JAVA_BYTE.withName("sa_family")); + SA_FAMILY_HANDLE = MemoryHandles.filterValue( + familyLayout.varHandle(byte.class, groupElement("sa_family")), + INT_TO_BYTE, + BYTE_TO_UNSIGNED_INT); + } else if (OFFSET_FAMILY == 0 && SIZEOF_FAMILY == 2) { + familyLayout = MemoryLayouts.JAVA_SHORT.withName("sa_family"); + SA_FAMILY_HANDLE = MemoryHandles.filterValue( + familyLayout.varHandle(short.class), + INT_TO_SHORT, + SHORT_TO_UNSIGNED_INT); + } else { + throw new InternalError("unknown family [offset:%d, size:%d]".formatted(OFFSET_FAMILY, SIZEOF_FAMILY)); + } + + SOCKADDR = MemoryLayout.ofUnion( + MemoryLayout.ofStruct(familyLayout, + MemoryLayout.ofValueBits(16, NETWORK_BYTE_ORDER).withName("sin_port"), + MemoryLayouts.BITS_32_BE.withName("sin_addr"), // network byte order + MemoryLayout.ofPaddingBits(64)) // pad to size of struct sockaddr + .withName("sockaddr_in"), + MemoryLayout.ofStruct(familyLayout, + MemoryLayout.ofValueBits(16, NETWORK_BYTE_ORDER).withName("sin6_port"), + MemoryLayout.ofValueBits(32, HOST_BYTE_ORDER).withName("sin6_flowinfo"), + MemoryLayout.ofSequence(16, MemoryLayouts.JAVA_BYTE).withName("sin6_addr"), + MemoryLayout.ofValueBits(32, HOST_BYTE_ORDER).withName("sin6_scope_id")) + .withName("sockaddr_in6")) + .withName("sockaddr"); + + PathElement sockaddr_in = groupElement("sockaddr_in"); + SIN_PORT_HANDLE = MemoryHandles.filterValue( + SOCKADDR.varHandle(short.class, sockaddr_in, groupElement("sin_port")), + INT_TO_SHORT, + SHORT_TO_UNSIGNED_INT); + SIN_ADDR_HANDLE = SOCKADDR.varHandle(int.class, sockaddr_in, groupElement("sin_addr")); + PathElement sockaddr_in6 = groupElement("sockaddr_in6"); + SIN6_PORT_HANDLE = MemoryHandles.filterValue( + SOCKADDR.varHandle(short.class, sockaddr_in6, groupElement("sin6_port")), + INT_TO_SHORT, + SHORT_TO_UNSIGNED_INT); + SIN6_FLOWINFO_HANDLE = SOCKADDR.varHandle(int.class, sockaddr_in6, groupElement("sin6_flowinfo")); + SIN6_ADDR_HANDLE = SOCKADDR.varHandle(byte.class, sockaddr_in6, groupElement("sin6_addr"), sequenceElement()); + SIN6_SCOPE_HANDLE = SOCKADDR.varHandle(int.class, sockaddr_in6, groupElement("sin6_scope_id")); + + SOCKADDR_IN_SIZE = (int) SOCKADDR.select(sockaddr_in).byteSize(); + SOCKADDR_IN6_SIZE = (int) SOCKADDR.select(sockaddr_in6).byteSize(); + SIN6_ADDR_OFFSET = bitsToBytes(SOCKADDR.offset(groupElement("sockaddr_in6"), groupElement("sin6_addr"))); + IPv4_MAPPED_OFFSET = SIN6_ADDR_OFFSET + 12; + + // byte-wise handle for equality comparison + MemoryLayout layout = MemoryLayout.ofSequence(SOCKADDR.byteSize(), MemoryLayouts.JAVA_BYTE); + BYTEWISE_HANDLE = layout.varHandle(byte.class, sequenceElement()); + IPv4_MAPPED_HANDLE = MemoryHandles.varHandle(int.class, ByteOrder.BIG_ENDIAN); + + debug(); + // Verifies the handcrafted layout against the natively derived sizes and offsets + if (VERIFY) + verify(); + } + + private static void verify() { + // sockaddr_in + PathElement sockaddr_in = groupElement("sockaddr_in"); + assert SIZEOF_SOCKADDR4 == SOCKADDR_IN_SIZE : "SIZEOF_SOCKADDR [%d] != SOCKADDR_IN_SIZE [%d]".formatted(SIZEOF_SOCKADDR4, SOCKADDR_IN_SIZE); + var offset = bitsToBytes(SOCKADDR.offset(sockaddr_in, groupElement("sin_port"))); + assert offset == OFFSET_SIN4_PORT : "OFFSET_SIN4_PORT [%d] != layout offset [%d]".formatted(OFFSET_SIN4_PORT, offset); + offset = bitsToBytes(SOCKADDR.offset(sockaddr_in, groupElement("sin_addr"))); + assert offset == OFFSET_SIN4_ADDR : "OFFSET_SIN4_ADDR [%d] != layout offset [%d]".formatted(OFFSET_SIN4_ADDR, offset); + + //sockaddr_in6 + PathElement sockaddr_in6 = groupElement("sockaddr_in6"); + assert SIZEOF_SOCKADDR6 == SOCKADDR_IN6_SIZE : "SIZEOF_SOCKADDR6 [%d] != SOCKADDR_IN6_SIZE [%d]".formatted(SIZEOF_SOCKADDR6, SOCKADDR_IN6_SIZE); + offset = bitsToBytes(SOCKADDR.offset(sockaddr_in6, groupElement("sin6_port"))); + assert offset == OFFSET_SIN6_PORT : "OFFSET_SIN6_PORT [%d] != layout offset [%d]".formatted(OFFSET_SIN6_PORT, offset); + offset = bitsToBytes(SOCKADDR.offset(sockaddr_in6, groupElement("sin6_addr"))); + assert offset == OFFSET_SIN6_ADDR : "OFFSET_SIN6_ADDR [%d] != layout offset [%d]".formatted(OFFSET_SIN6_ADDR, offset); + offset = bitsToBytes(SOCKADDR.offset(sockaddr_in6, groupElement("sin6_flowinfo"))); + assert offset == OFFSET_SIN6_FLOWINFO : "OFFSET_SIN6_FLOWINFO [%d] != layout offset [%d]".formatted(OFFSET_SIN6_FLOWINFO, offset); + offset = bitsToBytes(SOCKADDR.offset(sockaddr_in6, groupElement("sin6_scope_id"))); + assert offset == OFFSET_SIN6_SCOPE_ID : "OFFSET_SIN6_SCOPE_ID [%d] != layout offset [%d]".formatted(OFFSET_SIN6_SCOPE_ID, offset); + } + + private static void debug() { + if (DEBUG) { + System.out.println("SIZEOF_SOCKADDR4:" + SIZEOF_SOCKADDR4); + System.out.println("SIZEOF_SOCKADDR6:" + SIZEOF_SOCKADDR6); + System.out.println("SIZEOF_SOCKETADDRESS:" + SIZEOF_SOCKETADDRESS); + System.out.println("SIZEOF_FAMILY:" + SIZEOF_FAMILY); + System.out.println("OFFSET_FAMILY:" + OFFSET_FAMILY); + System.out.println("OFFSET_SIN4_PORT:" + OFFSET_SIN4_PORT); + System.out.println("OFFSET_SIN4_ADDR:" + OFFSET_SIN4_ADDR); + System.out.println("OFFSET_SIN6_PORT:" + OFFSET_SIN6_PORT); + System.out.println("OFFSET_SIN6_ADDR:" + OFFSET_SIN6_ADDR); + System.out.println("OFFSET_SIN6_SCOPE_ID:" + OFFSET_SIN6_SCOPE_ID); + System.out.println("OFFSET_SIN6_FLOWINFO:" + OFFSET_SIN6_FLOWINFO); + System.out.println("SOCKADDR layout:" + SOCKADDR); + System.out.println("SOCKADDR layout byteSize:" + SOCKADDR.byteSize()); + System.out.println("SOCKADDR layout byteAlignment:" + SOCKADDR.byteAlignment()); + } + } + + private static long bitsToBytes(long value) { + return value >>> 3; + } + + private static byte byteValue(int value) { + return (byte) value; + } + + private static short shortValue(int value) { + return (short) value; + } + + private NativeSocketLayout() { } // no instantiations + + public static void zero(MemoryAddress addr, int length) { + MemoryAddress.copy(MemorySegment.ofArray(new byte[length]).baseAddress(), addr, length); + } + + public static MemorySegment allocate() { + long base = UNSAFE.allocateMemory(SOCKADDR.byteSize()); + var segment = MemorySegment.ofNativeRestricted(MemoryAddress.ofLong(base), SOCKADDR.byteSize(), null, () -> UNSAFE.freeMemory(base), null); + zero(segment.baseAddress(), (int) SOCKADDR.byteSize()); + return segment; + } + + public static int family(MemoryAddress addr) { + return (int) SA_FAMILY_HANDLE.get(addr); + } + + public static void setFamily(MemoryAddress addr, int family) { + SA_FAMILY_HANDLE.set(addr, family); + } + + public static int getIPv4Port(MemoryAddress addr) { + return (int) SIN_PORT_HANDLE.get(addr); + } + + public static int getIPv6Port(MemoryAddress addr) { + return (int) SIN6_PORT_HANDLE.get(addr); + } + + public static void setIPv4Port(MemoryAddress addr, int port) { + SIN_PORT_HANDLE.set(addr, port); + } + + public static void setIPv6Port(MemoryAddress addr, int port) { + SIN6_PORT_HANDLE.set(addr, port); + } + + private static final byte[] intToByteArray(int value) { + return new byte[] { (byte) (value >>> 24), + (byte) (value >>> 16), + (byte) (value >>> 8), + (byte) value}; + } + + public static byte[] getIPv4Address(MemoryAddress addr) { + return intToByteArray((int) SIN_ADDR_HANDLE.get(addr)); + } + + public static void setIPv4Address(MemoryAddress address, int ipAddress) { + SIN_ADDR_HANDLE.set(address, ipAddress); + } + + public static void setMappedAddress(MemoryAddress addr, int ipAddress) { + zero(addr.addOffset(SIN6_ADDR_OFFSET) , 10); + SIN6_ADDR_HANDLE.set(addr, (long) 10, (byte) 0xFF); + SIN6_ADDR_HANDLE.set(addr, (long) 11, (byte) 0xFF); + IPv4_MAPPED_HANDLE.set(addr.addOffset(IPv4_MAPPED_OFFSET), ipAddress); + } + + public static byte[] getIPv6Address(MemoryAddress addr) { + byte[] bytes = new byte[16]; + MemoryAddress.copy(addr.addOffset(SIN6_ADDR_OFFSET), MemorySegment.ofArray(bytes).baseAddress(), 16); + return bytes; + } + + public static void setIPv6Address(MemoryAddress addr, byte[] ipAddress) { + MemoryAddress.copy(MemorySegment.ofArray(ipAddress).baseAddress(), addr.addOffset(SIN6_ADDR_OFFSET), 16); + } + + public static int getScopeId(MemoryAddress addr) { + return (int) SIN6_SCOPE_HANDLE.get(addr); + } + + public static void setScopeId(MemoryAddress addr, int scopeId) { + SIN6_SCOPE_HANDLE.set(addr, scopeId); + } + + public static int getFlowInfo(MemoryAddress addr) { + return (int) SIN6_FLOWINFO_HANDLE.get(addr); + } + + public static void setFlowInfo(MemoryAddress addr, int flowInfo) { + SIN6_FLOWINFO_HANDLE.set(addr, flowInfo); + } + + /** + * Find a mismatch between the given memory addresses. + * @return the byte offset of the first mismatch or -1 if no mismatch + */ + public static int mismatch(MemoryAddress addr1, MemoryAddress addr2) { + final int length = (int) SOCKADDR.byteSize(); + int i = ArraysSupport.vectorizedMismatch(null, + addr1.toRawLongValue(), + null, + addr2.toRawLongValue(), + length, + ArraysSupport.LOG2_ARRAY_BYTE_INDEX_SCALE); + if (i >= 0) + return i; + i = length - ~i; + for (; i < length; i++) { + if ((byte) BYTEWISE_HANDLE.get(addr1, (long) i) != (byte) BYTEWISE_HANDLE.get(addr2, (long) i)) { + return i; + } + } + return -1; + } + + public static int hash(MemoryAddress addr, IntBinaryOperator hasher) { + int h = 0; + final int length = (int) SOCKADDR.byteSize(); + for (int i = 0; i < length; i++) { // TODO: REMOVE + h = hasher.applyAsInt(h, (byte) BYTEWISE_HANDLE.get(addr, (long) i)); + + } + int h2 = 0; + for (int i = 0; i < length; i++) { + h2 = 31 * h2 + (byte) BYTEWISE_HANDLE.get(addr, (long) i); + } + assert h == h2; // TODO: this is currently not tested! + return h; + } + + private static native int AFINET(); + private static native int AFINET6(); + private static native int sizeofSockAddr4(); + private static native int sizeofSockAddr6(); + private static native int sizeofFamily(); + private static native int offsetFamily(); + private static native int offsetSin4Port(); + private static native int offsetSin4Addr(); + private static native int offsetSin6Port(); + private static native int offsetSin6Addr(); + private static native int offsetSin6ScopeId(); + private static native int offsetSin6FlowInfo(); + + static { + IOUtil.load(); + } +}