org.apache.sshd.common.util.buffer.ByteArrayBuffer Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.sshd.common.util.buffer;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.IntUnaryOperator;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.NumberUtils;
import org.apache.sshd.common.util.Readable;
import org.apache.sshd.common.util.ValidateUtils;
/**
* Provides an implementation of {@link Buffer} using a backing byte array
*
* @author Apache MINA SSHD Project
*/
public class ByteArrayBuffer extends Buffer {
/**
* Initial default allocated buffer size if none specified
*/
public static final int DEFAULT_SIZE = 256;
private byte[] data;
private int rpos;
private int wpos;
/**
* Allocates a buffer for writing purposes with {@value #DEFAULT_SIZE} bytes
*/
public ByteArrayBuffer() {
this(DEFAULT_SIZE, false);
}
/**
* Allocates a buffer for writing purposes
*
* @param size Initial buffer size - Note: it is rounded to the closest power of 2 that is greater or
* equal to it.
* @see #ByteArrayBuffer(int, boolean)
*/
public ByteArrayBuffer(int size) {
this(size, true);
}
/**
* Allocates a buffer for writing purposes
*
* @param size Initial buffer size
* @param roundOff Whether to round it to closest power of 2 that is greater or equal to the specified size
*/
public ByteArrayBuffer(int size, boolean roundOff) {
this(new byte[roundOff ? BufferUtils.getNextPowerOf2(size) : size], false);
}
/**
* Wraps data bytes for reading
*
* @param data Data bytes to read from
* @see #ByteArrayBuffer(byte[], boolean)
*/
public ByteArrayBuffer(byte[] data) {
this(data, 0, data.length, true);
}
/**
* @param data Data bytes to use
* @param read Whether the data bytes are for reading or writing
*/
public ByteArrayBuffer(byte[] data, boolean read) {
this(data, 0, data.length, read);
}
/**
* Wraps data bytes for reading
*
* @param data Data bytes to read from
* @param off Offset to read from
* @param len Available bytes from given offset
* @see #ByteArrayBuffer(byte[], int, int, boolean)
*/
public ByteArrayBuffer(byte[] data, int off, int len) {
this(data, off, len, true);
}
/**
* @param data Data bytes to use
* @param off Offset to read/write (according to read parameter)
* @param len Available bytes from given offset
* @param read Whether the data bytes are for reading or writing
*/
public ByteArrayBuffer(byte[] data, int off, int len, boolean read) {
if ((off < 0) || (len < 0)) {
throw new IndexOutOfBoundsException("Invalid offset(" + off + ")/length(" + len + ")");
}
this.data = data;
this.rpos = off;
this.wpos = (read ? len : 0) + off;
}
@Override
public int rpos() {
return rpos;
}
@Override
public void rpos(int rpos) {
this.rpos = rpos;
}
@Override
public int wpos() {
return wpos;
}
@Override
public void wpos(int wpos) {
if (wpos > this.wpos) {
ensureCapacity(wpos - this.wpos);
}
this.wpos = wpos;
}
@Override
public int available() {
return wpos - rpos;
}
@Override
public int capacity() {
return data.length - wpos;
}
@Override
public byte[] array() {
return data;
}
@Override
public byte[] getBytesConsumed() {
byte[] consumed = new byte[rpos];
System.arraycopy(data, 0, consumed, 0, rpos);
return consumed;
}
@Override
public byte rawByte(int pos) {
return data[pos];
}
@Override
public long rawUInt(int pos) {
return BufferUtils.getUInt(data, pos, Integer.BYTES);
}
@Override
public void compact() {
int avail = available();
if (avail > 0) {
System.arraycopy(data, rpos, data, 0, avail);
}
wpos -= rpos;
rpos = 0;
}
@Override
public Buffer clear(boolean wipeData) {
rpos = 0;
wpos = 0;
if (wipeData) {
Arrays.fill(data, (byte) 0);
}
return this;
}
@Override
public byte getByte() {
ensureAvailable(Byte.BYTES);
return data[rpos++];
}
@Override
public void putByte(byte b) {
ensureCapacity(Byte.BYTES);
data[wpos++] = b;
}
@Override
public int putBuffer(Readable buffer, boolean expand) {
int required = expand ? buffer.available() : Math.min(buffer.available(), capacity());
ensureCapacity(required);
buffer.getRawBytes(data, wpos, required);
wpos += required;
return required;
}
@Override
public void putBuffer(ByteBuffer buffer) {
int required = buffer.remaining();
ensureCapacity(required + Integer.SIZE);
putUInt(required);
buffer.get(data, wpos, required);
wpos += required;
}
@Override
public void putRawBytes(byte[] d, int off, int len) {
ValidateUtils.checkTrue(len >= 0, "Negative raw bytes length: %d", len);
ensureCapacity(len);
System.arraycopy(d, off, data, wpos, len);
wpos += len;
}
@Override
public String getString(Charset charset) {
Objects.requireNonNull(charset, "No charset specified");
int reqLen = getInt();
int len = ensureAvailable(reqLen);
String s = new String(data, rpos, len, charset);
rpos += len;
return s;
}
@Override
public void getRawBytes(byte[] buf, int off, int len) {
ensureAvailable(len);
copyRawBytes(0, buf, off, len);
rpos += len;
}
@Override
protected void copyRawBytes(int offset, byte[] buf, int pos, int len) {
if ((offset < 0) || (pos < 0) || (len < 0)) {
throw new IndexOutOfBoundsException(
"Invalid offset(" + offset + ")/position(" + pos + ")/length(" + len + ") required");
}
System.arraycopy(data, rpos + offset, buf, pos, len);
}
@Override
public Buffer ensureCapacity(int capacity, IntUnaryOperator growthFactor) {
ValidateUtils.checkTrue(capacity >= 0, "Negative capacity requested: %d", capacity);
int maxSize = size();
int curPos = wpos();
int remaining = maxSize - curPos;
if (remaining < capacity) {
int minimum = curPos + capacity;
int actual = growthFactor.applyAsInt(minimum);
if (actual < minimum) {
throw new IllegalStateException(
"ensureCapacity(" + capacity + ") actual (" + actual + ") below min. (" + minimum + ")");
}
byte[] tmp = new byte[actual];
System.arraycopy(data, 0, tmp, 0, data.length);
data = tmp;
}
return this;
}
@Override
protected int size() {
return data.length;
}
/**
* Creates a compact buffer (i.e., one that starts at offset zero) containing a copy of the original data
*
* @param data The original data buffer
* @return A {@link ByteArrayBuffer} containing a copy of the original data starting at zero read
* position
* @see #getCompactClone(byte[], int, int)
*/
public static ByteArrayBuffer getCompactClone(byte[] data) {
return getCompactClone(data, 0, NumberUtils.length(data));
}
/**
* Creates a compact buffer (i.e., one that starts at offset zero) containing a copy of the original data
*
* @param data The original data buffer
* @param offset The offset of the valid data in the buffer
* @param len The size (in bytes) of of the valid data in the buffer
* @return A {@link ByteArrayBuffer} containing a copy of the original data starting at zero read
* position
*/
public static ByteArrayBuffer getCompactClone(byte[] data, int offset, int len) {
byte[] clone = (len > 0) ? new byte[len] : GenericUtils.EMPTY_BYTE_ARRAY;
if (len > 0) {
System.arraycopy(data, offset, clone, 0, len);
}
return new ByteArrayBuffer(clone, true);
}
}