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