--- /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(); + } +}