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.io.hfile; 019 020import java.io.DataInputStream; 021import java.io.DataOutput; 022import java.io.IOException; 023import java.io.OutputStream; 024import java.nio.ByteBuffer; 025import org.apache.hadoop.hbase.nio.ByteBuff; 026import org.apache.hadoop.hbase.util.Bytes; 027import org.apache.yetus.audience.InterfaceAudience; 028 029/** 030 * Various types of HFile blocks. Ordinal values of these enum constants must not be relied upon. 031 * The values in the enum appear in the order they appear in a version 2 HFile. 032 */ 033@InterfaceAudience.Private 034public enum BlockType { 035 036 // Scanned block section 037 038 /** Data block, both versions */ 039 DATA("DATABLK*", BlockCategory.DATA), 040 041 /** An encoded data block (e.g. with prefix compression), version 2 */ 042 ENCODED_DATA("DATABLKE", BlockCategory.DATA) { 043 @Override 044 public int getId() { 045 return DATA.ordinal(); 046 } 047 }, 048 049 /** Version 2 leaf index block. Appears in the data block section */ 050 LEAF_INDEX("IDXLEAF2", BlockCategory.INDEX), 051 052 /** Bloom filter block, version 2 */ 053 BLOOM_CHUNK("BLMFBLK2", BlockCategory.BLOOM), 054 055 // Non-scanned block section 056 057 /** Meta blocks */ 058 META("METABLKc", BlockCategory.META), 059 060 /** Intermediate-level version 2 index in the non-data block section */ 061 INTERMEDIATE_INDEX("IDXINTE2", BlockCategory.INDEX), 062 063 // Load-on-open section. 064 065 /** Root index block, also used for the single-level meta index, version 2 */ 066 ROOT_INDEX("IDXROOT2", BlockCategory.INDEX), 067 068 /** File info, version 2 */ 069 FILE_INFO("FILEINF2", BlockCategory.META), 070 071 /** General Bloom filter metadata, version 2 */ 072 GENERAL_BLOOM_META("BLMFMET2", BlockCategory.BLOOM), 073 074 /** Delete Family Bloom filter metadata, version 2 */ 075 DELETE_FAMILY_BLOOM_META("DFBLMET2", BlockCategory.BLOOM), 076 077 // Trailer 078 079 /** Fixed file trailer, both versions (always just a magic string) */ 080 TRAILER("TRABLK\"$", BlockCategory.META), 081 082 // Legacy blocks 083 084 /** Block index magic string in version 1 */ 085 INDEX_V1("IDXBLK)+", BlockCategory.INDEX); 086 087 public enum BlockCategory { 088 DATA, 089 META, 090 INDEX, 091 BLOOM, 092 ALL_CATEGORIES, 093 UNKNOWN; 094 095 /** 096 * Throws an exception if the block category passed is the special category meaning "all 097 * categories". 098 */ 099 public void expectSpecific() { 100 if (this == ALL_CATEGORIES) { 101 throw new IllegalArgumentException( 102 "Expected a specific block " + "category but got " + this); 103 } 104 } 105 } 106 107 public static final int MAGIC_LENGTH = 8; 108 109 private final byte[] magic; 110 private final BlockCategory metricCat; 111 112 private BlockType(String magicStr, BlockCategory metricCat) { 113 magic = Bytes.toBytes(magicStr); 114 this.metricCat = metricCat; 115 assert magic.length == MAGIC_LENGTH; 116 } 117 118 /** 119 * Use this instead of {@link #ordinal()}. They work exactly the same, except DATA and 120 * ENCODED_DATA get the same id using this method (overridden for {@link #ENCODED_DATA}). 121 * @return block type id from 0 to the number of block types - 1 122 */ 123 public int getId() { 124 // Default implementation, can be overridden for individual enum members. 125 return ordinal(); 126 } 127 128 public void writeToStream(OutputStream out) throws IOException { 129 out.write(magic); 130 } 131 132 public void write(DataOutput out) throws IOException { 133 out.write(magic); 134 } 135 136 public void write(ByteBuffer buf) { 137 buf.put(magic); 138 } 139 140 public void write(ByteBuff buf) { 141 buf.put(magic); 142 } 143 144 public BlockCategory getCategory() { 145 return metricCat; 146 } 147 148 public static BlockType parse(byte[] buf, int offset, int length) throws IOException { 149 if (length != MAGIC_LENGTH) { 150 throw new IOException( 151 "Magic record of invalid length: " + Bytes.toStringBinary(buf, offset, length)); 152 } 153 154 for (BlockType blockType : values()) 155 if (Bytes.compareTo(blockType.magic, 0, MAGIC_LENGTH, buf, offset, MAGIC_LENGTH) == 0) 156 return blockType; 157 158 throw new IOException( 159 "Invalid HFile block magic: " + Bytes.toStringBinary(buf, offset, MAGIC_LENGTH)); 160 } 161 162 public static BlockType read(DataInputStream in) throws IOException { 163 byte[] buf = new byte[MAGIC_LENGTH]; 164 in.readFully(buf); 165 return parse(buf, 0, buf.length); 166 } 167 168 public static BlockType read(ByteBuff buf) throws IOException { 169 byte[] magicBuf = new byte[Math.min(buf.limit() - buf.position(), MAGIC_LENGTH)]; 170 buf.get(magicBuf); 171 BlockType blockType = parse(magicBuf, 0, magicBuf.length); 172 // If we got here, we have read exactly MAGIC_LENGTH bytes. 173 return blockType; 174 } 175 176 /** 177 * Put the magic record out to the specified byte array position. 178 * @param bytes the byte array 179 * @param offset position in the array 180 * @return incremented offset 181 */ 182 public int put(byte[] bytes, int offset) { 183 System.arraycopy(magic, 0, bytes, offset, MAGIC_LENGTH); 184 return offset + MAGIC_LENGTH; 185 } 186 187 /** 188 * Reads a magic record of the length {@link #MAGIC_LENGTH} from the given stream and expects it 189 * to match this block type. 190 */ 191 public void readAndCheck(DataInputStream in) throws IOException { 192 byte[] buf = new byte[MAGIC_LENGTH]; 193 in.readFully(buf); 194 if (Bytes.compareTo(buf, magic) != 0) { 195 throw new IOException("Invalid magic: expected " + Bytes.toStringBinary(magic) + ", got " 196 + Bytes.toStringBinary(buf)); 197 } 198 } 199 200 /** 201 * Reads a magic record of the length {@link #MAGIC_LENGTH} from the given byte buffer and expects 202 * it to match this block type. 203 */ 204 public void readAndCheck(ByteBuffer in) throws IOException { 205 byte[] buf = new byte[MAGIC_LENGTH]; 206 in.get(buf); 207 if (Bytes.compareTo(buf, magic) != 0) { 208 throw new IOException("Invalid magic: expected " + Bytes.toStringBinary(magic) + ", got " 209 + Bytes.toStringBinary(buf)); 210 } 211 } 212 213 /** Returns whether this block type is encoded or unencoded data block */ 214 public final boolean isData() { 215 return this == DATA || this == ENCODED_DATA; 216 } 217 218}