package sun.nio.ch;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ProtocolFamily;
import java.net.SocketException;
import java.net.StandardProtocolFamily;
import java.net.UnknownHostException;
import java.nio.channels.UnsupportedAddressTypeException;
import jdk.internal.access.JavaNetInetAddressAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Unsafe;
import jdk.internal.util.ArraysSupport;
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();
private final long address;
long address() {
return address;
}
NativeSocketAddress() {
long base = UNSAFE.allocateMemory(SIZEOF_SOCKETADDRESS);
UNSAFE.setMemory(base, SIZEOF_SOCKETADDRESS, (byte) 0);
this.address = base;
}
static NativeSocketAddress[] allocate(int count) {
NativeSocketAddress[] array = new NativeSocketAddress[count];
for (int i = 0; i < count; i++) {
try {
array[i] = new NativeSocketAddress();
} catch (OutOfMemoryError e) {
freeAll(array);
throw e;
}
}
return 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);
}
}
}
int encode(ProtocolFamily protocolFamily, InetSocketAddress isa) {
if (protocolFamily == StandardProtocolFamily.INET) {
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;
} else {
putFamily(AF_INET6);
putAddress(AF_INET6, isa.getAddress());
putPort(AF_INET6, isa.getPort());
UNSAFE.putInt(address + OFFSET_SIN6_FLOWINFO, 0);
return SIZEOF_SOCKADDR6;
}
}
InetSocketAddress decode() throws SocketException {
int family = family();
if (family != AF_INET && family != AF_INET6)
throw new SocketException("Socket family not recognized");
return new InetSocketAddress(address(family), port(family));
}
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;
}
@Override
public boolean equals(Object other) {
if (other instanceof NativeSocketAddress) {
return mismatch((NativeSocketAddress) other) < 0;
} else {
return false;
}
}
@Override
public int hashCode() {
int h = 0;
for (int offset = 0; offset < SIZEOF_SOCKETADDRESS; offset++) {
h = 31 * h + UNSAFE.getByte(address + offset);
}
return h;
}
@Override
public String toString() {
int family = family();
if (family == AF_INET || family == AF_INET6) {
return ((family == AF_INET) ? "AF_INET" : "AF_INET6")
+ ", address=" + address(family) + ", port=" + port(family);
} else {
return "<unknown>";
}
}
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 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 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 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 InetAddress address(int family) {
int len;
int offset;
int scope_id;
if (family == AF_INET) {
len = 4;
offset = OFFSET_SIN4_ADDR;
scope_id = 0;
} else {
len = 16;
offset = OFFSET_SIN6_ADDR;
scope_id = UNSAFE.getInt(address + OFFSET_SIN6_SCOPE_ID);
}
byte[] bytes = new byte[len];
UNSAFE.copyMemory(null, address + offset, bytes, ARRAY_BASE_OFFSET, len);
try {
if (scope_id == 0) {
return InetAddress.getByAddress(bytes);
} else {
return Inet6Address.getByAddress(null, bytes, scope_id);
}
} catch (UnknownHostException e) {
throw new InternalError(e);
}
}
private void putAddress(int family, InetAddress ia) {
if (family == AF_INET) {
putAddress(address + OFFSET_SIN4_ADDR, (Inet4Address) ia);
} else {
int scope_id;
if (ia instanceof Inet4Address) {
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);
scope_id = 0;
} else {
var inet6Address = (Inet6Address) ia;
putAddress(address + OFFSET_SIN6_ADDR, inet6Address);
scope_id = inet6Address.getScopeId();
}
UNSAFE.putInt(address + OFFSET_SIN6_SCOPE_ID, scope_id);
}
}
private static void putAddress(long address, Inet4Address ia) {
int ipAddress = JNINA.addressValue(ia);
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();
}
}