/* * 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.kafka.common.utils; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; /** * This classes exposes low-level methods for reading/writing from byte streams or buffers. */ public final class ByteUtils { private ByteUtils() {} /** * Read an unsigned integer from the current position in the buffer, incrementing the position by 4 bytes * * @param buffer The buffer to read from * @return The integer read, as a long to avoid signedness */ public static long readUnsignedInt(ByteBuffer buffer) { return buffer.getInt() & 0xffffffffL; } /** * Read an unsigned integer from the given position without modifying the buffers position * * @param buffer the buffer to read from * @param index the index from which to read the integer * @return The integer read, as a long to avoid signedness */ public static long readUnsignedInt(ByteBuffer buffer, int index) { return buffer.getInt(index) & 0xffffffffL; } /** * Read an unsigned integer stored in little-endian format from the {@link InputStream}. * * @param in The stream to read from * @return The integer read (MUST BE TREATED WITH SPECIAL CARE TO AVOID SIGNEDNESS) */ public static int readUnsignedIntLE(InputStream in) throws IOException { return in.read() | (in.read() << 8) | (in.read() << 16) | (in.read() << 24); } /** * Read an unsigned integer stored in little-endian format from a byte array * at a given offset. * * @param buffer The byte array to read from * @param offset The position in buffer to read from * @return The integer read (MUST BE TREATED WITH SPECIAL CARE TO AVOID SIGNEDNESS) */ public static int readUnsignedIntLE(byte[] buffer, int offset) { return (buffer[offset] << 0 & 0xff) | ((buffer[offset + 1] & 0xff) << 8) | ((buffer[offset + 2] & 0xff) << 16) | ((buffer[offset + 3] & 0xff) << 24); } /** * Write the given long value as a 4 byte unsigned integer. Overflow is ignored. * * @param buffer The buffer to write to * @param index The position in the buffer at which to begin writing * @param value The value to write */ public static void writeUnsignedInt(ByteBuffer buffer, int index, long value) { buffer.putInt(index, (int) (value & 0xffffffffL)); } /** * Write the given long value as a 4 byte unsigned integer. Overflow is ignored. * * @param buffer The buffer to write to * @param value The value to write */ public static void writeUnsignedInt(ByteBuffer buffer, long value) { buffer.putInt((int) (value & 0xffffffffL)); } /** * Write an unsigned integer in little-endian format to the {@link OutputStream}. * * @param out The stream to write to * @param value The value to write */ public static void writeUnsignedIntLE(OutputStream out, int value) throws IOException { out.write(value); out.write(value >>> 8); out.write(value >>> 16); out.write(value >>> 24); } /** * Write an unsigned integer in little-endian format to a byte array * at a given offset. * * @param buffer The byte array to write to * @param offset The position in buffer to write to * @param value The value to write */ public static void writeUnsignedIntLE(byte[] buffer, int offset, int value) { buffer[offset] = (byte) value; buffer[offset + 1] = (byte) (value >>> 8); buffer[offset + 2] = (byte) (value >>> 16); buffer[offset + 3] = (byte) (value >>> 24); } /** * Read an integer stored in variable-length format using zig-zag decoding from * <a href="http://code.google.com/apis/protocolbuffers/docs/encoding.html"> Google Protocol Buffers</a>. * * @param buffer The buffer to read from * @return The integer read * * @throws IllegalArgumentException if variable-length value does not terminate after 5 bytes have been read */ public static int readVarint(ByteBuffer buffer) { int value = 0; int i = 0; int b; while (((b = buffer.get()) & 0x80) != 0) { value |= (b & 0x7f) << i; i += 7; if (i > 28) throw illegalVarintException(value); } value |= b << i; return (value >>> 1) ^ -(value & 1); } /** * Read an integer stored in variable-length format using zig-zag decoding from * <a href="http://code.google.com/apis/protocolbuffers/docs/encoding.html"> Google Protocol Buffers</a>. * * @param in The input to read from * @return The integer read * * @throws IllegalArgumentException if variable-length value does not terminate after 5 bytes have been read * @throws IOException if {@link DataInput} throws {@link IOException} */ public static int readVarint(DataInput in) throws IOException { int value = 0; int i = 0; int b; while (((b = in.readByte()) & 0x80) != 0) { value |= (b & 0x7f) << i; i += 7; if (i > 28) throw illegalVarintException(value); } value |= b << i; return (value >>> 1) ^ -(value & 1); } /** * Read a long stored in variable-length format using zig-zag decoding from * <a href="http://code.google.com/apis/protocolbuffers/docs/encoding.html"> Google Protocol Buffers</a>. * * @param in The input to read from * @return The long value read * * @throws IllegalArgumentException if variable-length value does not terminate after 10 bytes have been read * @throws IOException if {@link DataInput} throws {@link IOException} */ public static long readVarlong(DataInput in) throws IOException { long value = 0L; int i = 0; long b; while (((b = in.readByte()) & 0x80) != 0) { value |= (b & 0x7f) << i; i += 7; if (i > 63) throw illegalVarlongException(value); } value |= b << i; return (value >>> 1) ^ -(value & 1); } /** * Read a long stored in variable-length format using zig-zag decoding from * <a href="http://code.google.com/apis/protocolbuffers/docs/encoding.html"> Google Protocol Buffers</a>. * * @param buffer The buffer to read from * @return The long value read * * @throws IllegalArgumentException if variable-length value does not terminate after 10 bytes have been read */ public static long readVarlong(ByteBuffer buffer) { long value = 0L; int i = 0; long b; while (((b = buffer.get()) & 0x80) != 0) { value |= (b & 0x7f) << i; i += 7; if (i > 63) throw illegalVarlongException(value); } value |= b << i; return (value >>> 1) ^ -(value & 1); } /** * Write the given integer following the variable-length zig-zag encoding from * <a href="http://code.google.com/apis/protocolbuffers/docs/encoding.html"> Google Protocol Buffers</a> * into the output. * * @param value The value to write * @param out The output to write to */ public static void writeVarint(int value, DataOutput out) throws IOException { int v = (value << 1) ^ (value >> 31); while ((v & 0xffffff80) != 0L) { out.writeByte((v & 0x7f) | 0x80); v >>>= 7; } out.writeByte((byte) v); } /** * Write the given integer following the variable-length zig-zag encoding from * <a href="http://code.google.com/apis/protocolbuffers/docs/encoding.html"> Google Protocol Buffers</a> * into the buffer. * * @param value The value to write * @param buffer The output to write to */ public static void writeVarint(int value, ByteBuffer buffer) { int v = (value << 1) ^ (value >> 31); while ((v & 0xffffff80) != 0L) { byte b = (byte) ((v & 0x7f) | 0x80); buffer.put(b); v >>>= 7; } buffer.put((byte) v); } /** * Write the given integer following the variable-length zig-zag encoding from * <a href="http://code.google.com/apis/protocolbuffers/docs/encoding.html"> Google Protocol Buffers</a> * into the output. * * @param value The value to write * @param out The output to write to */ public static void writeVarlong(long value, DataOutput out) throws IOException { long v = (value << 1) ^ (value >> 63); while ((v & 0xffffffffffffff80L) != 0L) { out.writeByte(((int) v & 0x7f) | 0x80); v >>>= 7; } out.writeByte((byte) v); } /** * Write the given integer following the variable-length zig-zag encoding from * <a href="http://code.google.com/apis/protocolbuffers/docs/encoding.html"> Google Protocol Buffers</a> * into the buffer. * * @param value The value to write * @param buffer The buffer to write to */ public static void writeVarlong(long value, ByteBuffer buffer) { long v = (value << 1) ^ (value >> 63); while ((v & 0xffffffffffffff80L) != 0L) { byte b = (byte) ((v & 0x7f) | 0x80); buffer.put(b); v >>>= 7; } buffer.put((byte) v); } /** * Number of bytes needed to encode an integer in variable-length format. * * @param value The signed value */ public static int sizeOfVarint(int value) { int v = (value << 1) ^ (value >> 31); int bytes = 1; while ((v & 0xffffff80) != 0L) { bytes += 1; v >>>= 7; } return bytes; } /** * Number of bytes needed to encode a long in variable-length format. * * @param value The signed value */ public static int sizeOfVarlong(long value) { long v = (value << 1) ^ (value >> 63); int bytes = 1; while ((v & 0xffffffffffffff80L) != 0L) { bytes += 1; v >>>= 7; } return bytes; } private static IllegalArgumentException illegalVarintException(int value) { throw new IllegalArgumentException("Varint is too long, the most significant bit in the 5th byte is set, " + "converted value: " + Integer.toHexString(value)); } private static IllegalArgumentException illegalVarlongException(long value) { throw new IllegalArgumentException("Varlong is too long, most significant bit in the 10th byte is set, " + "converted value: " + Long.toHexString(value)); } }