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 org.apache.hadoop.hbase.classification.InterfaceAudience;
20  import org.apache.hadoop.hbase.classification.InterfaceStability;
21  import org.apache.hadoop.hbase.util.Bytes;
22
23  import java.io.IOException;
24  import java.io.OutputStream;
25  import java.util.HashMap;
26  import java.util.Map;
27  
28  /**
29   * Provide access to all data block encoding algorithms. All of the algorithms
30   * are required to have unique id which should <b>NEVER</b> be changed. If you
31   * want to add a new algorithm/version, assign it a new id. Announce the new id
32   * in the HBase mailing list to prevent collisions.
33   */
34  @InterfaceAudience.Public
35  @InterfaceStability.Evolving
36  public enum DataBlockEncoding {
37  
38    /** Disable data block encoding. */
39    NONE(0, null),
40    // id 1 is reserved for the BITSET algorithm to be added later
41    PREFIX(2, "org.apache.hadoop.hbase.io.encoding.PrefixKeyDeltaEncoder"),
42    DIFF(3, "org.apache.hadoop.hbase.io.encoding.DiffKeyDeltaEncoder"),
43    FAST_DIFF(4, "org.apache.hadoop.hbase.io.encoding.FastDiffDeltaEncoder"),
44    // id 5 is reserved for the COPY_KEY algorithm for benchmarking
45    // COPY_KEY(5, "org.apache.hadoop.hbase.io.encoding.CopyKeyDataBlockEncoder"),
46    PREFIX_TREE(6, "org.apache.hadoop.hbase.codec.prefixtree.PrefixTreeCodec"),
47    ROW_INDEX_V1(7, "org.apache.hadoop.hbase.io.encoding.RowIndexCodecV1");
48
49    private final short id;
50    private final byte[] idInBytes;
51    private DataBlockEncoder encoder;
52    private final String encoderCls;
53
54    public static final int ID_SIZE = Bytes.SIZEOF_SHORT;
55
56    /** Maps data block encoding ids to enum instances. */
57    private static Map<Short, DataBlockEncoding> idToEncoding =
58        new HashMap<Short, DataBlockEncoding>();
59
60    static {
61      for (DataBlockEncoding algo : values()) {
62        if (idToEncoding.containsKey(algo.id)) {
63          throw new RuntimeException(String.format(
64              "Two data block encoder algorithms '%s' and '%s' have " +
65              "the same id %d",
66              idToEncoding.get(algo.id).toString(), algo.toString(),
67              (int) algo.id));
68        }
69        idToEncoding.put(algo.id, algo);
70      }
71    }
72
73    private DataBlockEncoding(int id, String encoderClsName) {
74      if (id < Short.MIN_VALUE || id > Short.MAX_VALUE) {
75        throw new AssertionError(
76            "Data block encoding algorithm id is out of range: " + id);
77      }
78      this.id = (short) id;
79      this.idInBytes = Bytes.toBytes(this.id);
80      if (idInBytes.length != ID_SIZE) {
81        // White this may seem redundant, if we accidentally serialize
82        // the id as e.g. an int instead of a short, all encoders will break.
83        throw new RuntimeException("Unexpected length of encoder ID byte " +
84            "representation: " + Bytes.toStringBinary(idInBytes));
85      }
86      this.encoderCls = encoderClsName;
87    }
88
89    /**
90     * @return name converted to bytes.
91     */
92    public byte[] getNameInBytes() {
93      return Bytes.toBytes(toString());
94    }
95
96    /**
97     * @return The id of a data block encoder.
98     */
99    public short getId() {
100     return id;
101   }
102
103   /**
104    * Writes id in bytes.
105    * @param stream where the id should be written.
106    */
107   public void writeIdInBytes(OutputStream stream) throws IOException {
108     stream.write(idInBytes);
109   }
110 
111
112   /**
113    * Writes id bytes to the given array starting from offset.
114    *
115    * @param dest output array
116    * @param offset starting offset of the output array
117    * @throws IOException
118    */
119   public void writeIdInBytes(byte[] dest, int offset) throws IOException {
120     System.arraycopy(idInBytes, 0, dest, offset, ID_SIZE);
121   }
122
123   /**
124    * Return new data block encoder for given algorithm type.
125    * @return data block encoder if algorithm is specified, null if none is
126    *         selected.
127    */
128   public DataBlockEncoder getEncoder() {
129     if (encoder == null && id != 0) {
130       // lazily create the encoder
131       encoder = createEncoder(encoderCls);
132     }
133     return encoder;
134   }
135
136   /**
137    * Find and create data block encoder for given id;
138    * @param encoderId id of data block encoder.
139    * @return Newly created data block encoder.
140    */
141   public static DataBlockEncoder getDataBlockEncoderById(short encoderId) {
142     if (!idToEncoding.containsKey(encoderId)) {
143       throw new IllegalArgumentException(String.format(
144           "There is no data block encoder for given id '%d'",
145           (int) encoderId));
146     }
147
148     return idToEncoding.get(encoderId).getEncoder();
149   }
150
151   /**
152    * Find and return the name of data block encoder for the given id.
153    * @param encoderId id of data block encoder
154    * @return name, same as used in options in column family
155    */
156   public static String getNameFromId(short encoderId) {
157     return idToEncoding.get(encoderId).toString();
158   }
159
160   /**
161    * Check if given encoder has this id.
162    * @param encoder encoder which id will be checked
163    * @param encoderId id which we except
164    * @return true if id is right for given encoder, false otherwise
165    * @exception IllegalArgumentException
166    *            thrown when there is no matching data block encoder
167    */
168   public static boolean isCorrectEncoder(DataBlockEncoder encoder,
169       short encoderId) {
170     if (!idToEncoding.containsKey(encoderId)) {
171       throw new IllegalArgumentException(String.format(
172           "There is no data block encoder for given id '%d'",
173           (int) encoderId));
174     }
175
176     DataBlockEncoding algorithm = idToEncoding.get(encoderId);
177     String encoderCls = encoder.getClass().getName();
178     return encoderCls.equals(algorithm.encoderCls);
179   }
180
181   public static DataBlockEncoding getEncodingById(short dataBlockEncodingId) {
182     return idToEncoding.get(dataBlockEncodingId);
183   }
184
185   protected static DataBlockEncoder createEncoder(String fullyQualifiedClassName){
186       try {
187         return (DataBlockEncoder)Class.forName(fullyQualifiedClassName).newInstance();
188       } catch (InstantiationException e) {
189         throw new RuntimeException(e);
190       } catch (IllegalAccessException e) {
191         throw new RuntimeException(e);
192       } catch (ClassNotFoundException e) {
193         throw new IllegalArgumentException(e);
194       }
195   }
196
197 }