mirror of
https://github.com/2003scape/deep-c-rsc.git
synced 2024-03-22 05:49:51 -04:00
1366 lines
30 KiB
Java
1366 lines
30 KiB
Java
/*
|
|
* @(#) $(JCGO)/goclsp/vm/gnu/java/nio/VMChannel.java --
|
|
* VM specific channel operations implementation.
|
|
**
|
|
* Project: JCGO (http://www.ivmaisoft.com/jcgo/)
|
|
* Copyright (C) 2001-2009 Ivan Maidanski <ivmai@ivmaisoft.com>
|
|
* All rights reserved.
|
|
**
|
|
* Class specification origin: GNU Classpath v0.93 vm/reference
|
|
*/
|
|
|
|
/*
|
|
* This is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
**
|
|
* This software 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 (GPL) for more details.
|
|
**
|
|
* Linking this library statically or dynamically with other modules is
|
|
* making a combined work based on this library. Thus, the terms and
|
|
* conditions of the GNU General Public License cover the whole
|
|
* combination.
|
|
**
|
|
* As a special exception, the copyright holders of this library give you
|
|
* permission to link this library with independent modules to produce an
|
|
* executable, regardless of the license terms of these independent
|
|
* modules, and to copy and distribute the resulting executable under
|
|
* terms of your choice, provided that you also meet, for each linked
|
|
* independent module, the terms and conditions of the license of that
|
|
* module. An independent module is a module which is not derived from
|
|
* or based on this library. If you modify this library, you may extend
|
|
* this exception to your version of the library, but you are not
|
|
* obligated to do so. If you do not wish to do so, delete this
|
|
* exception statement from your version.
|
|
*/
|
|
|
|
package gnu.java.nio;
|
|
|
|
import gnu.java.net.VMAccessorGnuJavaNet;
|
|
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.io.InterruptedIOException;
|
|
import java.io.SyncFailedException;
|
|
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.MappedByteBuffer;
|
|
|
|
import java.nio.channels.NonReadableChannelException;
|
|
import java.nio.channels.NonWritableChannelException;
|
|
|
|
import java.net.InetSocketAddress;
|
|
import java.net.SocketAddress;
|
|
import java.net.SocketException;
|
|
|
|
public final class VMChannel
|
|
{
|
|
|
|
private static abstract class Handle
|
|
{ /* used by VM classes only */
|
|
|
|
Handle() {}
|
|
|
|
abstract int getNativeFD()
|
|
throws IOException;
|
|
|
|
abstract void setNonBlocking(boolean on)
|
|
throws IOException;
|
|
|
|
abstract int available()
|
|
throws IOException;
|
|
|
|
abstract int read(byte[] buffer, int off, int len)
|
|
throws IOException;
|
|
|
|
abstract void write(byte[] buffer, int[] offArr, int len)
|
|
throws IOException;
|
|
|
|
abstract void flush(boolean metadata)
|
|
throws IOException;
|
|
|
|
abstract void close()
|
|
throws IOException;
|
|
|
|
abstract boolean needsCloseOnFinalize();
|
|
}
|
|
|
|
private static final class FileHandle extends Handle
|
|
{ /* used by VM classes only */
|
|
|
|
private int fd = -1;
|
|
|
|
private final int mode;
|
|
|
|
private boolean isNonBlocking;
|
|
|
|
FileHandle(int mode)
|
|
{
|
|
this.mode = mode;
|
|
}
|
|
|
|
int getNativeFD()
|
|
throws IOException
|
|
{
|
|
int fd = this.fd;
|
|
if (fd == -1)
|
|
throw new IOException("invalid file descriptor");
|
|
return fd;
|
|
}
|
|
|
|
void setNonBlocking(boolean on)
|
|
throws IOException
|
|
{
|
|
getNativeFD();
|
|
isNonBlocking = on;
|
|
}
|
|
|
|
synchronized int available()
|
|
throws IOException
|
|
{
|
|
int fd = getNativeFD();
|
|
return (mode & FileChannelImpl.READ) != 0 ? fileAvailable(fd) : 0;
|
|
}
|
|
|
|
synchronized int read(byte[] buffer, int off, int len)
|
|
throws IOException
|
|
{
|
|
int fd = getNativeFD();
|
|
if ((mode & FileChannelImpl.READ) == 0)
|
|
throw new NonReadableChannelException();
|
|
return fileRead(fd, buffer, off, len, isNonBlocking);
|
|
}
|
|
|
|
synchronized void write(byte[] buffer, int[] offArr, int len)
|
|
throws IOException
|
|
{
|
|
int fd = getNativeFD();
|
|
if ((mode & FileChannelImpl.WRITE) == 0)
|
|
throw new NonWritableChannelException();
|
|
fileWrite(fd, buffer, offArr, len, isNonBlocking);
|
|
}
|
|
|
|
void flush(boolean metadata)
|
|
throws IOException
|
|
{
|
|
int fd = getNativeFD();
|
|
if ((mode & FileChannelImpl.WRITE) != 0)
|
|
fileFlush(fd, metadata);
|
|
}
|
|
|
|
void close()
|
|
throws IOException
|
|
{
|
|
int fd = this.fd;
|
|
this.fd = -1;
|
|
VMChannel.close(fd);
|
|
}
|
|
|
|
boolean needsCloseOnFinalize()
|
|
{
|
|
return !isStdInOutErr(fd);
|
|
}
|
|
|
|
void setNativeFD(int fd)
|
|
throws IOException
|
|
{
|
|
if (this.fd != -1)
|
|
throw new IOException("file descriptor already initialized");
|
|
this.fd = fd;
|
|
}
|
|
|
|
synchronized long position()
|
|
throws IOException
|
|
{
|
|
return filePosition(getNativeFD());
|
|
}
|
|
|
|
synchronized void seek(long newPosition)
|
|
throws IOException
|
|
{
|
|
fileSeek(getNativeFD(), newPosition);
|
|
}
|
|
|
|
synchronized void truncate(long size)
|
|
throws IOException
|
|
{
|
|
fileTruncate(getNativeFD(), size);
|
|
}
|
|
|
|
synchronized long size()
|
|
throws IOException
|
|
{
|
|
return fileSize(getNativeFD());
|
|
}
|
|
|
|
synchronized boolean lock(long pos, long len, boolean shared, boolean wait)
|
|
throws IOException
|
|
{
|
|
return fileLock(getNativeFD(), pos, len, shared, wait);
|
|
}
|
|
|
|
synchronized void unlock(long pos, long len)
|
|
throws IOException
|
|
{
|
|
fileUnlock(getNativeFD(), pos, len);
|
|
}
|
|
}
|
|
|
|
private static final class SocketHandle extends Handle
|
|
{ /* used by VM classes only */
|
|
|
|
private int fd = -1;
|
|
|
|
private final boolean stream;
|
|
|
|
private boolean isNonBlocking;
|
|
|
|
private boolean isConnectPending;
|
|
|
|
private InetSocketAddress peerSocketAddress;
|
|
|
|
SocketHandle(boolean stream)
|
|
{
|
|
this.stream = stream;
|
|
}
|
|
|
|
int getNativeFD()
|
|
throws IOException
|
|
{
|
|
int fd = this.fd;
|
|
if (fd == -1)
|
|
throw new IOException("invalid file descriptor");
|
|
return fd;
|
|
}
|
|
|
|
void setNonBlocking(boolean on)
|
|
throws IOException
|
|
{
|
|
int fd = getNativeFD();
|
|
if (isNonBlocking != on)
|
|
{
|
|
VMAccessorGnuJavaNet.socketSetNonBlockingVMPlainSocketImpl(fd, on);
|
|
isNonBlocking = on;
|
|
}
|
|
}
|
|
|
|
int available()
|
|
throws IOException
|
|
{
|
|
return VMAccessorGnuJavaNet.socketAvailableVMPlainSocketImpl(
|
|
getNativeFD());
|
|
}
|
|
|
|
int read(byte[] buffer, int off, int len)
|
|
throws IOException
|
|
{
|
|
int fd = getNativeFD();
|
|
if (peerSocketAddress == null || isConnectPending)
|
|
throw new SocketException("not connected");
|
|
int[] offArr = { off };
|
|
VMAccessorGnuJavaNet.socketRecvFromVMPlainSocketImpl(fd, buffer, offArr,
|
|
len, false, false, false, stream, isNonBlocking);
|
|
return offArr[0] - off;
|
|
}
|
|
|
|
void write(byte[] buffer, int[] offArr, int len)
|
|
throws IOException
|
|
{
|
|
int fd = getNativeFD();
|
|
if (peerSocketAddress == null || isConnectPending)
|
|
throw new SocketException("not connected");
|
|
VMAccessorGnuJavaNet.socketSendToVMPlainSocketImpl(fd, buffer, offArr, len,
|
|
null, false, isNonBlocking);
|
|
}
|
|
|
|
void flush(boolean metadata)
|
|
throws IOException
|
|
{
|
|
throw new IOException("not a file");
|
|
}
|
|
|
|
void close()
|
|
throws IOException
|
|
{
|
|
int fd = this.fd;
|
|
this.fd = -1;
|
|
if (fd != -1)
|
|
VMAccessorGnuJavaNet.socketCloseVMPlainSocketImpl(fd);
|
|
}
|
|
|
|
boolean needsCloseOnFinalize()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void setNativeFD(int fd)
|
|
throws IOException
|
|
{
|
|
if (this.fd != -1)
|
|
throw new IOException("file descriptor already initialized");
|
|
this.fd = fd;
|
|
}
|
|
|
|
InetSocketAddress receive(byte[] buffer, int[] offArr, int len)
|
|
throws IOException
|
|
{
|
|
return VMAccessorGnuJavaNet.socketRecvFromVMPlainSocketImpl(
|
|
getDatagramSocketFD(), buffer, offArr, len, false, false, true,
|
|
stream, isNonBlocking);
|
|
}
|
|
|
|
void send(byte[] buffer, int[] offArr, int len, InetSocketAddress address)
|
|
throws IOException
|
|
{
|
|
VMAccessorGnuJavaNet.socketSendToVMPlainSocketImpl(getDatagramSocketFD(),
|
|
buffer, offArr, len, address, false, isNonBlocking);
|
|
}
|
|
|
|
VMChannel accept()
|
|
throws IOException
|
|
{
|
|
int[] fdArr = { getNativeFD() };
|
|
if (!stream)
|
|
throw new SocketException("not a stream socket");
|
|
InetSocketAddress address =
|
|
VMAccessorGnuJavaNet.socketAcceptVMPlainSocketImpl(fdArr, isNonBlocking);
|
|
VMChannel ch = null;
|
|
if (address != null)
|
|
{
|
|
try
|
|
{
|
|
ch = new VMChannel();
|
|
}
|
|
catch (OutOfMemoryError e)
|
|
{
|
|
if (fdArr[0] != -1)
|
|
{
|
|
try
|
|
{
|
|
VMAccessorGnuJavaNet.socketCloseVMPlainSocketImpl(fdArr[0]);
|
|
}
|
|
catch (IOException ex) {}
|
|
}
|
|
throw e;
|
|
}
|
|
State chState = ch.getState();
|
|
chState.setNativeSocketFD(fdArr[0], true);
|
|
chState.getSocketHandle().peerSocketAddress = address;
|
|
}
|
|
return ch;
|
|
}
|
|
|
|
boolean connect(InetSocketAddress address, int timeout)
|
|
throws IOException
|
|
{
|
|
if (address == null)
|
|
throw new NullPointerException();
|
|
int fd = getNativeFD();
|
|
if (peerSocketAddress != null)
|
|
throw new SocketException(isConnectPending ? "connection pending" :
|
|
"already connected");
|
|
boolean completed = false;
|
|
try
|
|
{
|
|
isConnectPending = true;
|
|
peerSocketAddress = address;
|
|
if (VMAccessorGnuJavaNet.socketConnectVMPlainSocketImpl(fd, address,
|
|
isNonBlocking ? 0 : timeout != 0 ? timeout : -1))
|
|
isConnectPending = false;
|
|
completed = true;
|
|
}
|
|
finally
|
|
{
|
|
if (!completed)
|
|
{
|
|
peerSocketAddress = null;
|
|
isConnectPending = false;
|
|
}
|
|
}
|
|
return !isConnectPending;
|
|
}
|
|
|
|
InetSocketAddress getPeerSocketAddress()
|
|
throws IOException
|
|
{
|
|
InetSocketAddress address = peerSocketAddress;
|
|
if (address != null && isConnectPending)
|
|
{
|
|
try
|
|
{
|
|
if (!VMAccessorGnuJavaNet.socketConnectVMPlainSocketImpl(getNativeFD(),
|
|
address, 0))
|
|
return null;
|
|
}
|
|
catch (IOException e)
|
|
{
|
|
peerSocketAddress = null;
|
|
isConnectPending = false;
|
|
throw e;
|
|
}
|
|
isConnectPending = false;
|
|
}
|
|
return address;
|
|
}
|
|
|
|
void disconnect()
|
|
throws IOException
|
|
{
|
|
VMAccessorGnuJavaNet.socketDisconnectVMPlainSocketImpl(
|
|
getDatagramSocketFD());
|
|
peerSocketAddress = null;
|
|
}
|
|
|
|
private int getDatagramSocketFD()
|
|
throws IOException
|
|
{
|
|
int fd = this.fd;
|
|
if (fd == -1)
|
|
throw new IOException("invalid file descriptor");
|
|
if (stream)
|
|
throw new SocketException("not a datagram socket");
|
|
return fd;
|
|
}
|
|
}
|
|
|
|
public final class State
|
|
{
|
|
|
|
private Handle handle;
|
|
|
|
private boolean closed;
|
|
|
|
State() {}
|
|
|
|
public boolean isValid()
|
|
{
|
|
return handle != null;
|
|
}
|
|
|
|
public boolean isClosed()
|
|
{
|
|
return closed;
|
|
}
|
|
|
|
public int getNativeFD()
|
|
throws IOException
|
|
{
|
|
return getHandle().getNativeFD();
|
|
}
|
|
|
|
void setNativeFD(int fileFd)
|
|
throws IOException
|
|
{
|
|
setNativeFileFD(fileFd, FileChannelImpl.READ | FileChannelImpl.WRITE);
|
|
}
|
|
|
|
public void close()
|
|
throws IOException
|
|
{
|
|
Handle handle = getHandle();
|
|
this.handle = null;
|
|
closed = true;
|
|
handle.close();
|
|
}
|
|
|
|
public String toString()
|
|
{
|
|
if (closed)
|
|
return "<<closed>>";
|
|
Handle handle = this.handle;
|
|
if (handle != null)
|
|
{
|
|
try
|
|
{
|
|
return String.valueOf(handle.getNativeFD());
|
|
}
|
|
catch (IOException e) {}
|
|
}
|
|
return "<<invalid>>";
|
|
}
|
|
|
|
final Handle getHandle()
|
|
throws IOException
|
|
{ /* used by VM classes only */
|
|
Handle handle = this.handle;
|
|
if (handle == null)
|
|
throw new IOException("invalid file descriptor");
|
|
return handle;
|
|
}
|
|
|
|
final void setNativeFileFD(int fd, int mode)
|
|
throws IOException
|
|
{ /* used by VM classes only */
|
|
checkUnset();
|
|
FileHandle handle;
|
|
try
|
|
{
|
|
handle = new FileHandle(mode);
|
|
}
|
|
catch (OutOfMemoryError e)
|
|
{
|
|
try
|
|
{
|
|
VMChannel.close(fd);
|
|
}
|
|
catch (IOException ex) {}
|
|
throw e;
|
|
}
|
|
handle.setNativeFD(fd);
|
|
this.handle = handle;
|
|
}
|
|
|
|
final FileHandle getFileHandle()
|
|
throws IOException
|
|
{ /* used by VM classes only */
|
|
Handle handle = this.handle;
|
|
if (!(handle instanceof FileHandle))
|
|
throw new IOException(handle != null ? "not a file" :
|
|
"invalid file descriptor");
|
|
return (FileHandle) handle;
|
|
}
|
|
|
|
final void setNativeSocketFD(int fd, boolean stream)
|
|
throws IOException
|
|
{ /* used by VM classes only */
|
|
checkUnset();
|
|
SocketHandle handle;
|
|
try
|
|
{
|
|
handle = new SocketHandle(stream);
|
|
}
|
|
catch (OutOfMemoryError e)
|
|
{
|
|
if (fd != -1)
|
|
{
|
|
try
|
|
{
|
|
VMAccessorGnuJavaNet.socketCloseVMPlainSocketImpl(fd);
|
|
}
|
|
catch (IOException ex) {}
|
|
}
|
|
throw e;
|
|
}
|
|
handle.setNativeFD(fd);
|
|
this.handle = handle;
|
|
}
|
|
|
|
final SocketHandle getSocketHandle()
|
|
throws IOException
|
|
{ /* used by VM classes only */
|
|
Handle handle = this.handle;
|
|
if (!(handle instanceof SocketHandle))
|
|
throw new IOException(handle != null ? "not a socket" :
|
|
"invalid file descriptor");
|
|
return (SocketHandle) handle;
|
|
}
|
|
|
|
private void checkUnset()
|
|
throws IOException
|
|
{
|
|
if (handle != null || closed)
|
|
throw new IOException("file descriptor already initialized");
|
|
}
|
|
|
|
protected void finalize()
|
|
throws Throwable
|
|
{
|
|
Handle handle = this.handle;
|
|
if (handle != null && handle.needsCloseOnFinalize())
|
|
close();
|
|
}
|
|
}
|
|
|
|
private static final byte[] EMPTY_BUF = {};
|
|
|
|
private static final int stdin_fd = getStdinFD0();
|
|
|
|
private static final int stdout_fd = getStdoutFD0();
|
|
|
|
private static final int stderr_fd = getStderrFD0();
|
|
|
|
private static final boolean lockingOpHasPos = lockingOpHasPos0() != 0;
|
|
|
|
private static final boolean preventBlocking =
|
|
VMAccessorJavaLang.preventIOBlockingVMRuntime();
|
|
|
|
private final State state = new State();
|
|
|
|
public VMChannel() {}
|
|
|
|
VMChannel(int fileFd)
|
|
throws IOException
|
|
{
|
|
state.setNativeFD(fileFd);
|
|
}
|
|
|
|
public State getState()
|
|
{
|
|
return state;
|
|
}
|
|
|
|
public static VMChannel getStdin()
|
|
throws IOException
|
|
{
|
|
VMChannel ch = new VMChannel();
|
|
ch.state.setNativeFileFD(stdin_fd, FileChannelImpl.READ);
|
|
return ch;
|
|
}
|
|
|
|
public static VMChannel getStdout()
|
|
throws IOException
|
|
{
|
|
VMChannel ch = new VMChannel();
|
|
ch.state.setNativeFileFD(stdout_fd, FileChannelImpl.WRITE);
|
|
return ch;
|
|
}
|
|
|
|
public static VMChannel getStderr()
|
|
throws IOException
|
|
{
|
|
VMChannel ch = new VMChannel();
|
|
ch.state.setNativeFileFD(stderr_fd, FileChannelImpl.WRITE);
|
|
return ch;
|
|
}
|
|
|
|
public void setBlocking(boolean blocking)
|
|
throws IOException
|
|
{
|
|
state.getHandle().setNonBlocking(!blocking);
|
|
}
|
|
|
|
public int available()
|
|
throws IOException
|
|
{
|
|
return state.getHandle().available();
|
|
}
|
|
|
|
public int read(ByteBuffer dst)
|
|
throws IOException
|
|
{
|
|
int len = dst.remaining();
|
|
byte[] buffer;
|
|
int off;
|
|
boolean hasArray = false;
|
|
if (dst.hasArray())
|
|
{
|
|
buffer = dst.array();
|
|
off = dst.arrayOffset() + dst.position();
|
|
hasArray = true;
|
|
}
|
|
else
|
|
{
|
|
buffer = len > 0 ? new byte[len] : EMPTY_BUF;
|
|
off = 0;
|
|
}
|
|
int res = state.getHandle().read(buffer, off, len);
|
|
if (res > 0)
|
|
{
|
|
if (hasArray)
|
|
dst.position(dst.position() + res);
|
|
else dst.put(buffer, 0, res);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
public long readScattering(ByteBuffer[] dsts, int offset, int length)
|
|
throws IOException
|
|
{
|
|
if ((offset | length) < 0 || dsts.length - offset < length)
|
|
throw new IndexOutOfBoundsException();
|
|
long result = 0L;
|
|
while (length-- > 0)
|
|
{
|
|
ByteBuffer dst = dsts[offset++];
|
|
int res = read(dst);
|
|
if (result == 0L || res >= 0)
|
|
result += res;
|
|
if (res <= 0 && (res != 0 || dst.remaining() > 0))
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public int read()
|
|
throws IOException
|
|
{
|
|
byte[] buffer = new byte[1];
|
|
int res;
|
|
while ((res = state.getHandle().read(buffer, 0, 1)) == 0)
|
|
{
|
|
threadYield();
|
|
checkThreadInterrupted();
|
|
}
|
|
return res > 0 ? buffer[0] & 0xff : -1;
|
|
}
|
|
|
|
public int write(ByteBuffer src)
|
|
throws IOException
|
|
{
|
|
int len = src.remaining();
|
|
byte[] buffer;
|
|
int off;
|
|
if (src.hasArray())
|
|
{
|
|
buffer = src.array();
|
|
off = src.arrayOffset() + src.position();
|
|
}
|
|
else
|
|
{
|
|
buffer = len > 0 ? new byte[len] : EMPTY_BUF;
|
|
src.get(buffer, 0, len);
|
|
src.position(src.position() - len);
|
|
off = 0;
|
|
}
|
|
int[] offArr = { off };
|
|
try
|
|
{
|
|
state.getHandle().write(buffer, offArr, len);
|
|
}
|
|
finally
|
|
{
|
|
if ((len = offArr[0] - off) > 0)
|
|
src.position(src.position() + len);
|
|
}
|
|
return len;
|
|
}
|
|
|
|
public long writeGathering(ByteBuffer[] srcs, int offset, int length)
|
|
throws IOException
|
|
{
|
|
if ((offset | length) < 0 || srcs.length - offset < length)
|
|
throw new IndexOutOfBoundsException();
|
|
long result = 0L;
|
|
while (length-- > 0)
|
|
{
|
|
ByteBuffer src = srcs[offset++];
|
|
int res = write(src);
|
|
if (res <= 0 && src.remaining() > 0)
|
|
break;
|
|
result += res;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public void write(int b)
|
|
throws IOException
|
|
{
|
|
byte[] buffer = { (byte) b };
|
|
int[] offArr = new int[1];
|
|
do
|
|
{
|
|
state.getHandle().write(buffer, offArr, 1);
|
|
if (offArr[0] > 0)
|
|
break;
|
|
threadYield();
|
|
checkThreadInterrupted();
|
|
} while (true);
|
|
}
|
|
|
|
public SocketAddress receive(ByteBuffer dst)
|
|
throws IOException
|
|
{
|
|
int len = dst.remaining();
|
|
byte[] buffer;
|
|
int off;
|
|
boolean hasArray = false;
|
|
if (dst.hasArray())
|
|
{
|
|
buffer = dst.array();
|
|
off = dst.arrayOffset() + dst.position();
|
|
hasArray = true;
|
|
}
|
|
else
|
|
{
|
|
buffer = len > 0 ? new byte[len] : EMPTY_BUF;
|
|
off = 0;
|
|
}
|
|
int[] offArr = { off };
|
|
InetSocketAddress address;
|
|
try
|
|
{
|
|
address = state.getSocketHandle().receive(buffer, offArr, len);
|
|
}
|
|
finally
|
|
{
|
|
if ((len = offArr[0] - off) > 0)
|
|
{
|
|
if (hasArray)
|
|
dst.position(dst.position() + len);
|
|
else dst.put(buffer, 0, len);
|
|
}
|
|
}
|
|
return address;
|
|
}
|
|
|
|
public int send(ByteBuffer src, InetSocketAddress address)
|
|
throws IOException
|
|
{
|
|
int len = src.remaining();
|
|
if (address == null)
|
|
throw new NullPointerException();
|
|
byte[] buffer;
|
|
int off;
|
|
if (src.hasArray())
|
|
{
|
|
buffer = src.array();
|
|
off = src.arrayOffset() + src.position();
|
|
}
|
|
else
|
|
{
|
|
buffer = len > 0 ? new byte[len] : EMPTY_BUF;
|
|
src.get(buffer, 0, len);
|
|
src.position(src.position() - len);
|
|
off = 0;
|
|
}
|
|
int[] offArr = { off };
|
|
try
|
|
{
|
|
state.getSocketHandle().send(buffer, offArr, len, address);
|
|
}
|
|
finally
|
|
{
|
|
if ((len = offArr[0] - off) > 0)
|
|
src.position(src.position() + len);
|
|
}
|
|
return len;
|
|
}
|
|
|
|
public void initSocket(boolean stream)
|
|
throws IOException
|
|
{
|
|
if (state.isValid() || state.isClosed())
|
|
throw new IOException("cannot reinitialize this channel");
|
|
state.setNativeSocketFD(VMAccessorGnuJavaNet.socketCreateVMPlainSocketImpl(
|
|
stream), stream);
|
|
}
|
|
|
|
public boolean connect(InetSocketAddress address, int timeout)
|
|
throws SocketException
|
|
{
|
|
try
|
|
{
|
|
return state.getSocketHandle().connect(address, timeout);
|
|
}
|
|
catch (SocketException e)
|
|
{
|
|
throw e;
|
|
}
|
|
catch (IOException e)
|
|
{
|
|
throw (SocketException) (new SocketException()).initCause(e);
|
|
}
|
|
}
|
|
|
|
public void disconnect()
|
|
throws IOException
|
|
{
|
|
state.getSocketHandle().disconnect();
|
|
}
|
|
|
|
public InetSocketAddress getLocalAddress()
|
|
throws IOException
|
|
{
|
|
return state.isValid() ?
|
|
VMAccessorGnuJavaNet.socketGetLocalAddrPortVMPlainSocketImpl(
|
|
state.getSocketHandle().getNativeFD()) : null;
|
|
}
|
|
|
|
public InetSocketAddress getPeerAddress()
|
|
throws IOException
|
|
{
|
|
return state.isValid() ? state.getSocketHandle().getPeerSocketAddress() :
|
|
null;
|
|
}
|
|
|
|
public VMChannel accept()
|
|
throws IOException
|
|
{
|
|
return state.getSocketHandle().accept();
|
|
}
|
|
|
|
public void openFile(String path, int mode)
|
|
throws IOException
|
|
{
|
|
if (state.isValid() || state.isClosed())
|
|
throw new IOException("cannot reinitialize this channel");
|
|
int[] fdArr = new int[1];
|
|
checkIOResCode(fileOpen(path, mode, fdArr));
|
|
state.setNativeFileFD(fdArr[0], mode);
|
|
}
|
|
|
|
public long position()
|
|
throws IOException
|
|
{
|
|
return state.getFileHandle().position();
|
|
}
|
|
|
|
public void seek(long newPosition)
|
|
throws IOException
|
|
{
|
|
state.getFileHandle().seek(newPosition);
|
|
}
|
|
|
|
public void truncate(long size)
|
|
throws IOException
|
|
{
|
|
state.getFileHandle().truncate(size);
|
|
}
|
|
|
|
public long size()
|
|
throws IOException
|
|
{
|
|
return state.getFileHandle().size();
|
|
}
|
|
|
|
public boolean lock(long pos, long len, boolean shared, boolean wait)
|
|
throws IOException
|
|
{
|
|
return wait && lockingOpHasPos ?
|
|
fileLock(state.getFileHandle().getNativeFD(), pos, len, shared,
|
|
true) : state.getFileHandle().lock(pos, len, shared, wait);
|
|
}
|
|
|
|
public void unlock(long pos, long len)
|
|
throws IOException
|
|
{
|
|
state.getFileHandle().unlock(pos, len);
|
|
}
|
|
|
|
public MappedByteBuffer map(char mapMode, long pos, int len)
|
|
throws IOException
|
|
{
|
|
/* not implemented */
|
|
state.getFileHandle();
|
|
throw new IOException("VMChannel.map() not implemented");
|
|
}
|
|
|
|
public boolean flush(boolean metadata)
|
|
throws IOException
|
|
{
|
|
state.getHandle().flush(metadata);
|
|
return true;
|
|
}
|
|
|
|
public void close()
|
|
throws IOException
|
|
{
|
|
state.close();
|
|
}
|
|
|
|
static final void close(int fileFd)
|
|
throws IOException
|
|
{ /* used by VM classes only */
|
|
if (fileFd != -1 && !isStdInOutErr(fileFd))
|
|
{
|
|
int res;
|
|
do
|
|
{
|
|
res = fileClose0(fileFd);
|
|
} while (res < 0 && isIOErrorInterrupted0(res) != 0);
|
|
checkIOResCode(res);
|
|
}
|
|
}
|
|
|
|
static final boolean isStdInOutErr(int fileFd)
|
|
{ /* used by VM classes only */
|
|
return fileFd == stdin_fd || fileFd == stdout_fd || fileFd == stderr_fd;
|
|
}
|
|
|
|
static final int fileAvailable(int fd)
|
|
throws IOException
|
|
{ /* used by VM classes only */
|
|
int res;
|
|
do
|
|
{
|
|
res = fileAvailable0(fd);
|
|
if (res >= 0)
|
|
return res;
|
|
} while (isIOErrorInterrupted0(res) != 0);
|
|
long position;
|
|
do
|
|
{
|
|
position = fileSeek0(0L, fd, 0);
|
|
} while (position < 0L && isIOErrorInterrupted0((int) position) != 0);
|
|
if (position >= 0L)
|
|
{
|
|
long size;
|
|
do
|
|
{
|
|
size = fileSeek0(0L, fd, -1);
|
|
} while (size < 0L && isIOErrorInterrupted0((int) size) != 0);
|
|
if (position != size)
|
|
fileSeek(fd, position);
|
|
if (size < 0L)
|
|
checkIOResCode((int) size);
|
|
position = size - position;
|
|
return position > 0L ? (position < (long) (-1 >>> 1) ?
|
|
(int) position : -1 >>> 1) : 0;
|
|
}
|
|
do
|
|
{
|
|
res = fileSelect0(fd, 0);
|
|
} while (res < 0 && isIOErrorInterrupted0(res) != 0);
|
|
return res > 0 ? 1 : 0;
|
|
}
|
|
|
|
static final long filePosition(int fd)
|
|
throws IOException
|
|
{ /* used by VM classes only */
|
|
long position;
|
|
do
|
|
{
|
|
position = fileSeek0(0L, fd, 0);
|
|
} while (position < 0L && isIOErrorInterrupted0((int) position) != 0);
|
|
if (position < 0L)
|
|
checkIOResCode((int) position);
|
|
return position;
|
|
}
|
|
|
|
static final void fileSeek(int fd, long newPosition)
|
|
throws IOException
|
|
{ /* used by VM classes only */
|
|
long position;
|
|
do
|
|
{
|
|
position = fileSeek0(newPosition, fd, 1);
|
|
} while (position < 0L && isIOErrorInterrupted0((int) position) != 0);
|
|
if (position < 0L)
|
|
checkIOResCode((int) position);
|
|
if (position != newPosition)
|
|
throw new IOException("seek() failed");
|
|
}
|
|
|
|
static final void fileTruncate(int fd, long size)
|
|
throws IOException
|
|
{ /* used by VM classes only */
|
|
long position = filePosition(fd);
|
|
try
|
|
{
|
|
if (position != size)
|
|
fileSeek(fd, size);
|
|
checkIOResCode(fileWrite0(EMPTY_BUF, 0, 0, fd));
|
|
if (position >= size)
|
|
position = size;
|
|
}
|
|
finally
|
|
{
|
|
if (position != size)
|
|
fileSeek(fd, position);
|
|
}
|
|
}
|
|
|
|
static final long fileSize(int fd)
|
|
throws IOException
|
|
{ /* used by VM classes only */
|
|
long position = filePosition(fd);
|
|
long size;
|
|
do
|
|
{
|
|
size = fileSeek0(0L, fd, -1);
|
|
} while (size < 0L && isIOErrorInterrupted0((int) size) != 0);
|
|
if (position != size)
|
|
fileSeek(fd, position);
|
|
if (size < 0L)
|
|
checkIOResCode((int) size);
|
|
return size;
|
|
}
|
|
|
|
static final boolean fileLock(int fd, long pos, long len, boolean shared,
|
|
boolean wait)
|
|
throws IOException
|
|
{ /* used by VM classes only */
|
|
long position;
|
|
if (lockingOpHasPos || (position = filePosition(fd)) == pos)
|
|
return fileLockInner(pos, len, fd, shared, wait);
|
|
boolean res;
|
|
try
|
|
{
|
|
fileSeek(fd, pos);
|
|
res = fileLockInner(pos, len, fd, shared, wait);
|
|
}
|
|
finally
|
|
{
|
|
fileSeek(fd, position);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static final void fileUnlock(int fd, long pos, long len)
|
|
throws IOException
|
|
{ /* used by VM classes only */
|
|
long position;
|
|
if (lockingOpHasPos || (position = filePosition(fd)) == pos)
|
|
fileUnlockInner(pos, len, fd);
|
|
else
|
|
{
|
|
try
|
|
{
|
|
fileSeek(fd, pos);
|
|
fileUnlockInner(pos, len, fd);
|
|
}
|
|
finally
|
|
{
|
|
fileSeek(fd, position);
|
|
}
|
|
}
|
|
}
|
|
|
|
static final int fileRead(int fd, byte[] buffer, int off, int len,
|
|
boolean isNonBlocking)
|
|
throws IOException
|
|
{ /* used by VM classes only */
|
|
if ((off | len) < 0 || buffer.length - off < len)
|
|
throw new ArrayIndexOutOfBoundsException();
|
|
int res = 0;
|
|
if (len > 0 && (!isNonBlocking || fileSelect0(fd, 0) != 0))
|
|
{
|
|
if (isNonBlocking)
|
|
{
|
|
res = fileRead0(buffer, off, len, fd);
|
|
if (res < 0)
|
|
{
|
|
if (isIOErrorInterrupted0(res) == 0)
|
|
checkIOResCode(res);
|
|
res = 0;
|
|
}
|
|
else if (res == 0)
|
|
res = -1;
|
|
}
|
|
else
|
|
{
|
|
checkThreadInterrupted();
|
|
do
|
|
{
|
|
if (!preventBlocking || fileSelect0(fd, 0) != 0)
|
|
{
|
|
res = fileRead0(buffer, off, len, fd);
|
|
if (res >= 0 || isIOErrorInterrupted0(res) == 0)
|
|
break;
|
|
}
|
|
threadYield();
|
|
checkThreadInterrupted();
|
|
} while (true);
|
|
checkIOResCode(res);
|
|
if (res == 0)
|
|
res = -1;
|
|
}
|
|
if (res > len)
|
|
throw new InternalError("read() fault");
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static final void fileWrite(int fd, byte[] buffer, int[] offArr, int len,
|
|
boolean isNonBlocking)
|
|
throws IOException
|
|
{ /* used by VM classes only */
|
|
int off = offArr[0];
|
|
if ((off | len) < 0 || buffer.length - off < len)
|
|
throw new ArrayIndexOutOfBoundsException();
|
|
if (len > 0 && (!isNonBlocking || fileSelect0(fd, 1) != 0))
|
|
{
|
|
int remain = len;
|
|
if (!isNonBlocking)
|
|
checkThreadInterrupted();
|
|
do
|
|
{
|
|
if (isNonBlocking || !preventBlocking || fileSelect0(fd, 1) != 0)
|
|
{
|
|
int res = fileWrite0(buffer, off, remain, fd);
|
|
if (res > 0)
|
|
{
|
|
off += res;
|
|
offArr[0] = off;
|
|
if ((remain -= res) <= 0)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (res == 0)
|
|
throw new IOException("no space left on device");
|
|
if (isIOErrorInterrupted0(res) == 0)
|
|
checkIOResCode(res);
|
|
}
|
|
if (isNonBlocking)
|
|
break;
|
|
}
|
|
threadYield();
|
|
if (remain == len)
|
|
checkThreadInterrupted();
|
|
} while (true);
|
|
if (remain < 0)
|
|
throw new InternalError("write() fault");
|
|
}
|
|
}
|
|
|
|
static final void fileFlush(int fd, boolean metadata)
|
|
throws IOException
|
|
{ /* used by VM classes only */
|
|
do
|
|
{
|
|
int res = fileFlush0(fd, metadata ? 1 : 0);
|
|
if (res >= 0)
|
|
break;
|
|
if (isIOErrorInterrupted0(res) == 0)
|
|
{
|
|
if (isStdInOutErr(fd))
|
|
break;
|
|
throw new SyncFailedException(getIOErrorMsg0(res));
|
|
}
|
|
threadYield();
|
|
} while (true);
|
|
}
|
|
|
|
final void setNativeFileFD(int fd, int mode)
|
|
throws IOException
|
|
{ /* used by VM classes only */
|
|
state.setNativeFileFD(fd, mode);
|
|
}
|
|
|
|
static final VMChannel createUnlessExists(File file)
|
|
throws IOException
|
|
{ /* used by VM classes only */
|
|
VMChannel ch = new VMChannel();
|
|
int mode = FileChannelImpl.WRITE | FileChannelImpl.EXCL;
|
|
int[] fdArr = new int[1];
|
|
int res = fileOpen(file.getPath(), mode, fdArr);
|
|
if (res < 0)
|
|
{
|
|
if (isIOErrorFileExists0(res) == 0)
|
|
checkIOResCode(res);
|
|
return null;
|
|
}
|
|
ch.setNativeFileFD(fdArr[0], mode);
|
|
return ch;
|
|
}
|
|
|
|
static final void checkIOResCode(int res)
|
|
throws IOException
|
|
{ /* used by VM classes only */
|
|
if (res < 0)
|
|
throw new IOException(getIOErrorMsg0(res));
|
|
}
|
|
|
|
static final boolean isIORetryNeededOnce(int res)
|
|
{ /* used by VM classes only */
|
|
if (res >= 0 || isIOErrorNoResources0(res) == 0)
|
|
return false;
|
|
VMAccessorJavaLang.gcOnNoResourcesVMRuntime();
|
|
Runtime.getRuntime().runFinalization();
|
|
return true;
|
|
}
|
|
|
|
static boolean isThreadInterrupted()
|
|
{
|
|
return Thread.currentThread().isInterrupted();
|
|
}
|
|
|
|
private static void checkThreadInterrupted()
|
|
throws IOException
|
|
{
|
|
if (Thread.interrupted())
|
|
throw new InterruptedIOException();
|
|
}
|
|
|
|
private static void threadYield()
|
|
{
|
|
Thread.yield();
|
|
}
|
|
|
|
private static int fileOpen(String path, int mode, int[] fdArr)
|
|
{
|
|
if (path == null)
|
|
throw new NullPointerException();
|
|
int res;
|
|
boolean retrying = false;
|
|
do
|
|
{
|
|
res = fileOpen0(fdArr, path, mode);
|
|
if (!retrying && isIORetryNeededOnce(res))
|
|
retrying = true;
|
|
else if (res >= 0 || isIOErrorInterrupted0(res) == 0)
|
|
break;
|
|
} while (true);
|
|
return res;
|
|
}
|
|
|
|
private static boolean fileLockInner(long pos, long len, int fd,
|
|
boolean shared, boolean wait)
|
|
throws IOException
|
|
{
|
|
boolean retrying = false;
|
|
do
|
|
{
|
|
int res = fileLock0(pos, len, fd, shared ? 1 : 0, wait ? 1 : 0);
|
|
if (!retrying && isIORetryNeededOnce(res))
|
|
retrying = true;
|
|
else
|
|
{
|
|
if (res >= 0)
|
|
break;
|
|
if (isIOErrorInterrupted0(res) == 0)
|
|
checkIOResCode(res);
|
|
if (!wait)
|
|
return false;
|
|
threadYield();
|
|
}
|
|
} while (true);
|
|
return true;
|
|
}
|
|
|
|
private static void fileUnlockInner(long pos, long len, int fd)
|
|
throws IOException
|
|
{
|
|
int res;
|
|
while ((res = fileLock0(pos, len, fd, -1, 0)) < 0)
|
|
{
|
|
if (isIOErrorInterrupted0(res) == 0)
|
|
checkIOResCode(res);
|
|
threadYield();
|
|
}
|
|
}
|
|
|
|
private static native int getStdinFD0();
|
|
|
|
private static native int getStdoutFD0();
|
|
|
|
private static native int getStderrFD0();
|
|
|
|
private static native int isIOErrorFileExists0(int res);
|
|
|
|
private static native int isIOErrorNoResources0(int res);
|
|
|
|
private static native int isIOErrorInterrupted0(int res);
|
|
|
|
private static native String getIOErrorMsg0(int res);
|
|
|
|
private static native int fileOpen0(int[] fdArr, String path, int mode);
|
|
|
|
private static native int fileRead0(byte[] buffer, int off, int len,
|
|
int fd); /* blocking syscall */
|
|
|
|
private static native int fileWrite0(byte[] buffer, int off, int len,
|
|
int fd); /* blocking syscall */
|
|
|
|
private static native int fileAvailable0(int fd);
|
|
|
|
private static native int fileSelect0(int fd, int iswrite);
|
|
|
|
private static native long fileSeek0(long ofs, int fd, int direction);
|
|
|
|
private static native int lockingOpHasPos0();
|
|
|
|
private static native int fileLock0(long pos, long len, int fd,
|
|
int sharedOrUnlock, int doWait); /* blocking syscall */
|
|
|
|
private static native int fileFlush0(int fd,
|
|
int metadata); /* blocking syscall */
|
|
|
|
private static native int fileClose0(int fd);
|
|
}
|