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. 031 * <p> 032 * CellChunkMap is a byte array (chunk) holding all that is needed to access a Cell, which is 033 * actually saved on another deeper chunk. Per Cell we have a reference to this deeper byte array B 034 * (chunk ID, integer), offset in bytes in B (integer), length in bytes in B (integer) and seqID of 035 * the cell (long). In order to save reference to byte array we use the Chunk's ID given by 036 * ChunkCreator. 037 * <p> 038 * The CellChunkMap memory layout on chunk A relevant to a deeper byte array B, holding the actual 039 * cell data: 040 * 041 * <pre> 042 * 043 * < header > <--------------- first Cell -----------------> <-- second Cell ... 044 * --------------------------------------------------------------------------------------- ... 045 * integer | integer | integer | integer | long | 046 * 4 bytes | 4 bytes | 4 bytes | 4 bytes | 8 bytes | 047 * ChunkID | chunkID of | offset in B | length of | sequence | ... 048 * of this | chunk B with | where Cell's | Cell's | ID of | 049 * chunk A | Cell data | data starts | data in B | the Cell | 050 * --------------------------------------------------------------------------------------- ... 051 * </pre> 052 */ 053@InterfaceAudience.Private 054public class CellChunkMap<T extends Cell> extends CellFlatMap<T> { 055 056 private final Chunk[] chunks; // the array of chunks, on which the index is based 057 058 // number of cell-representations in a chunk 059 // depends on the size of the chunks (may be index chunks or regular data chunks) 060 // each chunk starts with its own ID following the cells data 061 private final int numOfCellRepsInChunk; 062 063 /** 064 * C-tor for creating CellChunkMap from existing Chunk array, which must be ordered (decreasingly 065 * or increasingly according to parameter "descending") 066 * @param comparator a tool for comparing cells 067 * @param chunks ordered array of index chunk with cell representations 068 * @param min the index of the first cell (usually 0) 069 * @param max number of Cells or the index of the cell after the maximal cell 070 * @param descending the order of the given array 071 */ 072 public CellChunkMap(Comparator<? super T> comparator, Chunk[] chunks, int min, int max, 073 boolean descending) { 074 super(comparator, min, max, descending); 075 this.chunks = chunks; 076 if (chunks != null && chunks.length != 0 && chunks[0] != null) { 077 this.numOfCellRepsInChunk = 078 (chunks[0].size - ChunkCreator.SIZEOF_CHUNK_HEADER) / ClassSize.CELL_CHUNK_MAP_ENTRY; 079 } else { // In case the chunks array was not allocated 080 this.numOfCellRepsInChunk = 0; 081 } 082 } 083 084 /** 085 * To be used by base (CellFlatMap) class only to create a sub-CellFlatMap Should be used only to 086 * create only CellChunkMap from CellChunkMap 087 */ 088 @Override 089 protected CellFlatMap<T> createSubCellFlatMap(int min, int max, boolean descending) { 090 return new CellChunkMap<>(this.comparator(), this.chunks, min, max, descending); 091 } 092 093 @Override 094 protected T getCell(int i) { 095 // get the index of the relevant chunk inside chunk array 096 int chunkIndex = (i / numOfCellRepsInChunk); 097 ByteBuffer block = chunks[chunkIndex].getData();// get the ByteBuffer of the relevant chunk 098 int j = i - chunkIndex * numOfCellRepsInChunk; // get the index of the cell-representation 099 100 // find inside the offset inside the chunk holding the index, skip bytes for chunk id 101 int offsetInBytes = ChunkCreator.SIZEOF_CHUNK_HEADER + j * ClassSize.CELL_CHUNK_MAP_ENTRY; 102 103 // find the chunk holding the data of the cell, the chunkID is stored first 104 int chunkId = ByteBufferUtils.toInt(block, offsetInBytes); 105 Chunk chunk = ChunkCreator.getInstance().getChunk(chunkId); 106 if (chunk == null) { 107 // this should not happen 108 throw new IllegalArgumentException("In CellChunkMap, cell must be associated with chunk." 109 + ". We were looking for a cell at index " + i); 110 } 111 112 // find the offset of the data of the cell, skip integer for chunkID, offset is stored second 113 int offsetOfCell = ByteBufferUtils.toInt(block, offsetInBytes + Bytes.SIZEOF_INT); 114 // find the length of the data of the cell, skip two integers for chunkID and offset, 115 // length is stored third 116 int lengthOfCell = ByteBufferUtils.toInt(block, offsetInBytes + 2 * Bytes.SIZEOF_INT); 117 // find the seqID of the cell, skip three integers for chunkID, offset, and length 118 // the seqID is plain written as part of the cell representation 119 long cellSeqID = ByteBufferUtils.toLong(block, offsetInBytes + 3 * Bytes.SIZEOF_INT); 120 121 ByteBuffer buf = chunk.getData(); // get the ByteBuffer where the cell data is stored 122 if (buf == null) { 123 // this should not happen 124 throw new IllegalArgumentException( 125 "In CellChunkMap, chunk must be associated with ByteBuffer." + " Chunk: " + chunk 126 + " Chunk ID: " + chunk.getId() + ", is from pool: " + chunk.isFromPool() 127 + ". We were looking for a cell at index " + i); 128 } 129 130 @SuppressWarnings("unchecked") 131 T cell = (T) new ByteBufferChunkKeyValue(buf, offsetOfCell, lengthOfCell, cellSeqID); 132 133 return cell; 134 } 135}