001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.regionserver;
019
020import java.nio.ByteBuffer;
021import java.util.Comparator;
022import org.apache.hadoop.hbase.Cell;
023import org.apache.hadoop.hbase.util.ByteBufferUtils;
024import org.apache.hadoop.hbase.util.Bytes;
025import org.apache.hadoop.hbase.util.ClassSize;
026import org.apache.yetus.audience.InterfaceAudience;
027
028/**
029 * CellChunkMap is an array of serialized representations of Cell (pointing to Chunks with full Cell
030 * data) and can be allocated both off-heap and on-heap. CellChunkMap is a byte array (chunk)
031 * holding all that is needed to access a Cell, which is actually saved on another deeper chunk. Per
032 * Cell we have a reference to this deeper byte array B (chunk ID, integer), offset in bytes in B
033 * (integer), length in bytes in B (integer) and seqID of the cell (long). In order to save
034 * reference to byte array we use the Chunk's ID given by ChunkCreator. The CellChunkMap memory
035 * layout on chunk A relevant to a deeper byte array B, holding the actual cell data: < header >
036 * <--------------- first Cell -----------------> <-- second Cell ...
037 * --------------------------------------------------------------------------------------- ...
038 * integer | integer | integer | integer | long | 4 bytes | 4 bytes | 4 bytes | 4 bytes | 8 bytes |
039 * ChunkID | chunkID of | offset in B | length of | sequence | ... of this | chunk B with | where
040 * Cell's | Cell's | ID of | chunk A | Cell data | data starts | data in B | the Cell |
041 * --------------------------------------------------------------------------------------- ...
042 */
043@InterfaceAudience.Private
044public class CellChunkMap extends CellFlatMap {
045
046  private final Chunk[] chunks; // the array of chunks, on which the index is based
047
048  // number of cell-representations in a chunk
049  // depends on the size of the chunks (may be index chunks or regular data chunks)
050  // each chunk starts with its own ID following the cells data
051  private final int numOfCellRepsInChunk;
052
053  /**
054   * C-tor for creating CellChunkMap from existing Chunk array, which must be ordered (decreasingly
055   * or increasingly according to parameter "descending")
056   * @param comparator a tool for comparing cells
057   * @param chunks     ordered array of index chunk with cell representations
058   * @param min        the index of the first cell (usually 0)
059   * @param max        number of Cells or the index of the cell after the maximal cell
060   * @param descending the order of the given array
061   */
062  public CellChunkMap(Comparator<? super Cell> comparator, Chunk[] chunks, int min, int max,
063    boolean descending) {
064    super(comparator, min, max, descending);
065    this.chunks = chunks;
066    if (chunks != null && chunks.length != 0 && chunks[0] != null) {
067      this.numOfCellRepsInChunk =
068        (chunks[0].size - ChunkCreator.SIZEOF_CHUNK_HEADER) / ClassSize.CELL_CHUNK_MAP_ENTRY;
069    } else { // In case the chunks array was not allocated
070      this.numOfCellRepsInChunk = 0;
071    }
072  }
073
074  /*
075   * To be used by base (CellFlatMap) class only to create a sub-CellFlatMap Should be used only to
076   * create only CellChunkMap from CellChunkMap
077   */
078  @Override
079  protected CellFlatMap createSubCellFlatMap(int min, int max, boolean descending) {
080    return new CellChunkMap(this.comparator(), this.chunks, min, max, descending);
081  }
082
083  @Override
084  protected Cell getCell(int i) {
085    // get the index of the relevant chunk inside chunk array
086    int chunkIndex = (i / numOfCellRepsInChunk);
087    ByteBuffer block = chunks[chunkIndex].getData();// get the ByteBuffer of the relevant chunk
088    int j = i - chunkIndex * numOfCellRepsInChunk; // get the index of the cell-representation
089
090    // find inside the offset inside the chunk holding the index, skip bytes for chunk id
091    int offsetInBytes = ChunkCreator.SIZEOF_CHUNK_HEADER + j * ClassSize.CELL_CHUNK_MAP_ENTRY;
092
093    // find the chunk holding the data of the cell, the chunkID is stored first
094    int chunkId = ByteBufferUtils.toInt(block, offsetInBytes);
095    Chunk chunk = ChunkCreator.getInstance().getChunk(chunkId);
096    if (chunk == null) {
097      // this should not happen
098      throw new IllegalArgumentException("In CellChunkMap, cell must be associated with chunk."
099        + ". We were looking for a cell at index " + i);
100    }
101
102    // find the offset of the data of the cell, skip integer for chunkID, offset is stored second
103    int offsetOfCell = ByteBufferUtils.toInt(block, offsetInBytes + Bytes.SIZEOF_INT);
104    // find the length of the data of the cell, skip two integers for chunkID and offset,
105    // length is stored third
106    int lengthOfCell = ByteBufferUtils.toInt(block, offsetInBytes + 2 * Bytes.SIZEOF_INT);
107    // find the seqID of the cell, skip three integers for chunkID, offset, and length
108    // the seqID is plain written as part of the cell representation
109    long cellSeqID = ByteBufferUtils.toLong(block, offsetInBytes + 3 * Bytes.SIZEOF_INT);
110
111    ByteBuffer buf = chunk.getData(); // get the ByteBuffer where the cell data is stored
112    if (buf == null) {
113      // this should not happen
114      throw new IllegalArgumentException(
115        "In CellChunkMap, chunk must be associated with ByteBuffer." + " Chunk: " + chunk
116          + " Chunk ID: " + chunk.getId() + ", is from pool: " + chunk.isFromPool()
117          + ". We were looking for a cell at index " + i);
118    }
119
120    return new ByteBufferChunkKeyValue(buf, offsetOfCell, lengthOfCell, cellSeqID);
121  }
122}