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.ArrayList;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.apache.hadoop.hbase.util.Bytes;
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  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, new PrefixKeyDeltaEncoder()),
40    DIFF(3, new DiffKeyDeltaEncoder()),
41    FAST_DIFF(4, new FastDiffDeltaEncoder());
42  
43    private final short id;
44    private final byte[] idInBytes;
45    private final DataBlockEncoder encoder;
46  
47    public static final int ID_SIZE = Bytes.SIZEOF_SHORT;
48  
49    /** Maps data block encoding ids to enum instances. */
50    private static Map<Short, DataBlockEncoding> idToEncoding =
51        new HashMap<Short, DataBlockEncoding>();
52  
53    static {
54      for (DataBlockEncoding algo : values()) {
55        if (idToEncoding.containsKey(algo.id)) {
56          throw new RuntimeException(String.format(
57              "Two data block encoder algorithms '%s' and '%s' have " +
58              "the same id %d",
59              idToEncoding.get(algo.id).toString(), algo.toString(),
60              (int) algo.id));
61        }
62        idToEncoding.put(algo.id, algo);
63      }
64    }
65  
66    private DataBlockEncoding(int id, DataBlockEncoder encoder) {
67      if (id < Short.MIN_VALUE || id > Short.MAX_VALUE) {
68        throw new AssertionError(
69            "Data block encoding algorithm id is out of range: " + id);
70      }
71      this.id = (short) id;
72      this.idInBytes = Bytes.toBytes(this.id);
73      if (idInBytes.length != ID_SIZE) {
74        // White this may seem redundant, if we accidentally serialize
75        // the id as e.g. an int instead of a short, all encoders will break.
76        throw new RuntimeException("Unexpected length of encoder ID byte " +
77            "representation: " + Bytes.toStringBinary(idInBytes));
78      }
79      this.encoder = encoder;
80    }
81  
82    /**
83     * @return name converted to bytes.
84     */
85    public byte[] getNameInBytes() {
86      return Bytes.toBytes(toString());
87    }
88  
89    /**
90     * @return The id of a data block encoder.
91     */
92    public short getId() {
93      return id;
94    }
95  
96    /**
97     * Writes id in bytes.
98     * @param stream where the id should be written.
99     */
100   public void writeIdInBytes(OutputStream stream) throws IOException {
101     stream.write(idInBytes);
102   }
103 
104   /**
105    * Return new data block encoder for given algorithm type.
106    * @return data block encoder if algorithm is specified, null if none is
107    *         selected.
108    */
109   public DataBlockEncoder getEncoder() {
110     return encoder;
111   }
112 
113   /**
114    * Provide access to all data block encoders, even those which are not
115    * exposed in the enum. Useful for testing and benchmarking.
116    * @return list of all data block encoders.
117    */
118   public static List<DataBlockEncoder> getAllEncoders() {
119     ArrayList<DataBlockEncoder> encoders = new ArrayList<DataBlockEncoder>();
120     for (DataBlockEncoding algo : values()) {
121       DataBlockEncoder encoder = algo.getEncoder();
122       if (encoder != null) {
123         encoders.add(encoder);
124       }
125     }
126 
127     // Add encoders that are only used in testing.
128     encoders.add(new CopyKeyDataBlockEncoder());
129     return encoders;
130   }
131 
132   /**
133    * Find and create data block encoder for given id;
134    * @param encoderId id of data block encoder.
135    * @return Newly created data block encoder.
136    */
137   public static DataBlockEncoder getDataBlockEncoderById(short encoderId) {
138     if (!idToEncoding.containsKey(encoderId)) {
139       throw new IllegalArgumentException(String.format(
140           "There is no data block encoder for given id '%d'",
141           (int) encoderId));
142     }
143 
144     return idToEncoding.get(encoderId).getEncoder();
145   }
146 
147   /**
148    * Find and return the name of data block encoder for the given id.
149    * @param encoderId id of data block encoder
150    * @return name, same as used in options in column family
151    */
152   public static String getNameFromId(short encoderId) {
153     return idToEncoding.get(encoderId).toString();
154   }
155 
156   /**
157    * Check if given encoder has this id.
158    * @param encoder encoder which id will be checked
159    * @param encoderId id which we except
160    * @return true if id is right for given encoder, false otherwise
161    * @exception IllegalArgumentException
162    *            thrown when there is no matching data block encoder
163    */
164   public static boolean isCorrectEncoder(DataBlockEncoder encoder,
165       short encoderId) {
166     if (!idToEncoding.containsKey(encoderId)) {
167       throw new IllegalArgumentException(String.format(
168           "There is no data block encoder for given id '%d'",
169           (int) encoderId));
170     }
171 
172     DataBlockEncoding algorithm = idToEncoding.get(encoderId);
173     return algorithm.getClass().equals(encoder.getClass());
174   }
175 
176   public static DataBlockEncoding getEncodingById(short dataBlockEncodingId) {
177     return idToEncoding.get(dataBlockEncodingId);
178   }
179 
180 }