mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
1145 lines
35 KiB
Java
1145 lines
35 KiB
Java
// 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 streamer;
|
|
|
|
import java.nio.charset.Charset;
|
|
import java.util.Arrays;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
/**
|
|
* This class represents a slice in a buffer.
|
|
*/
|
|
public class ByteBuffer {
|
|
|
|
public static final String SEQUENCE_NUMBER = "seq";
|
|
|
|
public byte data[];
|
|
public int offset = 0;
|
|
public int length = 0;
|
|
public int cursor = 0;
|
|
|
|
private int refCount = 1;
|
|
|
|
private Order order;
|
|
|
|
/**
|
|
* Create buffer of size no less than length. Buffer can be a bit larger than
|
|
* length. Offset also can be set to non-zero value to leave some place for
|
|
* future headers.
|
|
*/
|
|
public ByteBuffer(int minLength) {
|
|
// Get buffer of acceptable size from buffer pool
|
|
data = BufferPool.allocateNewBuffer(minLength);
|
|
offset = 0;
|
|
length = minLength;
|
|
}
|
|
|
|
public ByteBuffer(byte data[]) {
|
|
if (data == null)
|
|
throw new NullPointerException("Data must be non-null.");
|
|
|
|
this.data = data;
|
|
offset = 0;
|
|
length = data.length;
|
|
}
|
|
|
|
public ByteBuffer(byte[] data, int offset, int length) {
|
|
if (data == null)
|
|
throw new NullPointerException("Data must be non-null.");
|
|
|
|
this.data = data;
|
|
this.offset = offset;
|
|
this.length = length;
|
|
}
|
|
|
|
/**
|
|
* Create byte buffer of requested size with some space reserved for future
|
|
* headers.
|
|
*/
|
|
public ByteBuffer(int minLength, boolean reserveSpaceForHeader) {
|
|
// Get buffer of acceptable size from buffer pool
|
|
data = BufferPool.allocateNewBuffer(128 + minLength);
|
|
offset = 128; // 100 bytes should be enough for headers
|
|
length = minLength;
|
|
}
|
|
|
|
/**
|
|
* Create empty buffer with given order only.
|
|
*/
|
|
public ByteBuffer(Order order) {
|
|
this.order = order;
|
|
}
|
|
|
|
public void setOrder(Order order) {
|
|
this.order = order;
|
|
}
|
|
|
|
public Order getOrder() {
|
|
return order;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return toString(length);
|
|
}
|
|
|
|
/**
|
|
* Return string representation of this byte buffer.
|
|
*
|
|
* @param maxLength
|
|
* number of bytes to show in string
|
|
*/
|
|
public String toString(int maxLength) {
|
|
return "ByteRange(){offset=" + offset + ", length=" + length + ", cursor=" + cursor + ", data=" + ((data == null) ? "null" : toHexString(maxLength))
|
|
+ ((metadata == null || metadata.size() == 0) ? "" : ", metadata=" + metadata) + "}";
|
|
}
|
|
|
|
/**
|
|
* Return string representation of this byte buffer as hexadecimal numbers,
|
|
* e.g. "[0x01, 0x02]".
|
|
*
|
|
* @param maxLength
|
|
* number of bytes to show in string
|
|
*/
|
|
public String toHexString(int maxLength) {
|
|
StringBuilder builder = new StringBuilder(maxLength * 6);
|
|
builder.append('[');
|
|
for (int i = 0; i < maxLength && i < length; i++) {
|
|
if (i > 0)
|
|
builder.append(", ");
|
|
int b = data[offset + i] & 0xff;
|
|
builder.append("0x" + ((b < 16) ? "0" : "") + Integer.toString(b, 16));
|
|
}
|
|
builder.append(']');
|
|
return builder.toString();
|
|
}
|
|
|
|
/**
|
|
* Return string representation of this byte buffer as hexadecimal numbers,
|
|
* e.g. "01 02".
|
|
*
|
|
* @param maxLength
|
|
* number of bytes to show in string
|
|
*/
|
|
public String toPlainHexString(int maxLength) {
|
|
StringBuilder builder = new StringBuilder(maxLength * 3);
|
|
for (int i = 0; i < maxLength && i < length; i++) {
|
|
if (i > 0)
|
|
builder.append(" ");
|
|
int b = data[offset + i] & 0xff;
|
|
builder.append(String.format("%02x", b));
|
|
}
|
|
return builder.toString();
|
|
}
|
|
|
|
/**
|
|
* Return string representation of this byte buffer as dump, e.g.
|
|
* "0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 .................".
|
|
*
|
|
* @param maxLength
|
|
* number of bytes to show in string
|
|
*/
|
|
public String dump() {
|
|
StringBuilder builder = new StringBuilder(length * 4);
|
|
int i = addBytesToBuilder(builder);
|
|
int end = i - 1;
|
|
if (end % 16 != 15) {
|
|
int begin = end & ~0xf;
|
|
for (int j = 0; j < (15 - (end % 16)); j++) {
|
|
builder.append(" ");
|
|
}
|
|
builder.append(' ');
|
|
builder.append(toASCIIString(begin, end));
|
|
builder.append('\n');
|
|
}
|
|
return builder.toString();
|
|
}
|
|
|
|
protected int addBytesToBuilder(StringBuilder builder) {
|
|
int i = 0;
|
|
for (; i < length; i++) {
|
|
if (i % 16 == 0) {
|
|
builder.append(String.format("%04x", i));
|
|
}
|
|
|
|
builder.append(' ');
|
|
int b = data[offset + i] & 0xff;
|
|
builder.append(String.format("%02x", b));
|
|
|
|
if (i % 16 == 15) {
|
|
builder.append(' ');
|
|
builder.append(toASCIIString(i - 15, i));
|
|
builder.append('\n');
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
private String toASCIIString(int start, int finish) {
|
|
StringBuffer sb = new StringBuffer(16);
|
|
for (int i = start; i <= finish; i++) {
|
|
char ch = (char)data[offset + i];
|
|
if (ch < ' ' || ch >= 0x7f) {
|
|
sb.append('.');
|
|
} else {
|
|
sb.append(ch);
|
|
}
|
|
}
|
|
return sb.toString();
|
|
}
|
|
|
|
/**
|
|
* Return string representation of this byte buffer as hexadecimal numbers,
|
|
* e.g. "01 02".
|
|
*/
|
|
public String toPlainHexString() {
|
|
return toPlainHexString(length);
|
|
}
|
|
|
|
public void extend(int newLength) {
|
|
if (data.length < newLength)
|
|
Arrays.copyOf(data, newLength);
|
|
}
|
|
|
|
public void ref() {
|
|
refCount++;
|
|
}
|
|
|
|
public void unref() {
|
|
refCount--;
|
|
|
|
if (refCount == 0) {
|
|
// Return buffer to buffer pool
|
|
BufferPool.recycleBuffer(data);
|
|
|
|
data = null;
|
|
}
|
|
|
|
}
|
|
|
|
public boolean isSoleOwner() {
|
|
return refCount == 1;
|
|
}
|
|
|
|
/**
|
|
* Create shared lightweight copy of part of this buffer.
|
|
*/
|
|
public ByteBuffer slice(int offset, int length, boolean copyMetadata) {
|
|
ref();
|
|
|
|
if (this.length < (offset + length))
|
|
throw new RuntimeException("Length of region is larger that length of this buffer. Buffer length: " + this.length + ", offset: " + offset + ", new region length: "
|
|
+ length + ".");
|
|
|
|
ByteBuffer slice = new ByteBuffer(data, this.offset + offset, length);
|
|
|
|
if (copyMetadata && metadata != null)
|
|
slice.metadata = new HashMap<String, Object>(metadata);
|
|
|
|
return slice;
|
|
}
|
|
|
|
private Map<String, Object> metadata = null;
|
|
|
|
public Object putMetadata(String key, Object value) {
|
|
if (metadata == null)
|
|
metadata = new HashMap<String, Object>();
|
|
return metadata.put(key, value);
|
|
}
|
|
|
|
public Object getMetadata(String key) {
|
|
return (metadata != null) ? metadata.get(key) : null;
|
|
}
|
|
|
|
/**
|
|
* Create new buffer, which holds data from both buffers. Expensive operation.
|
|
*
|
|
* @TODO if only one reference to this ByteBuffer exists, then extend this
|
|
* buffer instead of creating new buffer
|
|
* @TODO support list of buffers to avoid expensive joins until absolute
|
|
* necessary
|
|
*/
|
|
public ByteBuffer join(ByteBuffer buf) {
|
|
// Extend byte array for new data
|
|
int newLength = length + buf.length;
|
|
byte newData[] = new byte[newLength];
|
|
|
|
// Copy data from our buffer
|
|
System.arraycopy(data, offset, newData, 0, length);
|
|
|
|
// Copy data from other buffer
|
|
System.arraycopy(buf.data, buf.offset, newData, length, buf.length);
|
|
|
|
ByteBuffer newBuf = new ByteBuffer(newData);
|
|
|
|
// Copy our (older) metadata to new buffer, because handler might store some
|
|
// metadata in buffer, which is pushed back.
|
|
if (metadata != null)
|
|
newBuf.metadata = new HashMap<String, Object>(metadata);
|
|
|
|
return newBuf;
|
|
}
|
|
|
|
/**
|
|
* Copy used portion of buffer to new byte array. Expensive operation.
|
|
*/
|
|
public byte[] toByteArray() {
|
|
return Arrays.copyOfRange(data, offset, offset + length);
|
|
}
|
|
|
|
public short[] toShortArray() {
|
|
if (length % 2 != 0)
|
|
throw new ArrayIndexOutOfBoundsException("Length of byte array must be dividable by 2 without remainder. Array length: " + length + ", remainder: " + (length % 2)
|
|
+ ".");
|
|
|
|
short[] buf = new short[length / 2];
|
|
|
|
for (int i = 0, j = offset; i < buf.length; i++, j += 2) {
|
|
buf[i] = (short)((data[j + 0] & 0xFF) | ((data[j + 1] & 0xFF) << 8));
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
* Return array of int's in little endian order.
|
|
*/
|
|
public int[] toIntLEArray() {
|
|
if (length % 4 != 0)
|
|
throw new ArrayIndexOutOfBoundsException("Length of byte array must be dividable by 4 without remainder. Array length: " + length + ", remainder: " + (length % 4)
|
|
+ ".");
|
|
|
|
int[] buf = new int[length / 4];
|
|
|
|
for (int i = 0, j = offset; i < buf.length; i++, j += 4) {
|
|
buf[i] = (data[j + 0] & 0xFF) | ((data[j + 1] & 0xFF) << 8) | ((data[j + 2] & 0xFF) << 16) | ((data[j + 3] & 0xFF) << 24);
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
* Return array of int's in little endian order, but use only 3 bytes per int
|
|
* (3RGB).
|
|
*/
|
|
public int[] toInt3LEArray() {
|
|
if (length % 3 != 0)
|
|
throw new ArrayIndexOutOfBoundsException("Length of byte array must be dividable by 3 without remainder. Array length: " + length + ", remainder: " + (length % 3)
|
|
+ ".");
|
|
|
|
int[] buf = new int[length / 3];
|
|
|
|
for (int i = 0, j = offset; i < buf.length; i++, j += 3) {
|
|
buf[i] = (data[j + 0] & 0xFF) | ((data[j + 1] & 0xFF) << 8) | ((data[j + 2] & 0xFF) << 16);
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
* Helper method for test cases to convert array of byte arrays to array of
|
|
* byte buffers.
|
|
*/
|
|
public static ByteBuffer[] convertByteArraysToByteBuffers(byte[]... bas) {
|
|
ByteBuffer bufs[] = new ByteBuffer[bas.length];
|
|
|
|
int i = 0;
|
|
for (byte[] ba : bas) {
|
|
bufs[i++] = new ByteBuffer(ba);
|
|
}
|
|
return bufs;
|
|
}
|
|
|
|
/**
|
|
* Read signed int in network order. Cursor is advanced by 4.
|
|
*/
|
|
public int readSignedInt() {
|
|
if (cursor + 4 > length)
|
|
throw new ArrayIndexOutOfBoundsException("Cannot read 4 bytes from this buffer: " + this + ".");
|
|
|
|
int result = (((data[offset + cursor] & 0xff) << 24) + ((data[offset + cursor + 1] & 0xff) << 16) + ((data[offset + cursor + 2] & 0xff) << 8) + (data[offset + cursor + 3] & 0xff));
|
|
cursor += 4;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Read signed int in little endian order. Cursor is advanced by 4.
|
|
*/
|
|
public int readSignedIntLE() {
|
|
if (cursor + 4 > length)
|
|
throw new ArrayIndexOutOfBoundsException("Cannot read 4 bytes from this buffer: " + this + ".");
|
|
|
|
int result = (((data[offset + cursor + 3] & 0xff) << 24) + ((data[offset + cursor + 2] & 0xff) << 16) + ((data[offset + cursor + 1] & 0xff) << 8) + (data[offset + cursor] & 0xff));
|
|
cursor += 4;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Read unsigned int in little endian order. Cursor is advanced by 4.
|
|
*/
|
|
public long readUnsignedIntLE() {
|
|
if (cursor + 4 > length)
|
|
throw new ArrayIndexOutOfBoundsException("Cannot read 4 bytes from this buffer: " + this + ".");
|
|
|
|
long result = (((long)(data[offset + cursor + 3] & 0xff) << 24) + ((long)(data[offset + cursor + 2] & 0xff) << 16) + ((long)(data[offset + cursor + 1] & 0xff) << 8) + (data[offset
|
|
+ cursor + 0] & 0xff));
|
|
cursor += 4;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Read unsigned int in network order. Cursor is advanced by 4.
|
|
*/
|
|
public long readUnsignedInt() {
|
|
if (cursor + 4 > length)
|
|
throw new ArrayIndexOutOfBoundsException("Cannot read 4 bytes from this buffer: " + this + ".");
|
|
|
|
byte value1 = data[offset + cursor + 0];
|
|
byte value2 = data[offset + cursor + 1];
|
|
byte value3 = data[offset + cursor + 2];
|
|
byte value4 = data[offset + cursor + 3];
|
|
long result = calculateUnsignedInt(value1, value2, value3, value4);
|
|
cursor += 4;
|
|
return result;
|
|
}
|
|
|
|
protected static long calculateUnsignedInt(byte value1, byte value2, byte value3, byte value4) {
|
|
return (((long)calculateUnsignedByte(value1)) << 24)
|
|
+ (((long)calculateUnsignedByte(value2)) << 16)
|
|
+ (((long)calculateUnsignedByte(value3)) << 8)
|
|
+ calculateUnsignedByte(value4);
|
|
}
|
|
|
|
/**
|
|
* Read signed int in variable length format. Top most bit of each byte
|
|
* indicates that next byte contains additional bits. Cursor is advanced by
|
|
* 1-5 bytes.
|
|
*/
|
|
public int readVariableSignedIntLE() {
|
|
int result = 0;
|
|
|
|
for (int shift = 0; shift < 32; shift += 7) {
|
|
int b = readUnsignedByte();
|
|
result |= (b & 0x7f) << shift;
|
|
if ((b & 0x80) == 0)
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Read unsigned int in network order in variable length format. Cursor is
|
|
* advanced by 1 to 4 bytes.
|
|
*
|
|
* Two most significant bits of first byte indicates length of field: 0x00 - 1
|
|
* byte, 0x40 - 2 bytes, 0x80 - 3 bytes, 0xc0 - 4 bytes.
|
|
*
|
|
* @see http://msdn.microsoft.com/en-us/library/cc241614.aspx
|
|
*/
|
|
public int readEncodedUnsignedInt() {
|
|
int firstByte = readUnsignedByte();
|
|
int result;
|
|
switch (firstByte & 0xc0) {
|
|
default:
|
|
case 0x00:
|
|
result = firstByte & 0x3f;
|
|
break;
|
|
case 0x40:
|
|
result = (firstByte & 0x3f << 8) | readUnsignedByte();
|
|
break;
|
|
case 0x80:
|
|
result = (((firstByte & 0x3f << 8) | readUnsignedByte()) << 8) | readUnsignedByte();
|
|
break;
|
|
case 0xc0:
|
|
result = ((((firstByte & 0x3f << 8) | readUnsignedByte()) << 8) | readUnsignedByte() << 8) | readUnsignedByte();
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Read unsigned byte. Cursor is advanced by 1.
|
|
*/
|
|
public int readUnsignedByte() {
|
|
if (cursor + 1 > length)
|
|
throw new ArrayIndexOutOfBoundsException("Cannot read 1 byte from this buffer: " + this + ".");
|
|
|
|
byte value = data[offset + cursor];
|
|
int b = calculateUnsignedByte(value);
|
|
cursor += 1;
|
|
return b;
|
|
}
|
|
|
|
protected static int calculateUnsignedByte(byte value) {
|
|
return value & 0xff;
|
|
}
|
|
|
|
/**
|
|
* Read signed byte. Cursor is advanced by 1.
|
|
*/
|
|
public byte readSignedByte() {
|
|
if (cursor + 1 > length)
|
|
throw new ArrayIndexOutOfBoundsException("Cannot read 1 byte from this buffer: " + this + ".");
|
|
|
|
byte b = data[offset + cursor];
|
|
cursor += 1;
|
|
return b;
|
|
}
|
|
|
|
/**
|
|
* Read unsigned short in network order. Cursor is advanced by 2.
|
|
*/
|
|
public int readUnsignedShort() {
|
|
if (cursor + 2 > length)
|
|
throw new ArrayIndexOutOfBoundsException("Cannot read 2 bytes from this buffer: " + this + ".");
|
|
|
|
byte value1 = data[offset + cursor];
|
|
byte value2 = data[offset + cursor + 1];
|
|
int result = calculateUnsignedShort(value1, value2);
|
|
cursor += 2;
|
|
return result;
|
|
}
|
|
|
|
protected static int calculateUnsignedShort(byte value1, byte value2) {
|
|
return (calculateUnsignedByte(value1) << 8) | calculateUnsignedByte(value2);
|
|
}
|
|
|
|
/**
|
|
* Read signed short in little endian order. Cursor is advanced by 2.
|
|
*/
|
|
public short readSignedShortLE() {
|
|
if (cursor + 2 > length)
|
|
throw new ArrayIndexOutOfBoundsException("Cannot read 2 bytes from this buffer: " + this + ".");
|
|
|
|
short result = (short)(((data[offset + cursor + 1] & 0xff) << 8) | (data[offset + cursor] & 0xff));
|
|
cursor += 2;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Read signed short in network order. Cursor is advanced by 2.
|
|
*/
|
|
public short readSignedShort() {
|
|
if (cursor + 2 > length)
|
|
throw new ArrayIndexOutOfBoundsException("Cannot read 2 bytes from this buffer: " + this + ".");
|
|
|
|
byte value1 = data[offset + cursor + 0];
|
|
byte value2 = data[offset + cursor + 1];
|
|
short result = calculateSignedShort(value1, value2);
|
|
cursor += 2;
|
|
return result;
|
|
}
|
|
|
|
protected static short calculateSignedShort(byte value1, byte value2) {
|
|
return (short)calculateUnsignedShort(value1, value2);
|
|
}
|
|
|
|
/**
|
|
* Read unsigned short in network order in variable length format. Cursor is
|
|
* advanced by 1 or 2 bytes.
|
|
*
|
|
* Most significant bit of first byte indicates length of field: 0 - 1 byte, 1
|
|
* - 2 bytes.
|
|
*/
|
|
public int readVariableUnsignedShort() {
|
|
int firstByte = readUnsignedByte();
|
|
|
|
int result;
|
|
if ((firstByte & 0x80) == 0) {
|
|
result = firstByte & 0x7f;
|
|
} else {
|
|
int secondByte = readUnsignedByte();
|
|
result = (((firstByte & 0x7f) << 8) | secondByte);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Read integer in BER format.
|
|
*
|
|
* Most significant bit of first byte indicates type of date in first byte: if
|
|
* 0, then byte contains length (up to 7f), if 1, then byte contains number of
|
|
* following bytes with value in network order. Value 0x80 means unlimited
|
|
* length, which ends with two zero bytes (0x00 0x00) sequence.
|
|
*
|
|
* If -1 is returned by this method, then caller must seek two consecutive
|
|
* zeroes in buffer and consume all that data from buffer, including these two
|
|
* zeroes, but caller should not parse these two zeroes.
|
|
*
|
|
* @return length or -1, for unlimited length
|
|
*/
|
|
public long readBerLength() {
|
|
int firstByte = readUnsignedByte();
|
|
|
|
long result;
|
|
if ((firstByte & 0x80) == 0) {
|
|
result = firstByte & 0x7f;
|
|
} else {
|
|
int intLength = firstByte & 0x7f;
|
|
if (intLength != 0)
|
|
result = readUnsignedVarInt(intLength);
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Read integer in BER format.
|
|
*
|
|
* Most significant bit of first byte indicates type of date in first byte: if
|
|
* 0, then byte contains length (up to 7f), if 1, then byte contains number of
|
|
* following bytes with value in network order.
|
|
*/
|
|
public void writeBerLength(long length) {
|
|
if (length < 0)
|
|
throw new RuntimeException("Length cannot be less than zero: " + length + ". Data: " + this + ".");
|
|
|
|
if (length < 0x80) {
|
|
writeByte((int)length);
|
|
} else {
|
|
if (length < 0xff) {
|
|
writeByte(0x81);
|
|
writeByte((int)length);
|
|
} else if (length <= 0xffFF) {
|
|
writeByte(0x82);
|
|
writeShort((int)length);
|
|
} else if (length <= 0xffFFff) {
|
|
writeByte(0x83);
|
|
writeByte((int)(length >> 16));
|
|
writeShort((int)length);
|
|
} else if (length <= 0xffFFffFFL) {
|
|
writeByte(0x84);
|
|
writeInt((int)length);
|
|
} else if (length <= 0xffFFffFFffL) {
|
|
writeByte(0x85);
|
|
writeByte((int)(length >> 32));
|
|
writeInt((int)length);
|
|
} else if (length <= 0xffFFffFFffFFL) {
|
|
writeByte(0x86);
|
|
writeShort((int)(length >> 32));
|
|
writeInt((int)length);
|
|
} else if (length <= 0xffFFffFFffFFffL) {
|
|
writeByte(0x87);
|
|
writeByte((int)(length >> (32 + 16)));
|
|
writeShort((int)(length >> 32));
|
|
writeInt((int)length);
|
|
} else {
|
|
writeByte(0x88);
|
|
writeInt((int)(length >> 32));
|
|
writeInt((int)length);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Read signed variable length integers in network order.
|
|
*
|
|
* @param len
|
|
* length of integer
|
|
*/
|
|
public long readSignedVarInt(int len) {
|
|
long value = 0;
|
|
switch (len) {
|
|
case 0:
|
|
value = 0;
|
|
break;
|
|
case 1:
|
|
value = readSignedByte();
|
|
break;
|
|
case 2:
|
|
value = readSignedShort();
|
|
break;
|
|
case 3:
|
|
value = (readSignedByte() << 16) | readUnsignedShort();
|
|
break;
|
|
case 4:
|
|
value = readSignedInt();
|
|
break;
|
|
case 5:
|
|
value = readSignedByte() | readUnsignedInt();
|
|
break;
|
|
case 6:
|
|
value = readSignedShort() | readUnsignedInt();
|
|
break;
|
|
case 7:
|
|
value = (readSignedByte() << 24) | readUnsignedShort() | readUnsignedInt();
|
|
break;
|
|
case 8:
|
|
value = readSignedLong();
|
|
break;
|
|
default:
|
|
throw new RuntimeException("Cannot read integers which are more than 8 bytes long. Length: " + len + ". Data: " + this + ".");
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Read unsigned variable length integers in network order. Values, which are
|
|
* larger than 0x7FffFFffFFffFFff cannot be parsed by this method.
|
|
*/
|
|
public long readUnsignedVarInt(int len) {
|
|
long value = 0;
|
|
switch (len) {
|
|
case 0:
|
|
value = 0;
|
|
break;
|
|
case 1:
|
|
value = readUnsignedByte();
|
|
break;
|
|
case 2:
|
|
value = readUnsignedShort();
|
|
break;
|
|
case 3:
|
|
value = (readUnsignedByte() << 16) | readUnsignedShort();
|
|
break;
|
|
case 4:
|
|
value = readUnsignedInt();
|
|
break;
|
|
case 5:
|
|
value = readUnsignedByte() | readUnsignedInt();
|
|
break;
|
|
case 6:
|
|
value = readUnsignedShort() | readUnsignedInt();
|
|
break;
|
|
case 7:
|
|
value = (readUnsignedByte() << 16) | readUnsignedShort() | readUnsignedInt();
|
|
break;
|
|
case 8:
|
|
value = readSignedLong();
|
|
if (value < 0)
|
|
throw new RuntimeException("Cannot read 64 bit integers which are larger than 0x7FffFFffFFffFFff, because of lack of unsigned long type in Java. Value: " + value
|
|
+ ". Data: " + this + ".");
|
|
break;
|
|
default:
|
|
throw new RuntimeException("Cannot read integers which are more than 8 bytes long. Length: " + len + ". Data: " + this + ".");
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Read unsigned short in little endian order. Cursor is advanced by 2.
|
|
*/
|
|
public int readUnsignedShortLE() {
|
|
if (cursor + 2 > length)
|
|
throw new ArrayIndexOutOfBoundsException("Cannot read 2 bytes from this buffer: " + this + ".");
|
|
|
|
int result = (((data[offset + cursor + 1] & 0xff) << 8) | (data[offset + cursor] & 0xff));
|
|
cursor += 2;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Read unsigned short in network order in variable length format. Cursor is
|
|
* advanced by 1 or 2 bytes.
|
|
*
|
|
* Most significant bit of first byte indicates length of field: 0x00 - 1
|
|
* byte, 0x80 - 2 bytes.
|
|
*
|
|
* @see http://msdn.microsoft.com/en-us/library/cc241612.aspx
|
|
*/
|
|
public int readEncodedUnsignedShort() {
|
|
int firstByte = readUnsignedByte();
|
|
|
|
int result;
|
|
if ((firstByte & 0x80) == 0)
|
|
result = firstByte & 0x7f;
|
|
else {
|
|
int secondByte = readUnsignedByte();
|
|
result = (((firstByte & 0x7f) << 8) | secondByte);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Read signed short in network order in variable length format. Cursor is
|
|
* advanced by 1 or 2 bytes.
|
|
*
|
|
* Most significant bit of first byte indicates length of field: 0x00 - 1
|
|
* byte, 0x80 - 2 bytes. Second most significant bit indicates is value
|
|
* positive or negative.
|
|
*
|
|
* @see http://msdn.microsoft.com/en-us/library/cc241613.aspx
|
|
*/
|
|
public int readEncodedSignedShort() {
|
|
int firstByte = readUnsignedByte();
|
|
|
|
int result;
|
|
if ((firstByte & 0x80) == 0)
|
|
result = firstByte & 0x3f;
|
|
else {
|
|
int secondByte = readUnsignedByte();
|
|
result = (((firstByte & 0x3f) << 8) | secondByte);
|
|
}
|
|
|
|
if ((firstByte & 0x40) > 0)
|
|
return -result;
|
|
else
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Read signed long in little endian order. Cursor is advanced by 8 bytes.
|
|
*/
|
|
public long readSignedLongLE() {
|
|
return ((readSignedIntLE()) & 0xffFFffFFL) | (((long)readSignedIntLE()) << 32);
|
|
}
|
|
|
|
/**
|
|
* Read signed long in network order. Cursor is advanced by 8 bytes.
|
|
*/
|
|
public long readSignedLong() {
|
|
return (((long)readSignedInt()) << 32) | ((readSignedInt()) & 0xffFFffFFL);
|
|
}
|
|
|
|
/**
|
|
* Read string from buffer. Cursor is advanced by string length.
|
|
*/
|
|
public String readString(int length, Charset charset) {
|
|
if (cursor + length > this.length)
|
|
throw new ArrayIndexOutOfBoundsException("Cannot read " + length + " bytes from this buffer: " + this + ".");
|
|
|
|
String string = new String(data, offset + cursor, length, charset);
|
|
cursor += length;
|
|
return string;
|
|
}
|
|
|
|
/**
|
|
* Read string with '\0' character at end.
|
|
*/
|
|
public String readVariableString(Charset charset) {
|
|
|
|
int start = cursor;
|
|
|
|
// Find end of string
|
|
while (readUnsignedByte() != 0) {
|
|
}
|
|
|
|
String string = new String(data, offset + start, cursor - start - 1, charset);
|
|
|
|
return string;
|
|
}
|
|
|
|
/**
|
|
* Read wide string with wide '\0' character at end.
|
|
*/
|
|
public String readVariableWideString(Charset charset) {
|
|
|
|
int start = cursor;
|
|
|
|
// Find end of string
|
|
while (readUnsignedShortLE() != 0) {
|
|
}
|
|
|
|
String string = new String(data, offset + start, cursor - start - 2, charset);
|
|
|
|
return string;
|
|
}
|
|
|
|
/**
|
|
* Get bytes as lightweight slice. Cursor is advanced by data length.
|
|
*/
|
|
public ByteBuffer readBytes(int dataLength) {
|
|
if (cursor + dataLength > length)
|
|
throw new ArrayIndexOutOfBoundsException("Cannot read " + dataLength + " bytes from this buffer: " + this + ".");
|
|
|
|
ByteBuffer slice = slice(cursor, dataLength, false);
|
|
cursor += dataLength;
|
|
return slice;
|
|
}
|
|
|
|
/**
|
|
* Cursor is advanced by given number of bytes.
|
|
*/
|
|
public void skipBytes(int numOfBytes) {
|
|
if (cursor + numOfBytes > length)
|
|
throw new ArrayIndexOutOfBoundsException("Cannot read " + numOfBytes + " bytes from this buffer: " + this + ".");
|
|
|
|
cursor += numOfBytes;
|
|
}
|
|
|
|
/**
|
|
* Write byte. Cursor is advanced by 1.
|
|
*/
|
|
public void writeByte(int b) {
|
|
if (cursor + 1 > length)
|
|
throw new ArrayIndexOutOfBoundsException("Cannot write 1 byte to this buffer: " + this + ".");
|
|
|
|
data[offset + cursor] = (byte)b;
|
|
cursor += 1;
|
|
}
|
|
|
|
/**
|
|
* Write short in network order. Cursor is advanced by 2.
|
|
*/
|
|
public void writeShort(int x) {
|
|
if (cursor + 2 > length)
|
|
throw new ArrayIndexOutOfBoundsException("Cannot write 2 bytes to this buffer: " + this + ".");
|
|
|
|
data[offset + cursor] = (byte)(x >> 8);
|
|
data[offset + cursor + 1] = (byte)x;
|
|
cursor += 2;
|
|
}
|
|
|
|
/**
|
|
* Write short in little endian order. Cursor is advanced by 2.
|
|
*/
|
|
public void writeShortLE(int x) {
|
|
if (cursor + 2 > length)
|
|
throw new ArrayIndexOutOfBoundsException("Cannot write 2 bytes to this buffer: " + this + ".");
|
|
|
|
data[offset + cursor + 1] = (byte)(x >> 8);
|
|
data[offset + cursor] = (byte)x;
|
|
cursor += 2;
|
|
}
|
|
|
|
/**
|
|
* Write int in network order. Cursor is advanced by 4.
|
|
*/
|
|
public void writeInt(int i) {
|
|
if (cursor + 4 > length)
|
|
throw new ArrayIndexOutOfBoundsException("Cannot write 4 bytes to this buffer: " + this + ".");
|
|
|
|
data[offset + cursor] = (byte)(i >> 24);
|
|
data[offset + cursor + 1] = (byte)(i >> 16);
|
|
data[offset + cursor + 2] = (byte)(i >> 8);
|
|
data[offset + cursor + 3] = (byte)i;
|
|
cursor += 4;
|
|
}
|
|
|
|
public void writeIntLE(int i) {
|
|
if (cursor + 4 > length)
|
|
throw new ArrayIndexOutOfBoundsException("Cannot write 4 bytes to this buffer: " + this + ".");
|
|
|
|
data[offset + cursor] = (byte)i;
|
|
data[offset + cursor + 1] = (byte)(i >> 8);
|
|
data[offset + cursor + 2] = (byte)(i >> 16);
|
|
data[offset + cursor + 3] = (byte)(i >> 24);
|
|
cursor += 4;
|
|
}
|
|
|
|
/**
|
|
* Write int in variable length format. Cursor is advanced by number of bytes
|
|
* written (1-5).
|
|
*
|
|
* Topmost bit of each byte is set to 1 to indicate that next byte has data.
|
|
*/
|
|
public void writeVariableIntLE(int i) {
|
|
while (i != 0) {
|
|
// Get lower bits of number
|
|
int b = i & 0x7f;
|
|
i >>= 7;
|
|
|
|
if (i > 0)
|
|
// Set topmost bit of byte to indicate that next byte(s) contains
|
|
// remainder bits
|
|
b |= 0x80;
|
|
|
|
writeByte(b);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Write short in variable length format. Cursor is advanced by number of
|
|
* bytes written (1-2).
|
|
*
|
|
* Topmost bit of first byte is set to 1 to indicate that next byte has data.
|
|
*/
|
|
public void writeVariableShort(int length) {
|
|
if (length > 0x7f | length < 0)
|
|
writeShort(length | 0x8000);
|
|
else
|
|
writeByte(length);
|
|
}
|
|
|
|
/**
|
|
* Prepend given data to this byte buffer.
|
|
*/
|
|
public void prepend(ByteBuffer buf) {
|
|
prepend(buf.data, buf.offset, buf.length);
|
|
}
|
|
|
|
/**
|
|
* Prepend given data to this byte buffer.
|
|
*/
|
|
public void prepend(byte[] data) {
|
|
prepend(data, 0, data.length);
|
|
}
|
|
|
|
/**
|
|
* Prepend given data to this byte buffer.
|
|
*/
|
|
public void prepend(byte[] data, int offset, int length) {
|
|
if (!isSoleOwner()) {
|
|
throw new RuntimeException("Create full copy of this byte buffer data for modification. refCount: " + refCount + ".");
|
|
}
|
|
|
|
// If there is no enough space for header to prepend
|
|
if (!(this.offset >= length)) {
|
|
throw new RuntimeException("Reserve data to have enough space for header.");
|
|
}
|
|
|
|
// Copy header
|
|
System.arraycopy(data, offset, this.data, this.offset - length, length);
|
|
|
|
// Extend byte range to include header
|
|
this.offset -= length;
|
|
this.length += length;
|
|
cursor += length;
|
|
}
|
|
|
|
/**
|
|
* Write byte representation of given string, without string terminators (zero
|
|
* or zeroes at end of string).
|
|
*/
|
|
public void writeString(String str, Charset charset) {
|
|
writeBytes(str.getBytes(charset));
|
|
}
|
|
|
|
/**
|
|
* Write string of fixed size. When string is shorted, empty space is filled
|
|
* with zeros. When string is larger, it is truncated.
|
|
*/
|
|
public void writeFixedString(int length, String str, Charset charset) {
|
|
byte[] bytes = str.getBytes(charset);
|
|
writeBytes(bytes, 0, Math.min(bytes.length, length));
|
|
|
|
for (int i = bytes.length; i < length; i++)
|
|
writeByte(0);
|
|
}
|
|
|
|
public void writeBytes(ByteBuffer buf) {
|
|
writeBytes(buf.data, buf.offset, buf.length);
|
|
}
|
|
|
|
public void writeBytes(byte[] bytes) {
|
|
writeBytes(bytes, 0, bytes.length);
|
|
}
|
|
|
|
public void writeBytes(byte[] bytes, int offset, int length) {
|
|
System.arraycopy(bytes, offset, data, this.offset + cursor, length);
|
|
cursor += length;
|
|
}
|
|
|
|
/**
|
|
* Reduce length of buffer to cursor position.
|
|
*/
|
|
public void trimAtCursor() {
|
|
length = cursor;
|
|
}
|
|
|
|
/**
|
|
* Rewind cursor to beginning of the buffer.
|
|
*/
|
|
public void rewindCursor() {
|
|
cursor = 0;
|
|
}
|
|
|
|
/**
|
|
* Read RGB color in LE order. Cursor is advanced by 3.
|
|
*
|
|
* @return color as int, with red in lowest octet.
|
|
*/
|
|
public int readRGBColor() {
|
|
return readUnsignedByte() | (readUnsignedByte() << 8) | (readUnsignedByte() << 16);
|
|
}
|
|
|
|
public void assertThatBufferIsFullyRead() {
|
|
if (cursor != length)
|
|
throw new RuntimeException("Data in buffer is not read fully. Buf: " + this + ".");
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
final int prime = 31;
|
|
int result = 1;
|
|
|
|
int end = offset + length;
|
|
for (int i = offset; i < end; i++)
|
|
result = 31 * result + data[i];
|
|
|
|
result = prime * result + length;
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object obj) {
|
|
if (this == obj)
|
|
return true;
|
|
|
|
if (obj == null)
|
|
return false;
|
|
|
|
// Does not work in case of anonymous type(s)
|
|
if (getClass() != obj.getClass())
|
|
return false;
|
|
|
|
ByteBuffer other = (ByteBuffer)obj;
|
|
if (length != other.length)
|
|
return false;
|
|
|
|
for (int i = 0; i < length; i++)
|
|
if (data[offset + i] != other.data[other.offset + i])
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Return length of data left after cursor.
|
|
*/
|
|
public int remainderLength() {
|
|
if (length >= cursor)
|
|
return length - cursor;
|
|
else
|
|
throw new RuntimeException("Inconsistent state of buffer: cursor is after end of buffer: " + this + ".");
|
|
}
|
|
|
|
public Set<String> getMetadataKeys() {
|
|
if (metadata != null)
|
|
return metadata.keySet();
|
|
else
|
|
return new HashSet<String>(0);
|
|
}
|
|
|
|
/**
|
|
* Return unsigned value of byte at given position relative to cursor. Cursor
|
|
* is not advanced.
|
|
*/
|
|
public int peekUnsignedByte(int i) {
|
|
return data[offset + cursor + i] & 0xff;
|
|
}
|
|
|
|
/**
|
|
* Trim few first bytes.
|
|
*/
|
|
public void trimHeader(int length) {
|
|
offset += length;
|
|
this.length -= length;
|
|
rewindCursor();
|
|
}
|
|
|
|
}
|