mirror of
https://github.com/corda/corda.git
synced 2025-01-21 03:55:00 +00:00
Merge pull request #212 from dicej/net
various refinements to network implementation
This commit is contained in:
commit
573367e7a1
@ -149,46 +149,12 @@ throwSocketException(JNIEnv* e)
|
||||
throwSocketException(e, errorString(e));
|
||||
}
|
||||
|
||||
void
|
||||
init(JNIEnv* e, sockaddr_in* address, jstring hostString, jint port)
|
||||
void init(sockaddr_in* address, jint host, jint port)
|
||||
{
|
||||
const char* chars = e->GetStringUTFChars(hostString, 0);
|
||||
if (chars) {
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
hostent* host = gethostbyname(chars);
|
||||
e->ReleaseStringUTFChars(hostString, chars);
|
||||
if (host == 0) {
|
||||
throwIOException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(address, 0, sizeof(sockaddr_in));
|
||||
address->sin_family = AF_INET;
|
||||
address->sin_port = htons(port);
|
||||
address->sin_addr = *reinterpret_cast<in_addr*>(host->h_addr_list[0]);
|
||||
#else
|
||||
addrinfo hints;
|
||||
memset(&hints, 0, sizeof(addrinfo));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
addrinfo* result;
|
||||
int r = getaddrinfo(chars, 0, &hints, &result);
|
||||
e->ReleaseStringUTFChars(hostString, chars);
|
||||
if (r != 0) {
|
||||
throwIOException(e, gai_strerror(r));
|
||||
return;
|
||||
}
|
||||
|
||||
memset(address, 0, sizeof(sockaddr_in));
|
||||
address->sin_family = AF_INET;
|
||||
address->sin_port = htons(port);
|
||||
address->sin_addr = reinterpret_cast<sockaddr_in*>
|
||||
(result->ai_addr)->sin_addr;
|
||||
|
||||
freeaddrinfo(result);
|
||||
#endif
|
||||
}
|
||||
memset(address, 0, sizeof(sockaddr_in));
|
||||
address->sin_family = AF_INET;
|
||||
address->sin_port = htons(port);
|
||||
address->sin_addr.s_addr = htonl(host);
|
||||
}
|
||||
|
||||
inline bool
|
||||
@ -384,8 +350,17 @@ doWrite(int fd, const void* buffer, size_t count)
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
makeSocket(JNIEnv* e, int type = SOCK_STREAM, int protocol = IPPROTO_TCP)
|
||||
int doSend(int fd, sockaddr_in* address, const void* buffer, size_t count)
|
||||
{
|
||||
return sendto(fd,
|
||||
static_cast<const char*>(buffer),
|
||||
count,
|
||||
0,
|
||||
reinterpret_cast<sockaddr*>(address),
|
||||
sizeof(sockaddr_in));
|
||||
}
|
||||
|
||||
int makeSocket(JNIEnv* e, int type = SOCK_STREAM, int protocol = IPPROTO_TCP)
|
||||
{
|
||||
int s = ::socket(AF_INET, type, protocol);
|
||||
if (s < 0) {
|
||||
@ -405,43 +380,44 @@ Java_java_nio_channels_ServerSocketChannel_natDoAccept(JNIEnv *e, jclass, jint s
|
||||
return ::doAccept(e, socket);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_java_nio_channels_ServerSocketChannel_natDoListen(JNIEnv *e,
|
||||
jclass,
|
||||
jstring host,
|
||||
jint port)
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_java_nio_channels_ServerSocketChannel_natDoListen(JNIEnv* e,
|
||||
jclass,
|
||||
jint socket,
|
||||
jint host,
|
||||
jint port)
|
||||
{
|
||||
int s = makeSocket(e);
|
||||
if (s < 0) return s;
|
||||
if (e->ExceptionCheck()) return 0;
|
||||
|
||||
sockaddr_in address;
|
||||
init(e, &address, host, port);
|
||||
if (e->ExceptionCheck()) return 0;
|
||||
init(&address, host, port);
|
||||
|
||||
::doBind(e, s, &address);
|
||||
if (e->ExceptionCheck()) return 0;
|
||||
::doBind(e, socket, &address);
|
||||
if (e->ExceptionCheck())
|
||||
return;
|
||||
|
||||
::doListen(e, s);
|
||||
return s;
|
||||
::doListen(e, socket);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_java_nio_channels_DatagramChannel_bind(JNIEnv *e,
|
||||
jclass,
|
||||
jstring host,
|
||||
jint port)
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_java_nio_channels_SocketChannel_bind(JNIEnv* e,
|
||||
jclass,
|
||||
jint socket,
|
||||
jint host,
|
||||
jint port)
|
||||
{
|
||||
int s = makeSocket(e, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (s < 0) return s;
|
||||
if (e->ExceptionCheck()) return 0;
|
||||
|
||||
sockaddr_in address;
|
||||
init(e, &address, host, port);
|
||||
if (e->ExceptionCheck()) return 0;
|
||||
init(&address, host, port);
|
||||
|
||||
::doBind(e, s, &address);
|
||||
return s;
|
||||
::doBind(e, socket, &address);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_java_nio_channels_DatagramChannel_bind(JNIEnv* e,
|
||||
jclass c,
|
||||
jint socket,
|
||||
jint host,
|
||||
jint port)
|
||||
{
|
||||
Java_java_nio_channels_SocketChannel_bind(e, c, socket, host, port);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
@ -472,45 +448,42 @@ Java_java_nio_channels_SocketChannel_natSetTcpNoDelay(JNIEnv *e,
|
||||
setTcpNoDelay(e, socket, on);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_java_nio_channels_SocketChannel_natDoConnect(JNIEnv *e,
|
||||
jclass,
|
||||
jstring host,
|
||||
jint port,
|
||||
jboolean blocking,
|
||||
jbooleanArray retVal)
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_java_nio_channels_SocketChannel_natDoConnect(JNIEnv* e,
|
||||
jclass,
|
||||
jint socket,
|
||||
jint host,
|
||||
jint port)
|
||||
{
|
||||
int s = makeSocket(e);
|
||||
if (e->ExceptionCheck()) return 0;
|
||||
|
||||
setBlocking(e, s, blocking);
|
||||
|
||||
sockaddr_in address;
|
||||
init(e, &address, host, port);
|
||||
if (e->ExceptionCheck()) return 0;
|
||||
|
||||
jboolean connected = ::doConnect(e, s, &address);
|
||||
e->SetBooleanArrayRegion(retVal, 0, 1, &connected);
|
||||
|
||||
return s;
|
||||
init(&address, host, port);
|
||||
|
||||
return ::doConnect(e, socket, &address);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_java_nio_channels_DatagramChannel_connect(JNIEnv *e,
|
||||
jclass,
|
||||
jstring host,
|
||||
jint port)
|
||||
Java_java_nio_channels_SocketChannel_makeSocket(JNIEnv* e, jclass)
|
||||
{
|
||||
int s = makeSocket(e, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (e->ExceptionCheck()) return 0;
|
||||
return makeSocket(e);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_java_nio_channels_DatagramChannel_makeSocket(JNIEnv* e, jclass)
|
||||
{
|
||||
return makeSocket(e, SOCK_DGRAM, IPPROTO_UDP);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_java_nio_channels_DatagramChannel_connect(JNIEnv* e,
|
||||
jclass,
|
||||
jint socket,
|
||||
jint host,
|
||||
jint port)
|
||||
{
|
||||
sockaddr_in address;
|
||||
init(e, &address, host, port);
|
||||
if (e->ExceptionCheck()) return 0;
|
||||
|
||||
::doConnect(e, s, &address);
|
||||
|
||||
return s;
|
||||
init(&address, host, port);
|
||||
|
||||
return ::doConnect(e, socket, &address);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
@ -669,6 +642,51 @@ Java_java_nio_channels_DatagramChannel_write(JNIEnv* e,
|
||||
(e, c, socket, buffer, offset, length, blocking);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_java_nio_channels_DatagramChannel_send(JNIEnv* e,
|
||||
jclass,
|
||||
jint socket,
|
||||
jint host,
|
||||
jint port,
|
||||
jbyteArray buffer,
|
||||
jint offset,
|
||||
jint length,
|
||||
jboolean blocking)
|
||||
{
|
||||
sockaddr_in address;
|
||||
init(&address, host, port);
|
||||
|
||||
int r;
|
||||
if (blocking) {
|
||||
uint8_t* buf = static_cast<uint8_t*>(allocate(e, length));
|
||||
if (buf) {
|
||||
e->GetByteArrayRegion(
|
||||
buffer, offset, length, reinterpret_cast<jbyte*>(buf));
|
||||
r = ::doSend(socket, &address, buf, length);
|
||||
free(buf);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
jboolean isCopy;
|
||||
uint8_t* buf
|
||||
= static_cast<uint8_t*>(e->GetPrimitiveArrayCritical(buffer, &isCopy));
|
||||
|
||||
r = ::doSend(socket, &address, buf + offset, length);
|
||||
|
||||
e->ReleasePrimitiveArrayCritical(buffer, buf, 0);
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
if (eagain()) {
|
||||
return 0;
|
||||
} else {
|
||||
throwIOException(e);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_java_nio_channels_SocketChannel_natThrowWriteError(JNIEnv *e,
|
||||
jclass,
|
||||
@ -693,6 +711,14 @@ Java_java_nio_channels_SocketChannel_natCloseSocket(JNIEnv *,
|
||||
doClose(socket);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_java_nio_channels_DatagramChannel_close(JNIEnv *,
|
||||
jclass,
|
||||
jint socket)
|
||||
{
|
||||
doClose(socket);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class Pipe {
|
||||
|
@ -10,7 +10,21 @@
|
||||
|
||||
package java.io;
|
||||
|
||||
public abstract class Reader implements Closeable {
|
||||
import java.nio.CharBuffer;
|
||||
|
||||
public abstract class Reader implements Closeable, Readable {
|
||||
public int read(CharBuffer buffer) throws IOException {
|
||||
int c = read(buffer.array(),
|
||||
buffer.arrayOffset() + buffer.position(),
|
||||
buffer.remaining());
|
||||
|
||||
if (c > 0) {
|
||||
buffer.position(buffer.position() + c);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
char[] buffer = new char[1];
|
||||
int c = read(buffer);
|
||||
|
18
classpath/java/lang/Readable.java
Normal file
18
classpath/java/lang/Readable.java
Normal file
@ -0,0 +1,18 @@
|
||||
/* Copyright (c) 2008-2014, Avian Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software
|
||||
for any purpose with or without fee is hereby granted, provided
|
||||
that the above copyright notice and this permission notice appear
|
||||
in all copies.
|
||||
|
||||
There is NO WARRANTY for this software. See license.txt for
|
||||
details. */
|
||||
|
||||
package java.lang;
|
||||
|
||||
import java.nio.CharBuffer;
|
||||
import java.io.IOException;
|
||||
|
||||
public interface Readable {
|
||||
int read(CharBuffer buffer) throws IOException;
|
||||
}
|
21
classpath/java/net/BindException.java
Normal file
21
classpath/java/net/BindException.java
Normal file
@ -0,0 +1,21 @@
|
||||
/* Copyright (c) 2008-2014, Avian Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software
|
||||
for any purpose with or without fee is hereby granted, provided
|
||||
that the above copyright notice and this permission notice appear
|
||||
in all copies.
|
||||
|
||||
There is NO WARRANTY for this software. See license.txt for
|
||||
details. */
|
||||
|
||||
package java.net;
|
||||
|
||||
public class BindException extends SocketException {
|
||||
public BindException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public BindException() {
|
||||
this(null);
|
||||
}
|
||||
}
|
@ -62,9 +62,17 @@ public class InetAddress {
|
||||
(int)((addr[3] + 256) % 256);
|
||||
}
|
||||
|
||||
int getRawAddress() {
|
||||
public int getRawAddress() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
static native int ipv4AddressForName(String name);
|
||||
static native int ipv4AddressForName(String name) throws UnknownHostException;
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof InetAddress && ((InetAddress) o).ip == ip;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
|
@ -11,15 +11,25 @@
|
||||
package java.net;
|
||||
|
||||
public class InetSocketAddress extends SocketAddress {
|
||||
private final String host;
|
||||
private final InetAddress address;
|
||||
private final int port;
|
||||
|
||||
public InetSocketAddress(String host, int port) throws UnknownHostException {
|
||||
this.address = InetAddress.getByName(host);
|
||||
this.port = port;
|
||||
public InetSocketAddress(String host, int port) {
|
||||
InetAddress address;
|
||||
try {
|
||||
address = InetAddress.getByName(host);
|
||||
host = address.getHostName();
|
||||
} catch (UnknownHostException e) {
|
||||
address = null;
|
||||
}
|
||||
this.host = host;
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public InetSocketAddress(InetAddress address, int port) {
|
||||
this.host = address.getHostName();
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
}
|
||||
@ -29,10 +39,27 @@ public class InetSocketAddress extends SocketAddress {
|
||||
}
|
||||
|
||||
public String getHostName() {
|
||||
return address.getHostName();
|
||||
return host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof InetSocketAddress) {
|
||||
InetSocketAddress a = (InetSocketAddress) o;
|
||||
return a.address.equals(address) && a.port == port;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return port ^ address.hashCode();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getHostName() + ":" + port;
|
||||
}
|
||||
}
|
||||
|
@ -191,6 +191,9 @@ public class Socket implements Closeable, AutoCloseable {
|
||||
outputStream.close();
|
||||
}
|
||||
|
||||
public SocketAddress getRemoteSocketAddress() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
|
@ -54,14 +54,14 @@ class ArrayByteBuffer extends ByteBuffer {
|
||||
|
||||
public ByteBuffer put(ByteBuffer src) {
|
||||
int length = src.remaining();
|
||||
checkPut(position, length);
|
||||
checkPut(position, length, false);
|
||||
src.get(array, arrayOffset + position, length);
|
||||
position += length;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ByteBuffer put(byte[] src, int offset, int length) {
|
||||
checkPut(position, length);
|
||||
checkPut(position, length, false);
|
||||
|
||||
System.arraycopy(src, offset, array, arrayOffset + position, length);
|
||||
position += length;
|
||||
@ -70,7 +70,7 @@ class ArrayByteBuffer extends ByteBuffer {
|
||||
}
|
||||
|
||||
public ByteBuffer get(byte[] dst, int offset, int length) {
|
||||
checkGet(position, length);
|
||||
checkGet(position, length, false);
|
||||
|
||||
System.arraycopy(array, arrayOffset + position, dst, offset, length);
|
||||
position += length;
|
||||
|
92
classpath/java/nio/ArrayCharBuffer.java
Normal file
92
classpath/java/nio/ArrayCharBuffer.java
Normal file
@ -0,0 +1,92 @@
|
||||
/* Copyright (c) 2008-2014, Avian Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software
|
||||
for any purpose with or without fee is hereby granted, provided
|
||||
that the above copyright notice and this permission notice appear
|
||||
in all copies.
|
||||
|
||||
There is NO WARRANTY for this software. See license.txt for
|
||||
details. */
|
||||
|
||||
package java.nio;
|
||||
|
||||
class ArrayCharBuffer extends CharBuffer {
|
||||
private final char[] array;
|
||||
private final int arrayOffset;
|
||||
|
||||
ArrayCharBuffer(char[] array, int offset, int length, boolean readOnly) {
|
||||
super(readOnly);
|
||||
|
||||
this.array = array;
|
||||
this.arrayOffset = offset;
|
||||
this.capacity = length;
|
||||
this.limit = length;
|
||||
this.position = 0;
|
||||
}
|
||||
|
||||
public CharBuffer asReadOnlyBuffer() {
|
||||
CharBuffer b = new ArrayCharBuffer(array, arrayOffset, capacity, true);
|
||||
b.position(position());
|
||||
b.limit(limit());
|
||||
return b;
|
||||
}
|
||||
|
||||
public boolean hasArray() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public char[] array() {
|
||||
return array;
|
||||
}
|
||||
|
||||
public CharBuffer slice() {
|
||||
return new ArrayCharBuffer
|
||||
(array, arrayOffset + position, remaining(), true);
|
||||
}
|
||||
|
||||
public int arrayOffset() {
|
||||
return arrayOffset;
|
||||
}
|
||||
|
||||
protected void doPut(int position, char val) {
|
||||
array[arrayOffset + position] = val;
|
||||
}
|
||||
|
||||
public CharBuffer put(CharBuffer src) {
|
||||
int length = src.remaining();
|
||||
checkPut(position, length, false);
|
||||
src.get(array, arrayOffset + position, length);
|
||||
position += length;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CharBuffer put(char[] src, int offset, int length) {
|
||||
checkPut(position, length, false);
|
||||
|
||||
System.arraycopy(src, offset, array, arrayOffset + position, length);
|
||||
position += length;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public CharBuffer get(char[] dst, int offset, int length) {
|
||||
checkGet(position, length, false);
|
||||
|
||||
System.arraycopy(array, arrayOffset + position, dst, offset, length);
|
||||
position += length;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
protected char doGet(int position) {
|
||||
return array[arrayOffset+position];
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "(ArrayCharBuffer with array: " + array
|
||||
+ " arrayOffset: " + arrayOffset
|
||||
+ " position: " + position
|
||||
+ " limit: " + limit
|
||||
+ " capacity: " + capacity + ")";
|
||||
}
|
||||
}
|
14
classpath/java/nio/BufferOverflowException.java
Normal file
14
classpath/java/nio/BufferOverflowException.java
Normal file
@ -0,0 +1,14 @@
|
||||
/* Copyright (c) 2008-2014, Avian Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software
|
||||
for any purpose with or without fee is hereby granted, provided
|
||||
that the above copyright notice and this permission notice appear
|
||||
in all copies.
|
||||
|
||||
There is NO WARRANTY for this software. See license.txt for
|
||||
details. */
|
||||
|
||||
package java.nio;
|
||||
|
||||
public class BufferOverflowException extends RuntimeException {
|
||||
}
|
@ -69,7 +69,7 @@ public abstract class ByteBuffer
|
||||
|
||||
public ByteBuffer put(ByteBuffer src) {
|
||||
if (src.hasArray()) {
|
||||
checkPut(position, src.remaining());
|
||||
checkPut(position, src.remaining(), false);
|
||||
|
||||
put(src.array(), src.arrayOffset() + src.position, src.remaining());
|
||||
src.position(src.position() + src.remaining());
|
||||
@ -107,13 +107,14 @@ public abstract class ByteBuffer
|
||||
}
|
||||
|
||||
public ByteBuffer put(int offset, byte val) {
|
||||
checkPut(offset, 1);
|
||||
checkPut(offset, 1, true);
|
||||
doPut(offset, val);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ByteBuffer put(byte val) {
|
||||
put(position, val);
|
||||
checkPut(position, 1, false);
|
||||
doPut(position, val);
|
||||
++ position;
|
||||
return this;
|
||||
}
|
||||
@ -122,9 +123,7 @@ public abstract class ByteBuffer
|
||||
return put(arr, 0, arr.length);
|
||||
}
|
||||
|
||||
public ByteBuffer putLong(int position, long val) {
|
||||
checkPut(position, 8);
|
||||
|
||||
private void rawPutLong(int position, long val) {
|
||||
doPut(position , (byte) ((val >> 56) & 0xff));
|
||||
doPut(position + 1, (byte) ((val >> 48) & 0xff));
|
||||
doPut(position + 2, (byte) ((val >> 40) & 0xff));
|
||||
@ -133,54 +132,75 @@ public abstract class ByteBuffer
|
||||
doPut(position + 5, (byte) ((val >> 16) & 0xff));
|
||||
doPut(position + 6, (byte) ((val >> 8) & 0xff));
|
||||
doPut(position + 7, (byte) ((val ) & 0xff));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ByteBuffer putInt(int position, int val) {
|
||||
checkPut(position, 4);
|
||||
|
||||
private void rawPutInt(int position, int val) {
|
||||
doPut(position , (byte) ((val >> 24) & 0xff));
|
||||
doPut(position + 1, (byte) ((val >> 16) & 0xff));
|
||||
doPut(position + 2, (byte) ((val >> 8) & 0xff));
|
||||
doPut(position + 3, (byte) ((val ) & 0xff));
|
||||
}
|
||||
|
||||
private void rawPutShort(int position, short val) {
|
||||
doPut(position , (byte) ((val >> 8) & 0xff));
|
||||
doPut(position + 1, (byte) ((val ) & 0xff));
|
||||
}
|
||||
|
||||
public ByteBuffer putLong(int position, long val) {
|
||||
checkPut(position, 8, true);
|
||||
|
||||
rawPutLong(position, val);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ByteBuffer putInt(int position, int val) {
|
||||
checkPut(position, 4, true);
|
||||
|
||||
rawPutInt(position, val);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ByteBuffer putShort(int position, short val) {
|
||||
checkPut(position, 2);
|
||||
checkPut(position, 2, true);
|
||||
|
||||
doPut(position , (byte) ((val >> 8) & 0xff));
|
||||
doPut(position + 1, (byte) ((val ) & 0xff));
|
||||
rawPutShort(position, val);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ByteBuffer putLong(long val) {
|
||||
putLong(position, val);
|
||||
checkPut(position, 8, false);
|
||||
|
||||
rawPutLong(position, val);
|
||||
position += 8;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ByteBuffer putInt(int val) {
|
||||
putInt(position, val);
|
||||
checkPut(position, 4, false);
|
||||
|
||||
rawPutInt(position, val);
|
||||
position += 4;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ByteBuffer putShort(short val) {
|
||||
putShort(position, val);
|
||||
checkPut(position, 2, false);
|
||||
|
||||
rawPutShort(position, val);
|
||||
position += 2;
|
||||
return this;
|
||||
}
|
||||
|
||||
public byte get() {
|
||||
return get(position++);
|
||||
checkGet(position, 1, false);
|
||||
return doGet(position++);
|
||||
}
|
||||
|
||||
public byte get(int position) {
|
||||
checkGet(position, 1);
|
||||
checkGet(position, 1, true);
|
||||
return doGet(position);
|
||||
}
|
||||
|
||||
@ -189,8 +209,24 @@ public abstract class ByteBuffer
|
||||
}
|
||||
|
||||
public long getLong(int position) {
|
||||
checkGet(position, 8);
|
||||
checkGet(position, 8, true);
|
||||
|
||||
return rawGetLong(position);
|
||||
}
|
||||
|
||||
public int getInt(int position) {
|
||||
checkGet(position, 4, true);
|
||||
|
||||
return rawGetInt(position);
|
||||
}
|
||||
|
||||
public short getShort(int position) {
|
||||
checkGet(position, 2, true);
|
||||
|
||||
return rawGetShort(position);
|
||||
}
|
||||
|
||||
private long rawGetLong(int position) {
|
||||
return (((long) (doGet(position ) & 0xFF)) << 56)
|
||||
| (((long) (doGet(position + 1) & 0xFF)) << 48)
|
||||
| (((long) (doGet(position + 2) & 0xFF)) << 40)
|
||||
@ -201,48 +237,60 @@ public abstract class ByteBuffer
|
||||
| (((long) (doGet(position + 7) & 0xFF)) );
|
||||
}
|
||||
|
||||
public int getInt(int position) {
|
||||
checkGet(position, 4);
|
||||
|
||||
private int rawGetInt(int position) {
|
||||
return (((int) (doGet(position ) & 0xFF)) << 24)
|
||||
| (((int) (doGet(position + 1) & 0xFF)) << 16)
|
||||
| (((int) (doGet(position + 2) & 0xFF)) << 8)
|
||||
| (((int) (doGet(position + 3) & 0xFF)) );
|
||||
}
|
||||
|
||||
public short getShort(int position) {
|
||||
checkGet(position, 2);
|
||||
|
||||
private short rawGetShort(int position) {
|
||||
return (short) (( ((int) (doGet(position ) & 0xFF)) << 8)
|
||||
| (((int) (doGet(position + 1) & 0xFF)) ));
|
||||
}
|
||||
|
||||
public long getLong() {
|
||||
long r = getLong(position);
|
||||
checkGet(position, 8, false);
|
||||
|
||||
long r = rawGetLong(position);
|
||||
position += 8;
|
||||
return r;
|
||||
}
|
||||
|
||||
public int getInt() {
|
||||
int r = getInt(position);
|
||||
checkGet(position, 4, false);
|
||||
|
||||
int r = rawGetInt(position);
|
||||
position += 4;
|
||||
return r;
|
||||
}
|
||||
|
||||
public short getShort() {
|
||||
short r = getShort(position);
|
||||
checkGet(position, 2, false);
|
||||
|
||||
short r = rawGetShort(position);
|
||||
position += 2;
|
||||
return r;
|
||||
}
|
||||
|
||||
protected void checkPut(int position, int amount) {
|
||||
if (readOnly) throw new ReadOnlyBufferException();
|
||||
if (position < 0 || position+amount > limit)
|
||||
throw new IndexOutOfBoundsException();
|
||||
protected void checkPut(int position, int amount, boolean absolute) {
|
||||
if (readOnly) {
|
||||
throw new ReadOnlyBufferException();
|
||||
}
|
||||
|
||||
if (position < 0 || position+amount > limit) {
|
||||
throw absolute
|
||||
? new IndexOutOfBoundsException()
|
||||
: new BufferOverflowException();
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkGet(int position, int amount) {
|
||||
if (amount > limit-position) throw new IndexOutOfBoundsException();
|
||||
protected void checkGet(int position, int amount, boolean absolute) {
|
||||
if (amount > limit-position) {
|
||||
throw absolute
|
||||
? new IndexOutOfBoundsException()
|
||||
: new BufferUnderflowException();
|
||||
}
|
||||
}
|
||||
|
||||
public ByteBuffer order(ByteOrder order) {
|
||||
|
154
classpath/java/nio/CharBuffer.java
Normal file
154
classpath/java/nio/CharBuffer.java
Normal file
@ -0,0 +1,154 @@
|
||||
/* Copyright (c) 2008-2014, Avian Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software
|
||||
for any purpose with or without fee is hereby granted, provided
|
||||
that the above copyright notice and this permission notice appear
|
||||
in all copies.
|
||||
|
||||
There is NO WARRANTY for this software. See license.txt for
|
||||
details. */
|
||||
|
||||
package java.nio;
|
||||
|
||||
public abstract class CharBuffer
|
||||
extends Buffer
|
||||
implements Comparable<CharBuffer>
|
||||
{
|
||||
private final boolean readOnly;
|
||||
|
||||
protected CharBuffer(boolean readOnly) {
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
public static CharBuffer allocate(int capacity) {
|
||||
return new ArrayCharBuffer(new char[capacity], 0, capacity, false);
|
||||
}
|
||||
|
||||
public static CharBuffer wrap(char[] array) {
|
||||
return wrap(array, 0, array.length);
|
||||
}
|
||||
|
||||
public static CharBuffer wrap(char[] array, int offset, int length) {
|
||||
return new ArrayCharBuffer(array, offset, length, false);
|
||||
}
|
||||
|
||||
public abstract CharBuffer asReadOnlyBuffer();
|
||||
|
||||
public abstract CharBuffer slice();
|
||||
|
||||
protected abstract void doPut(int offset, char value);
|
||||
|
||||
public abstract CharBuffer put(char[] src, int offset, int length);
|
||||
|
||||
protected abstract char doGet(int offset);
|
||||
|
||||
public abstract CharBuffer get(char[] dst, int offset, int length);
|
||||
|
||||
public boolean hasArray() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public CharBuffer compact() {
|
||||
int remaining = remaining();
|
||||
|
||||
if (position != 0) {
|
||||
CharBuffer b = slice();
|
||||
position = 0;
|
||||
put(b);
|
||||
}
|
||||
|
||||
position = remaining;
|
||||
limit(capacity());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public CharBuffer put(CharBuffer src) {
|
||||
if (src.hasArray()) {
|
||||
checkPut(position, src.remaining(), false);
|
||||
|
||||
put(src.array(), src.arrayOffset() + src.position, src.remaining());
|
||||
src.position(src.position() + src.remaining());
|
||||
|
||||
return this;
|
||||
} else {
|
||||
char[] buffer = new char[src.remaining()];
|
||||
src.get(buffer);
|
||||
return put(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public int compareTo(CharBuffer o) {
|
||||
int end = (remaining() < o.remaining() ? remaining() : o.remaining());
|
||||
|
||||
for (int i = 0; i < end; ++i) {
|
||||
int d = get(position + i) - o.get(o.position + i);
|
||||
if (d != 0) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
return remaining() - o.remaining();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof CharBuffer && compareTo((CharBuffer) o) == 0;
|
||||
}
|
||||
|
||||
public char[] array() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public int arrayOffset() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public CharBuffer put(int offset, char val) {
|
||||
checkPut(offset, 1, true);
|
||||
doPut(offset, val);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CharBuffer put(char val) {
|
||||
put(position, val);
|
||||
++ position;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CharBuffer put(char[] arr) {
|
||||
return put(arr, 0, arr.length);
|
||||
}
|
||||
|
||||
public char get() {
|
||||
checkGet(position, 1, false);
|
||||
return doGet(position++);
|
||||
}
|
||||
|
||||
public char get(int position) {
|
||||
checkGet(position, 1, true);
|
||||
return doGet(position);
|
||||
}
|
||||
|
||||
public CharBuffer get(char[] dst) {
|
||||
return get(dst, 0, dst.length);
|
||||
}
|
||||
|
||||
protected void checkPut(int position, int amount, boolean absolute) {
|
||||
if (readOnly) {
|
||||
throw new ReadOnlyBufferException();
|
||||
}
|
||||
|
||||
if (position < 0 || position+amount > limit) {
|
||||
throw absolute
|
||||
? new IndexOutOfBoundsException()
|
||||
: new BufferOverflowException();
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkGet(int position, int amount, boolean absolute) {
|
||||
if (amount > limit-position) {
|
||||
throw absolute
|
||||
? new IndexOutOfBoundsException()
|
||||
: new BufferUnderflowException();
|
||||
}
|
||||
}
|
||||
}
|
@ -48,7 +48,7 @@ class DirectByteBuffer extends ByteBuffer {
|
||||
|
||||
public ByteBuffer put(ByteBuffer src) {
|
||||
if (src instanceof DirectByteBuffer) {
|
||||
checkPut(position, src.remaining());
|
||||
checkPut(position, src.remaining(), false);
|
||||
|
||||
DirectByteBuffer b = (DirectByteBuffer) src;
|
||||
|
||||
@ -69,7 +69,7 @@ class DirectByteBuffer extends ByteBuffer {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
checkPut(position, length);
|
||||
checkPut(position, length, false);
|
||||
|
||||
unsafe.copyMemory
|
||||
(src, baseOffset + offset, null, address + position, length);
|
||||
@ -84,7 +84,7 @@ class DirectByteBuffer extends ByteBuffer {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
checkGet(position, length);
|
||||
checkGet(position, length, false);
|
||||
|
||||
unsafe.copyMemory
|
||||
(null, address + position, dst, baseOffset + offset, length);
|
||||
|
@ -26,7 +26,7 @@ public class DatagramChannel extends SelectableChannel
|
||||
{
|
||||
public static final int InvalidSocket = -1;
|
||||
|
||||
private int socket = InvalidSocket;
|
||||
private int socket = makeSocket();
|
||||
private boolean blocking = true;
|
||||
private boolean connected = false;
|
||||
|
||||
@ -80,8 +80,12 @@ public class DatagramChannel extends SelectableChannel
|
||||
throw new UnsupportedAddressTypeException();
|
||||
}
|
||||
|
||||
socket = bind(inetAddress.getHostName(), inetAddress.getPort());
|
||||
configureBlocking();
|
||||
if (inetAddress == null) {
|
||||
bind(socket, 0, 0);
|
||||
} else {
|
||||
bind(socket, inetAddress.getAddress().getRawAddress(),
|
||||
inetAddress.getPort());
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
@ -94,10 +98,8 @@ public class DatagramChannel extends SelectableChannel
|
||||
throw new UnsupportedAddressTypeException();
|
||||
}
|
||||
|
||||
socket = connect(inetAddress.getHostName(), inetAddress.getPort());
|
||||
configureBlocking();
|
||||
|
||||
if (socket != 0) connected = true;
|
||||
connected = connect(socket, inetAddress.getAddress().getRawAddress(),
|
||||
inetAddress.getPort());
|
||||
|
||||
return this;
|
||||
}
|
||||
@ -145,6 +147,31 @@ public class DatagramChannel extends SelectableChannel
|
||||
}
|
||||
}
|
||||
|
||||
public int send(ByteBuffer b, SocketAddress address) throws IOException {
|
||||
if (b.remaining() == 0) return 0;
|
||||
|
||||
InetSocketAddress inetAddress;
|
||||
try {
|
||||
inetAddress = (InetSocketAddress) address;
|
||||
} catch (ClassCastException e) {
|
||||
throw new UnsupportedAddressTypeException();
|
||||
}
|
||||
|
||||
byte[] array = b.array();
|
||||
if (array == null) throw new NullPointerException();
|
||||
|
||||
int c = send
|
||||
(socket, inetAddress.getAddress().getRawAddress(),
|
||||
inetAddress.getPort(), array, b.arrayOffset() + b.position(),
|
||||
b.remaining(), blocking);
|
||||
|
||||
if (c > 0) {
|
||||
b.position(b.position() + c);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
private static String ipv4ToString(int address) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
@ -178,22 +205,35 @@ public class DatagramChannel extends SelectableChannel
|
||||
|
||||
/** TODO: This is probably incomplete. */
|
||||
public DatagramChannel disconnect() throws IOException {
|
||||
close();
|
||||
connect(socket, 0, 0);
|
||||
connected = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (isOpen()) {
|
||||
super.close();
|
||||
close(socket);
|
||||
}
|
||||
}
|
||||
|
||||
private static native int makeSocket();
|
||||
private static native void configureBlocking(int socket, boolean blocking)
|
||||
throws IOException;
|
||||
private static native int bind(String hostname, int port)
|
||||
private static native void bind(int socket, int host, int port)
|
||||
throws IOException;
|
||||
private static native int connect(String hostname, int port)
|
||||
private static native boolean connect(int socket, int host, int port)
|
||||
throws IOException;
|
||||
private static native int write(int socket, byte[] array, int offset,
|
||||
int length, boolean blocking)
|
||||
throws IOException;
|
||||
private static native int send(int socket, int host, int port,
|
||||
byte[] array, int offset,
|
||||
int length, boolean blocking)
|
||||
throws IOException;
|
||||
private static native int receive(int socket, byte[] array, int offset,
|
||||
int length, boolean blocking,
|
||||
int[] address)
|
||||
throws IOException;
|
||||
private static native void close(int socket);
|
||||
}
|
||||
|
@ -66,10 +66,10 @@ public class ServerSocketChannel extends SelectableChannel {
|
||||
}
|
||||
}
|
||||
|
||||
private int doListen(String host, int port) throws IOException {
|
||||
private void doListen(int socket, int host, int port) throws IOException {
|
||||
Socket.init();
|
||||
|
||||
return natDoListen(host, port);
|
||||
natDoListen(socket, host, port);
|
||||
}
|
||||
|
||||
public class Handle extends ServerSocket {
|
||||
@ -82,11 +82,10 @@ public class ServerSocketChannel extends SelectableChannel {
|
||||
} catch (ClassCastException e) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
channel.socket = doListen(a.getHostName(), a.getPort());
|
||||
channel.configureBlocking(channel.isBlocking());
|
||||
doListen(channel.socket, a.getAddress().getRawAddress(), a.getPort());
|
||||
}
|
||||
}
|
||||
|
||||
private static native int natDoAccept(int socket) throws IOException;
|
||||
private static native int natDoListen(String host, int port) throws IOException;
|
||||
private static native void natDoListen(int socket, int host, int port) throws IOException;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ public class SocketChannel extends SelectableChannel
|
||||
{
|
||||
public static final int InvalidSocket = -1;
|
||||
|
||||
int socket = InvalidSocket;
|
||||
int socket = makeSocket();
|
||||
boolean connected = false;
|
||||
boolean readyToConnect = false;
|
||||
boolean blocking = true;
|
||||
@ -64,7 +64,7 @@ public class SocketChannel extends SelectableChannel
|
||||
} catch (ClassCastException e) {
|
||||
throw new UnsupportedAddressTypeException();
|
||||
}
|
||||
socket = doConnect(a.getHostName(), a.getPort());
|
||||
doConnect(socket, a.getAddress().getRawAddress(), a.getPort());
|
||||
configureBlocking(blocking);
|
||||
return connected;
|
||||
}
|
||||
@ -98,13 +98,10 @@ public class SocketChannel extends SelectableChannel
|
||||
}
|
||||
}
|
||||
|
||||
private int doConnect(String host, int port) throws IOException {
|
||||
if (host == null) throw new NullPointerException();
|
||||
|
||||
boolean b[] = new boolean[1];
|
||||
int s = natDoConnect(host, port, blocking, b);
|
||||
connected = b[0];
|
||||
return s;
|
||||
private void doConnect(int socket, int host, int port)
|
||||
throws IOException
|
||||
{
|
||||
connected = natDoConnect(socket, host, port);
|
||||
}
|
||||
|
||||
public int read(ByteBuffer b) throws IOException {
|
||||
@ -170,21 +167,40 @@ public class SocketChannel extends SelectableChannel
|
||||
|
||||
public class Handle extends Socket {
|
||||
public Handle() throws IOException {
|
||||
super();
|
||||
}
|
||||
super();
|
||||
}
|
||||
|
||||
public void setTcpNoDelay(boolean on) throws SocketException {
|
||||
public void setTcpNoDelay(boolean on) throws SocketException {
|
||||
natSetTcpNoDelay(socket, on);
|
||||
}
|
||||
|
||||
public void bind(SocketAddress address)
|
||||
throws IOException
|
||||
{
|
||||
InetSocketAddress a;
|
||||
try {
|
||||
a = (InetSocketAddress) address;
|
||||
} catch (ClassCastException e) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
if (a == null) {
|
||||
bind(socket, 0, 0);
|
||||
} else {
|
||||
bind(socket, a.getAddress().getRawAddress(), a.getPort());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static native int makeSocket();
|
||||
private static native void configureBlocking(int socket, boolean blocking)
|
||||
throws IOException;
|
||||
|
||||
private static native void natSetTcpNoDelay(int socket, boolean on)
|
||||
throws SocketException;
|
||||
|
||||
private static native int natDoConnect(String host, int port, boolean blocking, boolean[] connected)
|
||||
private static native void bind(int socket, int host, int port)
|
||||
throws IOException;
|
||||
private static native boolean natDoConnect(int socket, int host, int port)
|
||||
throws IOException;
|
||||
private static native void natFinishConnect(int socket)
|
||||
throws IOException;
|
||||
|
@ -1,4 +1,6 @@
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.BufferOverflowException;
|
||||
|
||||
public class Buffers {
|
||||
static {
|
||||
@ -57,7 +59,7 @@ public class Buffers {
|
||||
|
||||
private static native void freeNative(ByteBuffer b);
|
||||
|
||||
public static void main(String[] args) {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Factory array = new Factory() {
|
||||
public ByteBuffer allocate(int capacity) {
|
||||
return ByteBuffer.allocate(capacity);
|
||||
@ -99,6 +101,34 @@ public class Buffers {
|
||||
test(native_, array);
|
||||
test(native_, direct);
|
||||
test(native_, native_);
|
||||
|
||||
try {
|
||||
ByteBuffer.allocate(1).getInt();
|
||||
expect(false);
|
||||
} catch (BufferUnderflowException e) {
|
||||
// cool
|
||||
}
|
||||
|
||||
try {
|
||||
ByteBuffer.allocate(1).getInt(0);
|
||||
expect(false);
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
// cool
|
||||
}
|
||||
|
||||
try {
|
||||
ByteBuffer.allocate(1).putInt(1);
|
||||
expect(false);
|
||||
} catch (BufferOverflowException e) {
|
||||
// cool
|
||||
}
|
||||
|
||||
try {
|
||||
ByteBuffer.allocate(1).putInt(0, 1);
|
||||
expect(false);
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
// cool
|
||||
}
|
||||
}
|
||||
|
||||
private interface Factory {
|
||||
|
@ -22,20 +22,28 @@ public class Datagrams {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
test(true);
|
||||
test(false);
|
||||
}
|
||||
|
||||
private static void test(boolean send) throws Exception {
|
||||
final String Hostname = "localhost";
|
||||
final int Port = 22043;
|
||||
final SocketAddress Address = new InetSocketAddress(Hostname, Port);
|
||||
final int InPort = 22043;
|
||||
final int OutPort = 22044;
|
||||
final SocketAddress InAddress = new InetSocketAddress(Hostname, InPort);
|
||||
final SocketAddress OutAddress = new InetSocketAddress(Hostname, OutPort);
|
||||
final byte[] Message = "hello, world!".getBytes();
|
||||
|
||||
DatagramChannel out = DatagramChannel.open();
|
||||
try {
|
||||
out.configureBlocking(false);
|
||||
out.connect(Address);
|
||||
out.socket().bind(OutAddress);
|
||||
if (! send) out.connect(InAddress);
|
||||
|
||||
DatagramChannel in = DatagramChannel.open();
|
||||
try {
|
||||
in.configureBlocking(false);
|
||||
in.socket().bind(Address);
|
||||
in.socket().bind(InAddress);
|
||||
|
||||
Selector selector = Selector.open();
|
||||
try {
|
||||
@ -53,14 +61,18 @@ public class Datagrams {
|
||||
switch (state) {
|
||||
case 0: {
|
||||
if (outKey.isWritable()) {
|
||||
out.write(ByteBuffer.wrap(Message));
|
||||
if (send) {
|
||||
out.send(ByteBuffer.wrap(Message), InAddress);
|
||||
} else {
|
||||
out.write(ByteBuffer.wrap(Message));
|
||||
}
|
||||
state = 1;
|
||||
}
|
||||
} break;
|
||||
|
||||
case 1: {
|
||||
if (inKey.isReadable()) {
|
||||
in.receive(inBuffer);
|
||||
expect(in.receive(inBuffer).equals(OutAddress));
|
||||
if (! inBuffer.hasRemaining()) {
|
||||
expect(equal(inBuffer.array(),
|
||||
inBuffer.arrayOffset(),
|
||||
|
@ -206,5 +206,12 @@ public class Strings {
|
||||
expect("abc".lastIndexOf('b', 100) == 1);
|
||||
|
||||
testTrivialPattern();
|
||||
|
||||
{ String s = "hello, world!";
|
||||
java.nio.CharBuffer buffer = java.nio.CharBuffer.allocate(s.length());
|
||||
new java.io.InputStreamReader
|
||||
(new java.io.ByteArrayInputStream(s.getBytes())).read(buffer);
|
||||
expect(s.equals(new String(buffer.array())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user