All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.kudu.util.Slice Maven / Gradle / Ivy

Go to download

org.apache.kudu:kudu-client with netty package relocations reverted and netty classes stripped away so that camel-quarkus-kudu can use quarkus-netty as a replacement

There is a newer version: 3.15.0
Show newest version
/*
 * Copyright 2009 Red Hat, Inc.
 *
 * Red Hat 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.
 *
 * Copyright 2011 Dain Sundstrom 
 * Copyright 2011 FuseSource Corp. http://fusesource.com
 */

package org.apache.kudu.util;

import static java.nio.ByteOrder.LITTLE_ENDIAN;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.FileChannel;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel;
import java.nio.charset.Charset;
import java.util.Arrays;

import com.google.common.base.Preconditions;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import com.google.common.primitives.Shorts;
import org.apache.yetus.audience.InterfaceAudience;

/**
 * Little Endian slice of a byte array.
 *
 * The slice holds on to a reference of the underlying byte array meaning it
 * cannot be garbage collected until the Slice itself can be garbage collected.
 */
@InterfaceAudience.Private
public final class Slice implements Comparable {
  private final byte[] data;
  private final int offset;
  private final int length;

  private int hash;

  public Slice(int length) {
    data = new byte[length];
    this.offset = 0;
    this.length = length;
  }

  public Slice(byte[] data) {
    Preconditions.checkNotNull(data, "array is null");
    this.data = data;
    this.offset = 0;
    this.length = data.length;
  }

  public Slice(byte[] data, int offset, int length) {
    Preconditions.checkNotNull(data, "array is null");
    this.data = data;
    this.offset = offset;
    this.length = length;
  }

  /**
   * Length of this slice.
   */
  public int length() {
    return length;
  }

  /**
   * Gets the array underlying this slice.
   */
  public byte[] getRawArray() {
    return data;
  }

  /**
   * Gets the offset of this slice in the underlying array.
   */
  public int getRawOffset() {
    return offset;
  }

  /**
   * Gets a byte at the specified absolute {@code index} in this buffer.
   *
   * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or
   * {@code index + 1} is greater than {@code this.capacity}
   */
  public byte getByte(int index) {
    Preconditions.checkPositionIndexes(index, index + 1, this.length);
    index += offset;
    return data[index];
  }

  /**
   * Gets an unsigned byte at the specified absolute {@code index} in this
   * buffer.
   *
   * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or
   * {@code index + 1} is greater than {@code this.capacity}
   */
  public short getUnsignedByte(int index) {
    return (short) (getByte(index) & 0xFF);
  }

  /**
   * Gets a 16-bit short integer at the specified absolute {@code index} in
   * this slice.
   *
   * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or
   * {@code index + 2} is greater than {@code this.capacity}
   */
  public short getShort(int index) {
    Preconditions.checkPositionIndexes(index, index + Shorts.BYTES, this.length);
    index += offset;
    return (short) ((data[index] & 0xFF) | (data[index + 1] << 8));
  }

  /**
   * Gets a 32-bit integer at the specified absolute {@code index} in
   * this buffer.
   *
   * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or
   * {@code index + 4} is greater than {@code this.capacity}
   */
  public int getInt(int index) {
    Preconditions.checkPositionIndexes(index, index + Ints.BYTES, this.length);
    index += offset;
    return (data[index] & 0xff) |
        (data[index + 1] & 0xff) << 8 |
        (data[index + 2] & 0xff) << 16 |
        (data[index + 3] & 0xff) << 24;
  }

  /**
   * Gets a 64-bit long integer at the specified absolute {@code index} in
   * this buffer.
   *
   * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or
   * {@code index + 8} is greater than {@code this.capacity}
   */
  public long getLong(int index) {
    Preconditions.checkPositionIndexes(index, index + Longs.BYTES, this.length);
    index += offset;
    return ((long) data[index] & 0xff) |
        ((long) data[index + 1] & 0xff) << 8 |
        ((long) data[index + 2] & 0xff) << 16 |
        ((long) data[index + 3] & 0xff) << 24 |
        ((long) data[index + 4] & 0xff) << 32 |
        ((long) data[index + 5] & 0xff) << 40 |
        ((long) data[index + 6] & 0xff) << 48 |
        ((long) data[index + 7] & 0xff) << 56;
  }

  /**
   * Transfers this buffer's data to the specified destination starting at
   * the specified absolute {@code index}.
   *
   * @param dstIndex the first index of the destination
   * @param length the number of bytes to transfer
   * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0},
   * if the specified {@code dstIndex} is less than {@code 0},
   * if {@code index + length} is greater than
   * {@code this.capacity}, or
   * if {@code dstIndex + length} is greater than
   * {@code dst.capacity}
   */
  public void getBytes(int index, Slice dst, int dstIndex, int length) {
    getBytes(index, dst.data, dstIndex, length);
  }

