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

org.apache.hadoop.hbase.io.encoding.RowIndexSeekerV1 Maven / Gradle / Ivy

There is a newer version: 4.15.0-HBase-1.5
Show newest version
/*
 * 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.hadoop.hbase.io.encoding;

import java.nio.ByteBuffer;

import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.KeyValue.KVComparator;
import org.apache.hadoop.hbase.NoTagsKeyValue;
import org.apache.hadoop.hbase.classification.InterfaceAudience;
import org.apache.hadoop.hbase.io.encoding.AbstractDataBlockEncoder.AbstractEncodedSeeker;
import org.apache.hadoop.hbase.util.ByteBufferUtils;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.SimpleMutableByteRange;
import org.apache.hadoop.io.WritableUtils;

@InterfaceAudience.Private
public class RowIndexSeekerV1 extends AbstractEncodedSeeker {

  private ByteBuffer currentBuffer;
  private SeekerState current = new SeekerState(); // always valid
  private SeekerState previous = new SeekerState(); // may not be valid

  private int rowNumber;
  private ByteBuffer rowOffsets = null;

  public RowIndexSeekerV1(KVComparator comparator,
      HFileBlockDecodingContext decodingCtx) {
    super(comparator, decodingCtx);
  }

  @Override
  public void setCurrentBuffer(ByteBuffer buffer) {
    int onDiskSize = Bytes.toIntUnsafe(buffer.array(), buffer.arrayOffset()
        + buffer.limit() - Bytes.SIZEOF_INT);
    // int onDiskSize = buffer.getInt(buffer.limit() - Bytes.SIZEOF_INT);

    // Data part
    ByteBuffer dup = buffer.duplicate();
    dup.position(buffer.position());
    dup.limit(buffer.position() + onDiskSize);
    currentBuffer = dup.slice();
    current.currentBuffer = currentBuffer;
    ByteBufferUtils.skip(buffer, onDiskSize);

    // Row offset
    rowNumber = buffer.getInt();
    // equals Bytes.SIZEOF_INT * rowNumber
    int totalRowOffsetsLength = rowNumber << 2;
    ByteBuffer rowDup = buffer.duplicate();
    rowDup.position(buffer.position());
    rowDup.limit(buffer.position() + totalRowOffsetsLength);
    rowOffsets = rowDup.slice();

    decodeFirst();
  }

  @Override
  public ByteBuffer getKeyDeepCopy() {
    ByteBuffer keyBuffer = ByteBuffer.allocate(current.keyLength);
    keyBuffer.put(current.keyBuffer.getBytes(), current.keyBuffer.getOffset(),
        current.keyLength);
    keyBuffer.rewind();
    return keyBuffer;
  }

  @Override
  public ByteBuffer getValueShallowCopy() {
    ByteBuffer dup = currentBuffer.duplicate();
    dup.position(current.valueOffset);
    dup.limit(current.valueOffset + current.valueLength);
    return dup.slice();
  }

  ByteBuffer getKeyValueBuffer() {
    ByteBuffer kvBuffer = createKVBuffer();
    kvBuffer.putInt(current.keyLength);
    kvBuffer.putInt(current.valueLength);
    kvBuffer.put(current.keyBuffer.getBytes(), current.keyBuffer.getOffset(),
        current.keyLength);
    ByteBufferUtils.copyFromBufferToBuffer(kvBuffer, currentBuffer,
        current.valueOffset, current.valueLength);
    if (current.tagsLength > 0) {
      // Put short as unsigned
      kvBuffer.put((byte) (current.tagsLength >> 8 & 0xff));
      kvBuffer.put((byte) (current.tagsLength & 0xff));
      if (current.tagsOffset != -1) {
        ByteBufferUtils.copyFromBufferToBuffer(kvBuffer, currentBuffer,
            current.tagsOffset, current.tagsLength);
      }
    }
    if (includesMvcc()) {
      ByteBufferUtils.writeVLong(kvBuffer, current.getSequenceId());
    }
    kvBuffer.rewind();
    return kvBuffer;
  }

  protected ByteBuffer createKVBuffer() {
    int kvBufSize = (int) KeyValue.getKeyValueDataStructureSize(
        current.keyLength, current.valueLength, current.tagsLength);
    if (includesMvcc()) {
      kvBufSize += WritableUtils.getVIntSize(current.getSequenceId());
    }
    ByteBuffer kvBuffer = ByteBuffer.allocate(kvBufSize);
    return kvBuffer;
  }

  @Override
  public Cell getKeyValue() {
    return current.toCell();
  }

  @Override
  public void rewind() {
    currentBuffer.rewind();
    decodeFirst();
  }

  @Override
  public boolean next() {
    if (!currentBuffer.hasRemaining()) {
      return false;
    }
    decodeNext();
    previous.invalidate();
    return true;
  }

  @Override
  public int seekToKeyInBlock(byte[] key, int offset, int length,
      boolean seekBefore) {
    return seekToKeyInBlock(new KeyValue.KeyOnlyKeyValue(key, offset, length),
        seekBefore);
  }

  private int binarySearch(Cell seekCell, boolean seekBefore) {
    int low = 0;
    int high = rowNumber - 1;
    int mid = (low + high) >>> 1;
    int comp = 0;
    SimpleMutableByteRange row = new SimpleMutableByteRange();
    while (low <= high) {
      mid = (low + high) >>> 1;
      getRow(mid, row);
      comp = comparator.compareRows(row.getBytes(), row.getOffset(),
          row.getLength(), seekCell.getRowArray(), seekCell.getRowOffset(),
          seekCell.getRowLength());
      if (comp < 0) {
        low = mid + 1;
      } else if (comp > 0) {
        high = mid - 1;
      } else {
        // key found
        if (seekBefore) {
          return mid - 1;
        } else {
          return mid;
        }
      }
    }
    // key not found.
    if (comp > 0) {
      return mid - 1;
    } else {
      return mid;
    }
  }

  private void getRow(int index, SimpleMutableByteRange row) {
    int offset = Bytes.toIntUnsafe(rowOffsets.array(), rowOffsets.arrayOffset()
        + (index << 2)); // index * Bytes.SIZEOF_INT
    int position = currentBuffer.arrayOffset() + offset + Bytes.SIZEOF_LONG;
    short rowLen = Bytes.toShortUnsafe(currentBuffer.array(), position);
    row.set(currentBuffer.array(), position + Bytes.SIZEOF_SHORT, rowLen);
  }

  @Override
  public int seekToKeyInBlock(Cell seekCell, boolean seekBefore) {
    previous.invalidate();
    int index = binarySearch(seekCell, seekBefore);
    if (index < 0) {
      return HConstants.INDEX_KEY_MAGIC; // using optimized index key
    } else {
      int offset = Bytes.toIntUnsafe(rowOffsets.array(),
          rowOffsets.arrayOffset() + (index << 2));
      if (offset != 0) {
        decodeAtPosition(offset);
      }
    }
    do {
      int comp;
      comp = comparator.compareOnlyKeyPortion(seekCell, current.currentKey);
      if (comp == 0) { // exact match
        if (seekBefore) {
          if (!previous.isValid()) {
            // The caller (seekBefore) has to ensure that we are not at the
            // first key in the block.
            throw new IllegalStateException("Cannot seekBefore if "
                + "positioned at the first key in the block: key="
                + Bytes.toStringBinary(seekCell.getRowArray()));
          }
          moveToPrevious();
          return 1;
        }
        return 0;
      }

      if (comp < 0) { // already too large, check previous
        if (previous.isValid()) {
          moveToPrevious();
        } else {
          return HConstants.INDEX_KEY_MAGIC; // using optimized index key
        }
        return 1;
      }

      // move to next, if more data is available
      if (currentBuffer.hasRemaining()) {
        previous.copyFromNext(current);
        decodeNext();
      } else {
        break;
      }
    } while (true);

    // we hit the end of the block, not an exact match
    return 1;
  }

  private void moveToPrevious() {
    if (!previous.isValid()) {
      throw new IllegalStateException(
          "Can move back only once and not in first key in the block.");
    }

    SeekerState tmp = previous;
    previous = current;
    current = tmp;

    // move after last key value
    currentBuffer.position(current.nextKvOffset);
    previous.invalidate();
  }

  @Override
  public int compareKey(KVComparator comparator, byte[] key, int offset,
      int length) {
    return comparator.compareFlatKey(key, offset, length,
        current.keyBuffer.getBytes(), current.keyBuffer.getOffset(),
        current.keyBuffer.getLength());
  }

  @Override
  public int compareKey(KVComparator comparator, Cell key) {
    return comparator.compareOnlyKeyPortion(key, new KeyValue.KeyOnlyKeyValue(
        current.keyBuffer.getBytes(), current.keyBuffer.getOffset(),
        current.keyBuffer.getLength()));
  }

  protected void decodeFirst() {
    decodeNext();
    previous.invalidate();
  }

  protected void decodeAtPosition(int position) {
    currentBuffer.position(position);
    decodeNext();
    previous.invalidate();
  }

  protected void decodeNext() {
    current.startOffset = currentBuffer.position();
    int p = currentBuffer.position() + currentBuffer.arrayOffset();
    long ll = Bytes.toLong(currentBuffer.array(), p);
    // Read top half as an int of key length and bottom int as value length
    current.keyLength = (int) (ll >> Integer.SIZE);
    current.valueLength = (int) (Bytes.MASK_FOR_LOWER_INT_IN_LONG ^ ll);
    ByteBufferUtils.skip(currentBuffer, Bytes.SIZEOF_LONG);
    // key part
    current.keyBuffer.set(currentBuffer.array(), currentBuffer.arrayOffset()
        + currentBuffer.position(), current.keyLength);
    ByteBufferUtils.skip(currentBuffer, current.keyLength);
    // value part
    current.valueOffset = currentBuffer.position();
    ByteBufferUtils.skip(currentBuffer, current.valueLength);
    if (includesTags()) {
      decodeTags();
    }
    if (includesMvcc()) {
      current.memstoreTS = ByteBufferUtils.readVLong(currentBuffer);
    } else {
      current.memstoreTS = 0;
    }
    current.nextKvOffset = currentBuffer.position();
    current.setKey(current.keyBuffer.getBytes(), current.keyBuffer.getOffset(),
        current.keyBuffer.getLength());
  }

  protected void decodeTags() {
    current.tagsLength = currentBuffer.getShort();
    current.tagsOffset = currentBuffer.position();
    ByteBufferUtils.skip(currentBuffer, current.tagsLength);
  }

  protected class SeekerState {
    /**
     * The size of a (key length, value length) tuple that prefixes each entry
     * in a data block.
     */
    public final static int KEY_VALUE_LEN_SIZE = 2 * Bytes.SIZEOF_INT;

    protected ByteBuffer currentBuffer;
    protected int startOffset = -1;
    protected int valueOffset = -1;
    protected int keyLength;
    protected int valueLength;
    protected int tagsLength = 0;
    protected int tagsOffset = -1;

    protected SimpleMutableByteRange keyBuffer = new SimpleMutableByteRange();
    protected long memstoreTS;
    protected int nextKvOffset;
    protected KeyValue.KeyOnlyKeyValue currentKey = new KeyValue.KeyOnlyKeyValue();

    protected boolean isValid() {
      return valueOffset != -1;
    }

    protected void invalidate() {
      valueOffset = -1;
      currentKey = new KeyValue.KeyOnlyKeyValue();
      currentBuffer = null;
    }

    protected void setKey(byte[] key, int offset, int length) {
      currentKey.setKey(key, offset, length);
    }

    protected long getSequenceId() {
      return memstoreTS;
    }

    /**
     * Copy the state from the next one into this instance (the previous state
     * placeholder). Used to save the previous state when we are advancing the
     * seeker to the next key/value.
     */
    protected void copyFromNext(SeekerState nextState) {
      keyBuffer.set(nextState.keyBuffer.getBytes(),
          nextState.keyBuffer.getOffset(), nextState.keyBuffer.getLength());
      currentKey.setKey(nextState.keyBuffer.getBytes(),
          nextState.keyBuffer.getOffset(), nextState.keyBuffer.getLength());

      startOffset = nextState.startOffset;
      valueOffset = nextState.valueOffset;
      keyLength = nextState.keyLength;
      valueLength = nextState.valueLength;
      nextKvOffset = nextState.nextKvOffset;
      memstoreTS = nextState.memstoreTS;
      currentBuffer = nextState.currentBuffer;
      tagsOffset = nextState.tagsOffset;
      tagsLength = nextState.tagsLength;
    }

    @Override
    public String toString() {
      return CellUtil.getCellKeyAsString(toCell());
    }

    protected int getCellBufSize() {
      int kvBufSize = KEY_VALUE_LEN_SIZE + keyLength + valueLength;
      if (includesTags()) {
        kvBufSize += Bytes.SIZEOF_SHORT + tagsLength;
      }
      return kvBufSize;
    }

    protected Cell formNoTagsKeyValue() {
      NoTagsKeyValue ret = new NoTagsKeyValue(currentBuffer.array(),
          currentBuffer.arrayOffset() + startOffset, getCellBufSize());
      if (includesMvcc()) {
        ret.setSequenceId(memstoreTS);
      }
      return ret;
    }

    public Cell toCell() {
      if (tagsOffset > 0) {
        KeyValue ret = new KeyValue(currentBuffer.array(),
            currentBuffer.arrayOffset() + startOffset, getCellBufSize());
        if (includesMvcc()) {
          ret.setSequenceId(memstoreTS);
        }
        return ret;
      } else {
        return formNoTagsKeyValue();
      }
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy