blob: c4383231f9076c1846132ff5cead791d975be07f [file] [log] [blame]
/*
* 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.iotdb.tsfile.utils;
import org.apache.iotdb.tsfile.common.conf.TSFileConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* BytesUtils is a utility class. It provides conversion among byte array and other type including
* integer, long, float, boolean, double and string. <br>
* It also provide other usable function as follows:<br>
* reading function which receives InputStream. <br>
* concat function to join a list of byte array to one.<br>
* get and set one bit in a byte array.
*/
public class BytesUtils {
private BytesUtils() {}
private static final Logger LOG = LoggerFactory.getLogger(BytesUtils.class);
/**
* integer convert to byte[4].
*
* @param i integer to convert
* @return byte[4] for integer
*/
public static byte[] intToBytes(int i) {
return new byte[] {
(byte) ((i >> 24) & 0xFF),
(byte) ((i >> 16) & 0xFF),
(byte) ((i >> 8) & 0xFF),
(byte) (i & 0xFF)
};
}
/**
* integer convert to byte array, then write four bytes to parameter desc start from index:offset.
*
* @param i integer to convert
* @param desc byte array be written
* @param offset position in desc byte array that conversion result should start
* @return byte array
*/
public static byte[] intToBytes(int i, byte[] desc, int offset) {
if (desc.length - offset < 4) {
throw new IllegalArgumentException("Invalid input: desc.length - offset < 4");
}
desc[0 + offset] = (byte) ((i >> 24) & 0xFF);
desc[1 + offset] = (byte) ((i >> 16) & 0xFF);
desc[2 + offset] = (byte) ((i >> 8) & 0xFF);
desc[3 + offset] = (byte) (i & 0xFF);
return desc;
}
/**
* convert an integer to a byte array which length is width, then copy this array to the parameter
* result from pos.
*
* @param srcNum input integer variable. All but the lowest {@code width} bits are 0.
* @param result byte array to convert
* @param pos start position
* @param width bit-width
*/
public static void intToBytes(int srcNum, byte[] result, int pos, int width) {
int cnt = pos & 0x07;
int index = pos >> 3;
try {
while (width > 0) {
int m = width + cnt >= 8 ? 8 - cnt : width;
width -= m;
int mask = 1 << (8 - cnt);
cnt += m;
byte y = (byte) (srcNum >>> width);
y = (byte) (y << (8 - cnt));
mask = ~(mask - (1 << (8 - cnt)));
result[index] = (byte) (result[index] & mask | y);
srcNum = srcNum & ~(-1 << width);
if (cnt == 8) {
index++;
cnt = 0;
}
}
} catch (Exception e) {
LOG.error(
"tsfile-common BytesUtils: cannot convert an integer {} to a byte array, "
+ "pos {}, width {}",
srcNum,
pos,
width,
e);
}
}
/**
* divide int to two bytes.
*
* @param i int
* @return two bytes in byte[] structure
*/
public static byte[] intToTwoBytes(int i) {
if (i > 0xFFFF) {
throw new IllegalArgumentException("Invalid input: " + i + " > 0xFFFF");
}
byte[] ret = new byte[2];
ret[1] = (byte) (i & 0xFF);
ret[0] = (byte) ((i >> 8) & 0xFF);
return ret;
}
/**
* concatenate two bytes to int.
*
* @param ret byte[]
* @return int value
*/
public static int twoBytesToInt(byte[] ret) {
if (ret.length != 2) {
throw new IllegalArgumentException("Invalid input: ret.length != 2");
}
int value = 0;
value |= ret[0];
value = value << 8;
value |= ret[1];
return value;
}
/**
* byte[4] convert to integer.
*
* @param bytes input byte[]
* @return integer
*/
public static int bytesToInt(byte[] bytes) {
// compatible to long
int length = bytes.length;
long r = 0;
for (int i = 0; i < length; i++) {
r += ((bytes[length - 1 - i] & 0xFF) << (i * 8));
}
if (r > Integer.MAX_VALUE) {
throw new RuntimeException("Row count is larger than Integer.MAX_VALUE");
}
return (int) r;
}
/**
* convert four-bytes byte array cut from parameters to integer.
*
* @param bytes source bytes which length should be greater than 4
* @param offset position in parameter byte array that conversion result should start
* @return integer
*/
public static int bytesToInt(byte[] bytes, int offset) {
if (bytes.length - offset < 4) {
throw new IllegalArgumentException("Invalid input: bytes.length - offset < 4");
}
int value = 0;
// high bit to low
for (int i = 0; i < 4; i++) {
int shift = (4 - 1 - i) * 8;
value += (bytes[offset + i] & 0x000000FF) << shift;
}
return value;
}
/**
* given a byte array, read width bits from specified position bits and convert it to an integer.
*
* @param result input byte array
* @param pos bit offset rather than byte offset
* @param width bit-width
* @return integer variable
*/
public static int bytesToInt(byte[] result, int pos, int width) {
int ret = 0;
int cnt = pos & 0x07;
int index = pos >> 3;
while (width > 0) {
int m = width + cnt >= 8 ? 8 - cnt : width;
width -= m;
ret = ret << m;
byte y = (byte) (result[index] & (0xff >> cnt));
y = (byte) ((y & 0xff) >>> (8 - cnt - m));
ret = ret | (y & 0xff);
cnt += m;
if (cnt == 8) {
cnt = 0;
index++;
}
}
return ret;
}
/**
* convert float to byte array.
*
* @param x float
* @return byte[4]
*/
public static byte[] floatToBytes(float x) {
byte[] b = new byte[4];
int l = Float.floatToIntBits(x);
for (int i = 3; i >= 0; i--) {
b[i] = (byte) l;
l = l >> 8;
}
return b;
}
/**
* float convert to boolean, then write four bytes to parameter desc start from index:offset.
*
* @param x float
* @param desc byte array be written
* @param offset position in desc byte array that conversion result should start
*/
public static void floatToBytes(float x, byte[] desc, int offset) {
if (desc.length - offset < 4) {
throw new IllegalArgumentException("Invalid input: desc.length - offset < 4");
}
int l = Float.floatToIntBits(x);
for (int i = 3 + offset; i >= offset; i--) {
desc[i] = (byte) l;
l = l >> 8;
}
}
/**
* convert byte[4] to float.
*
* @param b byte[4]
* @return float
*/
public static float bytesToFloat(byte[] b) {
if (b.length != 4) {
throw new IllegalArgumentException("Invalid input: b.length != 4");
}
int l;
l = b[3];
l &= 0xff;
l |= ((long) b[2] << 8);
l &= 0xffff;
l |= ((long) b[1] << 16);
l &= 0xffffff;
l |= ((long) b[0] << 24);
return Float.intBitsToFloat(l);
}
/**
* convert four-bytes byte array cut from parameters to float.
*
* @param b source bytes which length should be greater than 4
* @param offset position in parameter byte array that conversion result should start
* @return float
*/
public static float bytesToFloat(byte[] b, int offset) {
if (b.length - offset < 4) {
throw new IllegalArgumentException("Invalid input: b.length - offset < 4");
}
int l;
l = b[offset + 3];
l &= 0xff;
l |= ((long) b[offset + 2] << 8);
l &= 0xffff;
l |= ((long) b[offset + 1] << 16);
l &= 0xffffff;
l |= ((long) b[offset] << 24);
return Float.intBitsToFloat(l);
}
/**
* convert double to byte array.
*
* @param data double
* @return byte[8]
*/
public static byte[] doubleToBytes(double data) {
byte[] bytes = new byte[8];
long value = Double.doubleToLongBits(data);
for (int i = 7; i >= 0; i--) {
bytes[i] = (byte) value;
value = value >> 8;
}
return bytes;
}
/**
* convert double to byte into the given byte array started from offset.
*
* @param d input double
* @param bytes target byte[]
* @param offset start pos
*/
public static void doubleToBytes(double d, byte[] bytes, int offset) {
if (bytes.length - offset < 8) {
throw new IllegalArgumentException("Invalid input: bytes.length - offset < 8");
}
long value = Double.doubleToLongBits(d);
for (int i = 7; i >= 0; i--) {
bytes[offset + i] = (byte) value;
value = value >> 8;
}
}
/**
* convert byte array to double.
*
* @param bytes byte[8]
* @return double
*/
public static double bytesToDouble(byte[] bytes) {
long value = bytes[7];
value &= 0xff;
value |= ((long) bytes[6] << 8);
value &= 0xffff;
value |= ((long) bytes[5] << 16);
value &= 0xffffff;
value |= ((long) bytes[4] << 24);
value &= 0xffffffffL;
value |= ((long) bytes[3] << 32);
value &= 0xffffffffffL;
value |= ((long) bytes[2] << 40);
value &= 0xffffffffffffL;
value |= ((long) bytes[1] << 48);
value &= 0xffffffffffffffL;
value |= ((long) bytes[0] << 56);
return Double.longBitsToDouble(value);
}
/**
* convert eight-bytes byte array cut from parameters to double.
*
* @param bytes source bytes which length should be greater than 8
* @param offset position in parameter byte array that conversion result should start
* @return double
*/
public static double bytesToDouble(byte[] bytes, int offset) {
if (bytes.length - offset < 8) {
throw new IllegalArgumentException("Invalid input: bytes.length - offset < 8");
}
long value = bytes[offset + 7];
value &= 0xff;
value |= ((long) bytes[offset + 6] << 8);
value &= 0xffff;
value |= ((long) bytes[offset + 5] << 16);
value &= 0xffffff;
value |= ((long) bytes[offset + 4] << 24);
value &= 0xffffffffL;
value |= ((long) bytes[offset + 3] << 32);
value &= 0xffffffffffL;
value |= ((long) bytes[offset + 2] << 40);
value &= 0xffffffffffffL;
value |= ((long) bytes[offset + 1] << 48);
value &= 0xffffffffffffffL;
value |= ((long) bytes[offset] << 56);
return Double.longBitsToDouble(value);
}
/**
* convert boolean to byte[1].
*
* @param x boolean
* @return byte[]
*/
public static byte[] boolToBytes(boolean x) {
byte[] b = new byte[1];
if (x) {
b[0] = 1;
} else {
b[0] = 0;
}
return b;
}
public static byte boolToByte(boolean x) {
if (x) {
return 1;
} else {
return 0;
}
}
public static boolean byteToBool(byte b) {
return b == 1;
}
/**
* boolean convert to byte array, then write four bytes to parameter desc start from index:offset.
*
* @param x input boolean
* @param desc byte array be written
* @param offset position in desc byte array that conversion result should start
* @return byte[1]
*/
public static byte[] boolToBytes(boolean x, byte[] desc, int offset) {
if (x) {
desc[offset] = 1;
} else {
desc[offset] = 0;
}
return desc;
}
/**
* byte array to boolean.
*
* @param b input byte[1]
* @return boolean
*/
public static boolean bytesToBool(byte[] b) {
if (b.length != 1) {
throw new IllegalArgumentException("Invalid input: b.length != 1");
}
return b[0] != 0;
}
/**
* convert one-bytes byte array cut from parameters to boolean.
*
* @param b source bytes which length should be greater than 1
* @param offset position in parameter byte array that conversion result should start
* @return boolean
*/
public static boolean bytesToBool(byte[] b, int offset) {
if (b.length - offset < 1) {
throw new IllegalArgumentException("Invalid input: b.length - offset < 1");
}
return b[offset] != 0;
}
/**
* long to byte array with default converting length 8. It means the length of result byte array
* is 8.
*
* @param num long variable to be converted
* @return byte[8]
*/
public static byte[] longToBytes(long num) {
return longToBytes(num, 8);
}
/**
* specify the result array length. then, convert long to Big-Endian byte from low to high. <br>
* e.g.<br>
* the binary presentation of long number 1000L is {6 bytes equal 0000000} 00000011 11101000<br>
* if len = 2, it will return byte array :{00000011 11101000}(Big-Endian) if len = 1, it will
* return byte array :{11101000}.
*
* @param num long variable to be converted
* @param len length of result byte array
* @return byte array which length equals with parameter len
*/
public static byte[] longToBytes(long num, int len) {
byte[] byteNum = new byte[len];
for (int ix = 0; ix < len; ix++) {
byteNum[len - ix - 1] = (byte) ((num >> ix * 8) & 0xFF);
}
return byteNum;
}
/**
* long convert to byte array, then write four bytes to parameter desc start from index:offset.
*
* @param num input long variable
* @param desc byte array be written
* @param offset position in desc byte array that conversion result should start
* @return byte array
*/
public static byte[] longToBytes(long num, byte[] desc, int offset) {
for (int ix = 0; ix < 8; ++ix) {
int i = 64 - (ix + 1) * 8;
desc[ix + offset] = (byte) ((num >> i) & 0xff);
}
return desc;
}
/**
* convert a long to a byte array which length is width, then copy this array to the parameter
* result from pos.
*
* @param srcNum input long variable. All but the lowest {@code width} bits are 0.
* @param result byte array to convert
* @param pos start position
* @param width bit-width
*/
public static void longToBytes(long srcNum, byte[] result, int pos, int width) {
int cnt = pos & 0x07;
int index = pos >> 3;
try {
while (width > 0) {
int m = width + cnt >= 8 ? 8 - cnt : width;
width -= m;
int mask = 1 << (8 - cnt);
cnt += m;
byte y = (byte) (srcNum >>> width);
y = (byte) (y << (8 - cnt));
mask = ~(mask - (1 << (8 - cnt)));
result[index] = (byte) (result[index] & mask | y);
srcNum = srcNum & ~(-1L << width);
if (cnt == 8) {
index++;
cnt = 0;
}
}
} catch (Exception e) {
LOG.error(
"tsfile-common BytesUtils: cannot convert a long {} to a byte array, "
+ "pos {}, width {}",
srcNum,
pos,
width,
e);
}
}
/**
* convert byte array to long with default length 8. namely.
*
* @param byteNum input byte array
* @return long
*/
public static long bytesToLong(byte[] byteNum) {
return bytesToLong(byteNum, byteNum.length);
}
/**
* specify the input byte array length. then, convert byte array to long value from low to high.
* <br>
* e.g.<br>
* the input byte array is {00000011 11101000}. if len = 2, return 1000 if len = 1, return
* 232(only calculate the low byte).
*
* @param byteNum byte array to be converted
* @param len length of input byte array to be converted
* @return long
*/
public static long bytesToLong(byte[] byteNum, int len) {
long num = 0;
for (int ix = 0; ix < len; ix++) {
num <<= 8;
num |= (byteNum[ix] & 0xff);
}
return num;
}
/**
* given a byte array, read width bits from specified pos bits and convert it to an long.
*
* @param result input byte array
* @param pos bit offset rather than byte offset
* @param width bit-width
* @return long variable
*/
public static long bytesToLong(byte[] result, int pos, int width) {
long ret = 0;
int cnt = pos & 0x07;
int index = pos >> 3;
while (width > 0) {
int m = width + cnt >= 8 ? 8 - cnt : width;
width -= m;
ret = ret << m;
byte y = (byte) (result[index] & (0xff >> cnt));
y = (byte) ((y & 0xff) >>> (8 - cnt - m));
ret = ret | (y & 0xff);
cnt += m;
if (cnt == 8) {
cnt = 0;
index++;
}
}
return ret;
}
/**
* convert eight-bytes byte array cut from parameters to long.
*
* @param byteNum source bytes which length should be greater than 8
* @param len length of input byte array to be converted
* @param offset position in parameter byte array that conversion result should start
* @return long
*/
public static long bytesToLongFromOffset(byte[] byteNum, int len, int offset) {
if (byteNum.length - offset < len) {
throw new IllegalArgumentException("Invalid input: byteNum.length - offset < len");
}
long num = 0;
for (int ix = 0; ix < len; ix++) {
num <<= 8;
num |= (byteNum[offset + ix] & 0xff);
}
return num;
}
/**
* convert string to byte array using UTF-8 encoding.
*
* @param str input string
* @return byte array
*/
public static byte[] stringToBytes(String str) {
return str.getBytes(TSFileConfig.STRING_CHARSET);
}
/**
* convert byte array to string using UTF-8 encoding.
*
* @param byteStr input byte array
* @return string
*/
public static String bytesToString(byte[] byteStr) {
return new String(byteStr, TSFileConfig.STRING_CHARSET);
}
/**
* join two byte arrays to one.
*
* @param a one of byte array
* @param b another byte array
* @return byte array after joining
*/
public static byte[] concatByteArray(byte[] a, byte[] b) {
byte[] c = new byte[a.length + b.length];
System.arraycopy(a, 0, c, 0, a.length);
System.arraycopy(b, 0, c, a.length, b.length);
return c;
}
/**
* join a list of byte arrays into one array.
*
* @param list a list of byte array to join
* @return byte array after joining
*/
public static byte[] concatByteArrayList(List<byte[]> list) {
int size = list.size();
int len = 0;
for (byte[] cs : list) {
len += cs.length;
}
byte[] result = new byte[len];
int pos = 0;
for (int i = 0; i < size; i++) {
int l = list.get(i).length;
System.arraycopy(list.get(i), 0, result, pos, l);
pos += l;
}
return result;
}
/**
* Return the deepCopy of the given byte array.
*
* @param src input byte array
* @return byte array
*/
public static byte[] deepCopy(byte[] src) {
return subBytes(src, 0, src.length);
}
/**
* cut out specified length byte array from parameter start from input byte array src and return.
*
* @param src input byte array
* @param start start index of src
* @param length cut off length
* @return byte array
*/
public static byte[] subBytes(byte[] src, int start, int length) {
if ((start + length) > src.length) {
return new byte[0];
}
if (length <= 0) {
return new byte[0];
}
byte[] result = new byte[length];
System.arraycopy(src, start, result, 0, length);
return result;
}
/**
* get one bit in input integer. the offset is from low to high and start with 0<br>
* e.g.<br>
* data:1000(00000000 00000000 00000011 11101000), if offset is 4, return 0(111 "0" 1000) if
* offset is 9, return 1(00000 "1" 1 11101000).
*
* @param data input int variable
* @param offset bit offset
* @return 0 or 1
*/
public static int getIntN(int data, int offset) {
offset %= 32;
if ((data & (1 << (offset))) != 0) {
return 1;
} else {
return 0;
}
}
/**
* set one bit in input integer. the offset is from low to high and start with index 0<br>
* e.g.<br>
* data:1000({00000000 00000000 00000011 11101000}), if offset is 4, value is 1, return
* 1016({00000000 00000000 00000011 111 "1" 1000}) if offset is 9, value is 0 return 488({00000000
* 00000000 000000 "0" 1 11101000}) if offset is 0, value is 0 return 1000(no change).
*
* @param data input int variable
* @param offset bit offset
* @param value value to set
* @return int variable
*/
public static int setIntN(int data, int offset, int value) {
offset %= 32;
if (value == 1) {
return (data | (1 << (offset)));
} else {
return (data & ~(1 << (offset)));
}
}
/**
* get one bit in input byte. the offset is from high to low and start with 0<br>
* e.g.<br>
* data:16(00010000), if offset is 3, return 1(000 "1" 0000) if offset is 0, return 0("0"
* 0010000).
*
* @param data input byte variable
* @param offset bit offset
* @return 0/1
*/
public static int getByteN(byte data, int offset) {
offset %= 8;
if (((0xff & data) & (1 << (7 - offset))) != 0) {
return 1;
} else {
return 0;
}
}
/**
* set one bit in input byte. the offset is from high to low and start with index 0<br>
* e.g.<br>
* data:16(00010000), if offset is 3, value is 0, return 0({000 "0" 0000}) if offset is 6, value
* is 1, return 18({00010010}) if offset is 0, value is 0, return 16(no change).
*
* @param data input byte variable
* @param offset bit offset
* @param value value to set
* @return byte variable
*/
public static byte setByteN(byte data, int offset, int value) {
offset %= 8;
if (value == 1) {
return (byte) ((0xff & data) | (1 << (7 - offset)));
} else {
return (byte) ((0xff & data) & ~(1 << (7 - offset)));
}
}
/**
* get one bit in input long. the offset is from low to high and start with 0.
*
* @param data input long variable
* @param offset bit offset
* @return 0/1
*/
public static int getLongN(long data, int offset) {
offset %= 64;
if ((data & (1L << (offset))) != 0) {
return 1;
} else {
return 0;
}
}
/**
* set one bit in input long. the offset is from low to high and start with index 0.
*
* @param data input long variable
* @param offset bit offset
* @param value value to set
* @return long variable
*/
public static long setLongN(long data, int offset, int value) {
offset %= 64;
if (value == 1) {
return (data | (1L << (offset)));
} else {
return (data & ~(1L << (offset)));
}
}
/**
* read 8-byte array from an InputStream and convert it to a double number.
*
* @param in InputStream
* @return double
* @throws IOException cannot read double from InputStream
*/
public static double readDouble(InputStream in) throws IOException {
byte[] b = safeReadInputStreamToBytes(8, in);
return BytesUtils.bytesToDouble(b);
}
/**
* read 4-byte array from an InputStream and convert it to a float number.
*
* @param in InputStream
* @return float
* @throws IOException cannot read float from InputStream
*/
public static float readFloat(InputStream in) throws IOException {
byte[] b = safeReadInputStreamToBytes(4, in);
return BytesUtils.bytesToFloat(b);
}
/**
* read 1-byte array from an InputStream and convert it to a integer number.
*
* @param in InputStream
* @return boolean
* @throws IOException cannot read boolean from InputStream
*/
public static boolean readBool(InputStream in) throws IOException {
byte[] b = safeReadInputStreamToBytes(1, in);
return BytesUtils.bytesToBool(b);
}
/**
* read 4-byte array from an InputStream and convert it to a integer number.
*
* @param in InputStream
* @return integer
* @throws IOException cannot read int from InputStream
*/
public static int readInt(InputStream in) throws IOException {
byte[] b = safeReadInputStreamToBytes(4, in);
return BytesUtils.bytesToInt(b);
}
/**
* read 8-byte array from an InputStream and convert it to a long number.
*
* @param in InputStream
* @return long
* @throws IOException cannot read long from InputStream
*/
public static long readLong(InputStream in) throws IOException {
byte[] b = safeReadInputStreamToBytes(8, in);
return BytesUtils.bytesToLong(b);
}
/**
* read bytes specified length from InputStream safely.
*
* @param count number of byte to read
* @param in InputStream
* @return byte array
* @throws IOException cannot read from InputStream
*/
public static byte[] safeReadInputStreamToBytes(int count, InputStream in) throws IOException {
byte[] bytes = new byte[count];
int readCount = 0;
while (readCount < count) {
readCount += in.read(bytes, readCount, count - readCount);
}
return bytes;
}
/**
* we modify the order of serialization for fitting ByteBuffer.putShort()
*
* @param number input short number
* @return Bytes
*/
public static byte[] shortToBytes(short number) {
int temp = number;
byte[] b = new byte[2];
for (int i = b.length - 1; i >= 0; i--) {
b[i] = (byte) temp;
temp = temp >> 8;
}
return b;
}
/**
* we modify the order of serialization for fitting ByteBuffer.getShort()
*
* @param b bytes
* @return short number
*/
public static short bytesToShort(byte[] b) {
short s0 = (short) (b[1] & 0xff);
short s1 = (short) (b[0] & 0xff);
s1 <<= 8;
short s = (short) (s0 | s1);
return s;
}
public static Binary valueOf(String value) {
return new Binary(stringToBytes(value));
}
}