  /**
   * Transfers this buffer's data to the specified destination starting at
   * the specified absolute {@code index}.
   *
   * @param destinationIndex the first index of the destination
   * @param length the number of bytes to transfer
   * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0},
   * if the specified {@code dstIndex} is less than {@code 0},
   * if {@code index + length} is greater than
   * {@code this.capacity}, or
   * if {@code dstIndex + length} is greater than
   * {@code dst.length}
   */
  public void getBytes(int index, byte[] destination, int destinationIndex, int length) {
    Preconditions.checkPositionIndexes(index, index + length, this.length);
    Preconditions.checkPositionIndexes(destinationIndex,
        destinationIndex + length, destination.length);
    index += offset;
    System.arraycopy(data, index, destination, destinationIndex, length);
  }

  public byte[] getBytes() {
    return getBytes(0, length);
  }

  public byte[] getBytes(int index, int length) {
    index += offset;
    if (index == 0) {
      return Arrays.copyOf(data, length);
    } else {
      byte[] value = new byte[length];
      System.arraycopy(data, index, value, 0, length);
      return value;
    }
  }

  /**
   * Transfers this buffer's data to the specified destination starting at
   * the specified absolute {@code index} until the destination's position
   * reaches its limit.
   *
   * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or
   * if {@code index + dst.remaining()} is greater than
   * {@code this.capacity}
   */
  public void getBytes(int index, ByteBuffer destination) {
    Preconditions.checkPositionIndex(index, this.length);
    index += offset;
    destination.put(data, index, Math.min(length, destination.remaining()));
  }

  /**
   * Transfers this buffer's data to the specified stream starting at the
   * specified absolute {@code index}.
   *
   * @param length the number of bytes to transfer
   * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or
   * if {@code index + length} is greater than
   * {@code this.capacity}
   * @throws java.io.IOException if the specified stream threw an exception during I/O
   */
  public void getBytes(int index, OutputStream out, int length)
      throws IOException {
    Preconditions.checkPositionIndexes(index, index + length, this.length);
    index += offset;
    out.write(data, index, length);
  }

  /**
   * Transfers this buffer's data to the specified channel starting at the
   * specified absolute {@code index}.
   *
   * @param length the maximum number of bytes to transfer
   * @return the actual number of bytes written out to the specified channel
   * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or
   * if {@code index + length} is greater than
   * {@code this.capacity}
   * @throws java.io.IOException if the specified channel threw an exception during I/O
   */
  public int getBytes(int index, GatheringByteChannel out, int length)
      throws IOException {
    Preconditions.checkPositionIndexes(index, index + length, this.length);
    index += offset;
    return out.write(ByteBuffer.wrap(data, index, length));
  }

  /**
   * Sets the specified 16-bit short integer at the specified absolute
   * {@code index} in this buffer.  The 16 high-order bits of the specified
   * value are ignored.
   *
   * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or
   * {@code index + 2} is greater than {@code this.capacity}
   */
  public void setShort(int index, int value) {
    Preconditions.checkPositionIndexes(index, index + Shorts.BYTES, this.length);
    index += offset;
    data[index] = (byte) value;
    data[index + 1] = (byte) (value >>> 8);
  }

  /**
   * Sets the specified 32-bit integer at the specified absolute
   * {@code index} in this buffer.
   *
   * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or
   * {@code index + 4} is greater than {@code this.capacity}
   */
  public void setInt(int index, int value) {
    Preconditions.checkPositionIndexes(index, index + Ints.BYTES, this.length);
    index += offset;
    data[index] = (byte) value;
    data[index + 1] = (byte) (value >>> 8);
    data[index + 2] = (byte) (value >>> 16);
    data[index + 3] = (byte) (value >>> 24);
  }

  /**
   * Sets the specified 64-bit long integer at the specified absolute
   * {@code index} in this buffer.
   *
   * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or
   * {@code index + 8} is greater than {@code this.capacity}
   */
  public void setLong(int index, long value) {
    Preconditions.checkPositionIndexes(index, index + Longs.BYTES, this.length);
    index += offset;
    data[index] = (byte) value;
    data[index + 1] = (byte) (value >>> 8);
    data[index + 2] = (byte) (value >>> 16);
    data[index + 3] = (byte) (value >>> 24);
    data[index + 4] = (byte) (value >>> 32);
    data[index + 5] = (byte) (value >>> 40);
    data[index + 6] = (byte) (value >>> 48);
    data[index + 7] = (byte) (value >>> 56);
  }

