View Javadoc

1   /*
2    * Copyright 2011 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.io.hfile;
21  
22  import java.io.DataInputStream;
23  import java.io.DataOutput;
24  import java.io.IOException;
25  import java.io.OutputStream;
26  import java.nio.ByteBuffer;
27  
28  import org.apache.hadoop.hbase.util.Bytes;
29  
30  /**
31   * Various types of {@link HFile} blocks. Ordinal values of these enum constants
32   * must not be relied upon. The values in the enum appear in the order they
33   * appear in a version 2 {@link HFile}.
34   */
35  public enum BlockType {
36  
37    // Scanned block section
38  
39    /** Data block, both versions */
40    DATA("DATABLK*", BlockCategory.DATA),
41  
42    /** An encoded data block (e.g. with prefix compression), version 2 */
43    ENCODED_DATA("DATABLKE", BlockCategory.DATA) {
44      @Override
45      public int getId() {
46        return DATA.ordinal();
47      }
48    },
49  
50    /** Version 2 leaf index block. Appears in the data block section */
51    LEAF_INDEX("IDXLEAF2", BlockCategory.INDEX),
52  
53    /** Bloom filter block, version 2 */
54    BLOOM_CHUNK("BLMFBLK2", BlockCategory.BLOOM),
55  
56    // Non-scanned block section
57  
58    /** Meta blocks */
59    META("METABLKc", BlockCategory.META),
60  
61    /** Intermediate-level version 2 index in the non-data block section */
62    INTERMEDIATE_INDEX("IDXINTE2", BlockCategory.INDEX),
63  
64    // Load-on-open section.
65  
66    /** Root index block, also used for the single-level meta index, version 2 */
67    ROOT_INDEX("IDXROOT2", BlockCategory.INDEX),
68  
69    /** File info, version 2 */
70    FILE_INFO("FILEINF2", BlockCategory.META),
71  
72    /** General Bloom filter metadata, version 2 */
73    GENERAL_BLOOM_META("BLMFMET2", BlockCategory.BLOOM),
74  
75    /** Delete Family Bloom filter metadata, version 2 */
76    DELETE_FAMILY_BLOOM_META("DFBLMET2", BlockCategory.BLOOM),
77  
78    // Trailer
79  
80    /** Fixed file trailer, both versions (always just a magic string) */
81    TRAILER("TRABLK\"$", BlockCategory.META),
82  
83    // Legacy blocks
84  
85    /** Block index magic string in version 1 */
86    INDEX_V1("IDXBLK)+", BlockCategory.INDEX);
87  
88    public enum BlockCategory {
89      DATA, META, INDEX, BLOOM, ALL_CATEGORIES, UNKNOWN;
90  
91      /**
92       * Throws an exception if the block category passed is the special category
93       * meaning "all categories".
94       */
95      public void expectSpecific() {
96        if (this == ALL_CATEGORIES) {
97          throw new IllegalArgumentException("Expected a specific block " +
98              "category but got " + this);
99        }
100     }
101   }
102 
103   public static final int MAGIC_LENGTH = 8;
104 
105   private final byte[] magic;
106   private final BlockCategory metricCat;
107 
108   private BlockType(String magicStr, BlockCategory metricCat) {
109     magic = Bytes.toBytes(magicStr);
110     this.metricCat = metricCat;
111     assert magic.length == MAGIC_LENGTH;
112   }
113 
114   /**
115    * Use this instead of {@link #ordinal()}. They work exactly the same, except
116    * DATA and ENCODED_DATA get the same id using this method (overridden for
117    * {@link #ENCODED_DATA}).
118    * @return block type id from 0 to the number of block types - 1
119    */
120   public int getId() {
121     // Default implementation, can be overridden for individual enum members.
122     return ordinal();
123   }
124 
125   public void writeToStream(OutputStream out) throws IOException {
126     out.write(magic);
127   }
128 
129   public void write(DataOutput out) throws IOException {
130     out.write(magic);
131   }
132 
133   public void write(ByteBuffer buf) {
134     buf.put(magic);
135   }
136 
137   public BlockCategory getCategory() {
138     return metricCat;
139   }
140 
141   public static BlockType parse(byte[] buf, int offset, int length)
142       throws IOException {
143     if (length != MAGIC_LENGTH) {
144       throw new IOException("Magic record of invalid length: "
145           + Bytes.toStringBinary(buf, offset, length));
146     }
147 
148     for (BlockType blockType : values())
149       if (Bytes.compareTo(blockType.magic, 0, MAGIC_LENGTH, buf, offset,
150           MAGIC_LENGTH) == 0)
151         return blockType;
152 
153     throw new IOException("Invalid HFile block magic: "
154         + Bytes.toStringBinary(buf, offset, MAGIC_LENGTH));
155   }
156 
157   public static BlockType read(DataInputStream in) throws IOException {
158     byte[] buf = new byte[MAGIC_LENGTH];
159     in.readFully(buf);
160     return parse(buf, 0, buf.length);
161   }
162 
163   public static BlockType read(ByteBuffer buf) throws IOException {
164     BlockType blockType = parse(buf.array(),
165         buf.arrayOffset() + buf.position(),
166         Math.min(buf.limit() - buf.position(), MAGIC_LENGTH));
167 
168     // If we got here, we have read exactly MAGIC_LENGTH bytes.
169     buf.position(buf.position() + MAGIC_LENGTH);
170     return blockType;
171   }
172 
173   /**
174    * Put the magic record out to the specified byte array position.
175    *
176    * @param bytes the byte array
177    * @param offset position in the array
178    * @return incremented offset
179    */
180   public int put(byte[] bytes, int offset) {
181     System.arraycopy(magic, 0, bytes, offset, MAGIC_LENGTH);
182     return offset + MAGIC_LENGTH;
183   }
184 
185   /**
186    * Reads a magic record of the length {@link #MAGIC_LENGTH} from the given
187    * stream and expects it to match this block type.
188    */
189   public void readAndCheck(DataInputStream in) throws IOException {
190     byte[] buf = new byte[MAGIC_LENGTH];
191     in.readFully(buf);
192     if (Bytes.compareTo(buf, magic) != 0) {
193       throw new IOException("Invalid magic: expected "
194           + Bytes.toStringBinary(magic) + ", got " + Bytes.toStringBinary(buf));
195     }
196   }
197 
198   /**
199    * Reads a magic record of the length {@link #MAGIC_LENGTH} from the given
200    * byte buffer and expects it to match this block type.
201    */
202   public void readAndCheck(ByteBuffer in) throws IOException {
203     byte[] buf = new byte[MAGIC_LENGTH];
204     in.get(buf);
205     if (Bytes.compareTo(buf, magic) != 0) {
206       throw new IOException("Invalid magic: expected "
207           + Bytes.toStringBinary(magic) + ", got " + Bytes.toStringBinary(buf));
208     }
209   }
210 
211   /**
212    * @return whether this block type is encoded or unencoded data block
213    */
214   public final boolean isData() {
215     return this == DATA || this == ENCODED_DATA;
216   }
217 
218 }