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}