  /**
   * Sets the specified byte at the specified absolute {@code index} in this
   * buffer.  The 24 high-order bits of the specified value are ignored.
   *
   * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or
   * {@code index + 1} is greater than {@code this.capacity}
   */
  public void setByte(int index, int value) {
    Preconditions.checkPositionIndexes(index, index + 1, this.length);
    index += offset;
    data[index] = (byte) value;
  }

  /**
   * Transfers the specified source buffer's data to this buffer starting at
   * the specified absolute {@code index}.
   *
   * @param srcIndex the first index of the source
   * @param length the number of bytes to transfer
   * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0},
   * if the specified {@code srcIndex} is less than {@code 0},
   * if {@code index + length} is greater than
   * {@code this.capacity}, or
   * if {@code srcIndex + length} is greater than
   * {@code src.capacity}
   */
  public void setBytes(int index, Slice src, int srcIndex, int length) {
    setBytes(index, src.data, src.offset + srcIndex, length);
  }

  /**
   * Transfers the specified source array's data to this buffer starting at
   * the specified absolute {@code index}.
   *
   * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0},
   * if the specified {@code srcIndex} is less than {@code 0},
   * if {@code index + length} is greater than
   * {@code this.capacity}, or
   * if {@code srcIndex + length} is greater than {@code src.length}
   */
  public void setBytes(int index, byte[] source, int sourceIndex, int length) {
    Preconditions.checkPositionIndexes(index, index + length, this.length);
    Preconditions.checkPositionIndexes(sourceIndex, sourceIndex + length, source.length);
    index += offset;
    System.arraycopy(source, sourceIndex, data, index, length);
  }

  /**
   * Transfers the specified source buffer's data to this buffer starting at
   * the specified absolute {@code index} until the source buffer's position
   * reaches its limit.
   *
   * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or
   * if {@code index + src.remaining()} is greater than
   * {@code this.capacity}
   */
  public void setBytes(int index, ByteBuffer source) {
    Preconditions.checkPositionIndexes(index, index + source.remaining(), this.length);
    index += offset;
    source.get(data, index, source.remaining());
  }

  /**
   * Transfers the content of the specified source stream to this buffer
   * starting at the specified absolute {@code index}.
   *
   * @param length the number of bytes to transfer
   * @return the actual number of bytes read in from the specified channel.
   *         {@code -1} if the specified channel is closed.
   * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or
   * if {@code index + length} is greater than {@code this.capacity}
   * @throws java.io.IOException if the specified stream threw an exception during I/O
   */
  public int setBytes(int index, InputStream in, int length)
      throws IOException {
    Preconditions.checkPositionIndexes(index, index + length, this.length);
    index += offset;
    int readBytes = 0;
    do {
      int localReadBytes = in.read(data, index, length);
      if (localReadBytes < 0) {
        if (readBytes == 0) {
          return -1;
        } else {
          break;
        }
      }
      readBytes += localReadBytes;
      index += localReadBytes;
      length -= localReadBytes;
    } while (length > 0);

    return readBytes;
  }

  /**
   * Transfers the content of the specified source channel to this buffer
   * starting at the specified absolute {@code index}.
   *
   * @param length the maximum number of bytes to transfer
   * @return the actual number of bytes read in from the specified channel.
   *         {@code -1} if the specified channel is closed.
   * @throws IndexOutOfBoundsException if the specified {@code index} is less than {@code 0} or
   * if {@code index + length} is greater than {@code this.capacity}
   * @throws java.io.IOException if the specified channel threw an exception during I/O
   */
  public int setBytes(int index, ScatteringByteChannel in, int length)
      throws IOException {
    Preconditions.checkPositionIndexes(index, index + length, this.length);
    index += offset;
    ByteBuffer buf = ByteBuffer.wrap(data, index, length);
    int readBytes = 0;

    do {
      int localReadBytes;
      try {
        localReadBytes = in.read(buf);
      } catch (ClosedChannelException e) {
        localReadBytes = -1;
      }
      if (localReadBytes < 0) {
        if (readBytes == 0) {
          return -1;
        } else {
          break;
        }
      } else if (localReadBytes == 0) {
        break;
      }
      readBytes += localReadBytes;
    } while (readBytes < length);

    return readBytes;
  }

