| /* |
| * 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)); |
| } |
| } |