() {
public Void run() {
final Thread thr = new Thread(new Cleaner(), "XNIO cleaner thread");
thr.setDaemon(true);
thr.setContextClassLoader(null);
thr.start();
return null;
}
});
}
public void run() {
AutomaticReference> ref;
for (;;) try {
ref = (AutomaticReference>) QUEUE.remove();
try {
ref.free();
} finally {
LIVE_SET.remove(ref);
}
} catch (Throwable ignored) {
}
}
}
}
xnio-3.6.2.Final/api/src/main/java/org/xnio/Bits.java 0000664 0000000 0000000 00000022614 13241300306 0022241 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
*
* Copyright 2011 Red Hat, Inc. and/or its affiliates, and individual
* contributors as indicated by the @author tags.
*
* 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;
/**
* General bit-affecting utility methods.
*
* @author David M. Lloyd
*/
public final class Bits {
private Bits() {}
//--- Bit mask methods
/**
* Get an integer bit mask consisting of 1 bits in the given range. The returned {@code int}
* will have bits {@code low} through {@code high} set, and all other bits clear.
*
* @param low the low bit value
* @param high the high bit value
* @return the bit mask
*/
public static int intBitMask(int low, int high) {
assert low >= 0;
assert low <= high;
assert high < 32;
return (high == 31 ? 0 : (1 << high + 1)) - (1 << low);
}
/**
* Get a long bit mask consisting of 1 bits in the given range. The returned {@code long}
* will have bits {@code low} through {@code high} set, and all other bits clear.
*
* @param low the low bit value
* @param high the high bit value
* @return the bit mask
*/
public static long longBitMask(int low, int high) {
assert low >= 0;
assert low <= high;
assert high < 64;
return (high == 63 ? 0L : (1L << (long) high + 1L)) - (1L << (long) low);
}
//--- Flags methods
/**
* Determine if any of the {@code flags} in the given {@code var} are set.
*
* @param var the value to test
* @param flags the flags to test for
* @return {@code true} if any of {@code flags} are in {@code var}, {@code false} otherwise
*/
public static boolean anyAreSet(int var, int flags) {
return (var & flags) != 0;
}
/**
* Determine if all of the {@code flags} in the given {@code var} are set.
*
* @param var the value to test
* @param flags the flags to test for
* @return {@code true} if all of {@code flags} are in {@code var}, {@code false} otherwise
*/
public static boolean allAreSet(int var, int flags) {
return (var & flags) == flags;
}
/**
* Determine if any of the {@code flags} in the given {@code var} are clear.
*
* @param var the value to test
* @param flags the flags to test for
* @return {@code true} if not all of {@code flags} are in {@code var}, {@code false} otherwise
*/
public static boolean anyAreClear(int var, int flags) {
return (var & flags) != flags;
}
/**
* Determine if all of the {@code flags} in the given {@code var} are clear.
*
* @param var the value to test
* @param flags the flags to test for
* @return {@code true} if none of {@code flags} are in {@code var}, {@code false} otherwise
*/
public static boolean allAreClear(int var, int flags) {
return (var & flags) == 0;
}
/**
* Determine if any of the {@code flags} in the given {@code var} are set.
*
* @param var the value to test
* @param flags the flags to test for
* @return {@code true} if any of {@code flags} are in {@code var}, {@code false} otherwise
*/
public static boolean anyAreSet(long var, long flags) {
return (var & flags) != 0;
}
/**
* Determine if all of the {@code flags} in the given {@code var} are set.
*
* @param var the value to test
* @param flags the flags to test for
* @return {@code true} if all of {@code flags} are in {@code var}, {@code false} otherwise
*/
public static boolean allAreSet(long var, long flags) {
return (var & flags) == flags;
}
/**
* Determine if any of the {@code flags} in the given {@code var} are clear.
*
* @param var the value to test
* @param flags the flags to test for
* @return {@code true} if not all of {@code flags} are in {@code var}, {@code false} otherwise
*/
public static boolean anyAreClear(long var, long flags) {
return (var & flags) != flags;
}
/**
* Determine if all of the {@code flags} in the given {@code var} are clear.
*
* @param var the value to test
* @param flags the flags to test for
* @return {@code true} if none of {@code flags} are in {@code var}, {@code false} otherwise
*/
public static boolean allAreClear(long var, long flags) {
return (var & flags) == 0;
}
//--- Signed/unsigned methods
/**
* Convert a signed value to unsigned.
*
* @param v the signed byte
* @return the unsigned byte, as an int
*/
public static int unsigned(byte v) {
return v & 0xff;
}
/**
* Convert a signed value to unsigned.
*
* @param v the signed short
* @return the unsigned short, as an int
*/
public static int unsigned(short v) {
return v & 0xffff;
}
/**
* Convert a signed value to unsigned.
*
* @param v the signed int
* @return the unsigned int, as a long
*/
public static long unsigned(int v) {
return v & 0xffffffffL;
}
//--- Byte array read methods
/**
* Get a 16-bit signed little-endian short value from a byte array.
*
* @param b the byte array
* @param off the offset in the array
* @return the signed short value
*/
public static short shortFromBytesLE(byte[] b, int off) {
return (short) (b[off + 1] << 8 | b[off] & 0xff);
}
/**
* Get a 16-bit signed big-endian short value from a byte array.
*
* @param b the byte array
* @param off the offset in the array
* @return the signed short value
*/
public static short shortFromBytesBE(byte[] b, int off) {
return (short) (b[off] << 8 | b[off + 1] & 0xff);
}
/**
* Get a 16-bit signed little-endian char value from a byte array.
*
* @param b the byte array
* @param off the offset in the array
* @return the signed char value
*/
public static char charFromBytesLE(byte[] b, int off) {
return (char) (b[off + 1] << 8 | b[off] & 0xff);
}
/**
* Get a 16-bit signed big-endian char value from a byte array.
*
* @param b the byte array
* @param off the offset in the array
* @return the signed char value
*/
public static char charFromBytesBE(byte[] b, int off) {
return (char) (b[off] << 8 | b[off + 1] & 0xff);
}
/**
* Get a 24-bit signed little-endian int value from a byte array.
*
* @param b the byte array
* @param off the offset in the array
* @return the signed medium value as an int
*/
public static int mediumFromBytesLE(byte[] b, int off) {
return (b[off + 2] & 0xff) << 16 | (b[off + 1] & 0xff) << 8 | b[off] & 0xff;
}
/**
* Get a 24-bit signed big-endian int value from a byte array.
*
* @param b the byte array
* @param off the offset in the array
* @return the signed medium value as an int
*/
public static int mediumFromBytesBE(byte[] b, int off) {
return (b[off] & 0xff) << 16 | (b[off + 1] & 0xff) << 8 | b[off + 2] & 0xff;
}
/**
* Get a 32-bit signed little-endian int value from a byte array.
*
* @param b the byte array
* @param off the offset in the array
* @return the signed int value
*/
public static int intFromBytesLE(byte[] b, int off) {
return b[off + 3] << 24 | (b[off + 2] & 0xff) << 16 | (b[off + 1] & 0xff) << 8 | b[off] & 0xff;
}
/**
* Get a 32-bit signed big-endian int value from a byte array.
*
* @param b the byte array
* @param off the offset in the array
* @return the signed int value
*/
public static int intFromBytesBE(byte[] b, int off) {
return b[off] << 24 | (b[off + 1] & 0xff) << 16 | (b[off + 2] & 0xff) << 8 | b[off + 3] & 0xff;
}
/**
* Get a 64-bit signed little-endian long value from a byte array.
*
* @param b the byte array
* @param off the offset in the array
* @return the signed long value
*/
public static long longFromBytesLE(byte[] b, int off) {
return (b[off + 7] & 0xffL) << 56L | (b[off + 6] & 0xffL) << 48L | (b[off + 5] & 0xffL) << 40L | (b[off + 4] & 0xffL) << 32L | (b[off + 3] & 0xffL) << 24L | (b[off + 2] & 0xffL) << 16L | (b[off + 1] & 0xffL) << 8L | b[off] & 0xffL;
}
/**
* Get a 64-bit signed big-endian long value from a byte array.
*
* @param b the byte array
* @param off the offset in the array
* @return the signed long value
*/
public static long longFromBytesBE(byte[] b, int off) {
return (b[off] & 0xffL) << 56L | (b[off + 1] & 0xffL) << 48L | (b[off + 2] & 0xffL) << 40L | (b[off + 3] & 0xffL) << 32L | (b[off + 4] & 0xffL) << 24L | (b[off + 5] & 0xffL) << 16L | (b[off + 6] & 0xffL) << 8L | b[off + 7] & 0xffL;
}
}
xnio-3.6.2.Final/api/src/main/java/org/xnio/BrokenPipeException.java 0000664 0000000 0000000 00000004654 13241300306 0025261 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
*
* Copyright 2012 Red Hat, Inc. and/or its affiliates, and individual
* contributors as indicated by the @author tags.
*
* 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;
import java.io.IOException;
/**
* An exception that signifies that a pipe, stream, or channel was closed from the read side while the write side was
* still writing.
*
* @author David M. Lloyd
*/
public class BrokenPipeException extends IOException {
/**
* Constructs a {@code BrokenPipeException} with no detail message. The cause is not initialized, and may
* subsequently be initialized by a call to {@link #initCause(Throwable) initCause}.
*/
public BrokenPipeException() {
}
/**
* Constructs a {@code BrokenPipeException} with the specified detail message. The cause is not initialized, and may
* subsequently be initialized by a call to {@link #initCause(Throwable) initCause}.
*
* @param msg the detail message
*/
public BrokenPipeException(final String msg) {
super(msg);
}
/**
* Constructs a {@code BrokenPipeException} with the specified cause. The detail message is set to:
* (cause == null ? null : cause.toString())
* (which typically contains the class and detail message of {@code cause}).
*
* @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method)
*/
public BrokenPipeException(final Throwable cause) {
super(cause);
}
/**
* Constructs a {@code BrokenPipeException} with the specified detail message and cause.
*
* @param msg the detail message
* @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method)
*/
public BrokenPipeException(final String msg, final Throwable cause) {
super(msg, cause);
}
}
xnio-3.6.2.Final/api/src/main/java/org/xnio/BufferAllocator.java 0000664 0000000 0000000 00000003467 13241300306 0024417 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source
*
* Copyright 2011 Red Hat, Inc. and/or its affiliates, and individual
* contributors as indicated by the @author tags.
*
* 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;
import java.nio.Buffer;
import java.nio.ByteBuffer;
/**
* A simple allocator for buffers.
*
* @param the buffer type
*
* @author David M. Lloyd
*/
public interface BufferAllocator {
/**
* Allocate a buffer of the given size.
*
* @param size the size
* @return the buffer
* @throws IllegalArgumentException if the given buffer size is less than zero
*/
B allocate(int size) throws IllegalArgumentException;
/**
* A simple allocator for heap-array-backed byte buffers.
*/
BufferAllocator BYTE_BUFFER_ALLOCATOR = new BufferAllocator() {
public ByteBuffer allocate(final int size) {
return ByteBuffer.allocate(size);
}
};
/**
* A simple allocator for direct byte buffers.
*/
BufferAllocator DIRECT_BYTE_BUFFER_ALLOCATOR = new BufferAllocator() {
public ByteBuffer allocate(final int size) {
return ByteBuffer.allocateDirect(size);
}
};
}
xnio-3.6.2.Final/api/src/main/java/org/xnio/Buffers.java 0000664 0000000 0000000 00000253605 13241300306 0022742 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source
*
* Copyright 2008 Red Hat, Inc. and/or its affiliates.
*
* 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;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.nio.Buffer;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ReadOnlyBufferException;
import java.nio.ShortBuffer;
import java.nio.BufferOverflowException;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.util.Arrays;
import java.io.IOException;
import java.util.Random;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import static org.xnio._private.Messages.msg;
import org.wildfly.common.ref.CleanerReference;
import org.wildfly.common.ref.Reaper;
import org.wildfly.common.ref.Reference;
/**
* Buffer utility methods.
*
* @apiviz.exclude
*/
public final class Buffers {
private Buffers() {}
/**
* Flip a buffer.
*
* @see Buffer#flip()
* @param the buffer type
* @param buffer the buffer to flip
* @return the buffer instance
*/
public static T flip(T buffer) {
buffer.flip();
return buffer;
}
/**
* Clear a buffer.
*
* @see Buffer#clear()
* @param the buffer type
* @param buffer the buffer to clear
* @return the buffer instance
*/
public static T clear(T buffer) {
buffer.clear();
return buffer;
}
/**
* Set the buffer limit.
*
* @see Buffer#limit(int)
* @param the buffer type
* @param buffer the buffer to set
* @param limit the new limit
* @return the buffer instance
*/
public static T limit(T buffer, int limit) {
buffer.limit(limit);
return buffer;
}
/**
* Set the buffer mark.
*
* @see Buffer#mark()
* @param the buffer type
* @param buffer the buffer to mark
* @return the buffer instance
*/
public static T mark(T buffer) {
buffer.mark();
return buffer;
}
/**
* Set the buffer position.
*
* @see Buffer#position(int)
* @param the buffer type
* @param buffer the buffer to set
* @param position the new position
* @return the buffer instance
*/
public static T position(T buffer, int position) {
buffer.position(position);
return buffer;
}
/**
* Reset the buffer.
*
* @see Buffer#reset()
* @param the buffer type
* @param buffer the buffer to reset
* @return the buffer instance
*/
public static T reset(T buffer) {
buffer.reset();
return buffer;
}
/**
* Rewind the buffer.
*
* @see Buffer#rewind()
* @param the buffer type
* @param buffer the buffer to rewind
* @return the buffer instance
*/
public static T rewind(T buffer) {
buffer.rewind();
return buffer;
}
/**
* Slice the buffer. The original buffer's position will be moved up past the slice that was taken.
*
* @see ByteBuffer#slice()
* @param buffer the buffer to slice
* @param sliceSize the size of the slice
* @return the buffer slice
*/
public static ByteBuffer slice(ByteBuffer buffer, int sliceSize) {
final int oldRem = buffer.remaining();
if (sliceSize > oldRem || sliceSize < -oldRem) {
throw msg.bufferUnderflow();
}
final int oldPos = buffer.position();
final int oldLim = buffer.limit();
if (sliceSize < 0) {
// count from end (sliceSize is NEGATIVE)
buffer.limit(oldLim + sliceSize);
try {
return buffer.slice();
} finally {
buffer.limit(oldLim);
buffer.position(oldLim + sliceSize);
}
} else {
// count from start
buffer.limit(oldPos + sliceSize);
try {
return buffer.slice();
} finally {
buffer.limit(oldLim);
buffer.position(oldPos + sliceSize);
}
}
}
/**
* Copy a portion of the buffer into a newly allocated buffer. The original buffer's position will be moved up past the copy that was taken.
*
* @param buffer the buffer to slice
* @param count the size of the copy
* @param allocator the buffer allocator to use
* @return the buffer slice
*/
public static ByteBuffer copy(ByteBuffer buffer, int count, BufferAllocator allocator) {
final int oldRem = buffer.remaining();
if (count > oldRem || count < -oldRem) {
throw msg.bufferUnderflow();
}
final int oldPos = buffer.position();
final int oldLim = buffer.limit();
if (count < 0) {
// count from end (sliceSize is NEGATIVE)
final ByteBuffer target = allocator.allocate(-count);
buffer.position(oldLim + count);
try {
target.put(buffer);
return target;
} finally {
buffer.limit(oldLim);
buffer.position(oldLim + count);
}
} else {
// count from start
final ByteBuffer target = allocator.allocate(count);
buffer.limit(oldPos + count);
try {
target.put(buffer);
return target;
} finally {
buffer.limit(oldLim);
buffer.position(oldPos + count);
}
}
}
/**
* Copy as many bytes as possible from {@code source} into {@code destination}.
*
* @param destination the destination buffer
* @param source the source buffer
* @return the number of bytes put into the destination buffer
*/
public static int copy(final ByteBuffer destination, final ByteBuffer source) {
final int sr = source.remaining();
final int dr = destination.remaining();
if (dr >= sr) {
destination.put(source);
return sr;
} else {
destination.put(slice(source, dr));
return dr;
}
}
/**
* Copy as many bytes as possible from {@code sources} into {@code destinations} in a "scatter" fashion.
*
* @param destinations the destination buffers
* @param offset the offset into the destination buffers array
* @param length the number of buffers to update
* @param source the source buffer
* @return the number of bytes put into the destination buffers
*/
public static int copy(final ByteBuffer[] destinations, final int offset, final int length, final ByteBuffer source) {
int t = 0;
for (int i = 0; i < length; i ++) {
final ByteBuffer buffer = destinations[i + offset];
final int rem = buffer.remaining();
if (rem == 0) {
continue;
} else if (rem < source.remaining()) {
buffer.put(slice(source, rem));
t += rem;
} else {
t += source.remaining();
buffer.put(source);
return t;
}
}
return t;
}
/**
* Copy as many bytes as possible from {@code sources} into {@code destination} in a "gather" fashion.
*
* @param destination the destination buffer
* @param sources the source buffers
* @param offset the offset into the source buffers array
* @param length the number of buffers to read from
* @return the number of bytes put into the destination buffers
*/
public static int copy(final ByteBuffer destination, final ByteBuffer[] sources, final int offset, final int length) {
int t = 0;
for (int i = 0; i < length; i ++) {
final ByteBuffer buffer = sources[i + offset];
final int rem = buffer.remaining();
if (rem == 0) {
continue;
} else if (rem > destination.remaining()) {
t += destination.remaining();
destination.put(slice(buffer, destination.remaining()));
return t;
} else {
destination.put(buffer);
t += rem;
}
}
return t;
}
/**
* Copy as many bytes as possible from {@code sources} into {@code destinations} by a combined "scatter"/"gather" operation.
*
* @param destinations the destination buffers
* @param destOffset the offset into the destination buffers array
* @param destLength the number of buffers to write to
* @param sources the source buffers
* @param srcOffset the offset into the source buffers array
* @param srcLength the number of buffers to read from
* @return the number of bytes put into the destination buffers
*/
public static long copy(final ByteBuffer[] destinations, final int destOffset, final int destLength, final ByteBuffer[] sources, final int srcOffset, final int srcLength) {
long t = 0L;
int s = 0, d = 0;
if (destLength == 0 || srcLength == 0) {
return 0L;
}
ByteBuffer source = sources[srcOffset];
ByteBuffer dest = destinations[destOffset];
while (s < srcLength && d < destLength) {
source = sources[srcOffset + s];
dest = destinations[destOffset + d];
final int sr = source.remaining();
final int dr = dest.remaining();
if (sr < dr) {
dest.put(source);
s++;
t += sr;
} else if (sr > dr) {
dest.put(slice(source, dr));
d++;
t += dr;
} else {
dest.put(source);
s++;
d++;
t += sr;
}
}
return t;
}
/**
* Copy at most {@code count} bytes from {@code source} into {@code destination}.
*
* @param count the maximum number of bytes to copy
* @param destination the destination buffer
* @param source the source buffer
* @return the number of bytes put into the destination buffer
*/
public static int copy(int count, final ByteBuffer destination, final ByteBuffer source) {
int cnt = count >= 0? Math.min(Math.min(count, source.remaining()), destination.remaining()):
Math.max(Math.max(count, - source.remaining()), - destination.remaining());
final ByteBuffer copy = slice(source, cnt);
destination.put(copy);
return copy.position(); // cnt could be negative, so it is safer to return copy.position() instead of cnt
}
/**
* Copy at most {@code count} bytes from {@code sources} into {@code destinations} in a "scatter" fashion.
*
* @param count the maximum number of bytes to copy
* @param destinations the destination buffers
* @param offset the offset into the destination buffers array
* @param length the number of buffers to update
* @param source the source buffer
* @return the number of bytes put into the destination buffers
*/
public static int copy(int count, final ByteBuffer[] destinations, final int offset, final int length, final ByteBuffer source) {
if (source.remaining() > count) {
final int oldLimit = source.limit();
if (count < 0) {
// count from end (count is NEGATIVE)
throw msg.copyNegative();
} else {
try {
source.limit(source.position() + count);
return copy(destinations, offset, length, source);
} finally {
source.limit(oldLimit);
}
}
} else {
return copy(destinations, offset, length, source);
}
}
/**
* Copy at most {@code count} bytes from {@code sources} into {@code destination} in a "gather" fashion.
*
* @param count the maximum number of bytes to copy
* @param destination the destination buffer
* @param sources the source buffers
* @param offset the offset into the source buffers array
* @param length the number of buffers to read from
* @return the number of bytes put into the destination buffers
*/
public static int copy(int count, final ByteBuffer destination, final ByteBuffer[] sources, final int offset, final int length) {
if (destination.remaining() > count) {
if (count < 0) {
// count from end (count is NEGATIVE)
throw msg.copyNegative();
} else {
final int oldLimit = destination.limit();
try {
destination.limit(destination.position() + Math.min(count, destination.remaining()));
return copy(destination, sources, offset, length);
} finally {
destination.limit(oldLimit);
}
}
} else {
return copy(destination, sources, offset, length);
}
}
/**
* Copy at most {@code count} bytes from {@code sources} into {@code destinations} by a combined "scatter"/"gather" operation.
*
* @param count the maximum number of bytes to copy
* @param destinations the destination buffers
* @param destOffset the offset into the destination buffers array
* @param destLength the number of buffers to write to
* @param sources the source buffers
* @param srcOffset the offset into the source buffers array
* @param srcLength the number of buffers to read from
* @return the number of bytes put into the destination buffers
*/
public static long copy(long count, final ByteBuffer[] destinations, final int destOffset, final int destLength, final ByteBuffer[] sources, final int srcOffset, final int srcLength) {
long t = 0L;
int s = 0, d = 0;
if (count < 0) {
// count from end (count is NEGATIVE)
throw msg.copyNegative();
}
if (destLength == 0 || srcLength == 0 || count == 0L) {
return 0L;
}
while (s < srcLength && d < destLength) {
final ByteBuffer source = sources[srcOffset + s];
final ByteBuffer dest = destinations[destOffset + d];
final int sr = source.remaining();
final int dr = (int) Math.min(count, (long) dest.remaining());
if (sr < dr) {
dest.put(source);
s++;
t += sr;
count -= (long)sr;
} else if (sr > dr) {
dest.put(slice(source, dr));
d++;
t += dr;
count -= (long)dr;
} else {
dest.put(source);
s++;
d++;
t += sr;
count -= (long)sr;
}
}
return t;
}
/**
* Fill a buffer with a repeated value.
*
* @param buffer the buffer to fill
* @param value the value to fill
* @param count the number of bytes to fill
* @return the buffer instance
*/
public static ByteBuffer fill(ByteBuffer buffer, int value, int count) {
if (count > buffer.remaining()) {
throw msg.bufferUnderflow();
}
if (buffer.hasArray()) {
final int offs = buffer.arrayOffset();
Arrays.fill(buffer.array(), offs + buffer.position(), offs + buffer.limit(), (byte) value);
skip(buffer, count);
} else {
for (int i = count; i > 0; i--) {
buffer.put((byte)value);
}
}
return buffer;
}
/**
* Slice the buffer. The original buffer's position will be moved up past the slice that was taken.
*
* @see CharBuffer#slice()
* @param buffer the buffer to slice
* @param sliceSize the size of the slice
* @return the buffer slice
*/
public static CharBuffer slice(CharBuffer buffer, int sliceSize) {
if (sliceSize > buffer.remaining() || sliceSize < -buffer.remaining()) {
throw msg.bufferUnderflow();
}
final int oldPos = buffer.position();
final int oldLim = buffer.limit();
if (sliceSize < 0) {
// count from end (sliceSize is NEGATIVE)
buffer.limit(oldLim + sliceSize);
try {
return buffer.slice();
} finally {
buffer.limit(oldLim);
buffer.position(oldLim + sliceSize);
}
} else {
// count from start
buffer.limit(oldPos + sliceSize);
try {
return buffer.slice();
} finally {
buffer.limit(oldLim);
buffer.position(oldPos + sliceSize);
}
}
}
/**
* Fill a buffer with a repeated value.
*
* @param buffer the buffer to fill
* @param value the value to fill
* @param count the number of chars to fill
* @return the buffer instance
*/
public static CharBuffer fill(CharBuffer buffer, int value, int count) {
if (count > buffer.remaining()) {
throw msg.bufferUnderflow();
}
if (buffer.hasArray()) {
final int offs = buffer.arrayOffset();
Arrays.fill(buffer.array(), offs + buffer.position(), offs + buffer.limit(), (char) value);
skip(buffer, count);
} else {
for (int i = count; i > 0; i--) {
buffer.put((char)value);
}
}
return buffer;
}
/**
* Slice the buffer. The original buffer's position will be moved up past the slice that was taken.
*
* @see ShortBuffer#slice()
* @param buffer the buffer to slice
* @param sliceSize the size of the slice
* @return the buffer slice
*/
public static ShortBuffer slice(ShortBuffer buffer, int sliceSize) {
if (sliceSize > buffer.remaining() || sliceSize < -buffer.remaining()) {
throw msg.bufferUnderflow();
}
final int oldPos = buffer.position();
final int oldLim = buffer.limit();
if (sliceSize < 0) {
// count from end (sliceSize is NEGATIVE)
buffer.limit(oldLim + sliceSize);
try {
return buffer.slice();
} finally {
buffer.limit(oldLim);
buffer.position(oldLim + sliceSize);
}
} else {
// count from start
buffer.limit(oldPos + sliceSize);
try {
return buffer.slice();
} finally {
buffer.limit(oldLim);
buffer.position(oldPos + sliceSize);
}
}
}
/**
* Fill a buffer with a repeated value.
*
* @param buffer the buffer to fill
* @param value the value to fill
* @param count the number of shorts to fill
* @return the buffer instance
*/
public static ShortBuffer fill(ShortBuffer buffer, int value, int count) {
if (count > buffer.remaining()) {
throw msg.bufferUnderflow();
}
if (buffer.hasArray()) {
final int offs = buffer.arrayOffset();
Arrays.fill(buffer.array(), offs + buffer.position(), offs + buffer.limit(), (short) value);
skip(buffer, count);
} else {
for (int i = count; i > 0; i--) {
buffer.put((short)value);
}
}
return buffer;
}
/**
* Slice the buffer. The original buffer's position will be moved up past the slice that was taken.
*
* @see IntBuffer#slice()
* @param buffer the buffer to slice
* @param sliceSize the size of the slice
* @return the buffer slice
*/
public static IntBuffer slice(IntBuffer buffer, int sliceSize) {
if (sliceSize > buffer.remaining() || sliceSize < -buffer.remaining()) {
throw msg.bufferUnderflow();
}
final int oldPos = buffer.position();
final int oldLim = buffer.limit();
if (sliceSize < 0) {
// count from end (sliceSize is NEGATIVE)
buffer.limit(oldLim + sliceSize);
try {
return buffer.slice();
} finally {
buffer.limit(oldLim);
buffer.position(oldLim + sliceSize);
}
} else {
// count from start
buffer.limit(oldPos + sliceSize);
try {
return buffer.slice();
} finally {
buffer.limit(oldLim);
buffer.position(oldPos + sliceSize);
}
}
}
/**
* Fill a buffer with a repeated value.
*
* @param buffer the buffer to fill
* @param value the value to fill
* @param count the number of ints to fill
* @return the buffer instance
*/
public static IntBuffer fill(IntBuffer buffer, int value, int count) {
if (count > buffer.remaining()) {
throw msg.bufferUnderflow();
}
if (buffer.hasArray()) {
final int offs = buffer.arrayOffset();
Arrays.fill(buffer.array(), offs + buffer.position(), offs + buffer.limit(), value);
skip(buffer, count);
} else {
for (int i = count; i > 0; i--) {
buffer.put(value);
}
}
return buffer;
}
/**
* Slice the buffer. The original buffer's position will be moved up past the slice that was taken.
*
* @see LongBuffer#slice()
* @param buffer the buffer to slice
* @param sliceSize the size of the slice
* @return the buffer slice
*/
public static LongBuffer slice(LongBuffer buffer, int sliceSize) {
if (sliceSize > buffer.remaining() || sliceSize < -buffer.remaining()) {
throw msg.bufferUnderflow();
}
final int oldPos = buffer.position();
final int oldLim = buffer.limit();
if (sliceSize < 0) {
// count from end (sliceSize is NEGATIVE)
buffer.limit(oldLim + sliceSize);
try {
return buffer.slice();
} finally {
buffer.limit(oldLim);
buffer.position(oldLim + sliceSize);
}
} else {
// count from start
buffer.limit(oldPos + sliceSize);
try {
return buffer.slice();
} finally {
buffer.limit(oldLim);
buffer.position(oldPos + sliceSize);
}
}
}
/**
* Fill a buffer with a repeated value.
*
* @param buffer the buffer to fill
* @param value the value to fill
* @param count the number of longs to fill
* @return the buffer instance
*/
public static LongBuffer fill(LongBuffer buffer, long value, int count) {
if (count > buffer.remaining()) {
throw msg.bufferUnderflow();
}
if (buffer.hasArray()) {
final int offs = buffer.arrayOffset();
Arrays.fill(buffer.array(), offs + buffer.position(), offs + buffer.limit(), value);
skip(buffer, count);
} else {
for (int i = count; i > 0; i--) {
buffer.put(value);
}
}
return buffer;
}
/**
* Advance a buffer's position relative to its current position.
*
* @see Buffer#position(int)
* @param the buffer type
* @param buffer the buffer to set
* @param cnt the distance to skip
* @return the buffer instance
* @throws BufferUnderflowException if there are fewer than {@code cnt} bytes remaining
*/
public static T skip(T buffer, int cnt) throws BufferUnderflowException {
if (cnt < 0) {
throw msg.parameterOutOfRange("cnt");
}
if (cnt > buffer.remaining()) {
throw msg.bufferUnderflow();
}
buffer.position(buffer.position() + cnt);
return buffer;
}
/**
* Attempt to advance a buffer's position relative to its current position.
*
* @see Buffer#position(int)
* @param buffer the buffer to set
* @param cnt the distance to skip
* @return the actual number of bytes skipped
*/
public static int trySkip(Buffer buffer, int cnt) {
if (cnt < 0) {
throw msg.parameterOutOfRange("cnt");
}
final int rem = buffer.remaining();
if (cnt > rem) {
cnt = rem;
}
buffer.position(buffer.position() + cnt);
return cnt;
}
/**
* Attempt to advance a series of buffers' overall position relative to its current position.
*
* @see Buffer#position(int)
* @param buffers the buffers to set
* @param offs the offset into the buffers array
* @param len the number of buffers to consider
* @param cnt the distance to skip
* @return the actual number of bytes skipped
*/
public static long trySkip(Buffer[] buffers, int offs, int len, long cnt) {
if (cnt < 0L) {
throw msg.parameterOutOfRange("cnt");
}
if (len < 0) {
throw msg.parameterOutOfRange("len");
}
if (offs < 0) {
throw msg.parameterOutOfRange("offs");
}
if (offs > buffers.length) {
throw msg.parameterOutOfRange("offs");
}
if (offs + len > buffers.length) {
throw msg.parameterOutOfRange("offs");
}
long c = 0L;
for (int i = 0; i < len; i ++) {
final Buffer buffer = buffers[offs + i];
final int rem = buffer.remaining();
if (rem < cnt) {
buffer.position(buffer.position() + rem);
cnt -= (long) rem;
c += (long) rem;
} else {
buffer.position(buffer.position() + (int) cnt);
return c + cnt;
}
}
return c;
}
/**
* Rewind a buffer's position relative to its current position.
*
* @see Buffer#position(int)
* @param the buffer type
* @param buffer the buffer to set
* @param cnt the distance to skip backwards
* @return the buffer instance
*/
public static T unget(T buffer, int cnt) {
if (cnt < 0) {
throw msg.parameterOutOfRange("cnt");
}
if (cnt > buffer.position()) {
throw msg.bufferUnderflow();
}
buffer.position(buffer.position() - cnt);
return buffer;
}
/**
* Take a certain number of bytes from the buffer and return them in an array.
*
* @param buffer the buffer to read
* @param cnt the number of bytes to take
* @return the bytes
*/
public static byte[] take(ByteBuffer buffer, int cnt) {
if (cnt < 0) {
throw msg.parameterOutOfRange("cnt");
}
if (buffer.hasArray()) {
final int pos = buffer.position();
final int lim = buffer.limit();
if (lim - pos < cnt) {
throw new BufferUnderflowException();
}
final byte[] array = buffer.array();
final int offset = buffer.arrayOffset();
buffer.position(pos + cnt);
final int start = offset + pos;
return Arrays.copyOfRange(array, start, start + cnt);
}
final byte[] bytes = new byte[cnt];
buffer.get(bytes);
return bytes;
}
/**
* Take a certain number of chars from the buffer and return them in an array.
*
* @param buffer the buffer to read
* @param cnt the number of chars to take
* @return the chars
*/
public static char[] take(CharBuffer buffer, int cnt) {
if (cnt < 0) {
throw msg.parameterOutOfRange("cnt");
}
if (buffer.hasArray()) {
final int pos = buffer.position();
final int lim = buffer.limit();
if (lim - pos < cnt) {
throw new BufferUnderflowException();
}
final char[] array = buffer.array();
final int offset = buffer.arrayOffset();
buffer.position(pos + cnt);
final int start = offset + pos;
return Arrays.copyOfRange(array, start, start + cnt);
}
final char[] chars = new char[cnt];
buffer.get(chars);
return chars;
}
/**
* Take a certain number of shorts from the buffer and return them in an array.
*
* @param buffer the buffer to read
* @param cnt the number of shorts to take
* @return the shorts
*/
public static short[] take(ShortBuffer buffer, int cnt) {
if (cnt < 0) {
throw msg.parameterOutOfRange("cnt");
}
if (buffer.hasArray()) {
final int pos = buffer.position();
final int lim = buffer.limit();
if (lim - pos < cnt) {
throw new BufferUnderflowException();
}
final short[] array = buffer.array();
final int offset = buffer.arrayOffset();
buffer.position(pos + cnt);
final int start = offset + pos;
return Arrays.copyOfRange(array, start, start + cnt);
}
final short[] shorts = new short[cnt];
buffer.get(shorts);
return shorts;
}
/**
* Take a certain number of ints from the buffer and return them in an array.
*
* @param buffer the buffer to read
* @param cnt the number of ints to take
* @return the ints
*/
public static int[] take(IntBuffer buffer, int cnt) {
if (cnt < 0) {
throw msg.parameterOutOfRange("cnt");
}
if (buffer.hasArray()) {
final int pos = buffer.position();
final int lim = buffer.limit();
if (lim - pos < cnt) {
throw new BufferUnderflowException();
}
final int[] array = buffer.array();
final int offset = buffer.arrayOffset();
buffer.position(pos + cnt);
final int start = offset + pos;
return Arrays.copyOfRange(array, start, start + cnt);
}
final int[] ints = new int[cnt];
buffer.get(ints);
return ints;
}
/**
* Take a certain number of longs from the buffer and return them in an array.
*
* @param buffer the buffer to read
* @param cnt the number of longs to take
* @return the longs
*/
public static long[] take(LongBuffer buffer, int cnt) {
if (cnt < 0) {
throw msg.parameterOutOfRange("cnt");
}
if (buffer.hasArray()) {
final int pos = buffer.position();
final int lim = buffer.limit();
if (lim - pos < cnt) {
throw new BufferUnderflowException();
}
final long[] array = buffer.array();
final int offset = buffer.arrayOffset();
buffer.position(pos + cnt);
final int start = offset + pos;
return Arrays.copyOfRange(array, start, start + cnt);
}
final long[] longs = new long[cnt];
buffer.get(longs);
return longs;
}
private static final byte[] NO_BYTES = new byte[0];
/**
* Take all of the remaining bytes from the buffer and return them in an array.
*
* @param buffer the buffer to read
* @return the bytes
*/
public static byte[] take(ByteBuffer buffer) {
final int remaining = buffer.remaining();
if (remaining == 0) return NO_BYTES;
if (buffer.hasArray()) {
final int pos = buffer.position();
final int lim = buffer.limit();
final byte[] array = buffer.array();
final int offset = buffer.arrayOffset();
buffer.position(lim);
return Arrays.copyOfRange(array, offset + pos, offset + lim);
}
final byte[] bytes = new byte[remaining];
buffer.get(bytes);
return bytes;
}
/**
* Take all of the remaining bytes from the buffers and return them in an array.
*
* @param buffers the buffer to read
* @param offs the offset into the array
* @param len the number of buffers
* @return the bytes
*/
public static byte[] take(final ByteBuffer[] buffers, final int offs, final int len) {
if (len == 1) return take(buffers[offs]);
final long remaining = Buffers.remaining(buffers, offs, len);
if (remaining == 0L) return NO_BYTES;
if (remaining > Integer.MAX_VALUE) throw new OutOfMemoryError("Array too large");
final byte[] bytes = new byte[(int) remaining];
int o = 0;
int rem;
ByteBuffer buffer;
for (int i = 0; i < len; i ++) {
buffer = buffers[i + offs];
rem = buffer.remaining();
buffer.get(bytes, o, rem);
o += rem;
}
return bytes;
}
/**
* Take all of the remaining chars from the buffer and return them in an array.
*
* @param buffer the buffer to read
* @return the chars
*/
public static char[] take(CharBuffer buffer) {
final char[] chars = new char[buffer.remaining()];
buffer.get(chars);
return chars;
}
/**
* Take all of the remaining shorts from the buffer and return them in an array.
*
* @param buffer the buffer to read
* @return the shorts
*/
public static short[] take(ShortBuffer buffer) {
final short[] shorts = new short[buffer.remaining()];
buffer.get(shorts);
return shorts;
}
/**
* Take all of the remaining ints from the buffer and return them in an array.
*
* @param buffer the buffer to read
* @return the ints
*/
public static int[] take(IntBuffer buffer) {
final int[] ints = new int[buffer.remaining()];
buffer.get(ints);
return ints;
}
/**
* Take all of the remaining longs from the buffer and return them in an array.
*
* @param buffer the buffer to read
* @return the longs
*/
public static long[] take(LongBuffer buffer) {
final long[] longs = new long[buffer.remaining()];
buffer.get(longs);
return longs;
}
/**
* Create an object that returns the dumped form of the given byte buffer when its {@code toString()} method is called.
* Useful for logging byte buffers; if the {@code toString()} method is never called, the process of dumping the
* buffer is never performed.
*
* @param buffer the buffer
* @param indent the indentation to use
* @param columns the number of 8-byte columns
* @return a stringable object
*/
public static Object createDumper(final ByteBuffer buffer, final int indent, final int columns) {
if (columns <= 0) {
throw msg.parameterOutOfRange("columns");
}
if (indent < 0) {
throw msg.parameterOutOfRange("indent");
}
return new Object() {
public String toString() {
StringBuilder b = new StringBuilder();
try {
dump(buffer, b, indent, columns);
} catch (IOException e) {
// ignore, not possible!
}
return b.toString();
}
};
}
/**
* Dump a byte buffer to the given target.
*
* @param buffer the buffer
* @param dest the target
* @param indent the indentation to use
* @param columns the number of 8-byte columns
* @throws IOException if an error occurs during append
*/
public static void dump(final ByteBuffer buffer, final Appendable dest, final int indent, final int columns) throws IOException {
if (columns <= 0) {
throw msg.parameterOutOfRange("columns");
}
if (indent < 0) {
throw msg.parameterOutOfRange("indent");
}
final int pos = buffer.position();
final int remaining = buffer.remaining();
final int rowLength = (8 << (columns - 1));
final int n = Math.max(Integer.toString(buffer.remaining(), 16).length(), 4);
for (int idx = 0; idx < remaining; idx += rowLength) {
// state: start of line
for (int i = 0; i < indent; i ++) {
dest.append(' ');
}
final String s = Integer.toString(idx, 16);
for (int i = n - s.length(); i > 0; i --) {
dest.append('0');
}
dest.append(s);
dest.append(" - ");
appendHexRow(buffer, dest, pos + idx, columns);
appendTextRow(buffer, dest, pos + idx, columns);
dest.append('\n');
}
}
private static void appendHexRow(final ByteBuffer buffer, final Appendable dest, final int startPos, final int columns) throws IOException {
final int limit = buffer.limit();
int pos = startPos;
for (int c = 0; c < columns; c ++) {
for (int i = 0; i < 8; i ++) {
if (pos >= limit) {
dest.append(" ");
} else {
final int v = buffer.get(pos++) & 0xff;
final String hexVal = Integer.toString(v, 16);
if (v < 16) {
dest.append('0');
}
dest.append(hexVal);
}
dest.append(' ');
}
dest.append(' ');
dest.append(' ');
}
}
private static void appendTextRow(final ByteBuffer buffer, final Appendable dest, final int startPos, final int columns) throws IOException {
final int limit = buffer.limit();
int pos = startPos;
dest.append('[');
dest.append(' ');
for (int c = 0; c < columns; c ++) {
for (int i = 0; i < 8; i ++) {
if (pos >= limit) {
dest.append(' ');
} else {
final char v = (char) (buffer.get(pos++) & 0xff);
if (Character.isISOControl(v)) {
dest.append('.');
} else {
dest.append(v);
}
}
}
dest.append(' ');
}
dest.append(']');
}
/**
* Create an object that returns the dumped form of the given character buffer when its {@code toString()} method is called.
* Useful for logging character buffers; if the {@code toString()} method is never called, the process of dumping the
* buffer is never performed.
*
* @param buffer the buffer
* @param indent the indentation to use
* @param columns the number of 8-byte columns
* @return a stringable object
*/
public static Object createDumper(final CharBuffer buffer, final int indent, final int columns) {
if (columns <= 0) {
throw msg.parameterOutOfRange("columns");
}
if (indent < 0) {
throw msg.parameterOutOfRange("indent");
}
return new Object() {
public String toString() {
StringBuilder b = new StringBuilder();
try {
dump(buffer, b, indent, columns);
} catch (IOException e) {
// ignore, not possible!
}
return b.toString();
}
};
}
/**
* Dump a character buffer to the given target.
*
* @param buffer the buffer
* @param dest the target
* @param indent the indentation to use
* @param columns the number of 8-byte columns
* @throws IOException if an error occurs during append
*/
public static void dump(final CharBuffer buffer, final Appendable dest, final int indent, final int columns) throws IOException {
if (columns <= 0) {
throw msg.parameterOutOfRange("columns");
}
if (indent < 0) {
throw msg.parameterOutOfRange("indent");
}
final int pos = buffer.position();
final int remaining = buffer.remaining();
final int rowLength = (8 << (columns - 1));
final int n = Math.max(Integer.toString(buffer.remaining(), 16).length(), 4);
for (int idx = 0; idx < remaining; idx += rowLength) {
// state: start of line
for (int i = 0; i < indent; i ++) {
dest.append(' ');
}
final String s = Integer.toString(idx, 16);
for (int i = n - s.length(); i > 0; i --) {
dest.append('0');
}
dest.append(s);
dest.append(" - ");
appendHexRow(buffer, dest, pos + idx, columns);
appendTextRow(buffer, dest, pos + idx, columns);
dest.append('\n');
}
}
private static void appendHexRow(final CharBuffer buffer, final Appendable dest, final int startPos, final int columns) throws IOException {
final int limit = buffer.limit();
int pos = startPos;
for (int c = 0; c < columns; c ++) {
for (int i = 0; i < 8; i ++) {
if (pos >= limit) {
dest.append(" ");
} else {
final char v = buffer.get(pos++);
final String hexVal = Integer.toString(v, 16);
dest.append("0000".substring(hexVal.length()));
dest.append(hexVal);
}
dest.append(' ');
}
dest.append(' ');
dest.append(' ');
}
}
private static void appendTextRow(final CharBuffer buffer, final Appendable dest, final int startPos, final int columns) throws IOException {
final int limit = buffer.limit();
int pos = startPos;
dest.append('[');
dest.append(' ');
for (int c = 0; c < columns; c ++) {
for (int i = 0; i < 8; i ++) {
if (pos >= limit) {
dest.append(' ');
} else {
final char v = buffer.get(pos++);
if (Character.isISOControl(v) || Character.isHighSurrogate(v) || Character.isLowSurrogate(v)) {
dest.append('.');
} else {
dest.append(v);
}
}
}
dest.append(' ');
}
dest.append(']');
}
/**
* The empty byte buffer.
*/
public static final ByteBuffer EMPTY_BYTE_BUFFER = ByteBuffer.allocate(0);
/**
* The empty pooled byte buffer. Freeing or discarding this buffer has no effect.
*/
public static final Pooled EMPTY_POOLED_BYTE_BUFFER = emptyPooledByteBuffer();
/**
* Determine whether any of the buffers has remaining data.
*
* @param buffers the buffers
* @param offs the offset into the buffers array
* @param len the number of buffers to check
* @return {@code true} if any of the selected buffers has remaining data
*/
public static boolean hasRemaining(final Buffer[] buffers, final int offs, final int len) {
for (int i = 0; i < len; i ++) {
if (buffers[i + offs].hasRemaining()) {
return true;
}
}
return false;
}
/**
* Determine whether any of the buffers has remaining data.
*
* @param buffers the buffers
* @return {@code true} if any of the selected buffers has remaining data
*/
public static boolean hasRemaining(final Buffer[] buffers) {
return hasRemaining(buffers, 0, buffers.length);
}
/**
* Get the total remaining size of all the given buffers.
*
* @param buffers the buffers
* @param offs the offset into the buffers array
* @param len the number of buffers to check
* @return the number of remaining elements
*/
public static long remaining(final Buffer[] buffers, final int offs, final int len) {
long t = 0L;
for (int i = 0; i < len; i ++) {
t += buffers[i + offs].remaining();
}
return t;
}
/**
* Get the total remaining size of all the given buffers.
*
* @param buffers the buffers
* @return the number of remaining elements
*/
public static long remaining(final Buffer[] buffers) {
return remaining(buffers, 0, buffers.length);
}
/**
* Put the string into the byte buffer, encoding it using "modified UTF-8" encoding.
*
* @param dest the byte buffer
* @param orig the source bytes
* @return the byte buffer
* @throws BufferOverflowException if there is not enough space in the buffer for the complete string
* @see DataOutput#writeUTF(String)
*/
public static ByteBuffer putModifiedUtf8(ByteBuffer dest, String orig) throws BufferOverflowException {
final char[] chars = orig.toCharArray();
for (char c : chars) {
if (c > 0 && c <= 0x7f) {
dest.put((byte) c);
} else if (c <= 0x07ff) {
dest.put((byte)(0xc0 | 0x1f & c >> 6));
dest.put((byte)(0x80 | 0x3f & c));
} else {
dest.put((byte)(0xe0 | 0x0f & c >> 12));
dest.put((byte)(0x80 | 0x3f & c >> 6));
dest.put((byte)(0x80 | 0x3f & c));
}
}
return dest;
}
/**
* Get a 0-terminated string from the byte buffer, decoding it using "modified UTF-8" encoding.
*
* @param src the source buffer
* @return the string
* @throws BufferUnderflowException if the end of the buffer was reached before encountering a {@code 0}
*/
public static String getModifiedUtf8Z(ByteBuffer src) throws BufferUnderflowException {
final StringBuilder builder = new StringBuilder();
for (;;) {
final int ch = readUTFChar(src);
if (ch == -1) {
return builder.toString();
}
builder.append((char) ch);
}
}
/**
* Get a modified UTF-8 string from the remainder of the buffer.
*
* @param src the buffer
* @return the modified UTF-8 string
* @throws BufferUnderflowException if the buffer ends abruptly in the midst of a single character
*/
public static String getModifiedUtf8(ByteBuffer src) throws BufferUnderflowException {
final StringBuilder builder = new StringBuilder();
while (src.hasRemaining()) {
final int ch = readUTFChar(src);
if (ch == -1) {
builder.append('\0');
} else {
builder.append((char) ch);
}
}
return builder.toString();
}
private static int readUTFChar(final ByteBuffer src) throws BufferUnderflowException {
final int a = src.get() & 0xff;
if (a == 0) {
return -1;
} else if (a < 0x80) {
return (char)a;
} else if (a < 0xc0) {
return '?';
} else if (a < 0xe0) {
final int b = src.get() & 0xff;
if ((b & 0xc0) != 0x80) {
return '?';
}
return (a & 0x1f) << 6 | b & 0x3f;
} else if (a < 0xf0) {
final int b = src.get() & 0xff;
if ((b & 0xc0) != 0x80) {
return '?';
}
final int c = src.get() & 0xff;
if ((c & 0xc0) != 0x80) {
return '?';
}
return (a & 0x0f) << 12 | (b & 0x3f) << 6 | c & 0x3f;
}
return '?';
}
/**
* Read an ASCIIZ ({@code NUL}-terminated) string from a byte buffer, appending the results to the given string
* builder. If no {@code NUL} character is encountered, {@code false} is returned, indicating that more data needs
* to be acquired before the operation can be complete. On return, there may be data remaining
* in the source buffer. If an invalid byte is read, the character {@code '?'} is written
* to the string builder in its place.
*
* @param src the source buffer
* @param builder the destination builder
* @return {@code true} if the entire string was read, {@code false} if more data is needed
*/
public static boolean readAsciiZ(final ByteBuffer src, final StringBuilder builder) {
return readAsciiZ(src, builder, '?');
}
/**
* Read an ASCIIZ ({@code NUL}-terminated) string from a byte buffer, appending the results to the given string
* builder. If no {@code NUL} character is encountered, {@code false} is returned, indicating that more data needs
* to be acquired before the operation can be complete. On return, there may be data remaining
* in the source buffer. If an invalid byte is read, the character designated by {@code replacement} is written
* to the string builder in its place.
*
* @param src the source buffer
* @param builder the destination builder
* @param replacement the replacement character for invalid bytes
* @return {@code true} if the entire string was read, {@code false} if more data is needed
*/
public static boolean readAsciiZ(final ByteBuffer src, final StringBuilder builder, final char replacement) {
for (;;) {
if (! src.hasRemaining()) {
return false;
}
final byte b = src.get();
if (b == 0) {
return true;
}
builder.append(b < 0 ? replacement : (char) b);
}
}
/**
* Read a single line of ASCII text from a byte buffer, appending the results to the given string
* builder. If no {@code EOL} character is encountered, {@code false} is returned, indicating that more data needs
* to be acquired before the operation can be complete. On return, there may be data remaining
* in the source buffer. If an invalid byte is read, the character {@code '?'} is written
* to the string builder in its place. The {@code EOL} character will be included in the resultant string.
*
* @param src the source buffer
* @param builder the destination builder
* @return {@code true} if the entire string was read, {@code false} if more data is needed
*/
public static boolean readAsciiLine(final ByteBuffer src, final StringBuilder builder) {
return readAsciiLine(src, builder, '?', '\n');
}
/**
* Read a single line of ASCII text from a byte buffer, appending the results to the given string
* builder. If no {@code EOL} character is encountered, {@code false} is returned, indicating that more data needs
* to be acquired before the operation can be complete. On return, there may be data remaining
* in the source buffer. If an invalid byte is read, the character designated by {@code replacement} is written
* to the string builder in its place. The {@code EOL} character will be included in the resultant string.
*
* @param src the source buffer
* @param builder the destination builder
* @param replacement the replacement character for invalid bytes
* @return {@code true} if the entire string was read, {@code false} if more data is needed
*/
public static boolean readAsciiLine(final ByteBuffer src, final StringBuilder builder, final char replacement) {
return readAsciiLine(src, builder, replacement, '\n');
}
/**
* Read a single line of ASCII text from a byte buffer, appending the results to the given string
* builder, using the given delimiter character instead of {@code EOL}. If no delimiter character is encountered,
* {@code false} is returned, indicating that more data needs
* to be acquired before the operation can be complete. On return, there may be data remaining
* in the source buffer. If an invalid byte is read, the character designated by {@code replacement} is written
* to the string builder in its place. The delimiter character will be included in the resultant string.
*
* @param src the source buffer
* @param builder the destination builder
* @param replacement the replacement character for invalid bytes
* @param delimiter the character which marks the end of the line
* @return {@code true} if the entire string was read, {@code false} if more data is needed
*/
public static boolean readAsciiLine(final ByteBuffer src, final StringBuilder builder, final char replacement, final char delimiter) {
for (;;) {
if (! src.hasRemaining()) {
return false;
}
final byte b = src.get();
builder.append(b < 0 ? replacement : (char) b);
if (b == delimiter) {
return true;
}
}
}
/**
* Read the remainder of a buffer as ASCII text, appending the results to the given string
* builder. If an invalid byte is read, the character {@code '?'} is written
* to the string builder in its place.
*
* @param src the source buffer
* @param builder the destination builder
*/
public static void readAscii(final ByteBuffer src, final StringBuilder builder) {
readAscii(src, builder, '?');
}
/**
* Read the remainder of a buffer as ASCII text, appending the results to the given string
* builder. If an invalid byte is read, the character designated by {@code replacement} is written
* to the string builder in its place.
*
* @param src the source buffer
* @param builder the destination builder
* @param replacement the replacement character for invalid bytes
*/
public static void readAscii(final ByteBuffer src, final StringBuilder builder, final char replacement) {
for (;;) {
if (! src.hasRemaining()) {
return;
}
final byte b = src.get();
builder.append(b < 0 ? replacement : (char) b);
}
}
/**
* Read the remainder of a buffer as ASCII text, up to a certain limit, appending the results to the given string
* builder. If an invalid byte is read, the character designated by {@code replacement} is written
* to the string builder in its place.
*
* @param src the source buffer
* @param builder the destination builder
* @param limit the maximum number of characters to write
* @param replacement the replacement character for invalid bytes
*/
public static void readAscii(final ByteBuffer src, final StringBuilder builder, int limit, final char replacement) {
while (limit > 0) {
if (! src.hasRemaining()) {
return;
}
final byte b = src.get();
builder.append(b < 0 ? replacement : (char) b);
limit--;
}
}
/**
* Read a {@code NUL}-terminated Latin-1 string from a byte buffer, appending the results to the given string
* builder. If no {@code NUL} character is encountered, {@code false} is returned, indicating that more data needs
* to be acquired before the operation can be complete. On return, there may be data remaining
* in the source buffer.
*
* @param src the source buffer
* @param builder the destination builder
* @return {@code true} if the entire string was read, {@code false} if more data is needed
*/
public static boolean readLatin1Z(final ByteBuffer src, final StringBuilder builder) {
for (;;) {
if (! src.hasRemaining()) {
return false;
}
final byte b = src.get();
if (b == 0) {
return true;
}
builder.append((char) (b & 0xff));
}
}
/**
* Read a single line of Latin-1 text from a byte buffer, appending the results to the given string
* builder. If no {@code EOL} character is encountered, {@code false} is returned, indicating that more data needs
* to be acquired before the operation can be complete. On return, there may be data remaining
* in the source buffer. The {@code EOL} character will be included in the resultant string.
*
* @param src the source buffer
* @param builder the destination builder
* @return {@code true} if the entire string was read, {@code false} if more data is needed
*/
public static boolean readLatin1Line(final ByteBuffer src, final StringBuilder builder) {
for (;;) {
if (! src.hasRemaining()) {
return false;
}
final byte b = src.get();
builder.append((char) (b & 0xff));
if (b == '\n') {
return true;
}
}
}
/**
* Read a single line of Latin-1 text from a byte buffer, appending the results to the given string
* builder. If no delimiter character is encountered, {@code false} is returned, indicating that more data needs
* to be acquired before the operation can be complete. On return, there may be data remaining
* in the source buffer. The delimiter character will be included in the resultant string.
*
* @param src the source buffer
* @param builder the destination builder
* @param delimiter the character which marks the end of the line
* @return {@code true} if the entire string was read, {@code false} if more data is needed
*/
public static boolean readLatin1Line(final ByteBuffer src, final StringBuilder builder, final char delimiter) {
for (;;) {
if (! src.hasRemaining()) {
return false;
}
final byte b = src.get();
builder.append((char) (b & 0xff));
if (b == delimiter) {
return true;
}
}
}
/**
* Read the remainder of a buffer as Latin-1 text, appending the results to the given string
* builder.
*
* @param src the source buffer
* @param builder the destination builder
*/
public static void readLatin1(final ByteBuffer src, final StringBuilder builder) {
for (;;) {
if (! src.hasRemaining()) {
return;
}
final byte b = src.get();
builder.append((char) (b & 0xff));
}
}
/**
* Read a {@code NUL}-terminated {@link DataInput modified UTF-8} string from a byte buffer, appending the results to the given string
* builder. If no {@code NUL} byte is encountered, {@code false} is returned, indicating that more data needs
* to be acquired before the operation can be complete. On return, there may be data remaining
* in the source buffer. If an invalid byte sequence is read, the character {@code '?'} is written
* to the string builder in its place.
*
* @param src the source buffer
* @param builder the destination builder
* @return {@code true} if the entire string was read, {@code false} if more data is needed
*/
public static boolean readModifiedUtf8Z(final ByteBuffer src, final StringBuilder builder) {
return readModifiedUtf8Z(src, builder, '?');
}
/**
* Read a {@code NUL}-terminated {@link DataInput modified UTF-8} string from a byte buffer, appending the results to the given string
* builder. If no {@code NUL} byte is encountered, {@code false} is returned, indicating that more data needs
* to be acquired before the operation can be complete. On return, there may be data remaining
* in the source buffer. If an invalid byte sequence is read, the character designated by {@code replacement} is written
* to the string builder in its place.
*
* @param src the source buffer
* @param builder the destination builder
* @param replacement the replacement character to use
* @return {@code true} if the entire string was read, {@code false} if more data is needed
*/
public static boolean readModifiedUtf8Z(final ByteBuffer src, final StringBuilder builder, final char replacement) {
for (;;) {
if (! src.hasRemaining()) {
return false;
}
final int a = src.get() & 0xff;
if (a == 0) {
return true;
} else if (a < 0x80) {
builder.append((char)a);
} else if (a < 0xc0) {
builder.append(replacement);
} else if (a < 0xe0) {
if (src.hasRemaining()) {
final int b = src.get() & 0xff;
if ((b & 0xc0) != 0x80) {
builder.append(replacement);
} else {
builder.append((char) ((a & 0x1f) << 6 | b & 0x3f));
}
} else {
unget(src, 1);
return false;
}
} else if (a < 0xf0) {
if (src.hasRemaining()) {
final int b = src.get() & 0xff;
if ((b & 0xc0) != 0x80) {
builder.append(replacement);
} else {
if (src.hasRemaining()) {
final int c = src.get() & 0xff;
if ((c & 0xc0) != 0x80) {
builder.append(replacement);
} else {
builder.append((char) ((a & 0x0f) << 12 | (b & 0x3f) << 6 | c & 0x3f));
}
} else {
unget(src, 2);
return false;
}
}
} else {
unget(src, 1);
return false;
}
} else {
builder.append(replacement);
}
}
}
/**
* Read a single line of {@link DataInput modified UTF-8} text from a byte buffer, appending the results to the given string
* builder. If no {@code EOL} character is encountered, {@code false} is returned, indicating that more data needs
* to be acquired before the operation can be complete. On return, there may be data remaining
* in the source buffer. If an invalid byte is read, the character {@code '?'} is written
* to the string builder in its place. The {@code EOL} character will be included in the resultant string.
*
* @param src the source buffer
* @param builder the destination builder
* @return {@code true} if the entire string was read, {@code false} if more data is needed
*/
public static boolean readModifiedUtf8Line(final ByteBuffer src, final StringBuilder builder) {
return readModifiedUtf8Line(src, builder, '?');
}
/**
* Read a single line of {@link DataInput modified UTF-8} text from a byte buffer, appending the results to the given string
* builder. If no {@code EOL} character is encountered, {@code false} is returned, indicating that more data needs
* to be acquired before the operation can be complete. On return, there may be data remaining
* in the source buffer. If an invalid byte is read, the character designated by {@code replacement} is written
* to the string builder in its place. The {@code EOL} character will be included in the resultant string.
*
* @param src the source buffer
* @param builder the destination builder
* @param replacement the replacement character for invalid bytes
* @return {@code true} if the entire string was read, {@code false} if more data is needed
*/
public static boolean readModifiedUtf8Line(final ByteBuffer src, final StringBuilder builder, final char replacement) {
return readModifiedUtf8Line(src, builder, replacement, '\n');
}
/**
* Read a single line of {@link DataInput modified UTF-8} text from a byte buffer, appending the results to the given string
* builder. If no {@code EOL} character is encountered, {@code false} is returned, indicating that more data needs
* to be acquired before the operation can be complete. On return, there may be data remaining
* in the source buffer. If an invalid byte is read, the character designated by {@code replacement} is written
* to the string builder in its place. The delimiter character will be included in the resultant string.
*
* @param src the source buffer
* @param builder the destination builder
* @param replacement the replacement character for invalid bytes
* @param delimiter the character which marks the end of the line
* @return {@code true} if the entire string was read, {@code false} if more data is needed
*/
public static boolean readModifiedUtf8Line(final ByteBuffer src, final StringBuilder builder, final char replacement, final char delimiter) {
for (;;) {
if (! src.hasRemaining()) {
return false;
}
final int a = src.get() & 0xff;
if (a < 0x80) {
builder.append((char)a);
if (a == delimiter) {
return true;
}
} else if (a < 0xc0) {
builder.append(replacement);
} else if (a < 0xe0) {
if (src.hasRemaining()) {
final int b = src.get() & 0xff;
if ((b & 0xc0) != 0x80) {
builder.append(replacement);
} else {
final char ch = (char) ((a & 0x1f) << 6 | b & 0x3f);
builder.append(ch);
if (ch == delimiter) {
return true;
}
}
} else {
unget(src, 1);
return false;
}
} else if (a < 0xf0) {
if (src.hasRemaining()) {
final int b = src.get() & 0xff;
if ((b & 0xc0) != 0x80) {
builder.append(replacement);
} else {
if (src.hasRemaining()) {
final int c = src.get() & 0xff;
if ((c & 0xc0) != 0x80) {
builder.append(replacement);
} else {
final char ch = (char) ((a & 0x0f) << 12 | (b & 0x3f) << 6 | c & 0x3f);
builder.append(ch);
if (ch == delimiter) {
return true;
}
}
} else {
unget(src, 2);
return false;
}
}
} else {
unget(src, 1);
return false;
}
} else {
builder.append(replacement);
}
}
}
/**
* Read a single line of text from a byte buffer, appending the results to the given string
* builder. If no {@code EOL} character is encountered, {@code false} is returned, indicating that more data needs
* to be acquired before the operation can be complete. On return, there may be data remaining
* in the source buffer. Invalid bytes are handled according to the policy specified by the {@code decoder} instance.
* Since this method decodes only one character at a time, it should not be expected to have the same performance
* as the other optimized, character set-specific methods specified in this class.
* The {@code EOL} character will be included in the resultant string.
*
* @param src the source buffer
* @param builder the destination builder
* @param decoder the decoder to use
* @return {@code true} if the entire string was read, {@code false} if more data is needed
*/
public static boolean readLine(final ByteBuffer src, final StringBuilder builder, final CharsetDecoder decoder) {
return readLine(src, builder, decoder, '\n');
}
/**
* Read a single line of text from a byte buffer, appending the results to the given string
* builder. If no delimiter character is encountered, {@code false} is returned, indicating that more data needs
* to be acquired before the operation can be complete. On return, there may be data remaining
* in the source buffer. Invalid bytes are handled according to the policy specified by the {@code decoder} instance.
* Since this method decodes only one character at a time, it should not be expected to have the same performance
* as the other optimized, character set-specific methods specified in this class. The delimiter character will be
* included in the resultant string.
*
* @param src the source buffer
* @param builder the destination builder
* @param decoder the decoder to use
* @param delimiter the character which marks the end of the line
* @return {@code true} if the entire string was read, {@code false} if more data is needed
*/
public static boolean readLine(final ByteBuffer src, final StringBuilder builder, final CharsetDecoder decoder, final char delimiter) {
final CharBuffer oneChar = CharBuffer.allocate(1);
for (;;) {
final CoderResult coderResult = decoder.decode(src, oneChar, false);
if (coderResult.isUnderflow()) {
if (oneChar.hasRemaining()) {
return false;
}
} else if (oneChar.hasRemaining()) {
throw new IllegalStateException();
}
final char ch = oneChar.get(0);
builder.append(ch);
if (ch == delimiter) {
return true;
}
oneChar.clear();
}
}
/**
* Create a pooled wrapper around a buffer. The buffer is unreferenced for garbage collection when
* freed or discarded.
*
* @param buffer the buffer to wrap
* @param the buffer type
* @return the pooled wrapper
*/
public static Pooled pooledWrapper(final B buffer) {
return new Pooled() {
private volatile B buf = buffer;
public void discard() {
buf = null;
}
public void free() {
buf = null;
}
public B getResource() throws IllegalStateException {
final B buffer = buf;
if (buffer == null) {
throw new IllegalStateException();
}
return buffer;
}
public void close() {
free();
}
public String toString() {
return "Pooled wrapper around " + buffer;
}
};
}
/**
* Create a pooled wrapper around a buffer that was allocated via {@link ByteBufferPool}. The buffer is freed to the
* global pool when freed.
*
* @param buffer the buffer to wrap
* @return the pooled wrapper
*/
public static Pooled globalPooledWrapper(final ByteBuffer buffer) {
return new Pooled() {
private volatile ByteBuffer buf = buffer;
public void discard() {
ByteBuffer oldBuf = this.buf;
if (oldBuf == null) return;
final ByteBuffer buf = oldBuf.duplicate();
new CleanerReference(this.buf, null, new Reaper() {
public void reap(final Reference reference) {
// free the duplicate
ByteBufferPool.free(buf);
}
});
this.buf = null;
}
public void free() {
ByteBuffer oldBuf = this.buf;
if (oldBuf == null) return;
ByteBufferPool.free(oldBuf);
buf = null;
}
public ByteBuffer getResource() throws IllegalStateException {
final ByteBuffer buffer = buf;
if (buffer == null) {
throw new IllegalStateException();
}
return buffer;
}
public void close() {
free();
}
public String toString() {
return "Globally pooled wrapper around " + buffer;
}
};
}
/**
* Create a "pooled" empty buffer. Discarding or freeing the buffer has no effect; the returned buffer is
* always empty.
*
* @return a new pooled empty buffer
*/
public static Pooled emptyPooledByteBuffer() {
return new Pooled() {
public void discard() {
}
public void free() {
}
public ByteBuffer getResource() throws IllegalStateException {
return EMPTY_BYTE_BUFFER;
}
public void close() {
}
};
}
/**
* A buffer allocator which allocates slices off of the given buffer. Once the buffer is exhausted, further
* attempts to allocate buffers will result in {@link BufferUnderflowException}.
*
* @param buffer the source buffer
* @return the slice allocator
*/
public static BufferAllocator sliceAllocator(final ByteBuffer buffer) {
return new BufferAllocator() {
public ByteBuffer allocate(final int size) throws IllegalArgumentException {
return Buffers.slice(buffer, size);
}
};
}
/**
* A buffer pool which allocates a new buffer on every allocate request, and discards buffers on free.
*
* @param allocator the buffer allocator
* @param size the buffer size
* @param the buffer type
* @return the buffer pool
*/
public static Pool allocatedBufferPool(final BufferAllocator allocator, final int size) {
return new Pool() {
public Pooled allocate() {
return pooledWrapper(allocator.allocate(size));
}
};
}
/**
* A byte buffer pool which zeroes the content of the buffer before re-pooling it.
*
* @param delegate the delegate pool
* @return the wrapper pool
*/
public static Pool secureBufferPool(final Pool delegate) {
return new SecureByteBufferPool(delegate);
}
/**
* Determine whether the given pool is a secure pool. Note that this test will fail if used on a pool
* which wraps a secure pool.
*
* @param pool the pool to test
* @return {@code true} if it is a secure pool instance
*/
public static boolean isSecureBufferPool(Pool> pool) {
return pool instanceof SecureByteBufferPool;
}
/**
* Zero a buffer. Ensures that any potentially sensitive information in the buffer is
* overwritten.
*
* @param buffer the buffer
*/
public static void zero(ByteBuffer buffer) {
buffer.clear();
while (buffer.remaining() >= 8) {
buffer.putLong(0L);
}
while (buffer.hasRemaining()) {
buffer.put((byte) 0);
}
buffer.clear();
}
/**
* Zero a buffer. Ensures that any potentially sensitive information in the buffer is
* overwritten.
*
* @param buffer the buffer
*/
public static void zero(CharBuffer buffer) {
buffer.clear();
while (buffer.remaining() >= 32) {
buffer.put("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
}
while (buffer.hasRemaining()) {
buffer.put('\0');
}
buffer.clear();
}
/**
* Determine whether the given buffers list is comprised solely of direct buffers or solely of heap buffers.
*
* @param buffers the buffers
* @return {@code true} if all the buffers are direct, {@code false} if they are all heap buffers
* @throws IllegalArgumentException if both direct and heap buffers were found, or if a buffer is {@code null}
*/
public static boolean isDirect(Buffer... buffers) throws IllegalArgumentException {
return isDirect(buffers, 0, buffers.length);
}
/**
* Determine whether the given buffers list is comprised solely of direct buffers or solely of heap buffers.
*
* @param buffers the buffers
* @return {@code true} if all the buffers are direct, {@code false} if they are all heap buffers
* @throws IllegalArgumentException if both direct and heap buffers were found, or if a buffer is {@code null}
*/
public static boolean isDirect(final Buffer[] buffers, final int offset, final int length) {
boolean foundDirect = false;
boolean foundHeap = false;
for (int i = 0; i < length; i ++) {
final Buffer buffer = buffers[i + offset];
if (buffer == null) {
throw msg.nullParameter("buffer");
}
if (buffer.isDirect()) {
if (foundHeap) {
throw msg.mixedDirectAndHeap();
}
foundDirect = true;
} else {
if (foundDirect) {
throw msg.mixedDirectAndHeap();
}
foundHeap = true;
}
}
return foundDirect;
}
/**
* Assert the writability of the given buffers.
*
* @param buffers the buffers array
* @param offs the offset in the array to start searching
* @param len the number of buffers to check
* @throws ReadOnlyBufferException if any of the buffers are read-only
*/
public static void assertWritable(Buffer[] buffers, int offs, int len) throws ReadOnlyBufferException {
for (int i = 0; i < len; i ++) {
if (buffers[i + offs].isReadOnly()) {
throw msg.readOnlyBuffer();
}
}
}
/**
* Assert the writability of the given buffers.
*
* @param buffers the buffers array
* @throws ReadOnlyBufferException if any of the buffers are read-only
*/
public static void assertWritable(Buffer... buffers) throws ReadOnlyBufferException {
assertWritable(buffers, 0, buffers.length);
}
/**
* Add {@code count} bytes of random data to the target buffer.
*
* @param target the target buffer
* @param random the RNG
* @param count the number of bytes to add
*/
public static void addRandom(ByteBuffer target, Random random, int count) {
final byte[] bytes = new byte[count];
random.nextBytes(bytes);
target.put(bytes);
}
/**
* Add {@code count} bytes of random data to the target buffer using the thread-local RNG.
*
* @param target the target buffer
* @param count the number of bytes to add
*/
public static void addRandom(ByteBuffer target, int count) {
addRandom(target, IoUtils.getThreadLocalRandom(), count);
}
/**
* Add a random amount of random data to the target buffer.
*
* @param target the target buffer
* @param random the RNG
*/
public static void addRandom(ByteBuffer target, Random random) {
if (target.remaining() == 0) {
return;
}
addRandom(target, random, random.nextInt(target.remaining()));
}
/**
* Add a random amount of random data to the target buffer using the thread-local RNG.
*
* @param target the target buffer
*/
public static void addRandom(ByteBuffer target) {
addRandom(target, IoUtils.getThreadLocalRandom());
}
/**
* Fill a buffer from an input stream. Specially optimized for heap buffers. If a partial transfer occurs
* due to interruption, the buffer's position is updated accordingly.
*
* @param target the target buffer
* @param source the source stream
* @return the number of bytes transferred, or {@code -1} if no bytes were moved due to end-of-stream
* @throws IOException if the stream read fails
*/
public static int fillFromStream(ByteBuffer target, InputStream source) throws IOException {
final int remaining = target.remaining();
if (remaining == 0) {
return 0;
} else {
final int p = target.position();
if (target.hasArray()) {
// fast path
final int res;
try {
res = source.read(target.array(), p + target.arrayOffset(), remaining);
} catch (InterruptedIOException e) {
target.position(p + e.bytesTransferred);
throw e;
}
if (res > 0) {
target.position(p + res);
}
return res;
} else {
byte[] tmp = new byte[remaining];
final int res;
try {
res = source.read(tmp);
} catch (InterruptedIOException e) {
final int n = e.bytesTransferred;
target.put(tmp, 0, n);
target.position(p + n);
throw e;
}
if (res > 0) {
target.put(tmp, 0, res);
}
return res;
}
}
}
/**
* Get a debug-friendly description of the buffer.
*
* @param buffer the buffer to describe
* @return the string
*/
public static String debugString(ByteBuffer buffer) {
StringBuilder b = new StringBuilder();
b.append("1 buffer of ").append(buffer.remaining()).append(" bytes");
return b.toString();
}
/**
* Get a debug-friendly description of the buffer.
*
* @param buffers the buffers to describe
* @param offs the offset into the array
* @param len the number of buffers
* @return the string
*/
public static String debugString(ByteBuffer[] buffers, int offs, int len) {
StringBuilder b = new StringBuilder();
b.append(len).append(" buffer(s)");
if (len > 0) {
b.append(" of ").append(Buffers.remaining(buffers, offs, len)).append(" bytes");
}
return b.toString();
}
/**
* Empty a buffer to an output stream. Specially optimized for heap buffers. If a partial transfer occurs
* due to interruption, the buffer's position is updated accordingly.
*
* @param target the target stream
* @param source the source buffer
* @throws IOException if the stream write fails
*/
public static void emptyToStream(OutputStream target, ByteBuffer source) throws IOException {
final int remaining = source.remaining();
if (remaining == 0) {
return;
} else {
final int p = source.position();
if (source.hasArray()) {
// fast path
try {
target.write(source.array(), p + source.arrayOffset(), remaining);
} catch (InterruptedIOException e) {
source.position(p + e.bytesTransferred);
throw e;
}
source.position(source.limit());
return;
} else {
byte[] tmp = take(source);
try {
target.write(tmp);
} catch (InterruptedIOException e) {
source.position(p + e.bytesTransferred);
throw e;
} catch (IOException e) {
source.position(p);
throw e;
}
}
}
}
private static class SecureByteBufferPool implements Pool {
private final Pool delegate;
SecureByteBufferPool(final Pool delegate) {
this.delegate = delegate;
}
public Pooled allocate() {
return new SecurePooledByteBuffer(delegate.allocate());
}
}
private static class SecurePooledByteBuffer implements Pooled {
private static final AtomicIntegerFieldUpdater freedUpdater = AtomicIntegerFieldUpdater.newUpdater(SecurePooledByteBuffer.class, "freed");
private final Pooled allocated;
@SuppressWarnings("unused")
private volatile int freed;
SecurePooledByteBuffer(final Pooled allocated) {
this.allocated = allocated;
}
public void discard() {
if (freedUpdater.compareAndSet(this, 0, 1)) {
zero(allocated.getResource());
allocated.discard();
}
}
public void free() {
if (freedUpdater.compareAndSet(this, 0, 1)) {
zero(allocated.getResource());
allocated.free();
}
}
public ByteBuffer getResource() throws IllegalStateException {
// trust the delegate to handle illegal state since we can't do it securely by ourselves
return allocated.getResource();
}
public void close() {
free();
}
public String toString() {
return "Secure wrapper around " + allocated;
}
}
}
xnio-3.6.2.Final/api/src/main/java/org/xnio/ByteBufferPool.java 0000664 0000000 0000000 00000067346 13241300306 0024242 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source
*
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
*
* 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;
import static java.lang.Math.max;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.wildfly.common.Assert;
import org.wildfly.common.cpu.CacheInfo;
import org.wildfly.common.function.ExceptionBiConsumer;
import org.wildfly.common.function.ExceptionBiFunction;
import org.wildfly.common.function.ExceptionConsumer;
import org.wildfly.common.function.ExceptionFunction;
import org.wildfly.common.function.ExceptionRunnable;
import org.wildfly.common.function.ExceptionSupplier;
import org.wildfly.common.function.Functions;
/**
* A fast source of pooled buffers.
*
* @author David M. Lloyd
*/
public abstract class ByteBufferPool {
private static final boolean sliceLargeBuffers;
static {
sliceLargeBuffers = Boolean.parseBoolean(System.getProperty("xnio.buffer.slice-large-buffers", "true"));
}
private final ConcurrentLinkedQueue masterQueue = new ConcurrentLinkedQueue<>();
private final ThreadLocal threadLocalCache = ThreadLocal.withInitial(this::getDefaultCache);
private final ByteBufferPool.Cache defaultCache = new DefaultCache();
private final int size;
private final boolean direct;
ByteBufferPool(final int size, final boolean direct) {
assert Integer.bitCount(size) == 1;
assert size >= 0x10;
assert size <= 0x4000_0000;
this.size = size;
this.direct = direct;
}
// buffer pool size constants
/**
* The size of large buffers.
*/
public static final int LARGE_SIZE = 0x100000;
/**
* The size of medium buffers.
*/
public static final int MEDIUM_SIZE = 0x2000;
/**
* The size of small buffers.
*/
public static final int SMALL_SIZE = 0x40;
static final int CACHE_LINE_SIZE = max(64, CacheInfo.getSmallestDataCacheLineSize());
/**
* The large direct buffer pool. This pool produces buffers of {@link #LARGE_SIZE}.
*/
public static final ByteBufferPool LARGE_DIRECT = create(LARGE_SIZE, true);
/**
* The medium direct buffer pool. This pool produces buffers of {@link #MEDIUM_SIZE}.
*/
public static final ByteBufferPool MEDIUM_DIRECT = sliceLargeBuffers ? subPool(LARGE_DIRECT, MEDIUM_SIZE) : create(MEDIUM_SIZE, true);
/**
* The small direct buffer pool. This pool produces buffers of {@link #SMALL_SIZE}.
*/
public static final ByteBufferPool SMALL_DIRECT = subPool(MEDIUM_DIRECT, SMALL_SIZE);
/**
* The large heap buffer pool. This pool produces buffers of {@link #LARGE_SIZE}.
*/
public static final ByteBufferPool LARGE_HEAP = create(LARGE_SIZE, false);
/**
* The medium heap buffer pool. This pool produces buffers of {@link #MEDIUM_SIZE}.
*/
public static final ByteBufferPool MEDIUM_HEAP = create(MEDIUM_SIZE, false);
/**
* The small heap buffer pool. This pool produces buffers of {@link #SMALL_SIZE}.
*/
public static final ByteBufferPool SMALL_HEAP = create(SMALL_SIZE, false);
/**
* A set of buffer pools for each size, which can either be {@link #DIRECT} or {@link #HEAP}.
*/
public static final class Set {
private final ByteBufferPool small, normal, large;
Set(final ByteBufferPool small, final ByteBufferPool normal, final ByteBufferPool large) {
this.small = small;
this.normal = normal;
this.large = large;
}
/**
* Get the small buffer pool for this set.
*
* @return the small buffer pool for this set
*/
public ByteBufferPool getSmall() {
return small;
}
/**
* Get the medium buffer pool for this set.
*
* @return the medium buffer pool for this set
*/
public ByteBufferPool getNormal() {
return normal;
}
/**
* Get the large buffer pool for this set.
*
* @return the large buffer pool for this set
*/
public ByteBufferPool getLarge() {
return large;
}
/**
* The direct buffer source set.
*/
public static final Set DIRECT = new Set(SMALL_DIRECT, MEDIUM_DIRECT, LARGE_DIRECT);
/**
* The heap buffer source set.
*/
public static final Set HEAP = new Set(SMALL_HEAP, MEDIUM_HEAP, LARGE_HEAP);
}
/**
* Allocate a buffer from this source pool. The buffer must be freed through the {@link #free(ByteBuffer)} method.
*
* @return the allocated buffer
*/
public ByteBuffer allocate() {
return threadLocalCache.get().allocate();
}
/**
* Bulk-allocate buffers from this pool. The buffer must be freed through the {@link #free(ByteBuffer)} method.
*
* @param array the array of buffers to fill
* @param offs the offset into the array to fill
*/
public void allocate(ByteBuffer[] array, int offs) {
allocate(array, offs, array.length - offs);
}
/**
* Bulk-allocate buffers from this pool. The buffer must be freed through the {@link #free(ByteBuffer)} method.
*
* @param array the array of buffers to fill
* @param offs the offset into the array to fill
* @param len the number of buffers to fill in the array
*/
public void allocate(ByteBuffer[] array, int offs, int len) {
Assert.checkNotNullParam("array", array);
Assert.checkArrayBounds(array, offs, len);
for (int i = 0; i < len; i ++) {
array[offs + i] = allocate();
}
}
/**
* Free a buffer into its appropriate pool based on its size. Care must be taken to avoid
* returning a slice of a pooled buffer, since this could cause both the buffer and its slice
* to be separately repooled, leading to likely data corruption.
*
* @param buffer the buffer to free
*/
public static void free(ByteBuffer buffer) {
Assert.checkNotNullParam("buffer", buffer);
final int size = buffer.capacity();
if (Integer.bitCount(size) == 1 && ! buffer.isReadOnly()) {
if (buffer.isDirect()) {
if (size == MEDIUM_SIZE) {
MEDIUM_DIRECT.doFree(buffer);
} else if (size == SMALL_SIZE) {
SMALL_DIRECT.doFree(buffer);
} else if (size == LARGE_SIZE) {
LARGE_DIRECT.doFree(buffer);
}
} else {
if (size == MEDIUM_SIZE) {
MEDIUM_HEAP.doFree(buffer);
} else if (size == SMALL_SIZE) {
SMALL_HEAP.doFree(buffer);
} else if (size == LARGE_SIZE) {
LARGE_HEAP.doFree(buffer);
}
}
}
}
/**
* Bulk-free buffers from an array as with {@link #free(ByteBuffer)}. The freed entries will be assigned to
* {@code null}.
*
* @param array the buffer array
* @param offs the offset into the array
* @param len the number of buffers to free
*/
public static void free(ByteBuffer[] array, int offs, int len) {
Assert.checkArrayBounds(array, offs, len);
for (int i = 0; i < len; i ++) {
ByteBuffer buffer = array[offs + i];
if (buffer == null) {
continue;
}
final int size = buffer.capacity();
if (Integer.bitCount(size) == 1 && ! buffer.isReadOnly()) {
if (buffer.isDirect()) {
if (! (buffer instanceof MappedByteBuffer)) {
if (size == MEDIUM_SIZE) {
MEDIUM_DIRECT.doFree(buffer);
} else if (size == SMALL_SIZE) {
SMALL_DIRECT.doFree(buffer);
} else if (size == LARGE_SIZE) {
LARGE_DIRECT.doFree(buffer);
}
}
} else {
if (size == MEDIUM_SIZE) {
MEDIUM_HEAP.doFree(buffer);
} else if (size == SMALL_SIZE) {
SMALL_HEAP.doFree(buffer);
} else if (size == LARGE_SIZE) {
LARGE_HEAP.doFree(buffer);
}
}
}
array[offs + i] = null;
}
}
/**
* Free a buffer as with {@link #free(ByteBuffer)} except the buffer is first zeroed and cleared.
*
* @param buffer the buffer to free
*/
public static void zeroAndFree(ByteBuffer buffer) {
Buffers.zero(buffer);
free(buffer);
}
/**
* Determine if this source returns direct buffers.
* @return {@code true} if the buffers are direct, {@code false} if they are heap
*/
public boolean isDirect() {
return direct;
}
/**
* Get the size of buffers returned by this source. The size will be a power of two.
*
* @return the size of buffers returned by this source
*/
public int getSize() {
return size;
}
/**
* Flush thread-local caches. This is useful when a long blocking operation is being performed, wherein it is
* unlikely that buffers will be used; calling this method makes any cached buffers available to other threads.
*/
public void flushCaches() {
threadLocalCache.get().flush();
}
/**
* Flush all thread-local caches for all buffer sizes. This is useful when a long blocking operation is being performed, wherein it is
* unlikely that buffers will be used; calling this method makes any cached buffers available to other threads.
*/
public static void flushAllCaches() {
SMALL_HEAP.flushCaches();
MEDIUM_HEAP.flushCaches();
LARGE_HEAP.flushCaches();
SMALL_DIRECT.flushCaches();
MEDIUM_DIRECT.flushCaches();
LARGE_DIRECT.flushCaches();
}
/**
* Perform the given operation with the addition of a buffer cache of the given size. When this method returns,
* any cached free buffers will be returned to the next-higher cache or the global pool. If a cache size of 0
* is given, the action is simply run directly.
*
* @param the type of the first parameter
* @param the type of the second parameter
* @param the exception type thrown by the operation
* @param cacheSize the cache size to run under
* @param consumer the action to run
* @param param1 the first parameter to pass to the action
* @param param2 the second parameter to pass to the action
* @throws E if the nested action threw an exception
*/
public void acceptWithCacheEx(int cacheSize, ExceptionBiConsumer consumer, T param1, U param2) throws E {
Assert.checkMinimumParameter("cacheSize", 0, cacheSize);
Assert.checkNotNullParam("consumer", consumer);
final ThreadLocal threadLocalCache = this.threadLocalCache;
final Cache parent = threadLocalCache.get();
final Cache cache;
if (cacheSize == 0) {
consumer.accept(param1, param2);
return;
} else if (cacheSize <= 64) {
if (cacheSize == 1) {
cache = new OneCache(parent);
} else if (cacheSize == 2) {
cache = new TwoCache(parent);
} else {
cache = new MultiCache(parent, cacheSize);
}
threadLocalCache.set(cache);
try {
consumer.accept(param1, param2);
return;
} finally {
threadLocalCache.set(parent);
cache.destroy();
}
} else {
cache = new MultiCache(parent, 64);
threadLocalCache.set(cache);
try {
acceptWithCacheEx(cacheSize - 64, consumer, param1, param2);
return;
} finally {
cache.destroy();
}
}
}
/**
* Perform the given operation with the addition of a buffer cache of the given size. When this method returns,
* any cached free buffers will be returned to the next-higher cache or the global pool. If a cache size of 0
* is given, the action is simply run directly.
*
* @param the type of the parameter
* @param the exception type thrown by the operation
* @param cacheSize the cache size to run under
* @param consumer the action to run
* @param param the parameter to pass to the action
* @throws E if the nested action threw an exception
*/
public void acceptWithCacheEx(int cacheSize, ExceptionConsumer consumer, T param) throws E {
Assert.checkNotNullParam("consumer", consumer);
acceptWithCacheEx(cacheSize, Functions.exceptionConsumerBiConsumer(), consumer, param);
}
/**
* Perform the given operation with the addition of a buffer cache of the given size. When this method returns,
* any cached free buffers will be returned to the next-higher cache or the global pool. If a cache size of 0
* is given, the action is simply run directly.
*
* @param the exception type thrown by the operation
* @param cacheSize the cache size to run under
* @param runnable the action to run
* @throws E if the nested action threw an exception
*/
public void runWithCacheEx(int cacheSize, ExceptionRunnable runnable) throws E {
Assert.checkNotNullParam("runnable", runnable);
acceptWithCacheEx(cacheSize, Functions.exceptionRunnableConsumer(), runnable);
}
/**
* Perform the given operation with the addition of a buffer cache of the given size. When this method returns,
* any cached free buffers will be returned to the next-higher cache or the global pool. If a cache size of 0
* is given, the action is simply run directly.
*
* @param cacheSize the cache size to run under
* @param runnable the action to run
*/
public void runWithCache(int cacheSize, Runnable runnable) {
Assert.checkNotNullParam("runnable", runnable);
// todo: fix with wildfly-common 1.4
acceptWithCacheEx(cacheSize, Runnable::run, runnable);
}
/**
* Perform the given operation with the addition of a buffer cache of the given size. When this method returns,
* any cached free buffers will be returned to the next-higher cache or the global pool. If a cache size of 0
* is given, the action is simply run directly.
*
* @param the type of the first parameter
* @param the type of the second parameter
* @param the return type of the operation
* @param the exception type thrown by the operation
* @param cacheSize the cache size to run under
* @param function the action to run
* @param param1 the first parameter to pass to the action
* @param param2 the second parameter to pass to the action
* @return the result of the action
* @throws E if the nested action threw an exception
*/
public R applyWithCacheEx(int cacheSize, ExceptionBiFunction function, T param1, U param2) throws E {
Assert.checkMinimumParameter("cacheSize", 0, cacheSize);
Assert.checkNotNullParam("function", function);
final ThreadLocal threadLocalCache = this.threadLocalCache;
final Cache parent = threadLocalCache.get();
final Cache cache;
if (cacheSize == 0) {
return function.apply(param1, param2);
} else if (cacheSize <= 64) {
if (cacheSize == 1) {
cache = new OneCache(parent);
} else if (cacheSize == 2) {
cache = new TwoCache(parent);
} else {
cache = new MultiCache(parent, cacheSize);
}
threadLocalCache.set(cache);
try {
return function.apply(param1, param2);
} finally {
threadLocalCache.set(parent);
cache.destroy();
}
} else {
cache = new MultiCache(parent, 64);
threadLocalCache.set(cache);
try {
return applyWithCacheEx(cacheSize - 64, function, param1, param2);
} finally {
cache.destroy();
}
}
}
/**
* Perform the given operation with the addition of a buffer cache of the given size. When this method returns,
* any cached free buffers will be returned to the next-higher cache or the global pool. If a cache size of 0
* is given, the action is simply run directly.
*
* @param the type of the parameter
* @param the return type of the operation
* @param the exception type thrown by the operation
* @param cacheSize the cache size to run under
* @param function the action to run
* @param param the parameter to pass to the action
* @return the result of the action
* @throws E if the nested action threw an exception
*/
public R applyWithCacheEx(int cacheSize, ExceptionFunction function, T param) throws E {
return applyWithCacheEx(cacheSize, Functions.exceptionFunctionBiFunction(), function, param);
}
/**
* Perform the given operation with the addition of a buffer cache of the given size. When this method returns,
* any cached free buffers will be returned to the next-higher cache or the global pool. If a cache size of 0
* is given, the action is simply run directly.
*
* @param the return type of the operation
* @param the exception type thrown by the operation
* @param cacheSize the cache size to run under
* @param supplier the action to run
* @return the result of the action
* @throws E if the nested action threw an exception
*/
public R getWithCacheEx(int cacheSize, ExceptionSupplier supplier) throws E {
return applyWithCacheEx(cacheSize, Functions.exceptionSupplierFunction(), supplier);
}
// private
Cache getDefaultCache() {
return defaultCache;
}
ConcurrentLinkedQueue getMasterQueue() {
return masterQueue;
}
private ByteBuffer allocateMaster() {
ByteBuffer byteBuffer = masterQueue.poll();
if (byteBuffer == null) {
byteBuffer = createBuffer();
}
return byteBuffer;
}
static ByteBufferPool create(final int size, final boolean direct) {
assert Integer.bitCount(size) == 1;
assert size >= 0x10;
assert size <= 0x4000_0000;
return new ByteBufferPool(size, direct) {
ByteBuffer createBuffer() {
return isDirect() ? ByteBuffer.allocateDirect(getSize()) : ByteBuffer.allocate(getSize());
}
};
}
static ByteBufferPool subPool(final ByteBufferPool parent, final int size) {
// must be a power of two, not too small, and smaller than the parent buffer source
assert Integer.bitCount(size) == 1;
assert Integer.bitCount(parent.getSize()) == 1;
assert size >= 0x10;
assert size < parent.getSize();
// and thus..
assert parent.getSize() % size == 0;
return new ByteBufferPool(size, parent.isDirect()) {
ByteBuffer createBuffer() {
synchronized (this) {
// avoid a storm of mass-population by only allowing one thread to split a parent buffer at a time
ByteBuffer appearing = getMasterQueue().poll();
if (appearing != null) {
return appearing;
}
ByteBuffer parentBuffer = parent.allocate();
final int size = getSize();
ByteBuffer result = Buffers.slice(parentBuffer, size);
while (parentBuffer.hasRemaining()) {
// avoid false sharing between buffers
if (size < CACHE_LINE_SIZE) {
Buffers.skip(parentBuffer, CACHE_LINE_SIZE - size);
}
super.doFree(Buffers.slice(parentBuffer, size));
}
return result;
}
}
};
}
abstract ByteBuffer createBuffer();
final void freeMaster(ByteBuffer buffer) {
masterQueue.add(buffer);
}
final void doFree(final ByteBuffer buffer) {
assert buffer.capacity() == size;
assert buffer.isDirect() == direct;
buffer.clear();
threadLocalCache.get().free(buffer);
}
interface Cache {
void free(ByteBuffer bb);
void flushBuffer(ByteBuffer bb);
ByteBuffer allocate();
void destroy();
void flush();
}
static final class OneCache implements Cache {
private final Cache parent;
private ByteBuffer buffer;
OneCache(final Cache parent) {
this.parent = parent;
}
public void free(final ByteBuffer bb) {
if (buffer == null) {
buffer = bb;
} else {
parent.free(bb);
}
}
public void flushBuffer(final ByteBuffer bb) {
parent.flushBuffer(bb);
}
public ByteBuffer allocate() {
if (buffer != null) try {
return buffer;
} finally {
buffer = null;
} else {
return parent.allocate();
}
}
public void destroy() {
final ByteBuffer buffer = this.buffer;
if (buffer != null) {
this.buffer = null;
parent.free(buffer);
}
}
public void flush() {
final ByteBuffer buffer = this.buffer;
if (buffer != null) {
this.buffer = null;
flushBuffer(buffer);
}
parent.flush();
}
}
static final class TwoCache implements Cache {
private final Cache parent;
private ByteBuffer buffer1;
private ByteBuffer buffer2;
TwoCache(final Cache parent) {
this.parent = parent;
}
public void free(final ByteBuffer bb) {
if (buffer1 == null) {
buffer1 = bb;
} else if (buffer2 == null) {
buffer2 = bb;
} else {
parent.free(bb);
}
}
public void flushBuffer(final ByteBuffer bb) {
parent.flushBuffer(bb);
}
public ByteBuffer allocate() {
if (buffer1 != null) try {
return buffer1;
} finally {
buffer1 = null;
} else if (buffer2 != null) try {
return buffer2;
} finally {
buffer2 = null;
} else {
return parent.allocate();
}
}
public void destroy() {
final Cache parent = this.parent;
final ByteBuffer buffer1 = this.buffer1;
if (buffer1 != null) {
parent.free(buffer1);
this.buffer1 = null;
}
final ByteBuffer buffer2 = this.buffer2;
if (buffer2 != null) {
parent.free(buffer2);
this.buffer2 = null;
}
}
public void flush() {
final ByteBuffer buffer1 = this.buffer1;
if (buffer1 != null) {
flushBuffer(buffer1);
this.buffer1 = null;
}
final ByteBuffer buffer2 = this.buffer2;
if (buffer2 != null) {
flushBuffer(buffer2);
this.buffer2 = null;
}
parent.flush();
}
}
static final class MultiCache implements Cache {
private final Cache parent;
private final ByteBuffer[] cache;
private final long mask;
private long availableBits;
MultiCache(final Cache parent, final int size) {
this.parent = parent;
assert 0 < size && size <= 64;
cache = new ByteBuffer[size];
mask = availableBits = size == 64 ? ~0L : (1L << size) - 1;
}
public void free(final ByteBuffer bb) {
long posn = Long.lowestOneBit(~availableBits & mask);
if (posn != 0L) {
int bit = Long.numberOfTrailingZeros(posn);
// mark available
availableBits |= posn;
cache[bit] = bb;
} else {
// full
parent.free(bb);
}
}
public void flushBuffer(final ByteBuffer bb) {
parent.flushBuffer(bb);
}
public ByteBuffer allocate() {
long posn = Long.lowestOneBit(availableBits);
if (posn != 0L) {
int bit = Long.numberOfTrailingZeros(posn);
availableBits &= ~posn;
try {
return cache[bit];
} finally {
cache[bit] = null;
}
} else {
// empty
return parent.allocate();
}
}
public void destroy() {
final ByteBuffer[] cache = this.cache;
final Cache parent = this.parent;
long bits = ~availableBits & mask;
try {
while (bits != 0L) {
long posn = Long.lowestOneBit(bits);
int bit = Long.numberOfTrailingZeros(posn);
parent.free(cache[bit]);
bits &= ~posn;
cache[bit] = null;
}
} finally {
// should be 0, but maintain a consistent state in case a free failed
availableBits = bits;
}
}
public void flush() {
final ByteBuffer[] cache = this.cache;
final Cache parent = this.parent;
long bits = ~availableBits & mask;
try {
while (bits != 0L) {
long posn = Long.lowestOneBit(bits);
int bit = Long.numberOfTrailingZeros(posn);
flushBuffer(cache[bit]);
bits &= ~posn;
cache[bit] = null;
}
} finally {
// should be 0, but maintain a consistent state in case a free failed
availableBits = bits;
}
parent.flush();
}
}
final class DefaultCache implements Cache {
public void free(final ByteBuffer bb) {
freeMaster(bb);
}
public ByteBuffer allocate() {
return allocateMaster();
}
public void flushBuffer(final ByteBuffer bb) {
free(bb);
}
public void destroy() {
// no operation
}
public void flush() {
// no operation
}
}
}
xnio-3.6.2.Final/api/src/main/java/org/xnio/ByteBufferSlicePool.java 0000664 0000000 0000000 00000022135 13241300306 0025205 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source
*
* Copyright 2010 Red Hat, Inc. and/or its affiliates, and individual
* contributors as indicated by the @author tags.
*
* 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;
import java.nio.ByteBuffer;
import java.security.AccessController;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.HashSet;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import static org.xnio._private.Messages.msg;
/**
* A buffer pooled allocator. This pool uses a series of buffer regions to back the
* returned pooled buffers. When the buffer is no longer needed, it should be freed back into the pool; failure
* to do so will cause the corresponding buffer area to be unavailable until the buffer is garbage-collected.
*
* @author David M. Lloyd
* @deprecated See {@link ByteBufferPool}.
*/
public final class ByteBufferSlicePool implements Pool {
private static final int LOCAL_LENGTH;
static {
String value = AccessController.doPrivileged(new ReadPropertyAction("xnio.bufferpool.threadlocal.size", "12"));
int val;
try {
val = Integer.parseInt(value);
} catch (NumberFormatException ignored) {
val = 12;
}
LOCAL_LENGTH = val;
}
private final Set[ refSet = Collections.synchronizedSet(new HashSet][());
private final Queue sliceQueue;
private final BufferAllocator allocator;
private final int bufferSize;
private final int buffersPerRegion;
private final int threadLocalQueueSize;
private final ThreadLocal localQueueHolder = new ThreadLocal() {
protected ThreadLocalCache initialValue() {
//noinspection serial
return new ThreadLocalCache();
}
public void remove() {
final ArrayDeque deque = get().queue;
Slice slice = deque.poll();
while (slice != null) {
doFree(slice);
slice = deque.poll();
}
super.remove();
}
};
/**
* Construct a new instance.
*
* @param allocator the buffer allocator to use
* @param bufferSize the size of each buffer
* @param maxRegionSize the maximum region size for each backing buffer
* @param threadLocalQueueSize the number of buffers to cache on each thread
*/
public ByteBufferSlicePool(final BufferAllocator allocator, final int bufferSize, final int maxRegionSize, final int threadLocalQueueSize) {
if (bufferSize <= 0) {
throw msg.parameterOutOfRange("bufferSize");
}
if (maxRegionSize < bufferSize) {
throw msg.parameterOutOfRange("bufferSize");
}
buffersPerRegion = maxRegionSize / bufferSize;
this.bufferSize = bufferSize;
this.allocator = allocator;
sliceQueue = new ConcurrentLinkedQueue();
this.threadLocalQueueSize = threadLocalQueueSize;
}
/**
* Construct a new instance.
*
* @param allocator the buffer allocator to use
* @param bufferSize the size of each buffer
* @param maxRegionSize the maximum region size for each backing buffer
*/
public ByteBufferSlicePool(final BufferAllocator allocator, final int bufferSize, final int maxRegionSize) {
this(allocator, bufferSize, maxRegionSize, LOCAL_LENGTH);
}
/**
* Construct a new instance, using a direct buffer allocator.
*
* @param bufferSize the size of each buffer
* @param maxRegionSize the maximum region size for each backing buffer
*/
public ByteBufferSlicePool(final int bufferSize, final int maxRegionSize) {
this(BufferAllocator.DIRECT_BYTE_BUFFER_ALLOCATOR, bufferSize, maxRegionSize);
}
/** {@inheritDoc} */
public Pooled allocate() {
Slice slice;
if (threadLocalQueueSize > 0) {
ThreadLocalCache localCache = localQueueHolder.get();
if(localCache.outstanding != threadLocalQueueSize) {
localCache.outstanding++;
}
slice = localCache.queue.poll();
if (slice != null) {
return new PooledByteBuffer(slice, slice.slice());
}
}
final Queue sliceQueue = this.sliceQueue;
slice = sliceQueue.poll();
if (slice != null) {
return new PooledByteBuffer(slice, slice.slice());
}
synchronized (sliceQueue) {
slice = sliceQueue.poll();
if (slice != null) {
return new PooledByteBuffer(slice, slice.slice());
}
final int bufferSize = this.bufferSize;
final int buffersPerRegion = this.buffersPerRegion;
final ByteBuffer region = allocator.allocate(buffersPerRegion * bufferSize);
int idx = bufferSize;
for (int i = 1; i < buffersPerRegion; i ++) {
sliceQueue.add(new Slice(region, idx, bufferSize));
idx += bufferSize;
}
final Slice newSlice = new Slice(region, 0, bufferSize);
return new PooledByteBuffer(newSlice, newSlice.slice());
}
}
/**
* Return the size of the {@link ByteBuffer}s that are returned by {@link #allocate()}.
*/
public int getBufferSize() {
return bufferSize;
}
private void doFree(Slice region) {
if (threadLocalQueueSize > 0) {
final ThreadLocalCache localCache = localQueueHolder.get();
boolean cacheOk = false;
if(localCache.outstanding > 0) {
localCache.outstanding--;
cacheOk = true;
}
ArrayDeque localQueue = localCache.queue;
if (localQueue.size() == threadLocalQueueSize || !cacheOk) {
sliceQueue.add(region);
} else {
localQueue.add(region);
}
} else {
sliceQueue.add(region);
}
}
private final class PooledByteBuffer implements Pooled {
private final Slice region;
ByteBuffer buffer;
PooledByteBuffer(final Slice region, final ByteBuffer buffer) {
this.region = region;
this.buffer = buffer;
}
public void discard() {
final ByteBuffer buffer = this.buffer;
this.buffer = null;
if (buffer != null) {
// free when GC'd, no sooner
refSet.add(new Ref(buffer, region));
}
}
public void free() {
ByteBuffer buffer = this.buffer;
this.buffer = null;
if (buffer != null) {
// trust the user, repool the buffer
doFree(region);
}
}
public ByteBuffer getResource() {
final ByteBuffer buffer = this.buffer;
if (buffer == null) {
throw msg.bufferFreed();
}
return buffer;
}
public void close() {
free();
}
public String toString() {
return "Pooled buffer " + buffer;
}
}
private final class Slice {
private final ByteBuffer parent;
private Slice(final ByteBuffer parent, final int start, final int size) {
this.parent = (ByteBuffer)parent.duplicate().position(start).limit(start+size);
}
ByteBuffer slice() {
return parent.slice();
}
}
final class Ref extends AutomaticReference {
private final Slice region;
private Ref(final ByteBuffer referent, final Slice region) {
super(referent, AutomaticReference.PERMIT);
this.region = region;
}
protected void free() {
doFree(region);
refSet.remove(this);
}
}
private final class ThreadLocalCache {
final ArrayDeque queue = new ArrayDeque(threadLocalQueueSize) {
/**
* This sucks but there's no other way to ensure these buffers are returned to the pool.
*/
protected void finalize() {
final ArrayDeque deque = queue;
Slice slice = deque.poll();
while (slice != null) {
doFree(slice);
slice = deque.poll();
}
}
};
int outstanding = 0;
ThreadLocalCache() {
}
}
}
xnio-3.6.2.Final/api/src/main/java/org/xnio/ByteString.java 0000664 0000000 0000000 00000124270 13241300306 0023433 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source
*
* Copyright 2009 Red Hat, Inc. and/or its affiliates.
*
* 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;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.io.UnsupportedEncodingException;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.nio.ByteBuffer;
import static java.lang.Integer.signum;
import static java.lang.Math.abs;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.lang.System.arraycopy;
import static org.xnio._private.Messages.msg;
/**
* An immutable string of bytes. Since instances of this class are guaranteed to be immutable, they are
* safe to use as {@link Option} values and in an {@link OptionMap}. Some operations can treat this byte string
* as an ASCII- or ISO-8858-1-encoded character string.
*/
public final class ByteString implements Comparable, Serializable, CharSequence {
private static final long serialVersionUID = -5998895518404718196L;
private final byte[] bytes;
private final int offs;
private final int len;
private transient int hashCode;
private transient int hashCodeIgnoreCase;
private ByteString(final byte[] bytes, final int offs, final int len) {
this.bytes = bytes;
this.offs = offs;
this.len = len;
if (offs < 0) {
throw msg.parameterOutOfRange("offs");
}
if (len < 0) {
throw msg.parameterOutOfRange("len");
}
if (offs + len > bytes.length) {
throw msg.parameterOutOfRange("offs");
}
}
private static int calcHashCode(final byte[] bytes, final int offs, final int len) {
int hc = 31;
final int end = offs + len;
for (int i = offs; i < end; i++) {
hc = (hc << 5) - hc + (bytes[i] & 0xff);
}
return hc == 0 ? Integer.MAX_VALUE : hc;
}
private static int calcHashCodeIgnoreCase(final byte[] bytes, final int offs, final int len) {
int hc = 31;
final int end = offs + len;
for (int i = offs; i < end; i++) {
hc = (hc << 5) - hc + (upperCase(bytes[i]) & 0xff);
}
return hc == 0 ? Integer.MAX_VALUE : hc;
}
private ByteString(final byte[] bytes) {
this(bytes, 0, bytes.length);
}
/**
* Create a byte string of the given literal bytes. The given array is copied.
*
* @param bytes the bytes
* @return the byte string
*/
public static ByteString of(byte... bytes) {
return new ByteString(bytes.clone());
}
/**
* Create a byte string from the given array segment.
*
* @param b the byte array
* @param offs the offset into the array
* @param len the number of bytes to copy
* @return the new byte string
*/
public static ByteString copyOf(byte[] b, int offs, int len) {
return new ByteString(Arrays.copyOfRange(b, offs, offs + len));
}
/**
* Get a byte string from the bytes of a character string.
*
* @param str the character string
* @param charset the character set to use
* @return the byte string
* @throws UnsupportedEncodingException if the encoding is not supported
*/
public static ByteString getBytes(String str, String charset) throws UnsupportedEncodingException {
return new ByteString(str.getBytes(charset));
}
/**
* Get a byte string from the bytes of a character string.
*
* @param str the character string
* @param charset the character set to use
* @return the byte string
*/
public static ByteString getBytes(String str, Charset charset) {
return new ByteString(str.getBytes(charset));
}
/**
* Get a byte string from the bytes of the character string. The string must be a Latin-1 string.
*
* @param str the character string
* @return the byte string
*/
public static ByteString getBytes(String str) {
final int length = str.length();
return new ByteString(getStringBytes(false, new byte[length], 0, str, 0, length), 0, length);
}
/**
* Get a byte string from all remaining bytes of a ByteBuffer.
*
* @param buffer the buffer
* @return the byte string
*/
public static ByteString getBytes(ByteBuffer buffer) {
return new ByteString(Buffers.take(buffer));
}
/**
* Get a byte string from a ByteBuffer.
*
* @param buffer the buffer
* @param length the number of bytes to get
* @return the byte string
*/
public static ByteString getBytes(ByteBuffer buffer, int length) {
return new ByteString(Buffers.take(buffer, length));
}
/**
* Get a copy of the bytes of this ByteString.
*
* @return the copy
*/
public byte[] getBytes() {
return Arrays.copyOfRange(bytes, offs, len);
}
/**
* Copy the bytes of this ByteString into the destination array. If the array is too short to hold
* the bytes, then only enough bytes to fill the array will be copied.
*
* @param dest the destination array
*
* @deprecated Replaced by {@link #copyTo(byte[])}.
*/
public void getBytes(byte[] dest) {
copyTo(dest);
}
/**
* Copy the bytes of this ByteString into the destination array. If the array is too short to hold
* the bytes, then only enough bytes to fill the array will be copied.
*
* @param dest the destination array
* @param offs the offset into the destination array
*
* @deprecated Replaced by {@link #copyTo(byte[],int)}.
*/
public void getBytes(byte[] dest, int offs) {
copyTo(dest, offs);
}
/**
* Copy the bytes of this ByteString into the destination array. If the array is too short to hold
* the bytes, then only enough bytes to fill the array will be copied.
*
* @param dest the destination array
* @param offs the offset into the destination array
* @param len the maximum number of bytes to copy
*
* @deprecated Replaced by {@link #copyTo(byte[],int,int)}.
*/
public void getBytes(byte[] dest, int offs, int len) {
copyTo(dest, offs, len);
}
/**
* Copy {@code len} bytes from this string at offset {@code srcOffs} to the given array at the given offset.
*
* @param srcOffs the source offset
* @param dst the destination
* @param offs the destination offset
* @param len the number of bytes to copy
*/
public void copyTo(int srcOffs, byte[] dst, int offs, int len) {
arraycopy(bytes, srcOffs + this.offs, dst, offs, min(this.len, len));
}
/**
* Copy {@code len} bytes from this string to the given array at the given offset.
*
* @param dst the destination
* @param offs the destination offset
* @param len the number of bytes
*/
public void copyTo(byte[] dst, int offs, int len) {
copyTo(0, dst, offs, len);
}
/**
* Copy all the bytes from this string to the given array at the given offset.
*
* @param dst the destination
* @param offs the destination offset
*/
public void copyTo(byte[] dst, int offs) {
copyTo(dst, offs, dst.length - offs);
}
/**
* Copy all the bytes from this string to the given array at the given offset.
*
* @param dst the destination
*/
public void copyTo(byte[] dst) {
copyTo(dst, 0, dst.length);
}
/**
* Append the bytes of this string into the given buffer.
*
* @param dest the target buffer
*/
public void appendTo(ByteBuffer dest) {
dest.put(bytes, offs, len);
}
/**
* Append as many bytes as possible to a byte buffer.
*
* @param offs the start offset
* @param buffer the buffer to append to
* @return the number of bytes appended
*/
public int tryAppendTo(final int offs, final ByteBuffer buffer) {
final byte[] b = bytes;
final int len = min(buffer.remaining(), b.length - offs);
buffer.put(b, offs + this.offs, len);
return len;
}
/**
* Append to an output stream.
*
* @param output the stream to write to
* @throws IOException if an error occurs
*/
public void writeTo(OutputStream output) throws IOException {
// todo - determine if the output stream is trusted
output.write(bytes, offs, len);
}
/**
* Compare this string to another in a case-sensitive manner.
*
* @param other the other string
* @return -1, 0, or 1
*/
@Override
public int compareTo(final ByteString other) {
if (other == this) return 0;
final int length = this.len;
final int otherLength = other.len;
final int len1 = min(length, otherLength);
final byte[] bytes = this.bytes;
final byte[] otherBytes = other.bytes;
final int offs = this.offs;
final int otherOffs = other.offs;
int res;
for (int i = 0; i < len1; i++) {
res = signum(bytes[i + offs] - otherBytes[i + otherOffs]);
if (res != 0) return res;
}
// shorter strings sort higher
return signum(length - otherLength);
}
/**
* Compare this string to another in a case-insensitive manner.
*
* @param other the other string
* @return -1, 0, or 1
*/
public int compareToIgnoreCase(final ByteString other) {
if (other == this) return 0;
if (other == this) return 0;
final int length = this.len;
final int otherLength = other.len;
final int len1 = min(length, otherLength);
final byte[] bytes = this.bytes;
final byte[] otherBytes = other.bytes;
final int offs = this.offs;
final int otherOffs = other.offs;
int res;
for (int i = 0; i < len1; i++) {
res = signum(upperCase(bytes[i + offs]) - upperCase(otherBytes[i + otherOffs]));
if (res != 0) return res;
}
// shorter strings sort higher
return signum(length - otherLength);
}
private static int upperCase(byte b) {
return b >= 'a' && b <= 'z' ? b & 0xDF : b;
}
/**
* Convert this byte string to a standard string.
*
* @param charset the character set to use
* @return the standard string
* @throws UnsupportedEncodingException if the charset is unknown
*/
public String toString(String charset) throws UnsupportedEncodingException {
if ("ISO-8859-1".equalsIgnoreCase(charset) || "Latin-1".equalsIgnoreCase(charset) || "ISO-Latin-1".equals(charset)) return toString();
return new String(bytes, offs, len, charset);
}
/**
* Get the number of bytes in this byte string.
*
* @return the number of bytes
*/
public int length() {
return len;
}
/**
* Decode this byte string as a Latin-1 string.
*
* @return the Latin-1-decoded version of this string
*/
@SuppressWarnings("deprecation")
public String toString() {
return new String(bytes, 0, offs, len);
}
/**
* Decode this byte string as a UTF-8 string.
*
* @return the UTF-8-decoded version of this string
*/
public String toUtf8String() {
return new String(bytes, offs, len, StandardCharsets.UTF_8);
}
/**
* Get the byte at an index.
*
* @return the byte at an index
*/
public byte byteAt(int idx) {
if (idx < 0 || idx > len) {
throw new ArrayIndexOutOfBoundsException();
}
return bytes[idx + offs];
}
/**
* Get the substring of this string starting at the given offset.
*
* @param offs the offset
* @return the substring
*/
public ByteString substring(int offs) {
return substring(offs, len - offs);
}
/**
* Get the substring of this string starting at the given offset.
*
* @param offs the offset
* @param len the substring length
* @return the substring
*/
public ByteString substring(int offs, int len) {
if (this.len - offs < len) {
throw new IndexOutOfBoundsException();
}
return new ByteString(bytes, this.offs + offs, len);
}
/**
* Get the hash code for this ByteString.
*
* @return the hash code
*/
public int hashCode() {
int hashCode = this.hashCode;
if (hashCode == 0) {
this.hashCode = hashCode = calcHashCode(bytes, offs, len);
}
return hashCode;
}
public int hashCodeIgnoreCase() {
int hashCode = this.hashCodeIgnoreCase;
if (hashCode == 0) {
this.hashCodeIgnoreCase = hashCode = calcHashCodeIgnoreCase(bytes, offs, len);
}
return hashCode;
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
}
private static boolean equals(byte[] a, int aoff, byte[] b, int boff, int len) {
for (int i = 0; i < len; i ++) {
if (a[i + aoff] != b[i + boff]) return false;
}
return true;
}
private static boolean equalsIgnoreCase(byte[] a, int aoff, byte[] b, int boff, int len) {
for (int i = 0; i < len; i ++) {
if (upperCase(a[i + aoff]) != upperCase(b[i + boff])) return false;
}
return true;
}
/**
* Determine if this ByteString equals another ByteString.
*
* @param obj the other object
* @return {@code true} if they are equal
*/
public boolean equals(final Object obj) {
return (obj instanceof ByteString) && equals((ByteString) obj);
}
/**
* Determine if this ByteString equals another ByteString.
*
* @param other the other object
* @return {@code true} if they are equal
*/
public boolean equals(final ByteString other) {
final int len = this.len;
return this == other || other != null && len == other.len && equals(bytes, offs, other.bytes, other.offs, len);
}
/**
* Determine if this ByteString equals another ByteString, ignoring case (ASCII).
*
* @param other the other object
* @return {@code true} if they are equal
*/
public boolean equalsIgnoreCase(final ByteString other) {
final int len = this.len;
return this == other || other != null && len == other.len && equalsIgnoreCase(bytes, offs, other.bytes, other.offs, len);
}
/**
* Get the unsigned {@code int} value of this string. If the value is greater than would fit in 32 bits, only
* the low 32 bits are returned. Parsing stops on the first non-digit character.
*
* @param start the index to start at (must be less than or equal to length)
* @return the value
*/
public int toInt(final int start) {
final int len = this.len;
if (start >= len) {
return 0;
}
final byte[] bytes = this.bytes;
int v = 0;
byte b;
for (int i = start + offs; i < len; i ++) {
b = bytes[i];
if (b < '0' || b > '9') {
return v;
}
v = (v << 3) + (v << 1) + (b - '0');
}
return v;
}
/**
* Get the unsigned {@code int} value of this string. If the value is greater than would fit in 32 bits, only
* the low 32 bits are returned. Parsing stops on the first non-digit character.
*
* @return the value
*/
public int toInt() {
return toInt(0);
}
/**
* Get the unsigned {@code long} value of this string. If the value is greater than would fit in 64 bits, only
* the low 64 bits are returned. Parsing stops on the first non-digit character.
*
* @param start the index to start at (must be less than or equal to length)
* @return the value
*/
public long toLong(final int start) {
final int len = this.len;
if (start >= len) {
return 0;
}
final byte[] bytes = this.bytes;
long v = 0;
byte b;
for (int i = start; i < len; i ++) {
b = bytes[i];
if (b < '0' || b > '9') {
return v;
}
v = (v << 3) + (v << 1) + (b - '0');
}
return v;
}
/**
* Get the unsigned {@code long} value of this string. If the value is greater than would fit in 64 bits, only
* the low 64 bits are returned. Parsing stops on the first non-digit character.
*
* @return the value
*/
public long toLong() {
return toLong(0);
}
private static int decimalCount(int val) {
assert val >= 0;
// afaik no faster way exists to do this
if (val < 10) return 1;
if (val < 100) return 2;
if (val < 1000) return 3;
if (val < 10000) return 4;
if (val < 100000) return 5;
if (val < 1000000) return 6;
if (val < 10000000) return 7;
if (val < 100000000) return 8;
if (val < 1000000000) return 9;
return 10;
}
private static int decimalCount(long val) {
assert val >= 0;
// afaik no faster way exists to do this
if (val < 10L) return 1;
if (val < 100L) return 2;
if (val < 1000L) return 3;
if (val < 10000L) return 4;
if (val < 100000L) return 5;
if (val < 1000000L) return 6;
if (val < 10000000L) return 7;
if (val < 100000000L) return 8;
if (val < 1000000000L) return 9;
if (val < 10000000000L) return 10;
if (val < 100000000000L) return 11;
if (val < 1000000000000L) return 12;
if (val < 10000000000000L) return 13;
if (val < 100000000000000L) return 14;
if (val < 1000000000000000L) return 15;
if (val < 10000000000000000L) return 16;
if (val < 100000000000000000L) return 17;
if (val < 1000000000000000000L) return 18;
return 19;
}
private static final ByteString ZERO = new ByteString(new byte[] { '0' });
/**
* Get a string version of the given value.
*
* @param val the value
* @return the string
*/
public static ByteString fromLong(long val) {
if (val == 0) return ZERO;
// afaik no faster way exists to do this
int i = decimalCount(abs(val));
final byte[] b;
if (val < 0) {
b = new byte[++i];
b[0] = '-';
} else {
b = new byte[i];
}
long quo;
// modulus
int mod;
do {
quo = val / 10;
mod = (int) (val - ((quo << 3) + (quo << 1)));
b[--i] = (byte) (mod + '0');
val = quo;
} while (i > 0);
return new ByteString(b);
}
/**
* Get a string version of the given value.
*
* @param val the value
* @return the string
*/
public static ByteString fromInt(int val) {
if (val == 0) return ZERO;
// afaik no faster way exists to do this
int i = decimalCount(abs(val));
final byte[] b;
if (val < 0) {
b = new byte[++i];
b[0] = '-';
} else {
b = new byte[i];
}
int quo;
// modulus
int mod;
do {
quo = val / 10;
mod = val - ((quo << 3) + (quo << 1));
b[--i] = (byte) (mod + '0');
val = quo;
} while (i > 0);
return new ByteString(b);
}
/**
* Determine whether this {@code ByteString} is equal (case-sensitively) to the given {@code String}.
*
* @param str the string to check
* @return {@code true} if the given string is equal (case-sensitively) to this instance, {@code false} otherwise
*/
public boolean equalToString(String str) {
if (str == null) return false;
final byte[] bytes = this.bytes;
final int length = bytes.length;
if (str.length() != length) {
return false;
}
char ch;
final int end = offs + len;
for (int i = offs; i < end; i++) {
ch = str.charAt(i);
if (ch > 0xff || bytes[i] != (byte) str.charAt(i)) {
return false;
}
}
return true;
}
/**
* Determine whether this {@code ByteString} is equal (case-insensitively) to the given {@code String}.
*
* @param str the string to check
* @return {@code true} if the given string is equal (case-insensitively) to this instance, {@code false} otherwise
*/
public boolean equalToStringIgnoreCase(String str) {
if (str == null) return false;
final byte[] bytes = this.bytes;
final int length = bytes.length;
if (str.length() != length) {
return false;
}
char ch;
final int end = offs + len;
for (int i = offs; i < end; i++) {
ch = str.charAt(i);
if (ch > 0xff || upperCase(bytes[i]) != upperCase((byte) ch)) {
return false;
}
}
return true;
}
/**
* Get the index of the given character in this string.
*
* @param c the character
* @return the index, or -1 if it was not found
*/
public int indexOf(final char c) {
return indexOf(c, 0);
}
/**
* Get the index of the given character in this string.
*
* @param c the character
* @return the index, or -1 if it was not found
*/
public int indexOf(final char c, int start) {
if (c > 255) {
return -1;
}
final int len = this.len;
if (start > len) {
return -1;
}
start = max(0, start) + offs;
final byte[] bytes = this.bytes;
final byte bc = (byte) c;
final int end = start + len;
for (int i = start; i < end; i++) {
if (bytes[i] == bc) {
return i;
}
}
return -1;
}
/**
* Get the last index of the given character in this string.
*
* @param c the character
* @return the index, or -1 if it was not found
*/
public int lastIndexOf(final char c) {
return lastIndexOf(c, length() - 1);
}
/**
* Get the last index of the given character in this string.
*
* @param c the character
* @return the index, or -1 if it was not found
*/
public int lastIndexOf(final char c, int start) {
if (c > 255) {
return -1;
}
final byte[] bytes = this.bytes;
final int offs = this.offs;
start = min(start, len - 1) + offs;
final byte bc = (byte) c;
for (int i = start; i >= offs; --i) {
if (bytes[i] == bc) {
return i;
}
}
return -1;
}
// Linear array searches
private static int arrayIndexOf(byte[] a, int aOffs, byte[] b, int bOffs, int bLen) {
final int aLen = a.length - aOffs;
if (bLen > aLen || aLen < 0) {
return -1;
}
aOffs = max(0, aOffs);
if (bLen == 0) {
return aOffs;
}
final byte startByte = b[bOffs];
final int limit = aLen - bLen;
OUTER: for (int i = aOffs; i < limit; i ++) {
if (a[i] == startByte) {
for (int j = 1; j < bLen; j ++) {
if (a[i + j] != b[j + bOffs]) {
continue OUTER;
}
}
return i;
}
}
return -1;
}
private static int arrayIndexOf(byte[] a, int aOffs, String string) {
final int aLen = a.length - aOffs;
final int bLen = string.length();
if (bLen > aLen || aLen < 0) {
return -1;
}
aOffs = max(0, aOffs);
if (bLen == 0) {
return aOffs;
}
final char startChar = string.charAt(0);
if (startChar > 0xff) {
return -1;
}
char ch;
final int limit = aLen - bLen;
OUTER: for (int i = aOffs; i < limit; i ++) {
if (a[i] == startChar) {
for (int j = 1; j < bLen; j ++) {
ch = string.charAt(j);
if (ch > 0xff) {
return -1;
}
if (a[i + j] != ch) {
continue OUTER;
}
}
return i;
}
}
return -1;
}
private static int arrayIndexOfIgnoreCase(byte[] a, int aOffs, byte[] b, int bOffs, int bLen) {
final int aLen = a.length - aOffs;
if (bLen > aLen || aLen < 0) {
return -1;
}
aOffs = max(0, aOffs);
if (bLen == 0) {
return aOffs;
}
final int startChar = upperCase(b[bOffs]);
final int limit = aLen - bLen;
OUTER: for (int i = aOffs; i < limit; i ++) {
if (upperCase(a[i]) == startChar) {
for (int j = 1; j < bLen; j ++) {
if (upperCase(a[i + j]) != upperCase(b[j + bOffs])) {
continue OUTER;
}
}
return i;
}
}
return -1;
}
private static int arrayIndexOfIgnoreCase(byte[] a, int aOffs, String string) {
final int aLen = a.length - aOffs;
final int bLen = string.length();
if (bLen > aLen || aLen < 0) {
return -1;
}
aOffs = max(0, aOffs);
if (bLen == 0) {
return aOffs;
}
final char startChar = string.charAt(0);
if (startChar > 0xff) {
return -1;
}
final int startCP = upperCase((byte) startChar);
final int limit = aLen - bLen;
char ch;
OUTER: for (int i = aOffs; i < limit; i ++) {
if (upperCase(a[i]) == startCP) {
for (int j = 1; j < bLen; j ++) {
ch = string.charAt(j);
if (ch > 0xff) {
return -1;
}
// technically speaking, 'ı' (0x131) maps to I and 'ſ' (0x17F) maps to S, but this is unlikely to come up in ISO-8859-1
if (upperCase(a[i + j]) != upperCase((byte) ch)) {
continue OUTER;
}
}
return i;
}
}
return -1;
}
private static int arrayLastIndexOf(byte[] a, int aOffs, byte[] b, final int bOffs, final int bLen) {
final int aLen = a.length - aOffs;
if (bLen > aLen || aLen < 0 || aOffs < 0) {
return -1;
}
// move to the last possible position it could be
aOffs = min(aLen - bLen, aOffs);
if (bLen == 0) {
return aOffs;
}
final byte startByte = b[0];
OUTER: for (int i = aOffs - 1; i >= 0; i --) {
if (a[i] == startByte) {
for (int j = 1; j < bLen; j++) {
if (a[i + j] != b[bOffs + j]) {
continue OUTER;
}
return i;
}
}
}
return -1;
}
private static int arrayLastIndexOf(byte[] a, int aOffs, String string) {
final int aLen = a.length - aOffs;
final int bLen = string.length();
if (bLen > aLen || aLen < 0 || aOffs < 0) {
return -1;
}
// move to the last possible position it could be
aOffs = min(aLen - bLen, aOffs);
if (bLen == 0) {
return aOffs;
}
final char startChar = string.charAt(0);
if (startChar > 0xff) {
return -1;
}
final byte startByte = (byte) startChar;
char ch;
OUTER: for (int i = aOffs - 1; i >= 0; i --) {
if (a[i] == startByte) {
for (int j = 1; j < bLen; j++) {
ch = string.charAt(j);
if (ch > 0xff) {
return -1;
}
if (a[i + j] != (byte) ch) {
continue OUTER;
}
return i;
}
}
}
return -1;
}
private static int arrayLastIndexOfIgnoreCase(byte[] a, int aOffs, byte[] b, final int bOffs, final int bLen) {
final int aLen = a.length - aOffs;
if (bLen > aLen || aLen < 0 || aOffs < 0) {
return -1;
}
// move to the last possible position it could be
aOffs = min(aLen - bLen, aOffs);
if (bLen == 0) {
return aOffs;
}
final int startCP = upperCase(b[bOffs]);
OUTER: for (int i = aOffs - 1; i >= 0; i --) {
if (upperCase(a[i]) == startCP) {
for (int j = 1; j < bLen; j++) {
if (upperCase(a[i + j]) != upperCase(b[j + bOffs])) {
continue OUTER;
}
return i;
}
}
}
return -1;
}
private static int arrayLastIndexOfIgnoreCase(byte[] a, int aOffs, String string) {
final int aLen = a.length - aOffs;
final int bLen = string.length();
if (bLen > aLen || aLen < 0 || aOffs < 0) {
return -1;
}
// move to the last possible position it could be
aOffs = min(aLen - bLen, aOffs);
if (bLen == 0) {
return aOffs;
}
final char startChar = string.charAt(0);
if (startChar > 0xff) {
return -1;
}
final int startCP = upperCase((byte) startChar);
char ch;
OUTER: for (int i = aOffs - 1; i >= 0; i --) {
if (upperCase(a[i]) == startCP) {
for (int j = 1; j < bLen; j++) {
ch = string.charAt(j);
if (ch > 0xff) {
return -1;
}
// technically speaking, 'ı' (0x131) maps to I and 'ſ' (0x17F) maps to S, but this is unlikely to come up in ISO-8859-1
if (upperCase(a[i + j]) != upperCase((byte) ch)) {
continue OUTER;
}
return i;
}
}
}
return -1;
}
/**
* Determine whether this string contains another string (case-sensitive).
*
* @param other the string to test
* @return {@code true} if this string contains {@code other}, {@code false} otherwise
*/
public boolean contains(final ByteString other) {
if (other == this) return true;
if (other == null) return false;
final byte[] otherBytes = other.bytes;
return arrayIndexOf(bytes, offs, otherBytes, other.offs, other.len) != -1;
}
/**
* Determine whether this string contains another string (case-sensitive).
*
* @param other the string to test
* @return {@code true} if this string contains {@code other}, {@code false} otherwise
*/
public boolean contains(final String other) {
return other != null && toString().contains(other);
}
/**
* Determine whether this string contains another string (case-insensitive).
*
* @param other the string to test
* @return {@code true} if this string contains {@code other}, {@code false} otherwise
*/
public boolean containsIgnoreCase(final ByteString other) {
return other == this || other != null && arrayIndexOfIgnoreCase(bytes, offs, other.bytes, other.offs, other.len) != -1;
}
/**
* Determine whether this string contains another string (case-sensitive).
*
* @param other the string to test
* @return {@code true} if this string contains {@code other}, {@code false} otherwise
*/
public boolean containsIgnoreCase(final String other) {
return arrayIndexOfIgnoreCase(bytes, offs, other) != -1;
}
public int indexOf(final ByteString other) {
return arrayIndexOf(bytes, offs, other.bytes, other.offs, other.len);
}
public int indexOf(final ByteString other, int start) {
if (start > len) return -1;
if (start < 0) start = 0;
return arrayIndexOf(bytes, offs + start, other.bytes, other.offs, other.len);
}
public int indexOf(final String other) {
return arrayIndexOf(bytes, offs, other);
}
public int indexOf(final String other, int start) {
if (start > len) return -1;
if (start < 0) start = 0;
return arrayIndexOf(bytes, offs + start, other);
}
public int indexOfIgnoreCase(final ByteString other) {
return arrayIndexOfIgnoreCase(bytes, offs, other.bytes, other.offs, other.len);
}
public int indexOfIgnoreCase(final ByteString other, int start) {
if (start > len) return -1;
if (start < 0) start = 0;
return arrayIndexOfIgnoreCase(bytes, offs + start, other.bytes, other.offs, other.len);
}
public int indexOfIgnoreCase(final String other) {
return arrayIndexOfIgnoreCase(bytes, offs, other);
}
public int indexOfIgnoreCase(final String other, int start) {
if (start > len) return -1;
if (start < 0) start = 0;
return arrayIndexOfIgnoreCase(bytes, offs + start, other);
}
public int lastIndexOf(final ByteString other) {
return arrayLastIndexOf(bytes, offs, other.bytes, other.offs, other.len);
}
public int lastIndexOf(final ByteString other, int start) {
if (start > len) return -1;
if (start < 0) start = 0;
return arrayLastIndexOf(bytes, offs + start, other.bytes, other.offs, other.len);
}
public int lastIndexOf(final String other) {
return arrayLastIndexOf(bytes, offs, other);
}
public int lastIndexOf(final String other, int start) {
return arrayLastIndexOf(bytes, offs + start, other);
}
public int lastIndexOfIgnoreCase(final ByteString other) {
return arrayLastIndexOfIgnoreCase(bytes, offs, other.bytes, other.offs, other.len);
}
public int lastIndexOfIgnoreCase(final ByteString other, int start) {
if (start > len) return -1;
if (start < 0) start = 0;
return arrayLastIndexOfIgnoreCase(bytes, offs + start, other.bytes, other.offs, other.len);
}
public int lastIndexOfIgnoreCase(final String other) {
return arrayLastIndexOfIgnoreCase(bytes, offs, other);
}
public int lastIndexOfIgnoreCase(final String other, int start) {
return arrayLastIndexOfIgnoreCase(bytes, offs + start, other);
}
public boolean regionMatches(boolean ignoreCase, int offset, byte[] other, int otherOffset, int len) {
if (offset < 0 || otherOffset < 0 || offset + len > this.len || otherOffset + len > other.length) {
return false;
}
if (ignoreCase) {
return equalsIgnoreCase(bytes, offset + offs, other, otherOffset, len);
} else {
return equals(bytes, offset + offs, other, otherOffset, len);
}
}
public boolean regionMatches(boolean ignoreCase, int offset, ByteString other, int otherOffset, int len) {
if (offset < 0 || otherOffset < 0 || offset + len > this.len || otherOffset + len > other.len) {
return false;
}
if (ignoreCase) {
return equalsIgnoreCase(bytes, offset + offs, other.bytes, otherOffset, len);
} else {
return equals(bytes, offset + offs, other.bytes, otherOffset, len);
}
}
public boolean regionMatches(boolean ignoreCase, int offset, String other, int otherOffset, int len) {
if (offset < 0 || otherOffset < 0 || offset + len > this.len || otherOffset + len > other.length()) {
return false;
}
if (ignoreCase) {
return equalsIgnoreCase(bytes, offset + offs, other, otherOffset, len);
} else {
return equals(bytes, offset + offs, other, otherOffset, len);
}
}
private static boolean equalsIgnoreCase(final byte[] a, int aOffs, String string, int stringOffset, int length) {
char ch;
for (int i = 0; i < length; i ++) {
ch = string.charAt(i + stringOffset);
if (ch > 0xff) {
return false;
}
if (a[i + aOffs] != (byte) ch) {
return false;
}
}
return true;
}
private static boolean equals(final byte[] a, int aOffs, String string, int stringOffset, int length) {
char ch;
for (int i = 0; i < length; i ++) {
ch = string.charAt(i + stringOffset);
if (ch > 0xff) {
return false;
}
if (upperCase(a[i + aOffs]) != upperCase((byte) ch)) {
return false;
}
}
return true;
}
public boolean startsWith(ByteString prefix) {
return regionMatches(false, 0, prefix, 0, prefix.length());
}
public boolean startsWith(String prefix) {
return regionMatches(false, 0, prefix, 0, prefix.length());
}
public boolean startsWith(char prefix) {
return prefix <= 0xff && len > 0 && bytes[offs] == (byte) prefix;
}
public boolean startsWithIgnoreCase(ByteString prefix) {
return regionMatches(true, 0, prefix, 0, prefix.length());
}
public boolean startsWithIgnoreCase(String prefix) {
return regionMatches(true, 0, prefix, 0, prefix.length());
}
public boolean startsWithIgnoreCase(char prefix) {
return prefix <= 0xff && len > 0 && upperCase(bytes[offs]) == upperCase((byte) prefix);
}
public boolean endsWith(ByteString suffix) {
final int suffixLength = suffix.len;
return regionMatches(false, len - suffixLength, suffix, 0, suffixLength);
}
public boolean endsWith(String suffix) {
final int suffixLength = suffix.length();
return regionMatches(false, len - suffixLength, suffix, 0, suffixLength);
}
public boolean endsWith(char suffix) {
final int len = this.len;
return suffix <= 0xff && len > 0 && bytes[offs + len - 1] == (byte) suffix;
}
public boolean endsWithIgnoreCase(ByteString suffix) {
final int suffixLength = suffix.length();
return regionMatches(true, len - suffixLength, suffix, 0, suffixLength);
}
public boolean endsWithIgnoreCase(String suffix) {
final int suffixLength = suffix.length();
return regionMatches(true, len - suffixLength, suffix, 0, suffixLength);
}
public boolean endsWithIgnoreCase(char suffix) {
final int len = this.len;
return suffix <= 0xff && len > 0 && upperCase(bytes[offs + len - 1]) == upperCase((byte) suffix);
}
public ByteString concat(byte[] suffixBytes) {
return concat(suffixBytes, 0, suffixBytes.length);
}
public ByteString concat(byte[] suffixBytes, int offs, int len) {
if (len <= 0) { return this; }
final int length = this.len;
byte[] newBytes = Arrays.copyOfRange(bytes, this.offs, length + len);
System.arraycopy(suffixBytes, offs, newBytes, length, len);
return new ByteString(newBytes);
}
public ByteString concat(ByteString suffix) {
return concat(suffix.bytes, suffix.offs, suffix.len);
}
public ByteString concat(ByteString suffix, int offs, int len) {
return concat(suffix.bytes, offs + suffix.offs, min(len, suffix.len));
}
public ByteString concat(String suffix) {
return concat(suffix, 0, suffix.length());
}
@SuppressWarnings("deprecation")
private static byte[] getStringBytes(final boolean trust, final byte[] dst, final int dstOffs, final String src, final int srcOffs, final int len) {
if (trust) {
src.getBytes(srcOffs, srcOffs + len, dst, dstOffs);
} else {
for (int i = srcOffs; i < len; i++) {
char c = src.charAt(i);
if (c > 0xff) {
throw new IllegalArgumentException("Invalid string contents");
}
dst[i + dstOffs] = (byte) c;
}
}
return dst;
}
public ByteString concat(String suffix, int offs, int len) {
if (len <= 0) { return this; }
final byte[] bytes = this.bytes;
final int length = this.len;
byte[] newBytes = Arrays.copyOfRange(bytes, offs, offs + length + len);
getStringBytes(false, newBytes, length, suffix, offs, len);
return new ByteString(newBytes);
}
public static ByteString concat(String prefix, ByteString suffix) {
final int prefixLength = prefix.length();
final byte[] suffixBytes = suffix.bytes;
final int suffixLength = suffixBytes.length;
final byte[] newBytes = new byte[prefixLength + suffixLength];
getStringBytes(false, newBytes, 0, prefix, 0, prefixLength);
System.arraycopy(suffixBytes, suffix.offs, newBytes, prefixLength, suffixLength);
return new ByteString(newBytes);
}
public static ByteString concat(String prefix, String suffix) {
final int prefixLength = prefix.length();
final int suffixLength = suffix.length();
final byte[] newBytes = new byte[prefixLength + suffixLength];
getStringBytes(false, newBytes, 0, prefix, 0, prefixLength);
getStringBytes(false, newBytes, prefixLength, suffix, 0, suffixLength);
return new ByteString(newBytes);
}
public char charAt(final int index) {
if (index < 0 || index > len) {
throw new ArrayIndexOutOfBoundsException();
}
return (char) (bytes[index + offs] & 0xff);
}
public ByteString subSequence(final int start, final int end) {
return substring(start, end);
}
}
xnio-3.6.2.Final/api/src/main/java/org/xnio/Cancellable.java 0000664 0000000 0000000 00000000407 13241300306 0023521 0 ustar 00root root 0000000 0000000
package org.xnio;
/**
* An operation which may be cancelled.
*/
public interface Cancellable {
/**
* Cancel an operation. The actual cancel may be synchronous or asynchronous.
*
* @return this instance
*/
Cancellable cancel();
}
xnio-3.6.2.Final/api/src/main/java/org/xnio/ChainedChannelListener.java 0000664 0000000 0000000 00000001511 13241300306 0025663 0 ustar 00root root 0000000 0000000
package org.xnio;
import java.nio.channels.Channel;
/**
* A channel listener that chains calls to a number of other channel listeners.
*
* @author David M. Lloyd
*/
public final class ChainedChannelListener implements ChannelListener {
private final ChannelListener super T>[] listeners;
/**
* Construct a new instance. The given array will be copied.
*
* @param listeners the listeners to chain to
*/
public ChainedChannelListener(final ChannelListener super T>... listeners) {
this.listeners = listeners.clone();
}
public void handleEvent(final T channel) {
for (ChannelListener super T> listener : listeners) {
ChannelListeners.invokeChannelListener(channel, listener);
}
}
}
xnio-3.6.2.Final/api/src/main/java/org/xnio/ChannelDestination.java 0000664 0000000 0000000 00000001774 13241300306 0025116 0 ustar 00root root 0000000 0000000
package org.xnio;
import java.nio.channels.Channel;
import org.xnio.channels.BoundChannel;
/**
* A channel destination. This is the inverse of {@code ChannelSource}; it is used to accept a single connection from a remote
* peer.
*
* @param the channel type
*
* @since 1.2
*/
public interface ChannelDestination {
/**
* Accept a connection. The bind listener will be called when the channel is bound; the open listener will be called
* when the connection is accepted. It is not guaranteed that the open listener will be called after the bind listener.
*
* @param openListener the listener which will be notified when the channel is open, or {@code null} for none
* @param bindListener the listener which will be notified when the channel is bound locally, or {@code null} for none
* @return the future connection
*/
IoFuture extends T> accept(ChannelListener super T> openListener, ChannelListener super BoundChannel> bindListener);
}
xnio-3.6.2.Final/api/src/main/java/org/xnio/ChannelExceptionHandler.java 0000664 0000000 0000000 00000001030 13241300306 0026052 0 ustar 00root root 0000000 0000000
package org.xnio;
import java.io.IOException;
import java.nio.channels.Channel;
import java.util.EventListener;
/**
* An exception handler for utility channel listeners.
*
* @author David M. Lloyd
*/
public interface ChannelExceptionHandler extends EventListener {
/**
* Handle an exception on the channel.
*
* @param channel the channel
* @param exception the exception
*/
void handleException(T channel, IOException exception);
}
xnio-3.6.2.Final/api/src/main/java/org/xnio/ChannelListener.java 0000664 0000000 0000000 00000005243 13241300306 0024415 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source
*
* Copyright 2009 Red Hat, Inc. and/or its affiliates.
*
* 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;
import java.nio.channels.Channel;
import java.util.EventListener;
import org.jboss.logging.Logger;
import static org.xnio._private.Messages.listenerMsg;
/**
* A listener for channel events. Possible channel events include: channel readable, channel writable, channel
* opened, channel closed, channel bound, channel unbound.
*
* @param the channel type
*
* @since 2.0
*/
public interface ChannelListener extends EventListener {
/**
* Handle the event on this channel.
*
* @param channel the channel event
*/
void handleEvent(T channel);
/**
* A setter for a channel listener. The indirection is necessary
* because while covariance is supported on return types, contravariance is not supported on parameters.
*
* @param the channel type
*
* @since 2.0
*/
interface Setter {
/**
* Set the listener, or {@code null} to ignore the associated event type.
*
* @param listener the new listener
*/
void set(ChannelListener super T> listener);
}
/**
* A simple implementation of {@link Setter}.
*
* @param the channel type
*
* @since 3.0
*/
class SimpleSetter implements Setter {
private ChannelListener super T> channelListener;
/** {@inheritDoc} */
public void set(final ChannelListener super T> listener) {
listenerMsg.logf(SimpleSetter.class.getName(), Logger.Level.TRACE, null, "Setting channel listener to %s", listener);
channelListener = listener;
}
/**
* Get the channel listener set on this setter.
*
* @return the channel listener
*/
public ChannelListener super T> get() {
return channelListener;
}
public String toString() {
return "Simple channel listener setter (currently=" + channelListener + ")";
}
}
}
xnio-3.6.2.Final/api/src/main/java/org/xnio/ChannelListeners.java 0000664 0000000 0000000 00000135722 13241300306 0024606 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source
*
* Copyright 2011 Red Hat, Inc. and/or its affiliates, and individual
* contributors as indicated by the @author tags.
*
* 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;
import java.io.Closeable;
import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.FileChannel;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import org.xnio.channels.AcceptingChannel;
import org.xnio.channels.Channels;
import org.xnio.channels.ConnectedChannel;
import org.xnio.channels.StreamSinkChannel;
import org.xnio.channels.StreamSourceChannel;
import org.xnio.channels.SuspendableReadChannel;
import org.xnio.channels.SuspendableWriteChannel;
import org.xnio.channels.WritableMessageChannel;
import static org.xnio._private.Messages.listenerMsg;
import static org.xnio._private.Messages.msg;
/**
* Channel listener utility methods.
*
* @author David M. Lloyd
*/
@SuppressWarnings("unused")
public final class ChannelListeners {
private static final ChannelListener NULL_LISTENER = new ChannelListener() {
public void handleEvent(final Channel channel) {
}
public String toString() {
return "Null channel listener";
}
};
private static final ChannelListener.Setter> NULL_SETTER = new ChannelListener.Setter() {
public void set(final ChannelListener super Channel> channelListener) {
}
public String toString() {
return "Null channel listener setter";
}
};
private static ChannelListener CLOSING_CHANNEL_LISTENER = new ChannelListener() {
public void handleEvent(final Channel channel) {
IoUtils.safeClose(channel);
}
public String toString() {
return "Closing channel listener";
}
};
private ChannelListeners() {
}
/**
* Invoke a channel listener on a given channel, logging any errors.
*
* @param channel the channel
* @param channelListener the channel listener
* @param the channel type
* @return {@code true} if the listener completed successfully, or {@code false} if it failed
*/
public static ]