  public int setBytes(int index, FileChannel in, int position, int length)
      throws IOException {
    Preconditions.checkPositionIndexes(index, index + length, this.length);
    index += offset;
    ByteBuffer buf = ByteBuffer.wrap(data, index, length);
    int readBytes = 0;

    do {
      int localReadBytes;
      try {
        localReadBytes = in.read(buf, position + readBytes);
      } catch (ClosedChannelException e) {
        localReadBytes = -1;
      }
      if (localReadBytes < 0) {
        if (readBytes == 0) {
          return -1;
        } else {
          break;
        }
      } else if (localReadBytes == 0) {
        break;
      }
      readBytes += localReadBytes;
    } while (readBytes < length);

    return readBytes;
  }

  public Slice copySlice() {
    return copySlice(0, length);
  }

  /**
   * Returns a copy of this buffer's sub-region.  Modifying the content of
   * the returned buffer or this buffer does not affect each other at all.
   */
  public Slice copySlice(int index, int length) {
    Preconditions.checkPositionIndexes(index, index + length, this.length);

    index += offset;
    byte[] copiedArray = new byte[length];
    System.arraycopy(data, index, copiedArray, 0, length);
    return new Slice(copiedArray);
  }

  public byte[] copyBytes() {
    return copyBytes(0, length);
  }

  public byte[] copyBytes(int index, int length) {
    Preconditions.checkPositionIndexes(index, index + length, this.length);
    index += offset;
    if (index == 0) {
      return Arrays.copyOf(data, length);
    } else {
      byte[] value = new byte[length];
      System.arraycopy(data, index, value, 0, length);
      return value;
    }
  }

  /**
   * Returns a slice of this buffer's readable bytes. Modifying the content
   * of the returned buffer or this buffer affects each other's content
   * while they maintain separate indexes and marks.
   */
  public Slice slice() {
    return slice(0, length);
  }

  /**
   * Returns a slice of this buffer's sub-region. Modifying the content of
   * the returned buffer or this buffer affects each other's content while
   * they maintain separate indexes and marks.
   */
  public Slice slice(int index, int length) {
    if (index == 0 && length == this.length) {
      return this;
    }

    Preconditions.checkPositionIndexes(index, index + length, this.length);
    if (index >= 0 && length == 0) {
      return Slices.EMPTY_SLICE;
    }
    return new Slice(data, offset + index, length);
  }

  /**
   * Converts this buffer's readable bytes into a NIO buffer.  The returned
   * buffer shares the content with this buffer.
   */
  public ByteBuffer toByteBuffer() {
    return toByteBuffer(0, length);
  }

  /**
   * Converts this buffer's sub-region into a NIO buffer.  The returned
   * buffer shares the content with this buffer.
   */
  public ByteBuffer toByteBuffer(int index, int length) {
    Preconditions.checkPositionIndexes(index, index + length, this.length);
    index += offset;
    return ByteBuffer.wrap(data, index, length).order(LITTLE_ENDIAN);
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }

    Slice slice = (Slice) o;

    // do lengths match
    if (length != slice.length) {
      return false;
    }

    // if arrays have same base offset, some optimizations can be taken...
    if (offset == slice.offset && data == slice.data) {
      return true;
    }
    for (int i = 0; i < length; i++) {
      if (data[offset + i] != slice.data[slice.offset + i]) {
        return false;
      }
    }
    return true;
  }

  @Override
  public int hashCode() {
    if (hash != 0) {
      return hash;
    }

    int result = length;
    for (int i = offset; i < offset + length; i++) {
      result = 31 * result + data[i];
    }
    if (result == 0) {
      result = 1;
    }
    hash = result;
    return hash;
  }

  /**
   * Compares the content of the specified buffer to the content of this
   * buffer.  This comparison is performed byte by byte using an unsigned
   * comparison.
   */
  @Override
  public int compareTo(Slice that) {
    if (this == that) {
      return 0;
    }
    if (this.data == that.data && length == that.length && offset == that.offset) {
      return 0;
    }

    int minLength = Math.min(this.length, that.length);
    for (int i = 0; i < minLength; i++) {
      int thisByte = 0xFF & this.data[this.offset + i];
      int thatByte = 0xFF & that.data[that.offset + i];
      if (thisByte != thatByte) {
        return thisByte - thatByte;
      }
    }
    return this.length - that.length;
  }

  /**
   * Decodes this buffer's readable bytes into a string with the specified
   * character set name.
   */
  public String toString(Charset charset) {
    return toString(0, length, charset);
  }

  /**
   * Decodes this buffer's sub-region into a string with the specified
   * character set.
   */
  public String toString(int index, int length, Charset charset) {
    if (length == 0) {
      return "";
    }

    return Slices.decodeString(toByteBuffer(index, length), charset);
  }

  @Override
  public String toString() {
    return getClass().getSimpleName() + '(' +
        "length=" + length() +
        ')';
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy