1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 package org.apache.hadoop.examples.terasort; 20 21 import java.io.DataInput; 22 import java.io.DataOutput; 23 import java.io.IOException; 24 25 import org.apache.hadoop.io.Writable; 26 27 /** 28 * An unsigned 16 byte integer class that supports addition, multiplication, 29 * and left shifts. 30 */ 31 class Unsigned16 implements Writable { 32 private long hi8; 33 private long lo8; 34 Unsigned16()35 public Unsigned16() { 36 hi8 = 0; 37 lo8 = 0; 38 } 39 Unsigned16(long l)40 public Unsigned16(long l) { 41 hi8 = 0; 42 lo8 = l; 43 } 44 Unsigned16(Unsigned16 other)45 public Unsigned16(Unsigned16 other) { 46 hi8 = other.hi8; 47 lo8 = other.lo8; 48 } 49 50 @Override equals(Object o)51 public boolean equals(Object o) { 52 if (o instanceof Unsigned16) { 53 Unsigned16 other = (Unsigned16) o; 54 return other.hi8 == hi8 && other.lo8 == lo8; 55 } 56 return false; 57 } 58 59 @Override hashCode()60 public int hashCode() { 61 return (int) lo8; 62 } 63 64 /** 65 * Parse a hex string 66 * @param s the hex string 67 */ Unsigned16(String s)68 public Unsigned16(String s) throws NumberFormatException { 69 set(s); 70 } 71 72 /** 73 * Set the number from a hex string 74 * @param s the number in hexadecimal 75 * @throws NumberFormatException if the number is invalid 76 */ set(String s)77 public void set(String s) throws NumberFormatException { 78 hi8 = 0; 79 lo8 = 0; 80 final long lastDigit = 0xfl << 60; 81 for (int i = 0; i < s.length(); ++i) { 82 int digit = getHexDigit(s.charAt(i)); 83 if ((lastDigit & hi8) != 0) { 84 throw new NumberFormatException(s + " overflowed 16 bytes"); 85 } 86 hi8 <<= 4; 87 hi8 |= (lo8 & lastDigit) >>> 60; 88 lo8 <<= 4; 89 lo8 |= digit; 90 } 91 } 92 93 /** 94 * Set the number to a given long. 95 * @param l the new value, which is treated as an unsigned number 96 */ set(long l)97 public void set(long l) { 98 lo8 = l; 99 hi8 = 0; 100 } 101 102 /** 103 * Map a hexadecimal character into a digit. 104 * @param ch the character 105 * @return the digit from 0 to 15 106 * @throws NumberFormatException 107 */ getHexDigit(char ch)108 private static int getHexDigit(char ch) throws NumberFormatException { 109 if (ch >= '0' && ch <= '9') { 110 return ch - '0'; 111 } 112 if (ch >= 'a' && ch <= 'f') { 113 return ch - 'a' + 10; 114 } 115 if (ch >= 'A' && ch <= 'F') { 116 return ch - 'A' + 10; 117 } 118 throw new NumberFormatException(ch + " is not a valid hex digit"); 119 } 120 121 private static final Unsigned16 TEN = new Unsigned16(10); 122 fromDecimal(String s)123 public static Unsigned16 fromDecimal(String s) throws NumberFormatException { 124 Unsigned16 result = new Unsigned16(); 125 Unsigned16 tmp = new Unsigned16(); 126 for(int i=0; i < s.length(); i++) { 127 char ch = s.charAt(i); 128 if (ch < '0' || ch > '9') { 129 throw new NumberFormatException(ch + " not a valid decimal digit"); 130 } 131 int digit = ch - '0'; 132 result.multiply(TEN); 133 tmp.set(digit); 134 result.add(tmp); 135 } 136 return result; 137 } 138 139 /** 140 * Return the number as a hex string. 141 */ toString()142 public String toString() { 143 if (hi8 == 0) { 144 return Long.toHexString(lo8); 145 } else { 146 StringBuilder result = new StringBuilder(); 147 result.append(Long.toHexString(hi8)); 148 String loString = Long.toHexString(lo8); 149 for(int i=loString.length(); i < 16; ++i) { 150 result.append('0'); 151 } 152 result.append(loString); 153 return result.toString(); 154 } 155 } 156 157 /** 158 * Get a given byte from the number. 159 * @param b the byte to get with 0 meaning the most significant byte 160 * @return the byte or 0 if b is outside of 0..15 161 */ getByte(int b)162 public byte getByte(int b) { 163 if (b >= 0 && b < 16) { 164 if (b < 8) { 165 return (byte) (hi8 >> (56 - 8*b)); 166 } else { 167 return (byte) (lo8 >> (120 - 8*b)); 168 } 169 } 170 return 0; 171 } 172 173 /** 174 * Get the hexadecimal digit at the given position. 175 * @param p the digit position to get with 0 meaning the most significant 176 * @return the character or '0' if p is outside of 0..31 177 */ getHexDigit(int p)178 public char getHexDigit(int p) { 179 byte digit = getByte(p / 2); 180 if (p % 2 == 0) { 181 digit >>>= 4; 182 } 183 digit &= 0xf; 184 if (digit < 10) { 185 return (char) ('0' + digit); 186 } else { 187 return (char) ('A' + digit - 10); 188 } 189 } 190 191 /** 192 * Get the high 8 bytes as a long. 193 */ getHigh8()194 public long getHigh8() { 195 return hi8; 196 } 197 198 /** 199 * Get the low 8 bytes as a long. 200 */ getLow8()201 public long getLow8() { 202 return lo8; 203 } 204 205 /** 206 * Multiple the current number by a 16 byte unsigned integer. Overflow is not 207 * detected and the result is the low 16 bytes of the result. The numbers 208 * are divided into 32 and 31 bit chunks so that the product of two chucks 209 * fits in the unsigned 63 bits of a long. 210 * @param b the other number 211 */ multiply(Unsigned16 b)212 void multiply(Unsigned16 b) { 213 // divide the left into 4 32 bit chunks 214 long[] left = new long[4]; 215 left[0] = lo8 & 0xffffffffl; 216 left[1] = lo8 >>> 32; 217 left[2] = hi8 & 0xffffffffl; 218 left[3] = hi8 >>> 32; 219 // divide the right into 5 31 bit chunks 220 long[] right = new long[5]; 221 right[0] = b.lo8 & 0x7fffffffl; 222 right[1] = (b.lo8 >>> 31) & 0x7fffffffl; 223 right[2] = (b.lo8 >>> 62) + ((b.hi8 & 0x1fffffffl) << 2); 224 right[3] = (b.hi8 >>> 29) & 0x7fffffffl; 225 right[4] = (b.hi8 >>> 60); 226 // clear the cur value 227 set(0); 228 Unsigned16 tmp = new Unsigned16(); 229 for(int l=0; l < 4; ++l) { 230 for (int r=0; r < 5; ++r) { 231 long prod = left[l] * right[r]; 232 if (prod != 0) { 233 int off = l*32 + r*31; 234 tmp.set(prod); 235 tmp.shiftLeft(off); 236 add(tmp); 237 } 238 } 239 } 240 } 241 242 /** 243 * Add the given number into the current number. 244 * @param b the other number 245 */ add(Unsigned16 b)246 public void add(Unsigned16 b) { 247 long sumHi; 248 long sumLo; 249 long reshibit, hibit0, hibit1; 250 251 sumHi = hi8 + b.hi8; 252 253 hibit0 = (lo8 & 0x8000000000000000L); 254 hibit1 = (b.lo8 & 0x8000000000000000L); 255 sumLo = lo8 + b.lo8; 256 reshibit = (sumLo & 0x8000000000000000L); 257 if ((hibit0 & hibit1) != 0 | ((hibit0 ^ hibit1) != 0 && reshibit == 0)) 258 sumHi++; /* add carry bit */ 259 hi8 = sumHi; 260 lo8 = sumLo; 261 } 262 263 /** 264 * Shift the number a given number of bit positions. The number is the low 265 * order bits of the result. 266 * @param bits the bit positions to shift by 267 */ shiftLeft(int bits)268 public void shiftLeft(int bits) { 269 if (bits != 0) { 270 if (bits < 64) { 271 hi8 <<= bits; 272 hi8 |= (lo8 >>> (64 - bits)); 273 lo8 <<= bits; 274 } else if (bits < 128) { 275 hi8 = lo8 << (bits - 64); 276 lo8 = 0; 277 } else { 278 hi8 = 0; 279 lo8 = 0; 280 } 281 } 282 } 283 284 @Override readFields(DataInput in)285 public void readFields(DataInput in) throws IOException { 286 hi8 = in.readLong(); 287 lo8 = in.readLong(); 288 } 289 290 @Override write(DataOutput out)291 public void write(DataOutput out) throws IOException { 292 out.writeLong(hi8); 293 out.writeLong(lo8); 294 } 295 296 297 } 298