BufferKit.java
package space.sunqian.fs.io;
import space.sunqian.annotation.Nonnull;
import space.sunqian.annotation.Nullable;
import space.sunqian.fs.Fs;
import space.sunqian.fs.base.bytes.BytesKit;
import space.sunqian.fs.base.chars.CharsKit;
import space.sunqian.fs.base.math.MathKit;
import java.io.OutputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.CharBuffer;
import java.nio.ReadOnlyBufferException;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
/**
* Utilities for {@link Buffer} related.
*
* @author sunqian
*/
public class BufferKit {
/**
* Returns the actual start index (inclusive) of the backing array in the given buffer.
*
* @param buffer the given buffer
* @return the actual start index (inclusive) of the backing array in the given buffer
* @throws ReadOnlyBufferException if the buffer is backed by an array but is read-only
* @throws UnsupportedOperationException if the buffer is not backed by an accessible array
*/
public static int arrayStartIndex(
@Nonnull Buffer buffer
) throws ReadOnlyBufferException, UnsupportedOperationException {
return buffer.arrayOffset() + buffer.position();
}
/**
* Returns the actual start index (exclusive) of the backing array in the given buffer.
*
* @param buffer the given buffer
* @return the actual start index (exclusive) of the backing array in the given buffer
* @throws ReadOnlyBufferException if the buffer is backed by an array but is read-only
* @throws UnsupportedOperationException if the buffer is not backed by an accessible array
*/
public static int arrayEndIndex(
@Nonnull Buffer buffer
) throws ReadOnlyBufferException, UnsupportedOperationException {
return buffer.arrayOffset() + buffer.position() + buffer.remaining();
}
/**
* Reads all data from the source buffer into a new array, continuing until reaches the end of the source buffer,
* and returns the array.
* <p>
* If reaches the end of the source buffer and no data is read, returns {@code null}.
* <p>
* The buffer's position increments by the actual read number.
*
* @param src the source buffer
* @return a new array containing the read data, or {@code null} if reaches the end of the source buffer and no data
* is read
*/
public static byte @Nullable [] read(@Nonnull ByteBuffer src) {
int len = src.remaining();
if (len == 0) {
return null;
}
byte[] result = new byte[len];
src.get(result);
return result;
}
/**
* Reads a specified length of data from the source buffer into a new array, and returns the array. If the specified
* length is {@code 0}, returns an empty array without reading. Otherwise, this method keeps reading until the read
* number reaches the specified length or reaches the end of the source buffer.
* <p>
* If reaches the end of the source buffer and no data is read, returns {@code null}.
* <p>
* The buffer's position increments by the actual read number.
*
* @param src the source buffer
* @param len the specified read length, must {@code >= 0}
* @return a new array containing the read data, or {@code null} if reaches the end of the source buffer and no data
* is read
* @throws IllegalArgumentException if the specified read length is illegal
*/
public static byte @Nullable [] read(@Nonnull ByteBuffer src, int len) throws IllegalArgumentException {
IOChecker.checkLen(len);
if (len == 0) {
return new byte[0];
}
if (!src.hasRemaining()) {
return null;
}
int actualLen = Math.min(len, src.remaining());
byte[] result = new byte[actualLen];
src.get(result);
return result;
}
/**
* Reads data from the source buffer into the destination array, until the read number reaches the array's length or
* reaches the end of the source buffer, and returns the actual number of bytes read to.
* <p>
* If the array's length is {@code 0}, returns {@code 0} without reading. If reaches the end of the source buffer
* and no data is read, returns {@code -1}.
* <p>
* The buffer's position increments by the actual read number.
*
* @param src the source buffer
* @param dst the destination array
* @return the actual number of bytes read to, or {@code -1} if reaches the end of the source buffer and no data is
* read
*/
public static int readTo(@Nonnull ByteBuffer src, byte @Nonnull [] dst) {
return readTo0(src, dst, 0, dst.length);
}
/**
* Reads a specified length of data from the source buffer into the destination array, starting at the specified
* offset, until the read number reaches the specified length or reaches the end of the source buffer, and returns
* the actual number of bytes read to.
* <p>
* If the specified length is {@code 0}, returns {@code 0} without reading. If reaches the end of the source buffer
* and no data is read, returns {@code -1}.
* <p>
* The buffer's position increments by the actual read number.
*
* @param src the source buffer
* @param dst the destination array
* @param off the specified offset of the array
* @param len the specified length to read
* @return the actual number of bytes read to, or {@code -1} if reaches the end of the source buffer and no data is
* read
* @throws IndexOutOfBoundsException if the arguments are out of bounds
*/
public static int readTo(
@Nonnull ByteBuffer src, byte @Nonnull [] dst, int off, int len
) throws IndexOutOfBoundsException {
IOChecker.checkOffLen(off, len, dst.length);
return readTo0(src, dst, off, len);
}
/**
* Reads data from the source buffer into the destination buffer, until reaches the end of any buffer, and returns
* the actual number of bytes read to.
* <p>
* If the destination buffer's remaining is {@code 0}, returns {@code 0} without reading; if reaches the end of the
* source buffer and no data is read, returns {@code -1}.
* <p>
* The both buffers' positions increments by the actual read number.
*
* @param src the source buffer
* @param dst the destination buffer
* @return the actual number of bytes read to, or {@code -1} if reaches the end of the source buffer and no data is
* read
* @throws IORuntimeException if an I/O error occurs
*/
public static int readTo(@Nonnull ByteBuffer src, @Nonnull ByteBuffer dst) throws IORuntimeException {
if (!dst.hasRemaining()) {
return 0;
}
if (!src.hasRemaining()) {
return -1;
}
try {
int actualLen = Math.min(src.remaining(), dst.remaining());
if (src.remaining() <= dst.remaining()) {
dst.put(src);
} else {
ByteBuffer srcSlice = slice0(src, 0, dst.remaining());
dst.put(srcSlice);
src.position(src.position() + actualLen);
}
return actualLen;
} catch (Exception e) {
throw new IORuntimeException(e);
}
}
/**
* Reads a specified length of data from the source buffer into the destination buffer, until the read number
* reaches the specified length or reaches the end of any buffer, and returns the actual number of bytes read to.
* <p>
* If the specified length or destination buffer's remaining is {@code 0}, returns {@code 0} without reading; if
* reaches the end of the source buffer and no data is read, returns {@code -1}.
* <p>
* The both buffers' positions increments by the actual read number.
*
* @param src the source buffer
* @param dst the specified buffer
* @param len the specified length, must {@code >= 0}
* @return the actual number of bytes read to, or {@code -1} if reaches the end of the source buffer and no data is
* read
* @throws IllegalArgumentException if the specified read length is illegal
* @throws IORuntimeException if an I/O error occurs
*/
public static int readTo(
@Nonnull ByteBuffer src, @Nonnull ByteBuffer dst, int len
) throws IllegalArgumentException, IORuntimeException {
IOChecker.checkLen(len);
if (len == 0) {
return 0;
}
if (!dst.hasRemaining()) {
return 0;
}
if (!src.hasRemaining()) {
return -1;
}
try {
int actualLen = MathKit.min(src.remaining(), dst.remaining(), len);
ByteBuffer srcBuf;
if (src.remaining() > actualLen) {
srcBuf = slice0(src, 0, actualLen);
} else {
srcBuf = src;
}
dst.put(srcBuf);
if (srcBuf != src) {
src.position(src.position() + actualLen);
}
return actualLen;
} catch (Exception e) {
throw new IORuntimeException(e);
}
}
/**
* Reads data from the source buffer into the destination channel, until reaches the end of the source buffer, and
* returns the actual number of bytes read to.
* <p>
* If reaches the end of the source buffer and no data is read, returns {@code -1}.
* <p>
* The buffer's position increments by the actual read number.
*
* @param src the source buffer
* @param dst the destination channel
* @return the actual number of bytes read to, or {@code -1} if reaches the end of the source buffer and no data is
* read
* @throws IORuntimeException if an I/O error occurs
*/
public static int readTo(@Nonnull ByteBuffer src, @Nonnull WritableByteChannel dst) throws IORuntimeException {
if (src.remaining() == 0) {
return -1;
}
try {
int actualLen = src.remaining();
while (src.remaining() > 0) {
dst.write(src);
}
return actualLen;
} catch (Exception e) {
throw new IORuntimeException(e);
}
}
/**
* Reads a specified length of data from the source buffer into the destination channel, until the read number
* reaches the specified length or reaches the end of the source buffer, returns the actual number of bytes read
* to.
* <p>
* If the specified length is {@code 0}, returns {@code 0} without reading; if reaches the end of the source buffer
* and no data is read, returns {@code -1}.
* <p>
* The buffer's position increments by the actual read number.
*
* @param src the source buffer
* @param dst the destination channel
* @param len the specified length, must {@code >= 0}
* @return the actual number of bytes read to, or {@code -1} if reaches the end of the source buffer and no data is
* read
* @throws IllegalArgumentException if the specified read length is illegal
* @throws IORuntimeException if an I/O error occurs
*/
public static int readTo(
@Nonnull ByteBuffer src, @Nonnull WritableByteChannel dst, int len
) throws IllegalArgumentException, IORuntimeException {
IOChecker.checkLen(len);
return readTo0(src, dst, len);
}
/**
* Reads data from the source buffer into the output stream, until reaches the end of the source buffer, and returns
* the actual number of bytes read to.
* <p>
* If reaches the end of the source buffer and no data is read, returns {@code -1}.
* <p>
* The buffer's position increments by the actual read number.
*
* @param src the source buffer
* @param dst the output stream
* @return the actual number of bytes read to, or {@code -1} if reaches the end of the source buffer and no data is
* read
* @throws IORuntimeException if an I/O error occurs
*/
public static int readTo(@Nonnull ByteBuffer src, @Nonnull OutputStream dst) throws IORuntimeException {
if (src.remaining() == 0) {
return -1;
}
return readTo0WithActualLen(src, dst, src.remaining());
}
/**
* Reads a specified length of data from the source buffer into the output stream, until the read number reaches the
* specified length or reaches the end of the source buffer, returns the actual number of bytes read to.
* <p>
* If the specified length is {@code 0}, returns {@code 0} without reading; if reaches the end of the source buffer
* and no data is read, returns {@code -1}.
* <p>
* The buffer's position increments by the actual read number.
*
* @param src the source buffer
* @param dst the output stream
* @param len the specified length, must {@code >= 0}
* @return the actual number of bytes read to, or {@code -1} if reaches the end of the source buffer and no data is
* read
* @throws IllegalArgumentException if the specified read length is illegal
* @throws IORuntimeException if an I/O error occurs
*/
public static int readTo(
@Nonnull ByteBuffer src, @Nonnull OutputStream dst, int len
) throws IllegalArgumentException, IORuntimeException {
IOChecker.checkLen(len);
return readTo0(src, dst, len);
}
static int readTo0(@Nonnull ByteBuffer src, byte @Nonnull [] dst, int off, int len) {
if (len == 0) {
return 0;
}
if (!src.hasRemaining()) {
return -1;
}
int actualLen = Math.min(len, src.remaining());
src.get(dst, off, actualLen);
return actualLen;
}
static int readTo0(
@Nonnull ByteBuffer src, @Nonnull WritableByteChannel dst, int len
) throws IORuntimeException {
if (len == 0) {
return 0;
}
if (src.remaining() == 0) {
return -1;
}
try {
int actualLen = Math.min(src.remaining(), len);
int oldLimit = src.limit();
src.limit(src.position() + actualLen);
while (src.remaining() > 0) {
dst.write(src);
}
src.limit(oldLimit);
return actualLen;
} catch (Exception e) {
throw new IORuntimeException(e);
}
}
static int readTo0(
@Nonnull ByteBuffer src, @Nonnull OutputStream dst, int len
) throws IORuntimeException {
if (len == 0) {
return 0;
}
if (src.remaining() == 0) {
return -1;
}
int actualLen = Math.min(src.remaining(), len);
return readTo0WithActualLen(src, dst, actualLen);
}
static int readTo0WithActualLen(
@Nonnull ByteBuffer src, @Nonnull OutputStream dst, int actualLen
) throws IORuntimeException {
try {
if (src.hasArray()) {
dst.write(src.array(), BufferKit.arrayStartIndex(src), actualLen);
src.position(src.position() + actualLen);
} else {
byte[] buf = new byte[actualLen];
src.get(buf);
dst.write(buf);
}
return actualLen;
} catch (Exception e) {
throw new IORuntimeException(e);
}
}
/**
* Reads all data from the source buffer into a new array, continuing until reaches the end of the source buffer,
* and returns the array.
* <p>
* If reaches the end of the source buffer and no data is read, returns {@code null}.
* <p>
* The buffer's position increments by the actual read number.
*
* @param src the source buffer
* @return a new array containing the read data, or {@code null} if reaches the end of the source buffer and no data
* is read
*/
public static char @Nullable [] read(@Nonnull CharBuffer src) {
int len = src.remaining();
if (len == 0) {
return null;
}
char[] result = new char[len];
src.get(result);
return result;
}
/**
* Reads a specified length of data from the source buffer into a new array, and returns the array. If the specified
* length is {@code 0}, returns an empty array without reading. Otherwise, this method keeps reading until the read
* number reaches the specified length or reaches the end of the source buffer.
* <p>
* If reaches the end of the source buffer and no data is read, returns {@code null}.
* <p>
* The buffer's position increments by the actual read number.
*
* @param src the source buffer
* @param len the specified read length, must {@code >= 0}
* @return a new array containing the read data, or {@code null} if reaches the end of the source buffer and no data
* is read
* @throws IllegalArgumentException if the specified read length is illegal
*/
public static char @Nullable [] read(@Nonnull CharBuffer src, int len) throws IllegalArgumentException {
IOChecker.checkLen(len);
if (len == 0) {
return new char[0];
}
if (!src.hasRemaining()) {
return null;
}
int actualLen = Math.min(len, src.remaining());
char[] result = new char[actualLen];
src.get(result);
return result;
}
/**
* Reads data from the source buffer into the destination array, until the read number reaches the array's length or
* reaches the end of the source buffer, and returns the actual number of chars read to.
* <p>
* If the array's length is {@code 0}, returns {@code 0} without reading. If reaches the end of the source buffer
* and no data is read, returns {@code -1}.
* <p>
* The buffer's position increments by the actual read number.
*
* @param src the source buffer
* @param dst the destination array
* @return the actual number of chars read to, or {@code -1} if reaches the end of the source buffer and no data is
* read
*/
public static int readTo(@Nonnull CharBuffer src, char @Nonnull [] dst) {
return readTo0(src, dst, 0, dst.length);
}
/**
* Reads a specified length of data from the source buffer into the destination array, starting at the specified
* offset, until the read number reaches the specified length or reaches the end of the source buffer, and returns
* the actual number of chars read to.
* <p>
* If the specified length is {@code 0}, returns {@code 0} without reading. If reaches the end of the source buffer
* and no data is read, returns {@code -1}.
* <p>
* The buffer's position increments by the actual read number.
*
* @param src the source buffer
* @param dst the destination array
* @param off the specified offset of the array
* @param len the specified length to read
* @return the actual number of chars read to, or {@code -1} if reaches the end of the source buffer and no data is
* read
* @throws IndexOutOfBoundsException if the arguments are out of bounds
*/
public static int readTo(
@Nonnull CharBuffer src, char @Nonnull [] dst, int off, int len
) throws IndexOutOfBoundsException {
IOChecker.checkOffLen(off, len, dst.length);
return readTo0(src, dst, off, len);
}
/**
* Reads data from the source buffer into the destination buffer, until reaches the end of any buffer, and returns
* the actual number of chars read to.
* <p>
* If the destination buffer's remaining is {@code 0}, returns {@code 0} without reading; if reaches the end of the
* source buffer and no data is read, returns {@code -1}.
* <p>
* The both buffers' positions increments by the actual read number.
*
* @param src the source buffer
* @param dst the destination buffer
* @return the actual number of chars read to, or {@code -1} if reaches the end of the source buffer and no data is
* read
* @throws IORuntimeException if an I/O error occurs
*/
public static int readTo(@Nonnull CharBuffer src, @Nonnull CharBuffer dst) throws IORuntimeException {
if (!dst.hasRemaining()) {
return 0;
}
if (!src.hasRemaining()) {
return -1;
}
try {
int actualLen = Math.min(src.remaining(), dst.remaining());
if (src.remaining() <= dst.remaining()) {
dst.put(src);
} else {
CharBuffer srcSlice = slice0(src, 0, dst.remaining());
dst.put(srcSlice);
src.position(src.position() + actualLen);
}
return actualLen;
} catch (Exception e) {
throw new IORuntimeException(e);
}
}
/**
* Reads a specified length of data from the source buffer into the destination buffer, until the read number
* reaches the specified length or reaches the end of any buffer, and returns the actual number of chars read to.
* <p>
* If the specified length or destination buffer's remaining is {@code 0}, returns {@code 0} without reading; if
* reaches the end of the source buffer and no data is read, returns {@code -1}.
* <p>
* The both buffers' positions increments by the actual read number.
*
* @param src the source buffer
* @param dst the specified buffer
* @param len the specified length, must {@code >= 0}
* @return the actual number of chars read to, or {@code -1} if reaches the end of the source buffer and no data is
* read
* @throws IllegalArgumentException if the specified read length is illegal
* @throws IORuntimeException if an I/O error occurs
*/
public static int readTo(
@Nonnull CharBuffer src, @Nonnull CharBuffer dst, int len
) throws IllegalArgumentException, IORuntimeException {
IOChecker.checkLen(len);
if (len == 0) {
return 0;
}
if (!dst.hasRemaining()) {
return 0;
}
if (!src.hasRemaining()) {
return -1;
}
try {
int actualLen = MathKit.min(src.remaining(), dst.remaining(), len);
CharBuffer srcBuf;
if (src.remaining() > actualLen) {
srcBuf = slice0(src, 0, actualLen);
} else {
srcBuf = src;
}
dst.put(srcBuf);
if (srcBuf != src) {
src.position(src.position() + actualLen);
}
return actualLen;
} catch (Exception e) {
throw new IORuntimeException(e);
}
}
/**
* Reads data from the source buffer into the output appender, until reaches the end of the source buffer, and
* returns the actual number of bytes read to.
* <p>
* If reaches the end of the source buffer and no data is read, returns {@code -1}.
* <p>
* The buffer's position increments by the actual read number.
*
* @param src the source buffer
* @param dst the output appender
* @return the actual number of bytes read to, or {@code -1} if reaches the end of the source buffer and no data is
* read
* @throws IORuntimeException if an I/O error occurs
*/
public static int readTo(@Nonnull CharBuffer src, @Nonnull Appendable dst) throws IORuntimeException {
if (src.remaining() == 0) {
return -1;
}
return readTo0WithActualLen(src, dst, src.remaining());
}
/**
* Reads a specified length of data from the source buffer into the output appender, until the read number reaches
* the specified length or reaches the end of the source buffer, returns the actual number of bytes read to.
* <p>
* If the specified length is {@code 0}, returns {@code 0} without reading; if reaches the end of the source buffer
* and no data is read, returns {@code -1}.
* <p>
* The buffer's position increments by the actual read number.
*
* @param src the source buffer
* @param dst the output appender
* @param len the specified length, must {@code >= 0}
* @return the actual number of bytes read to, or {@code -1} if reaches the end of the source buffer and no data is
* read
* @throws IllegalArgumentException if the specified read length is illegal
* @throws IORuntimeException if an I/O error occurs
*/
public static int readTo(
@Nonnull CharBuffer src, @Nonnull Appendable dst, int len
) throws IllegalArgumentException, IORuntimeException {
IOChecker.checkLen(len);
return readTo0(src, dst, len);
}
static int readTo0(
@Nonnull CharBuffer src, char @Nonnull [] dst, int off, int len
) {
if (len == 0) {
return 0;
}
if (!src.hasRemaining()) {
return -1;
}
int actualLen = Math.min(len, src.remaining());
src.get(dst, off, actualLen);
return actualLen;
}
static int readTo0(
@Nonnull CharBuffer src, @Nonnull Appendable dst, int len
) throws IORuntimeException {
if (len == 0) {
return 0;
}
if (src.remaining() == 0) {
return -1;
}
int actualLen = Math.min(src.remaining(), len);
return readTo0WithActualLen(src, dst, actualLen);
}
static int readTo0WithActualLen(
@Nonnull CharBuffer src, @Nonnull Appendable dst, int actualLen
) throws IORuntimeException {
try {
dst.append(src, 0, actualLen);
src.position(src.position() + actualLen);
return actualLen;
} catch (Exception e) {
throw new IORuntimeException(e);
}
}
/**
* Reads all data from the source buffer as a string, continuing until reaches the end of the source buffer, and
* returns the string.
* <p>
* If reaches the end of the source buffer and no data is read, returns {@code null}.
* <p>
* The buffer's position increments by the actual read number.
*
* @param src the source buffer
* @return a string represents the read data, or {@code null} if reaches the end of the source buffer and no data is
* read
*/
public static @Nullable String string(@Nonnull CharBuffer src) {
char[] chars = read(src);
return chars == null ? null : new String(chars);
}
/**
* Reads a specified length of data from the source buffer as a string, and returns the string. If the specified
* length is {@code 0}, returns an empty string without reading. Otherwise, this method keeps reading until the read
* number reaches the specified length or reaches the end of the source buffer.
* <p>
* If reaches the end of the source buffer and no data is read, returns {@code null}.
* <p>
* The buffer's position increments by the actual read number.
*
* @param src the source buffer
* @param len the specified read length, must {@code >= 0}
* @return a string represents the read data, or {@code null} if reaches the end of the source buffer and no data is
* read
* @throws IllegalArgumentException if the specified read length is illegal
*/
public static @Nullable String string(@Nonnull CharBuffer src, int len) throws IllegalArgumentException {
char[] chars = read(src, len);
return chars == null ? null : new String(chars);
}
/**
* Reads all data from the source buffer as a string with {@link CharsKit#defaultCharset()}, continuing until
* reaches the end of the source buffer, and returns the string.
* <p>
* If reaches the end of the source buffer and no data is read, returns {@code null}.
* <p>
* The buffer's position increments by the actual read number.
*
* @param src the source buffer
* @return a string represents the read data, with {@link CharsKit#defaultCharset()}, or {@code null} if reaches the
* end of the source buffer and no data is read
*/
public static @Nullable String string(@Nonnull ByteBuffer src) {
return string(src, CharsKit.defaultCharset());
}
/**
* Reads all data from the source buffer as a string with the specified charset, continuing until reaches the end of
* the source buffer, and returns the string.
* <p>
* If reaches the end of the source buffer and no data is read, returns {@code null}.
* <p>
* The buffer's position increments by the actual read number.
*
* @param src the source buffer
* @param cs the specified charset
* @return a string represents the read data, with the specified charset, or {@code null} if reaches the end of the
* source buffer and no data is read
*/
public static @Nullable String string(@Nonnull ByteBuffer src, @Nonnull Charset cs) {
byte[] bytes = read(src);
return bytes == null ? null : new String(bytes, cs);
}
/**
* Writes string to the specified buffer with {@link CharsKit#defaultCharset()}.
*
* @param dst the specified buffer
* @param str the string to write to the buffer
* @throws IORuntimeException if an I/O error occurs
*/
public static void write(
@Nonnull ByteBuffer dst, @Nonnull String str
) throws IORuntimeException {
write(dst, str, CharsKit.defaultCharset());
}
/**
* Writes string to the specified buffer with the specified charset.
*
* @param dst the specified buffer
* @param str the string to write to the buffer
* @param charset the specified charset
* @throws IORuntimeException if an I/O error occurs
*/
public static void write(
@Nonnull ByteBuffer dst, @Nonnull String str, @Nonnull Charset charset
) throws IORuntimeException {
try {
byte[] bytes = str.getBytes(charset);
dst.put(bytes);
} catch (Exception e) {
throw new IORuntimeException(e);
}
}
/**
* Creates a new buffer with the specified length. Its content is a shared subsequence of the given buffer's
* content, starting at the given buffer's current position.
* <p>
* Changes to the given buffer's content will be visible in the new buffer, and vice versa. The new buffer's
* position will be zero, its capacity and limit will be the specified length. The new buffer will be direct if, and
* only if, the given buffer is direct, and it will be read-only if, and only if, the given buffer is read-only.
* <p>
* The position and limit of the given buffer will not be changed.
*
* @param src the given buffer
* @param len the specified length
* @return a new buffer whose content is a shared subsequence of the given buffer's content
* @throws IllegalArgumentException if the specified read length is illegal
*/
public static @Nonnull ByteBuffer slice(@Nonnull ByteBuffer src, int len) throws IllegalArgumentException {
IOChecker.checkLen(len);
return slice0(src, 0, len);
}
/**
* Creates a new buffer with the specified length. Its content is a shared subsequence of the given buffer's
* content, starting at the specified offset of given buffer's current position ({@code src.position() + off}).
* <p>
* Changes to the given buffer's content will be visible in the new buffer, and vice versa. The new buffer's
* position will be zero, its capacity and limit will be the specified length. The new buffer will be direct if, and
* only if, the given buffer is direct, and it will be read-only if, and only if, the given buffer is read-only.
* <p>
* The position and limit of the given buffer will not be changed.
*
* @param src the given buffer
* @param off the specified offset
* @param len the specified length
* @return a new buffer whose content is a shared subsequence of the given buffer's content
* @throws IndexOutOfBoundsException if the offset and length is out of bounds
*/
public static @Nonnull ByteBuffer slice(
@Nonnull ByteBuffer src, int off, int len
) throws IndexOutOfBoundsException {
IOChecker.checkOffLen(off, len, src.remaining());
return slice0(src, off, len);
}
static @Nonnull ByteBuffer slice0(@Nonnull ByteBuffer src, int off, int len) {
int pos = src.position();
int limit = src.limit();
src.position(pos + off);
src.limit(pos + off + len);
ByteBuffer slice = src.slice();
src.position(pos);
src.limit(limit);
return slice;
}
/**
* Creates a new buffer with the specified length. Its content is a shared subsequence of the given buffer's
* content, starting at the given buffer's current position.
* <p>
* Changes to the given buffer's content will be visible in the new buffer, and vice versa. The new buffer's
* position will be zero, its capacity and limit will be the specified length. The new buffer will be direct if, and
* only if, the given buffer is direct, and it will be read-only if, and only if, the given buffer is read-only.
* <p>
* The position and limit of the given buffer will not be changed.
*
* @param src the given buffer
* @param len the specified length
* @return a new buffer whose content is a shared subsequence of the given buffer's content
* @throws IllegalArgumentException if the specified read length is illegal
*/
public static @Nonnull CharBuffer slice(@Nonnull CharBuffer src, int len) throws IllegalArgumentException {
IOChecker.checkLen(len);
return slice0(src, 0, len);
}
/**
* Creates a new buffer with the specified length. Its content is a shared subsequence of the given buffer's
* content, starting at the specified offset of given buffer's current position ({@code src.position() + off}).
* <p>
* Changes to the given buffer's content will be visible in the new buffer, and vice versa. The new buffer's
* position will be zero, its capacity and limit will be the specified length. The new buffer will be direct if, and
* only if, the given buffer is direct, and it will be read-only if, and only if, the given buffer is read-only.
* <p>
* The position and limit of the given buffer will not be changed.
*
* @param src the given buffer
* @param off the specified offset
* @param len the specified length
* @return a new buffer whose content is a shared subsequence of the given buffer's content
* @throws IndexOutOfBoundsException if the offset and length is out of bounds
*/
public static @Nonnull CharBuffer slice(
@Nonnull CharBuffer src, int off, int len
) throws IndexOutOfBoundsException {
IOChecker.checkOffLen(off, len, src.remaining());
return slice0(src, off, len);
}
static @Nonnull CharBuffer slice0(@Nonnull CharBuffer src, int off, int len) {
int pos = src.position();
int limit = src.limit();
src.position(pos + off);
src.limit(pos + off + len);
CharBuffer slice = src.slice();
src.position(pos);
src.limit(limit);
return slice;
}
/**
* Returns a new buffer copied from the given source buffer. The content of the new buffer is independent and copied
* from {@code 0} to {@code src.capacity()} (not {@code src.position()} to {@code src.limit()}).
* <p>
* Position of the source buffer will not be changed. The new buffer's position, limit and capacity are same with
* the source buffer's, and the new buffer is direct if, and only if, the source buffer is direct.
*
* @param src the given source buffer
* @return a new buffer copied from the given source buffer
*/
public static @Nonnull ByteBuffer copy(@Nonnull ByteBuffer src) {
ByteBuffer dst = src.isDirect() ?
ByteBuffer.allocateDirect(src.capacity())
:
ByteBuffer.allocate(src.capacity());
int pos = src.position();
int limit = src.limit();
src.position(0);
src.limit(src.capacity());
dst.put(src);
src.position(pos);
src.limit(limit);
dst.position(pos);
dst.limit(limit);
return dst;
}
/**
* Returns a new buffer copied from the given source buffer. The content of the new buffer is independent and copied
* from {@code 0} to {@code src.capacity()} (not {@code src.position()} to {@code src.limit()}).
* <p>
* Position of the source buffer will not be changed. The new buffer's position, limit and capacity are same with
* the source buffer's, and the new buffer is direct if, and only if, the source buffer is direct.
*
* @param src the given source buffer
* @return a new buffer copied from the given source buffer
*/
public static @Nonnull CharBuffer copy(@Nonnull CharBuffer src) {
CharBuffer dst = src.isDirect() ?
directCharBuffer(src.capacity())
:
CharBuffer.allocate(src.capacity());
int pos = src.position();
int limit = src.limit();
src.position(0);
src.limit(src.capacity());
dst.put(src);
src.position(pos);
src.limit(limit);
dst.position(pos);
dst.limit(limit);
return dst;
}
/**
* Returns a new array copied from the remaining content of the given source buffer. Position of the source buffer
* will not be changed.
*
* @param src the given source buffer
* @return a new array copied from the remaining content of the given source buffer
*/
public static byte @Nonnull [] copyContent(@Nonnull ByteBuffer src) {
byte[] ret = new byte[src.remaining()];
int pos = src.position();
src.get(ret);
src.position(pos);
return ret;
}
/**
* Returns a new array copied from the remaining content of the given source buffer. Position of the source buffer
* will not be changed.
*
* @param src the given source buffer
* @return a new array copied from the remaining content of the given source buffer
*/
public static char @Nonnull [] copyContent(@Nonnull CharBuffer src) {
char[] ret = new char[src.remaining()];
int pos = src.position();
src.get(ret);
src.position(pos);
return ret;
}
/**
* Returns a new direct buffer of which content is copied from the given array.
* <p>
* Returned buffer's position is {@code 0}, limit and capacity is the array's length.
*
* @param src the given array
* @return a new direct buffer of which content is copied from the given array
*/
public static @Nonnull ByteBuffer copyDirect(byte @Nonnull [] src) {
ByteBuffer buf = ByteBuffer.allocateDirect(src.length);
buf.put(src);
buf.flip();
return buf;
}
/**
* Returns a new direct buffer of which content is a copy of data from the given array. The copied data starts at
* the specified offset and has the specified length.
* <p>
* Returned buffer's position is {@code 0}, limit and capacity is the specified length.
*
* @param src the given array
* @param off the specified offset
* @param len the specified length
* @return a new direct buffer of which content is a copy of data from the given array
*/
public static @Nonnull ByteBuffer copyDirect(
byte @Nonnull [] src, int off, int len
) throws IndexOutOfBoundsException {
IOChecker.checkOffLen(off, len, src.length);
ByteBuffer buf = ByteBuffer.allocateDirect(len);
buf.put(src, off, len);
buf.flip();
return buf;
}
/**
* Returns a new direct buffer of which content is copied from the given array.
* <p>
* Returned buffer's position is {@code 0}, limit and capacity is the array's length.
*
* @param src the given array
* @return a new direct buffer of which content is copied from the given array
*/
public static @Nonnull CharBuffer copyDirect(char @Nonnull [] src) {
CharBuffer buf = directCharBuffer(src.length);
buf.put(src);
buf.flip();
return buf;
}
/**
* Returns a new direct buffer of which content is a copy of data from the given array. The copied data starts at
* the specified offset and has the specified length.
* <p>
* Returned buffer's position is {@code 0}, limit and capacity is the specified length.
*
* @param src the given array
* @param off the specified offset
* @param len the specified length
* @return a new direct buffer of which content is a copy of data from the given array
*/
public static @Nonnull CharBuffer copyDirect(
char @Nonnull [] src, int off, int len
) throws IndexOutOfBoundsException {
IOChecker.checkOffLen(off, len, src.length);
CharBuffer buf = directCharBuffer(len);
buf.put(src, off, len);
buf.flip();
return buf;
}
/**
* Returns a new direct {@link CharBuffer} with the specified capacity, and its endian is
* {@link ByteOrder#BIG_ENDIAN}.
* <p>
* Returned buffer's position is {@code 0}, limit equals to the capacity.
*
* @param capacity the specified capacity
* @return a new direct {@link CharBuffer} with the specified capacity
* @throws IllegalArgumentException if the specified capacity is negative
*/
public static @Nonnull CharBuffer directCharBuffer(int capacity) throws IllegalArgumentException {
IOChecker.checkCapacity(capacity);
return ByteBuffer.allocateDirect(capacity * 2).order(ByteOrder.BIG_ENDIAN).asCharBuffer();
}
/**
* Reads all data from the source buffer and processes them by the given operator, then writes the result into the
* destination buffer, returns the number of bytes written. The position of the source buffer will be increment to
* its limit, and position of the destination buffer will be increment by the write number.
* <p>
* Note:
* <ul>
* <li>Make sure the destination has enough space to write;</li>
* <li>If an exception occurs during processing, the status of two buffers are undefined;</li>
* </ul>
*
* @param src the given source buffer
* @param dst the given destination buffer
* @param operator the given operator
* @return the number of bytes written
* @throws IORuntimeException if an I/O error occurs
*/
public static int process(
@Nonnull ByteBuffer src, @Nonnull ByteBuffer dst, @Nonnull ByteArrayOperator operator
) throws IORuntimeException {
try {
int len = src.remaining();
int ret;
if (src.hasArray()) {
if (dst.hasArray()) {
ret = operator.process(
src.array(),
src.arrayOffset() + src.position(),
dst.array(),
dst.arrayOffset() + dst.position(),
len
);
src.position(src.position() + len);
dst.position(dst.position() + ret);
} else {
byte[] dstArr = new byte[dst.remaining()];
ret = operator.process(
src.array(),
src.arrayOffset() + src.position(),
dstArr,
0,
len
);
src.position(src.position() + len);
dst.put(dstArr, 0, ret);
}
} else {
byte[] srcArr = Fs.nonnull(read(src), BytesKit.empty());
if (dst.hasArray()) {
ret = operator.process(
srcArr,
0,
dst.array(),
dst.arrayOffset() + dst.position(),
len
);
dst.position(dst.position() + ret);
} else {
byte[] dstArr = new byte[dst.remaining()];
ret = operator.process(
srcArr,
0,
dstArr,
0,
len
);
dst.put(dstArr, 0, ret);
}
}
return ret;
} catch (Exception e) {
throw new IORuntimeException(e);
}
}
/**
* Reads all data from the source buffer and processes them by the given operator, then writes the result into the
* destination buffer, returns the number of chars written. The position of the source buffer will be increment to
* its limit, and position of the destination buffer will be increment by the write number.
* <p>
* Note:
* <ul>
* <li>Make sure the destination has enough space to write;</li>
* <li>If an exception occurs during processing, the status of two buffers are undefined;</li>
* </ul>
*
* @param src the given source buffer
* @param dst the given destination buffer
* @param operator the given operator
* @return the number of chars written
* @throws IORuntimeException if an I/O error occurs
*/
public static int process(
@Nonnull CharBuffer src, @Nonnull CharBuffer dst, @Nonnull CharArrayOperator operator
) throws IORuntimeException {
try {
int len = src.remaining();
int ret;
if (src.hasArray()) {
if (dst.hasArray()) {
ret = operator.process(
src.array(),
src.arrayOffset() + src.position(),
dst.array(),
dst.arrayOffset() + dst.position(),
len
);
src.position(src.position() + len);
dst.position(dst.position() + ret);
} else {
char[] dstArr = new char[dst.remaining()];
ret = operator.process(
src.array(),
src.arrayOffset() + src.position(),
dstArr,
0,
len
);
src.position(src.position() + len);
dst.put(dstArr, 0, ret);
}
} else {
char[] srcArr = Fs.nonnull(read(src), CharsKit.empty());
if (dst.hasArray()) {
ret = operator.process(
srcArr,
0,
dst.array(),
dst.arrayOffset() + dst.position(),
len
);
dst.position(dst.position() + ret);
} else {
char[] dstArr = new char[dst.remaining()];
ret = operator.process(
srcArr,
0,
dstArr,
0,
len
);
dst.put(dstArr, 0, ret);
}
}
return ret;
} catch (Exception e) {
throw new IORuntimeException(e);
}
}
private BufferKit() {
}
}