001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package org.apache.hadoop.io;
020
021 import java.io.*;
022 import java.util.*;
023
024 import org.apache.hadoop.classification.InterfaceAudience;
025 import org.apache.hadoop.classification.InterfaceStability;
026 import org.apache.hadoop.util.ReflectionUtils;
027
028 /** A Comparator for {@link WritableComparable}s.
029 *
030 * <p>This base implemenation uses the natural ordering. To define alternate
031 * orderings, override {@link #compare(WritableComparable,WritableComparable)}.
032 *
033 * <p>One may optimize compare-intensive operations by overriding
034 * {@link #compare(byte[],int,int,byte[],int,int)}. Static utility methods are
035 * provided to assist in optimized implementations of this method.
036 */
037 @InterfaceAudience.Public
038 @InterfaceStability.Stable
039 public class WritableComparator implements RawComparator {
040
041 private static HashMap<Class, WritableComparator> comparators =
042 new HashMap<Class, WritableComparator>(); // registry
043
044 /** Get a comparator for a {@link WritableComparable} implementation. */
045 public static synchronized
046 WritableComparator get(Class<? extends WritableComparable> c) {
047 WritableComparator comparator = comparators.get(c);
048 if (comparator == null) {
049 // force the static initializers to run
050 forceInit(c);
051 // look to see if it is defined now
052 comparator = comparators.get(c);
053 // if not, use the generic one
054 if (comparator == null) {
055 comparator = new WritableComparator(c, true);
056 }
057 }
058 return comparator;
059 }
060
061 /**
062 * Force initialization of the static members.
063 * As of Java 5, referencing a class doesn't force it to initialize. Since
064 * this class requires that the classes be initialized to declare their
065 * comparators, we force that initialization to happen.
066 * @param cls the class to initialize
067 */
068 private static void forceInit(Class<?> cls) {
069 try {
070 Class.forName(cls.getName(), true, cls.getClassLoader());
071 } catch (ClassNotFoundException e) {
072 throw new IllegalArgumentException("Can't initialize class " + cls, e);
073 }
074 }
075
076 /** Register an optimized comparator for a {@link WritableComparable}
077 * implementation. Comparators registered with this method must be
078 * thread-safe. */
079 public static synchronized void define(Class c,
080 WritableComparator comparator) {
081 comparators.put(c, comparator);
082 }
083
084
085 private final Class<? extends WritableComparable> keyClass;
086 private final WritableComparable key1;
087 private final WritableComparable key2;
088 private final DataInputBuffer buffer;
089
090 /** Construct for a {@link WritableComparable} implementation. */
091 protected WritableComparator(Class<? extends WritableComparable> keyClass) {
092 this(keyClass, false);
093 }
094
095 protected WritableComparator(Class<? extends WritableComparable> keyClass,
096 boolean createInstances) {
097 this.keyClass = keyClass;
098 if (createInstances) {
099 key1 = newKey();
100 key2 = newKey();
101 buffer = new DataInputBuffer();
102 } else {
103 key1 = key2 = null;
104 buffer = null;
105 }
106 }
107
108 /** Returns the WritableComparable implementation class. */
109 public Class<? extends WritableComparable> getKeyClass() { return keyClass; }
110
111 /** Construct a new {@link WritableComparable} instance. */
112 public WritableComparable newKey() {
113 return ReflectionUtils.newInstance(keyClass, null);
114 }
115
116 /** Optimization hook. Override this to make SequenceFile.Sorter's scream.
117 *
118 * <p>The default implementation reads the data into two {@link
119 * WritableComparable}s (using {@link
120 * Writable#readFields(DataInput)}, then calls {@link
121 * #compare(WritableComparable,WritableComparable)}.
122 */
123 public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
124 try {
125 buffer.reset(b1, s1, l1); // parse key1
126 key1.readFields(buffer);
127
128 buffer.reset(b2, s2, l2); // parse key2
129 key2.readFields(buffer);
130
131 } catch (IOException e) {
132 throw new RuntimeException(e);
133 }
134
135 return compare(key1, key2); // compare them
136 }
137
138 /** Compare two WritableComparables.
139 *
140 * <p> The default implementation uses the natural ordering, calling {@link
141 * Comparable#compareTo(Object)}. */
142 @SuppressWarnings("unchecked")
143 public int compare(WritableComparable a, WritableComparable b) {
144 return a.compareTo(b);
145 }
146
147 public int compare(Object a, Object b) {
148 return compare((WritableComparable)a, (WritableComparable)b);
149 }
150
151 /** Lexicographic order of binary data. */
152 public static int compareBytes(byte[] b1, int s1, int l1,
153 byte[] b2, int s2, int l2) {
154 return FastByteComparisons.compareTo(b1, s1, l1, b2, s2, l2);
155 }
156
157 /** Compute hash for binary data. */
158 public static int hashBytes(byte[] bytes, int offset, int length) {
159 int hash = 1;
160 for (int i = offset; i < offset + length; i++)
161 hash = (31 * hash) + (int)bytes[i];
162 return hash;
163 }
164
165 /** Compute hash for binary data. */
166 public static int hashBytes(byte[] bytes, int length) {
167 return hashBytes(bytes, 0, length);
168 }
169
170 /** Parse an unsigned short from a byte array. */
171 public static int readUnsignedShort(byte[] bytes, int start) {
172 return (((bytes[start] & 0xff) << 8) +
173 ((bytes[start+1] & 0xff)));
174 }
175
176 /** Parse an integer from a byte array. */
177 public static int readInt(byte[] bytes, int start) {
178 return (((bytes[start ] & 0xff) << 24) +
179 ((bytes[start+1] & 0xff) << 16) +
180 ((bytes[start+2] & 0xff) << 8) +
181 ((bytes[start+3] & 0xff)));
182
183 }
184
185 /** Parse a float from a byte array. */
186 public static float readFloat(byte[] bytes, int start) {
187 return Float.intBitsToFloat(readInt(bytes, start));
188 }
189
190 /** Parse a long from a byte array. */
191 public static long readLong(byte[] bytes, int start) {
192 return ((long)(readInt(bytes, start)) << 32) +
193 (readInt(bytes, start+4) & 0xFFFFFFFFL);
194 }
195
196 /** Parse a double from a byte array. */
197 public static double readDouble(byte[] bytes, int start) {
198 return Double.longBitsToDouble(readLong(bytes, start));
199 }
200
201 /**
202 * Reads a zero-compressed encoded long from a byte array and returns it.
203 * @param bytes byte array with decode long
204 * @param start starting index
205 * @throws java.io.IOException
206 * @return deserialized long
207 */
208 public static long readVLong(byte[] bytes, int start) throws IOException {
209 int len = bytes[start];
210 if (len >= -112) {
211 return len;
212 }
213 boolean isNegative = (len < -120);
214 len = isNegative ? -(len + 120) : -(len + 112);
215 if (start+1+len>bytes.length)
216 throw new IOException(
217 "Not enough number of bytes for a zero-compressed integer");
218 long i = 0;
219 for (int idx = 0; idx < len; idx++) {
220 i = i << 8;
221 i = i | (bytes[start+1+idx] & 0xFF);
222 }
223 return (isNegative ? (i ^ -1L) : i);
224 }
225
226 /**
227 * Reads a zero-compressed encoded integer from a byte array and returns it.
228 * @param bytes byte array with the encoded integer
229 * @param start start index
230 * @throws java.io.IOException
231 * @return deserialized integer
232 */
233 public static int readVInt(byte[] bytes, int start) throws IOException {
234 return (int) readVLong(bytes, start);
235 }
236 }