/**
* Copyright 2013, Landz and its contributors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.xnio.nativeimpl;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Arrays;
/**
* @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a>
*/
public class Native {
static {
try {
System.loadLibrary("xnio");
} catch (Error error) {
System.out.println("XNIO native not available on this platform (%s)");
throw error;
}
final int[] constants = init();
}
private Native() {}
private static native int[] init();
// POSIX-ish
/**
* Call the UNIX dup2 system call.
*
* @param oldFd the old FD
* @param newFd the new FD
* @return the result
*/
public static native int dup2(int oldFd, int newFd);
/**
* Call the UNIX dup system call.
*
* @param oldFd the old FD
* @return the new FD or error
*/
public static native int dup(int oldFd);
/**
* Close the FD.
*
* @param fd the FD to close
* @return 0 for okay, a negative error code for error
*/
public static native int close(int fd);
/**
* Get a string for an error code.
*
* @param err a negative or positive error code
* @return the string
*/
public static native String strError(int err);
/**
* Shut down a socket.
*
* @param fd the socket FD
* @param read {@code true} to shut down reads
* @param write {@code true} to shut down writes
* @return 0 for okay, a negative error code for error
*/
public static native int shutdown(int fd, boolean read, boolean write);
public static native int await2(final int fd, final boolean writes);
public static native int await3(final int fd, final boolean writes, final long millis);
public static native byte[] getSockName(final int fd);
public static native byte[] getPeerName(final int fd);
// read
public static native long readLong(final int fd);
public static native int readD(final int fd, ByteBuffer b1, int p1, int l1);
public static native long readDD(final int fd, ByteBuffer b1, int p1, int l1, ByteBuffer b2, int p2, int l2);
public static native long readDDD(final int fd, ByteBuffer b1, int p1, int l1, ByteBuffer b2, int p2, int l2, ByteBuffer b3, int p3, int l3);
public static native int readH(final int fd, byte[] b1, int p1, int l1);
public static native long readHH(final int fd, byte[] b1, int p1, int l1, byte[] b2, int p2, int l2);
public static native long readHHH(final int fd, byte[] b1, int p1, int l1, byte[] b2, int p2, int l2, byte[] b3, int p3, int l3);
// slower
public static native long readMisc(final int fd, ByteBuffer[] buffers, int offs, int len);
// write
public static native int writeLong(final int fd, final long value);
public static native int writeD(final int fd, ByteBuffer b1, int p1, int l1);
public static native long writeDD(final int fd, ByteBuffer b1, int p1, int l1, ByteBuffer b2, int p2, int l2);
public static native long writeDDD(final int fd, ByteBuffer b1, int p1, int l1, ByteBuffer b2, int p2, int l2, ByteBuffer b3, int p3, int l3);
public static native int writeH(final int fd, byte[] b1, int p1, int l1);
public static native long writeHH(final int fd, byte[] b1, int p1, int l1, byte[] b2, int p2, int l2);
public static native long writeHHH(final int fd, byte[] b1, int p1, int l1, byte[] b2, int p2, int l2, byte[] b3, int p3, int l3);
// slower
public static native long writeMisc(final int fd, ByteBuffer[] buffers, int offs, int len);
public static native int flushTcpCork(final int fd);
// receive
public static native int recvDirect(final int fd, ByteBuffer buffer, byte[] srcAddr, byte[] destAddr);
public static native int recvHeap(final int fd, byte[] bytes, int offs, int len, int pos, int lim, byte[] srcAddr, byte[] destAddr);
public static native int recvMisc(final int fd, ByteBuffer[] buffers, int offs, int len, byte[] srcAddr, byte[] destAddr);
// send
public static native int sendDirect(final int fd, ByteBuffer buffer, int[] posAndLimit, byte[] destAddr);
public static native int sendHeap(final int fd, byte[] bytes, int offs, int len, byte[] destAddr);
public static native int sendMisc(final int fd, ByteBuffer[] buffers, int offs, int len, byte[] destAddr);
// transfer
public static native long xferHeap(final int srcFd, byte[] bytes, int[] posAndLimit, int destFd);
public static native long xferDirect(final int srcFd, ByteBuffer buffer, int[] posAndLimit, int destFd);
public static native long sendfile(final int dest, FileChannel src, long offset, long length);
// util
public static native int socketPair(int[] fds);
public static native int socketTcp();
public static native int socketTcp6();
public static native int socketUdp();
public static native int socketUdp6();
public static native int socketLocalStream();
public static native int socketLocalDatagram();
public static native int pipe(int[] fds);
public static native int bind(int fd, byte[] address);
public static native int accept(int fd);
public static native int connect(int fd, byte[] peerAddress);
public static native int listen(int fd, int backlog);
public static native int finishConnect(int fd);
// options
public static native int getOptBroadcast(int fd);
public static native int setOptBroadcast(int fd, boolean enabled);
public static native int getOptDontRoute(int fd);
public static native int setOptDontRoute(int fd, boolean enabled);
public static native int getOptKeepAlive(int fd);
public static native int setOptKeepAlive(int fd, boolean enabled);
public static native int getOptCloseAbort(int fd);
public static native int setOptCloseAbort(int fd, boolean enabled);
public static native int getOptOobInline(int fd);
public static native int setOptOobInline(int fd, boolean enabled);
public static native int getOptReceiveBuffer(int fd);
public static native int setOptReceiveBuffer(int fd, int size);
public static native int getOptReuseAddr(int fd);
public static native int setOptReuseAddr(int fd, boolean enabled);
public static native int getOptSendBuffer(int fd);
public static native int setOptSendBuffer(int fd, int size);
public static native int getOptDeferAccept(int fd);
public static native int setOptDeferAccept(int fd, boolean enabled);
public static native int getOptMaxSegSize(int fd);
public static native int setOptMaxSegSize(int fd, int size);
public static native int getOptTcpNoDelay(int fd);
public static native int setOptTcpNoDelay(int fd, boolean enabled);
public static native int getOptMulticastTtl(int fd);
public static native int setOptMulticastTtl(int fd, boolean enabled);
// linux
public static final int EPOLL_FLAG_READ = 0b0000_0001;
public static final int EPOLL_FLAG_WRITE = 0b0000_0010;
public static final int EPOLL_FLAG_EDGE = 0b0000_0100;
public static native int eventFD();
public static native int epollCreate();
public static native int epollWait(final int efd, long[] events, int timeout);
public static native int epollCtlAdd(final int efd, final int fd, final int flags, final int id);
public static native int epollCtlMod(final int efd, final int fd, final int flags, final int id);
public static native int epollCtlDel(final int efd, final int fd);
public static native int createTimer(final int seconds, final int nanos);
public static native long spliceToFile(int src, FileChannel dest, long destOffs, long length);
public static native long transfer(final int srcFd, final long count, final ByteBuffer throughBuffer, final int destFd);
public static native long tee(int src, int dest, long length, boolean more);
public static native int readTimer(final int fd);
// utilities
public static IOException exceptionFor(int err) {
String msg = strError(err);
return new IOException(msg);
}
@SuppressWarnings({ "deprecation" })
public static SocketAddress getSocketAddress(byte[] bytes) {
if (bytes == null) {
return null;
}
switch (bytes[0]) {
case 0: {
// inet 4
try {
return new InetSocketAddress(InetAddress.getByAddress(Arrays.copyOfRange(bytes, 1, 5)), (((bytes[5]) & 0xff) << 8) | ((bytes[6]) & 0xff));
} catch (UnknownHostException e) {
// ???
return null;
}
}
case 1: {
// inet 6
try {
return new InetSocketAddress(
Inet6Address.getByAddress(
null,
Arrays.copyOfRange(bytes, 1, 17),
(bytes[19] & 0xff) << 24 | (bytes[20] & 0xff) << 16 | (bytes[21] & 0xff) << 8 | (bytes[22] & 0xff)
),
(((bytes[17]) & 0xff) << 8) | ((bytes[18]) & 0xff));
} catch (UnknownHostException e) {
// ???
return null;
}
}
case 2: {
// local
int len = bytes[1] & 0xff;
return new LocalSocketAddress(new String(bytes, 0, 2, len - 2));
}
default: {
return null;
}
}
}
private static final byte[] INVALID_ADDR = { (byte) 0xff };
public static byte[] encodeSocketAddress(SocketAddress src) {
if (src == null) {
return INVALID_ADDR;
}
if (src instanceof InetSocketAddress) {
final InetSocketAddress inet = (InetSocketAddress) src;
final int port = inet.getPort();
final InetAddress address = inet.getAddress();
if (address instanceof Inet4Address) {
final Inet4Address inet4 = (Inet4Address) address;
final byte[] bytes = inet4.getAddress();
final byte[] result = new byte[7];
result[0] = 0;
System.arraycopy(bytes, 0, result, 1, 4);
result[5] = (byte) (port >> 8);
result[6] = (byte) port;
return result;
} else if (address instanceof Inet6Address) {
final Inet6Address inet6 = (Inet6Address) address;
final byte[] bytes = inet6.getAddress();
final byte[] result = new byte[23];
result[0] = 1;
System.arraycopy(bytes, 0, result, 1, 16);
result[17] = (byte) (port >> 8);
result[18] = (byte) port;
final int scopeId = inet6.getScopeId();
result[19] = (byte) (scopeId >> 24);
result[20] = (byte) (scopeId >> 16);
result[21] = (byte) (scopeId >> 8);
result[22] = (byte) scopeId;
return result;
} else {
return INVALID_ADDR;
}
} else if (src instanceof LocalSocketAddress) {
return encodeSocketAddress((LocalSocketAddress) src);
} else {
return INVALID_ADDR;
}
}
// public static byte[] encodeSocketAddress(LocalSocketAddress local) {
// final String name = local.getName();
// final byte[] result = new byte[2 + UNIX_PATH_LEN];
// result[0] = 2;
// if (! encodeTo(name, result, 1)) {
// return INVALID_ADDR;
// }
// return result;
// }
public static boolean encodeTo(String src, byte[] dest, int offs) {
final int srcLen = src.length();
try {
for (int i = 0; i < srcLen; i = src.offsetByCodePoints(i, 1)) {
int cp = src.codePointAt(i);
if (cp > 0 && cp <= 0x7f) {
// don't accidentally null-terminate the string
dest[offs ++] = (byte) cp;
} else if (cp <= 0x07ff) {
dest[offs ++] = (byte)(0xc0 | 0x1f & cp >> 6);
dest[offs ++] = (byte)(0x80 | 0x3f & cp);
} else if (cp <= 0xffff) {
dest[offs ++] = (byte)(0xe0 | 0x0f & cp >> 12);
dest[offs ++] = (byte)(0x80 | 0x3f & cp >> 6);
dest[offs ++] = (byte)(0x80 | 0x3f & cp);
} else if (cp <= 0x1fffff) {
dest[offs ++] = (byte)(0xf0 | 0x07 & cp >> 18);
dest[offs ++] = (byte)(0x80 | 0x3f & cp >> 12);
dest[offs ++] = (byte)(0x80 | 0x3f & cp >> 6);
dest[offs ++] = (byte)(0x80 | 0x3f & cp);
} else if (cp <= 0x3ffffff) {
dest[offs ++] = (byte)(0xf8 | 0x03 & cp >> 24);
dest[offs ++] = (byte)(0x80 | 0x3f & cp >> 18);
dest[offs ++] = (byte)(0x80 | 0x3f & cp >> 12);
dest[offs ++] = (byte)(0x80 | 0x3f & cp >> 6);
dest[offs ++] = (byte)(0x80 | 0x3f & cp);
} else if (cp >= 0) {
dest[offs ++] = (byte)(0xfc | 0x01 & cp >> 30);
dest[offs ++] = (byte)(0x80 | 0x3f & cp >> 24);
dest[offs ++] = (byte)(0x80 | 0x3f & cp >> 18);
dest[offs ++] = (byte)(0x80 | 0x3f & cp >> 12);
dest[offs ++] = (byte)(0x80 | 0x3f & cp >> 6);
dest[offs ++] = (byte)(0x80 | 0x3f & cp);
} else {
return false;
}
}
return true;
} catch (ArrayIndexOutOfBoundsException e) {
return false;
}
}
}