View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with this
4    * work for additional information regarding copyright ownership. The ASF
5    * licenses this file to you under the Apache License, Version 2.0 (the
6    * "License"); you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    * http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14   * License for the specific language governing permissions and limitations
15   * under the License.
16   */
17  package org.apache.hadoop.hbase.io.encoding;
18  
19  import java.io.IOException;
20  import java.io.OutputStream;
21  
22  import org.apache.hadoop.hbase.classification.InterfaceAudience;
23  import org.apache.hadoop.hbase.classification.InterfaceStability;
24  import org.apache.hadoop.hbase.util.Bytes;
25  
26  /**
27   * Provide access to all data block encoding algorithms. All of the algorithms
28   * are required to have unique id which should <b>NEVER</b> be changed. If you
29   * want to add a new algorithm/version, assign it a new id. Announce the new id
30   * in the HBase mailing list to prevent collisions.
31   */
32  @InterfaceAudience.Public
33  @InterfaceStability.Evolving
34  public enum DataBlockEncoding {
35  
36    /** Disable data block encoding. */
37    NONE(0, null),
38    // id 1 is reserved for the BITSET algorithm to be added later
39    PREFIX(2, "org.apache.hadoop.hbase.io.encoding.PrefixKeyDeltaEncoder"),
40    DIFF(3, "org.apache.hadoop.hbase.io.encoding.DiffKeyDeltaEncoder"),
41    FAST_DIFF(4, "org.apache.hadoop.hbase.io.encoding.FastDiffDeltaEncoder"),
42    // id 5 is reserved for the COPY_KEY algorithm for benchmarking
43    // COPY_KEY(5, "org.apache.hadoop.hbase.io.encoding.CopyKeyDataBlockEncoder"),
44    PREFIX_TREE(6, "org.apache.hadoop.hbase.codec.prefixtree.PrefixTreeCodec"),
45    ROW_INDEX_V1(7, "org.apache.hadoop.hbase.io.encoding.RowIndexCodecV1");
46  
47    private final short id;
48    private final byte[] idInBytes;
49    private DataBlockEncoder encoder;
50    private final String encoderCls;
51  
52    public static final int ID_SIZE = Bytes.SIZEOF_SHORT;
53  
54    /** Maps data block encoding ids to enum instances. */
55    private static DataBlockEncoding[] idArray = new DataBlockEncoding[Byte.MAX_VALUE + 1];
56  
57    static {
58      for (DataBlockEncoding algo : values()) {
59        if (idArray[algo.id] != null) {
60          throw new RuntimeException(String.format(
61            "Two data block encoder algorithms '%s' and '%s' have " + "the same id %d",
62            idArray[algo.id].toString(), algo.toString(), (int) algo.id));
63        }
64        idArray[algo.id] = algo;
65      }
66    }
67  
68    private DataBlockEncoding(int id, String encoderClsName) {
69      if (id < 0 || id > Byte.MAX_VALUE) {
70        throw new AssertionError(
71            "Data block encoding algorithm id is out of range: " + id);
72      }
73      this.id = (short) id;
74      this.idInBytes = Bytes.toBytes(this.id);
75      if (idInBytes.length != ID_SIZE) {
76        // White this may seem redundant, if we accidentally serialize
77        // the id as e.g. an int instead of a short, all encoders will break.
78        throw new RuntimeException("Unexpected length of encoder ID byte " +
79            "representation: " + Bytes.toStringBinary(idInBytes));
80      }
81      this.encoderCls = encoderClsName;
82    }
83  
84    /**
85     * @return name converted to bytes.
86     */
87    public byte[] getNameInBytes() {
88      return Bytes.toBytes(toString());
89    }
90  
91    /**
92     * @return The id of a data block encoder.
93     */
94    public short getId() {
95      return id;
96    }
97  
98    /**
99     * Writes id in bytes.
100    * @param stream where the id should be written.
101    */
102   public void writeIdInBytes(OutputStream stream) throws IOException {
103     stream.write(idInBytes);
104   }
105 
106 
107   /**
108    * Writes id bytes to the given array starting from offset.
109    *
110    * @param dest output array
111    * @param offset starting offset of the output array
112    * @throws IOException
113    */
114   public void writeIdInBytes(byte[] dest, int offset) throws IOException {
115     System.arraycopy(idInBytes, 0, dest, offset, ID_SIZE);
116   }
117 
118   /**
119    * Return new data block encoder for given algorithm type.
120    * @return data block encoder if algorithm is specified, null if none is
121    *         selected.
122    */
123   public DataBlockEncoder getEncoder() {
124     if (encoder == null && id != 0) {
125       // lazily create the encoder
126       encoder = createEncoder(encoderCls);
127     }
128     return encoder;
129   }
130 
131   /**
132    * Find and create data block encoder for given id;
133    * @param encoderId id of data block encoder.
134    * @return Newly created data block encoder.
135    */
136   public static DataBlockEncoder getDataBlockEncoderById(short encoderId) {
137     return getEncodingById(encoderId).getEncoder();
138   }
139 
140   /**
141    * Find and return the name of data block encoder for the given id.
142    * @param encoderId id of data block encoder
143    * @return name, same as used in options in column family
144    */
145   public static String getNameFromId(short encoderId) {
146     return getEncodingById(encoderId).toString();
147   }
148 
149   /**
150    * Check if given encoder has this id.
151    * @param encoder encoder which id will be checked
152    * @param encoderId id which we except
153    * @return true if id is right for given encoder, false otherwise
154    * @exception IllegalArgumentException
155    *            thrown when there is no matching data block encoder
156    */
157   public static boolean isCorrectEncoder(DataBlockEncoder encoder,
158       short encoderId) {
159     DataBlockEncoding algorithm = getEncodingById(encoderId);
160     String encoderCls = encoder.getClass().getName();
161     return encoderCls.equals(algorithm.encoderCls);
162   }
163 
164   public static DataBlockEncoding getEncodingById(short dataBlockEncodingId) {
165     DataBlockEncoding algorithm = null;
166     if (dataBlockEncodingId >= 0 && dataBlockEncodingId <= Byte.MAX_VALUE) {
167       algorithm = idArray[dataBlockEncodingId];
168     }
169     if (algorithm == null) {
170       throw new IllegalArgumentException(String.format(
171           "There is no data block encoder for given id '%d'",
172           (int) dataBlockEncodingId));
173     }
174     return algorithm;
175   }
176 
177   protected static DataBlockEncoder createEncoder(String fullyQualifiedClassName){
178       try {
179         return (DataBlockEncoder)Class.forName(fullyQualifiedClassName)
180             .getDeclaredConstructor().newInstance();
181       } catch (Exception e) {
182         throw new RuntimeException(e);
183       }
184   }
185 
186 }