
dorkbox.network.pipeline.ByteBufInput Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Network Show documentation
Show all versions of Network Show documentation
Encrypted, high-performance, and event-driven/reactive network stack for Java 11+
/*
* Copyright 2010 dorkbox, llc
*
* 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.
*
* Copyright (c) 2008, Nathan Sweet
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided with the distribution.
* - Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package dorkbox.network.pipeline;
import com.esotericsoftware.kryo.KryoException;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import io.netty.buffer.ByteBuf;
import java.io.DataInput;
import java.io.InputStream;
/**
* An {@link InputStream} which reads data from a {@link ByteBuf}.
*
* A read operation against this stream will occur at the {@code readerIndex}
* of its underlying buffer and the {@code readerIndex} will increase during
* the read operation.
*
* This stream implements {@link DataInput} for your convenience.
* The endianness of the stream is not always big endian but depends on
* the endianness of the underlying buffer.
*
* Utility methods are provided for efficiently reading primitive types and strings.
*
* Modified from KRYO to use ByteBuf.
*/
public
class ByteBufInput extends Input {
private char[] inputChars = new char[32];
private ByteBuf byteBuf;
private int startIndex;
/**
* Creates an uninitialized Input. {@link #setBuffer(ByteBuf)} must be called before the Input is used.
*/
public
ByteBufInput() {
}
public
ByteBufInput(ByteBuf buffer) {
setBuffer(buffer);
}
public final
void setBuffer(ByteBuf byteBuf) {
this.byteBuf = byteBuf;
if (byteBuf != null) {
startIndex = byteBuf.readerIndex(); // where the object starts...
}
else {
startIndex = 0;
}
}
public
ByteBuf getByteBuf() {
return byteBuf;
}
/**
* Sets a new buffer. The position and total are reset, discarding any buffered bytes.
*/
@Override
@Deprecated
public
void setBuffer(byte[] bytes) {
throw new RuntimeException("Cannot access this method!");
}
/**
* Sets a new buffer. The position and total are reset, discarding any buffered bytes.
*/
@Override
@Deprecated
public
void setBuffer(byte[] bytes, int offset, int count) {
throw new RuntimeException("Cannot access this method!");
}
@Override
@Deprecated
public
byte[] getBuffer() {
throw new RuntimeException("Cannot access this method!");
}
@Override
@Deprecated
public
InputStream getInputStream() {
throw new RuntimeException("Cannot access this method!");
}
/**
* Sets a new InputStream. The position and total are reset, discarding any buffered bytes.
*
* @param inputStream May be null.
*/
@Override
@Deprecated
public
void setInputStream(InputStream inputStream) {
throw new RuntimeException("Cannot access this method!");
}
/**
* Returns the number of bytes read.
*/
@Override
public
long total() {
return byteBuf.readerIndex() - startIndex;
}
/**
* Returns the current position in the buffer.
*/
@Override
public
int position() {
return byteBuf.readerIndex();
}
/**
* Sets the current position in the buffer.
*/
@Override
@Deprecated
public
void setPosition(int position) {
throw new RuntimeException("Cannot access this method!");
}
/**
* Returns the limit for the buffer.
*/
@Override
public
int limit() {
return byteBuf.writerIndex();
}
/**
* Sets the limit in the buffer.
*/
@Override
@Deprecated
public
void setLimit(int limit) {
throw new RuntimeException("Cannot access this method!");
}
/**
* Sets the position and total to zero.
*/
@Override
public
void rewind() {
byteBuf.readerIndex(startIndex);
}
/**
* Discards the specified number of bytes.
*/
@Override
public
void skip(int count) throws KryoException {
byteBuf.skipBytes(count);
}
/**
* Fills the buffer with more bytes. Can be overridden to fill the bytes from a source other than the InputStream.
*/
@Override
@Deprecated
protected
int fill(byte[] buffer, int offset, int count) throws KryoException {
throw new RuntimeException("Cannot access this method!");
}
// InputStream
/**
* Reads a single byte as an int from 0 to 255, or -1 if there are no more bytes are available.
*/
@Override
public
int read() throws KryoException {
return byteBuf.readByte() & 0xFF;
}
/**
* Reads bytes.length bytes or less and writes them to the specified byte[], starting at 0, and returns the number of bytes
* read.
*/
@Override
public
int read(byte[] bytes) throws KryoException {
int start = byteBuf.readerIndex();
byteBuf.readBytes(bytes);
int end = byteBuf.readerIndex();
return end - start; // return how many bytes were actually read.
}
/**
* Reads count bytes or less and writes them to the specified byte[], starting at offset, and returns the number of bytes read
* or -1 if no more bytes are available.
*/
@Override
public
int read(byte[] bytes, int offset, int count) throws KryoException {
if (bytes == null) {
throw new IllegalArgumentException("bytes cannot be null.");
}
int start = byteBuf.readerIndex();
byteBuf.readBytes(bytes, offset, count);
int end = byteBuf.readerIndex();
return end - start; // return how many bytes were actually read.
}
/**
* Discards the specified number of bytes.
*/
@Override
public
long skip(long count) throws KryoException {
long remaining = count;
while (remaining > 0) {
int skip = Math.max(Integer.MAX_VALUE, (int) remaining);
skip(skip);
remaining -= skip;
}
return count;
}
/**
* Closes the underlying InputStream, if any.
*/
@Override
@Deprecated
public
void close() throws KryoException {
// does nothing.
}
// byte
/**
* Reads a single byte.
*/
@Override
public
byte readByte() throws KryoException {
return byteBuf.readByte();
}
/**
* Reads a byte as an int from 0 to 255.
*/
@Override
public
int readByteUnsigned() throws KryoException {
return byteBuf.readUnsignedByte();
}
/**
* Reads the specified number of bytes into a new byte[].
*/
@Override
public
byte[] readBytes(int length) throws KryoException {
byte[] bytes = new byte[length];
readBytes(bytes, 0, length);
return bytes;
}
/**
* Reads bytes.length bytes and writes them to the specified byte[], starting at index 0.
*/
@Override
public
void readBytes(byte[] bytes) throws KryoException {
readBytes(bytes, 0, bytes.length);
}
/**
* Reads count bytes and writes them to the specified byte[], starting at offset.
*/
@Override
public
void readBytes(byte[] bytes, int offset, int count) throws KryoException {
if (bytes == null) {
throw new IllegalArgumentException("bytes cannot be null.");
}
byteBuf.readBytes(bytes, offset, count);
}
// int
/**
* Reads a 4 byte int.
*/
@Override
public
int readInt() throws KryoException {
return byteBuf.readInt();
}
/**
* Reads a 1-5 byte int. This stream may consider such a variable length encoding request as a hint. It is not guaranteed that
* a variable length encoding will be really used. The stream may decide to use native-sized integer representation for
* efficiency reasons.
**/
@Override
public
int readInt(boolean optimizePositive) throws KryoException {
return readVarInt(optimizePositive);
}
/**
* Reads a 1-5 byte int. It is guaranteed that a variable length encoding will be used.
*/
@Override
public
int readVarInt(boolean optimizePositive) throws KryoException {
ByteBuf buffer = byteBuf;
int b = buffer.readByte();
int result = b & 0x7F;
if ((b & 0x80) != 0) {
b = buffer.readByte();
result |= (b & 0x7F) << 7;
if ((b & 0x80) != 0) {
b = buffer.readByte();
result |= (b & 0x7F) << 14;
if ((b & 0x80) != 0) {
b = buffer.readByte();
result |= (b & 0x7F) << 21;
if ((b & 0x80) != 0) {
b = buffer.readByte();
result |= (b & 0x7F) << 28;
}
}
}
}
return optimizePositive ? result : result >>> 1 ^ -(result & 1);
}
/**
* Returns true if enough bytes are available to read an int with {@link #readInt(boolean)}.
*/
@Override
public
boolean canReadInt() throws KryoException {
ByteBuf buffer = byteBuf;
int limit = buffer.writerIndex();
if (limit - buffer.readerIndex() >= 5) {
return true;
}
if ((buffer.readByte() & 0x80) == 0) {
return true;
}
if (buffer.readerIndex() == limit) {
return false;
}
if ((buffer.readByte() & 0x80) == 0) {
return true;
}
if (buffer.readerIndex() == limit) {
return false;
}
if ((buffer.readByte() & 0x80) == 0) {
return true;
}
if (buffer.readerIndex() == limit) {
return false;
}
if ((buffer.readByte() & 0x80) == 0) {
return true;
}
if (buffer.readerIndex() == limit) {
return false;
}
return true;
}
/**
* Returns true if enough bytes are available to read a long with {@link #readLong(boolean)}.
*/
@Override
public
boolean canReadLong() throws KryoException {
ByteBuf buffer = byteBuf;
int limit = buffer.writerIndex();
if (limit - buffer.readerIndex() >= 9) {
return true;
}
if ((buffer.readByte() & 0x80) == 0) {
return true;
}
if (buffer.readerIndex() == limit) {
return false;
}
if ((buffer.readByte() & 0x80) == 0) {
return true;
}
if (buffer.readerIndex() == limit) {
return false;
}
if ((buffer.readByte() & 0x80) == 0) {
return true;
}
if (buffer.readerIndex() == limit) {
return false;
}
if ((buffer.readByte() & 0x80) == 0) {
return true;
}
if (buffer.readerIndex() == limit) {
return false;
}
if ((buffer.readByte() & 0x80) == 0) {
return true;
}
if (buffer.readerIndex() == limit) {
return false;
}
if ((buffer.readByte() & 0x80) == 0) {
return true;
}
if (buffer.readerIndex() == limit) {
return false;
}
if ((buffer.readByte() & 0x80) == 0) {
return true;
}
if (buffer.readerIndex() == limit) {
return false;
}
if ((buffer.readByte() & 0x80) == 0) {
return true;
}
if (buffer.readerIndex() == limit) {
return false;
}
return true;
}
// string
/**
* Reads the length and string of UTF8 characters, or null. This can read strings written by {@link Output#writeString(String)}
* , {@link Output#writeString(CharSequence)}, and {@link Output#writeAscii(String)}.
*
* @return May be null.
*/
@Override
public
String readString() {
ByteBuf buffer = byteBuf;
int b = buffer.readByte();
if ((b & 0x80) == 0) {
return readAscii(); // ASCII.
}
// Null, empty, or UTF8.
int charCount = readUtf8Length(b);
switch (charCount) {
case 0:
return null;
case 1:
return "";
}
charCount--;
if (inputChars.length < charCount) {
inputChars = new char[charCount];
}
readUtf8(charCount);
return new String(inputChars, 0, charCount);
}
private
int readUtf8Length(int b) {
int result = b & 0x3F; // Mask all but first 6 bits.
if ((b & 0x40) != 0) { // Bit 7 means another byte, bit 8 means UTF8.
ByteBuf buffer = byteBuf;
b = buffer.readByte();
result |= (b & 0x7F) << 6;
if ((b & 0x80) != 0) {
b = buffer.readByte();
result |= (b & 0x7F) << 13;
if ((b & 0x80) != 0) {
b = buffer.readByte();
result |= (b & 0x7F) << 20;
if ((b & 0x80) != 0) {
b = buffer.readByte();
result |= (b & 0x7F) << 27;
}
}
}
}
return result;
}
private
void readUtf8(int charCount) {
ByteBuf buffer = byteBuf;
char[] chars = this.inputChars; // will be the correct size.
// Try to read 7 bit ASCII chars.
int charIndex = 0;
int count = charCount;
int b;
while (charIndex < count) {
b = buffer.readByte();
if (b < 0) {
buffer.readerIndex(buffer.readerIndex() - 1);
break;
}
chars[charIndex++] = (char) b;
}
// If buffer didn't hold all chars or any were not ASCII, use slow path for remainder.
if (charIndex < charCount) {
readUtf8_slow(charCount, charIndex);
}
}
private
void readUtf8_slow(int charCount, int charIndex) {
ByteBuf buffer = byteBuf;
char[] chars = this.inputChars;
while (charIndex < charCount) {
int b = buffer.readByte() & 0xFF;
switch (b >> 4) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
chars[charIndex] = (char) b;
break;
case 12:
case 13:
chars[charIndex] = (char) ((b & 0x1F) << 6 | buffer.readByte() & 0x3F);
break;
case 14:
chars[charIndex] = (char) ((b & 0x0F) << 12 | (buffer.readByte() & 0x3F) << 6 | buffer.readByte() & 0x3F);
break;
}
charIndex++;
}
}
private
String readAscii() {
ByteBuf buffer = byteBuf;
int start = buffer.readerIndex() - 1;
int b;
do {
b = buffer.readByte();
} while ((b & 0x80) == 0);
int i = buffer.readerIndex() - 1;
buffer.setByte(i, buffer.getByte(i) & 0x7F); // Mask end of ascii bit.
int capp = buffer.readerIndex() - start;
byte[] ba = new byte[capp];
buffer.getBytes(start, ba);
@SuppressWarnings("deprecation")
String value = new String(ba, 0, 0, capp);
buffer.setByte(i, buffer.getByte(i) | 0x80);
return value;
}
/**
* Reads the length and string of UTF8 characters, or null. This can read strings written by {@link Output#writeString(String)}
* , {@link Output#writeString(CharSequence)}, and {@link Output#writeAscii(String)}.
*
* @return May be null.
*/
@Override
public
StringBuilder readStringBuilder() {
ByteBuf buffer = byteBuf;
int b = buffer.readByte();
if ((b & 0x80) == 0) {
return new StringBuilder(readAscii()); // ASCII.
}
// Null, empty, or UTF8.
int charCount = readUtf8Length(b);
switch (charCount) {
case 0:
return null;
case 1:
return new StringBuilder("");
}
charCount--;
if (inputChars.length < charCount) {
inputChars = new char[charCount];
}
readUtf8(charCount);
StringBuilder builder = new StringBuilder(charCount);
builder.append(inputChars, 0, charCount);
return builder;
}
// float
/**
* Reads a 4 byte float.
*/
@Override
public
float readFloat() throws KryoException {
return Float.intBitsToFloat(readInt());
}
/**
* Reads a 1-5 byte float with reduced precision.
*/
@Override
public
float readFloat(float precision, boolean optimizePositive) throws KryoException {
return readInt(optimizePositive) / precision;
}
// short
/**
* Reads a 2 byte short.
*/
@Override
public
short readShort() throws KryoException {
return byteBuf.readShort();
}
/**
* Reads a 2 byte short as an int from 0 to 65535.
*/
@Override
public
int readShortUnsigned() throws KryoException {
return byteBuf.readUnsignedShort();
}
// long
/**
* Reads an 8 byte long.
*/
@Override
public
long readLong() throws KryoException {
return byteBuf.readLong();
}
/**
* Reads a 1-9 byte long.
*/
@Override
public
long readLong(boolean optimizePositive) throws KryoException {
ByteBuf buffer = byteBuf;
int b = buffer.readByte();
long result = b & 0x7F;
if ((b & 0x80) != 0) {
b = buffer.readByte();
result |= (b & 0x7F) << 7;
if ((b & 0x80) != 0) {
b = buffer.readByte();
result |= (b & 0x7F) << 14;
if ((b & 0x80) != 0) {
b = buffer.readByte();
result |= (b & 0x7F) << 21;
if ((b & 0x80) != 0) {
b = buffer.readByte();
result |= (long) (b & 0x7F) << 28;
if ((b & 0x80) != 0) {
b = buffer.readByte();
result |= (long) (b & 0x7F) << 35;
if ((b & 0x80) != 0) {
b = buffer.readByte();
result |= (long) (b & 0x7F) << 42;
if ((b & 0x80) != 0) {
b = buffer.readByte();
result |= (long) (b & 0x7F) << 49;
if ((b & 0x80) != 0) {
b = buffer.readByte();
result |= (long) b << 56;
}
}
}
}
}
}
}
}
if (!optimizePositive) {
result = result >>> 1 ^ -(result & 1);
}
return result;
}
// boolean
/**
* Reads a 1 byte boolean.
*/
@Override
public
boolean readBoolean() throws KryoException {
return byteBuf.readBoolean();
}
// char
/**
* Reads a 2 byte char.
*/
@Override
public
char readChar() throws KryoException {
return byteBuf.readChar();
}
// double
/**
* Reads an 8 bytes double.
*/
@Override
public
double readDouble() throws KryoException {
return Double.longBitsToDouble(readLong());
}
/**
* Reads a 1-9 byte double with reduced precision.
*/
@Override
public
double readDouble(double precision, boolean optimizePositive) throws KryoException {
return readLong(optimizePositive) / precision;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy