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