/* * Copyright 2014 NAVER Corp. * * 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. */ package com.navercorp.pinpoint.common.util; import com.navercorp.pinpoint.common.Charsets; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; /** * @author emeroad */ public final class BytesUtils { public static final int SHORT_BYTE_LENGTH = 2; public static final int INT_BYTE_LENGTH = 4; public static final int LONG_BYTE_LENGTH = 8; public static final int LONG_LONG_BYTE_LENGTH = 16; public static final int VLONG_MAX_SIZE = 10; public static final int VINT_MAX_SIZE = 5; private static final byte[] EMPTY_BYTES = new byte[0]; private static final Charset UTF8_CHARSET = Charsets.UTF_8; private static final String UTF8 = Charsets.UTF_8_NAME; private BytesUtils() { } public static byte[] stringLongLongToBytes(final String string, final int maxStringSize, final long value1, final long value2) { if (string == null) { throw new NullPointerException("string must not be null"); } if (maxStringSize < 0) { throw new IndexOutOfBoundsException("maxStringSize"); } final byte[] stringBytes = toBytes(string); if (stringBytes.length > maxStringSize) { throw new IndexOutOfBoundsException("string is max " + stringBytes.length + ", string='" + string + "'"); } final byte[] buffer = new byte[LONG_LONG_BYTE_LENGTH + maxStringSize]; writeBytes(buffer, 0, stringBytes); writeFirstLong0(value1, buffer, maxStringSize); writeSecondLong0(value2, buffer, maxStringSize); return buffer; } public static int writeBytes(final byte[] buffer, int bufferOffset, final byte[] srcBytes) { if (srcBytes == null) { throw new NullPointerException("srcBytes must not be null"); } return writeBytes(buffer, bufferOffset, srcBytes, 0, srcBytes.length); } public static int writeBytes(final byte[] buffer, final int bufferOffset, final byte[] srcBytes, final int srcOffset, final int srcLength) { if (buffer == null) { throw new NullPointerException("buffer must not be null"); } if (srcBytes == null) { throw new NullPointerException("stringBytes must not be null"); } if (bufferOffset < 0) { throw new IndexOutOfBoundsException("negative bufferOffset:" + bufferOffset); } if (srcOffset < 0) { throw new IndexOutOfBoundsException("negative srcOffset offset:" + srcOffset); } System.arraycopy(srcBytes, srcOffset, buffer, bufferOffset, srcLength); return bufferOffset + srcLength; } public static long bytesToLong(final byte[] buf, final int offset) { if (buf == null) { throw new NullPointerException("buf must not be null"); } if (offset < 0) { throw new IndexOutOfBoundsException("negative offset:" + offset); } if (buf.length < offset + LONG_BYTE_LENGTH) { throw new IndexOutOfBoundsException("buf.length is too small. buf.length:" + buf.length + " offset:" + (offset + 8)); } final long rv = (((long) buf[offset] & 0xff) << 56) | (((long) buf[offset + 1] & 0xff) << 48) | (((long) buf[offset + 2] & 0xff) << 40) | (((long) buf[offset + 3] & 0xff) << 32) | (((long) buf[offset + 4] & 0xff) << 24) | (((long) buf[offset + 5] & 0xff) << 16) | (((long) buf[offset + 6] & 0xff) << 8) | (((long) buf[offset + 7] & 0xff)); return rv; } public static int bytesToInt(final byte[] buf, final int offset) { if (buf == null) { throw new NullPointerException("buf must not be null"); } if (offset < 0) { throw new IndexOutOfBoundsException("negative offset:" + offset); } if (buf.length < offset + INT_BYTE_LENGTH) { throw new IndexOutOfBoundsException("buf.length is too small. buf.length:" + buf.length + " offset:" + (offset + 4)); } final int v = ((buf[offset] & 0xff) << 24) | ((buf[offset + 1] & 0xff) << 16) | ((buf[offset + 2] & 0xff) << 8) | ((buf[offset + 3] & 0xff)); return v; } public static short bytesToShort(final byte[] buf, final int offset) { if (buf == null) { throw new NullPointerException("buf must not be null"); } if (offset < 0) { throw new IndexOutOfBoundsException("negative offset:" + offset); } if (buf.length < offset + SHORT_BYTE_LENGTH) { throw new IndexOutOfBoundsException("buf.length is too small. buf.length:" + buf.length + " offset:" + (offset + 2)); } final short v = (short) (((buf[offset] & 0xff) << 8) | ((buf[offset + 1] & 0xff))); return v; } public static int bytesToSVar32(final byte[] buffer, final int offset) { return zigzagToInt(bytesToVar32(buffer, offset)); } public static int bytesToVar32(final byte[] buffer, final int offset) { if (buffer == null) { throw new NullPointerException("buffer must not be null"); } checkBound(buffer.length, offset); // borrowing the protocol buffer's concept of variable-length encoding // copy https://github.com/google/protobuf 2.6.1 // CodedInputStream.java -> int readRawVarint32() // See implementation notes for readRawVarint64 fastpath: { int pos = offset; final int bufferSize = buffer.length; if (bufferSize == pos) { break fastpath; } int x; if ((x = buffer[pos++]) >= 0) { return x; } else if (bufferSize - pos < 9) { break fastpath; } else if ((x ^= (buffer[pos++] << 7)) < 0) { x ^= (~0 << 7); } else if ((x ^= (buffer[pos++] << 14)) >= 0) { x ^= (~0 << 7) ^ (~0 << 14); } else if ((x ^= (buffer[pos++] << 21)) < 0) { x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21); } else { int y = buffer[pos++]; x ^= y << 28; x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21) ^ (~0 << 28); if (y < 0 && buffer[pos++] < 0 && buffer[pos++] < 0 && buffer[pos++] < 0 && buffer[pos++] < 0 && buffer[pos] < 0) { break fastpath; // Will throw malformedVarint() } } return x; } return (int) readVar64SlowPath(buffer, offset); } public static long bytesToSVar64(final byte[] buffer, final int offset) { return zigzagToLong(bytesToVar64(buffer, offset)); } public static long bytesToVar64(final byte[] buffer, final int offset) { if (buffer == null) { throw new NullPointerException("buffer must not be null"); } checkBound(buffer.length, offset); // borrowing the protocol buffer's concept of variable-length encoding // copy https://github.com/google/protobuf 2.6.1 // CodedInputStream.java -> int readRawVarint32() // Implementation notes: // // Optimized for one-byte values, expected to be common. // The particular code below was selected from various candidates // empirically, by winning VarintBenchmark. // // Sign extension of (signed) Java bytes is usually a nuisance, but // we exploit it here to more easily obtain the sign of bytes read. // Instead of cleaning up the sign extension bits by masking eagerly, // we delay until we find the final (positive) byte, when we clear all // accumulated bits with one xor. We depend on javac to constant fold. fastpath: { int pos = offset; int bufferSize = buffer.length; if (bufferSize == pos) { break fastpath; } long x; int y; if ((y = buffer[pos++]) >= 0) { return y; } else if (bufferSize - pos < 9) { break fastpath; } else if ((x = y ^ (buffer[pos++] << 7)) < 0L) { x ^= (~0L << 7); } else if ((x ^= (buffer[pos++] << 14)) >= 0L) { x ^= (~0L << 7) ^ (~0L << 14); } else if ((x ^= (buffer[pos++] << 21)) < 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21); } else if ((x ^= ((long) buffer[pos++] << 28)) >= 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28); } else if ((x ^= ((long) buffer[pos++] << 35)) < 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35); } else if ((x ^= ((long) buffer[pos++] << 42)) >= 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42); } else if ((x ^= ((long) buffer[pos++] << 49)) < 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42) ^ (~0L << 49); } else { x ^= ((long) buffer[pos++] << 56); x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42) ^ (~0L << 49) ^ (~0L << 56); if (x < 0L) { if (buffer[pos] < 0L) { break fastpath; // Will throw malformedVarint() } } } return x; } return readVar64SlowPath(buffer, offset); } /** Variant of readRawVarint64 for when uncomfortably close to the limit. */ /* Visible for testing */ static long readVar64SlowPath(final byte[] buffer, int offset) { long result = 0; for (int shift = 0; shift < 64; shift += 7) { final byte b = buffer[offset++]; result |= (long) (b & 0x7F) << shift; if ((b & 0x80) == 0) { return result; } } throw new IllegalArgumentException("invalid varLong. start offset:" + offset + " readOffset:" + offset); } public static short bytesToShort(final byte byte1, final byte byte2) { return (short) (((byte1 & 0xff) << 8) | ((byte2 & 0xff))); } public static int writeLong(final long value, final byte[] buf, int offset) { if (buf == null) { throw new NullPointerException("buf must not be null"); } if (offset < 0) { throw new IndexOutOfBoundsException("negative offset:" + offset); } if (buf.length < offset + LONG_BYTE_LENGTH) { throw new IndexOutOfBoundsException("buf.length is too small. buf.length:" + buf.length + " offset:" + (offset + 8)); } buf[offset++] = (byte) (value >> 56); buf[offset++] = (byte) (value >> 48); buf[offset++] = (byte) (value >> 40); buf[offset++] = (byte) (value >> 32); buf[offset++] = (byte) (value >> 24); buf[offset++] = (byte) (value >> 16); buf[offset++] = (byte) (value >> 8); buf[offset++] = (byte) (value); return offset; } public static int writeShort(final short value, final byte[] buf, int offset) { if (buf == null) { throw new NullPointerException("buf must not be null"); } if (offset < 0) { throw new IndexOutOfBoundsException("negative offset:" + offset); } if (buf.length < offset + SHORT_BYTE_LENGTH) { throw new IndexOutOfBoundsException("buf.length is too small. buf.length:" + buf.length + " offset:" + (offset + 2)); } buf[offset++] = (byte) (value >> 8); buf[offset++] = (byte) (value); return offset; } public static int writeInt(final int value, final byte[] buf, int offset) { if (buf == null) { throw new NullPointerException("buf must not be null"); } if (offset < 0) { throw new IndexOutOfBoundsException("negative offset:" + offset); } if (buf.length < offset + INT_BYTE_LENGTH) { throw new IndexOutOfBoundsException("buf.length is too small. buf.length:" + buf.length + " offset:" + (offset + 4)); } buf[offset++] = (byte) (value >> 24); buf[offset++] = (byte) (value >> 16); buf[offset++] = (byte) (value >> 8); buf[offset++] = (byte) (value); return offset; } public static int writeSVar32(final int value, final byte[] buf, final int offset) { return writeVar32(intToZigZag(value), buf, offset); } public static int writeVar32(int value, final byte[] buf, int offset) { if (buf == null) { throw new NullPointerException("buf must not be null"); } checkBound(buf.length, offset); while (true) { if ((value & ~0x7F) == 0) { buf[offset++] = (byte)value; return offset; } else { buf[offset++] = (byte)((value & 0x7F) | 0x80); value >>>= 7; } } } public static int shortToUnsignedShort(short value) { return value & 0xffff; } public static byte[] intToSVar32(int value) { return intToVar32(intToZigZag(value)); } public static byte[] intToVar32(int value) { final int bufferSize = BytesUtils.computeVar32Size(value); final byte[] buffer = new byte[bufferSize]; writeVar64(value, buffer, 0); return buffer; } /** * copy google protocol buffer * https://github.com/google/protobuf/blob/master/java/src/main/java/com/google/protobuf/CodedOutputStream.java */ public static int computeVar32Size(final int value) { if ((value & (0xffffffff << 7)) == 0) return 1; if ((value & (0xffffffff << 14)) == 0) return 2; if ((value & (0xffffffff << 21)) == 0) return 3; if ((value & (0xffffffff << 28)) == 0) return 4; return 5; } public static int writeSVar64(final int value, final byte[] buf, final int offset) { return writeVar64(longToZigZag(value), buf, offset); } /** * copy google protocol buffer * https://github.com/google/protobuf/blob/master/java/src/main/java/com/google/protobuf/CodedOutputStream.java */ public static int writeVar64(long value, final byte[] buf, int offset) { if (buf == null) { throw new NullPointerException("buf must not be null"); } checkBound(buf.length, offset); while (true) { if ((value & ~0x7FL) == 0) { buf[offset++] = (byte)value; return offset; } else { buf[offset++] = (byte)(((int)value & 0x7F) | 0x80); value >>>= 7; } } } static void checkBound(final int bufferLength, final int offset) { if (offset < 0) { throw new IndexOutOfBoundsException("negative offset:" + offset); } if (offset >= bufferLength) { throw new IndexOutOfBoundsException("invalid offset:" + offset + " bufferLength:" + bufferLength); } } public static byte[] longToSVar64(long value) { return longToVar64(longToZigZag(value)); } public static byte[] longToVar64(long value) { final int bufferSize = BytesUtils.computeVar64Size(value); final byte[] buffer = new byte[bufferSize]; writeVar64(value, buffer, 0); return buffer; } /** * copy google protocol buffer * https://github.com/google/protobuf/blob/master/java/src/main/java/com/google/protobuf/CodedOutputStream.java */ public static int computeVar64Size(final long value) { if ((value & (0xffffffffffffffffL << 7)) == 0) return 1; if ((value & (0xffffffffffffffffL << 14)) == 0) return 2; if ((value & (0xffffffffffffffffL << 21)) == 0) return 3; if ((value & (0xffffffffffffffffL << 28)) == 0) return 4; if ((value & (0xffffffffffffffffL << 35)) == 0) return 5; if ((value & (0xffffffffffffffffL << 42)) == 0) return 6; if ((value & (0xffffffffffffffffL << 49)) == 0) return 7; if ((value & (0xffffffffffffffffL << 56)) == 0) return 8; if ((value & (0xffffffffffffffffL << 63)) == 0) return 9; return 10; } private static int writeFirstLong0(final long value, final byte[] buf, int offset) { buf[offset] = (byte) (value >> 56); buf[1 + offset] = (byte) (value >> 48); buf[2 + offset] = (byte) (value >> 40); buf[3 + offset] = (byte) (value >> 32); buf[4 + offset] = (byte) (value >> 24); buf[5 + offset] = (byte) (value >> 16); buf[6 + offset] = (byte) (value >> 8); buf[7 + offset] = (byte) (value); return offset; } private static int writeSecondLong0(final long value, final byte[] buf, int offset) { buf[8 + offset] = (byte) (value >> 56); buf[9 + offset] = (byte) (value >> 48); buf[10 + offset] = (byte) (value >> 40); buf[11 + offset] = (byte) (value >> 32); buf[12 + offset] = (byte) (value >> 24); buf[13 + offset] = (byte) (value >> 16); buf[14 + offset] = (byte) (value >> 8); buf[15 + offset] = (byte) (value); return offset; } public static byte[] add(final String prefix, final long postfix) { if (prefix == null) { throw new NullPointerException("prefix must not be null"); } byte[] agentByte = toBytes(prefix); return add(agentByte, postfix); } public static byte[] add(final byte[] preFix, final long postfix) { byte[] buf = new byte[preFix.length + LONG_BYTE_LENGTH]; System.arraycopy(preFix, 0, buf, 0, preFix.length); writeLong(postfix, buf, preFix.length); return buf; } public static byte[] add(final byte[] preFix, final short postfix) { if (preFix == null) { throw new NullPointerException("preFix must not be null"); } byte[] buf = new byte[preFix.length + SHORT_BYTE_LENGTH]; System.arraycopy(preFix, 0, buf, 0, preFix.length); writeShort(postfix, buf, preFix.length); return buf; } public static byte[] add(final byte[] preFix, final int postfix) { if (preFix == null) { throw new NullPointerException("preFix must not be null"); } byte[] buf = new byte[preFix.length + INT_BYTE_LENGTH]; System.arraycopy(preFix, 0, buf, 0, preFix.length); writeInt(postfix, buf, preFix.length); return buf; } public static byte[] add(final int preFix, final short postFix) { byte[] buf = new byte[INT_BYTE_LENGTH + SHORT_BYTE_LENGTH]; writeInt(preFix, buf, 0); writeShort(postFix, buf, 4); return buf; } public static byte[] add(final long preFix, final short postFix) { byte[] buf = new byte[LONG_BYTE_LENGTH + SHORT_BYTE_LENGTH]; writeLong(preFix, buf, 0); writeShort(postFix, buf, 8); return buf; } @Deprecated public static byte[] add(final long preFix, final short postFix, final int intArg, final short shortArg) { byte[] buf = new byte[LONG_BYTE_LENGTH + SHORT_BYTE_LENGTH + INT_BYTE_LENGTH + SHORT_BYTE_LENGTH]; int offset = 0; writeLong(preFix, buf, offset); offset += LONG_BYTE_LENGTH; writeShort(postFix, buf, offset); offset += SHORT_BYTE_LENGTH; writeInt(intArg, buf, offset); offset += INT_BYTE_LENGTH; writeShort(shortArg, buf, offset); return buf; } public static byte[] toBytes(final String value) { if (value == null) { return null; } try { return value.getBytes(Charsets.UTF_8_NAME); } catch (UnsupportedEncodingException e) { return value.getBytes(Charsets.UTF_8); } } public static byte[] merge(final byte[] b1, final byte[] b2) { if (b1 == null) { throw new NullPointerException("b1 must not be null"); } if (b2 == null) { throw new NullPointerException("b2 must not be null"); } final byte[] result = new byte[b1.length + b2.length]; System.arraycopy(b1, 0, result, 0, b1.length); System.arraycopy(b2, 0, result, b1.length, b2.length); return result; } public static byte[] toFixedLengthBytes(final String str, final int length) { if (length < 0) { throw new IndexOutOfBoundsException("negative length:" + length); } final byte[] b1 = toBytes(str); if (b1 == null) { return new byte[length]; } if (b1.length > length) { throw new IndexOutOfBoundsException("String is longer then target length of bytes."); } byte[] b = new byte[length]; System.arraycopy(b1, 0, b, 0, b1.length); return b; } public static int intToZigZag(final int n) { return (n << 1) ^ (n >> 31); } public static int zigzagToInt(final int n) { return (n >>> 1) ^ -(n & 1); } public static long longToZigZag(final long n) { return (n << 1) ^ (n >> 63); } public static long zigzagToLong(final long n) { return (n >>> 1) ^ -(n & 1); } public static byte[] concat(final byte[]... arrays) { int totalLength = 0; final int length = arrays.length; for (int i = 0; i < length; i++) { totalLength += arrays[i].length; } byte[] result = new byte[totalLength]; int currentIndex = 0; for (int i = 0; i < length; i++) { System.arraycopy(arrays[i], 0, result, currentIndex, arrays[i].length); currentIndex += arrays[i].length; } return result; } public static String safeTrim(final String string) { if (string == null) { return null; } return string.trim(); } public static String toString(final byte[] bytes) { if (bytes == null) { return null; } return toString(bytes, 0, bytes.length); } public static String toString(final byte [] bytes, final int offset, final int length) { if (bytes == null) { return null; } if (offset < 0) { throw new IndexOutOfBoundsException("negative offset:" + offset); } if (length == 0) { return ""; } try { return new String(bytes, offset, length, UTF8); } catch (UnsupportedEncodingException e) { return new String(bytes, offset, length, UTF8_CHARSET); } } public static String toStringAndRightTrim(final byte[] bytes, final int offset, final int length) { String string = toString(bytes, offset, length); return trimRight(string); } public static String trimRight(final String string) { if (string == null) { return null; } final int length = string.length(); int index = length; // need to use Character.isWhitespace()? may not needed. while (string.charAt(index - 1) <= ' ') { index--; } if (index == length) { return string; } else { return string.substring(0, index); } } }