/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.examples.terasort; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import org.apache.hadoop.io.Writable; /** * An unsigned 16 byte integer class that supports addition, multiplication, * and left shifts. */ class Unsigned16 implements Writable { private long hi8; private long lo8; public Unsigned16() { hi8 = 0; lo8 = 0; } public Unsigned16(long l) { hi8 = 0; lo8 = l; } public Unsigned16(Unsigned16 other) { hi8 = other.hi8; lo8 = other.lo8; } @Override public boolean equals(Object o) { if (o instanceof Unsigned16) { Unsigned16 other = (Unsigned16) o; return other.hi8 == hi8 && other.lo8 == lo8; } return false; } @Override public int hashCode() { return (int) lo8; } /** * Parse a hex string * @param s the hex string */ public Unsigned16(String s) throws NumberFormatException { set(s); } /** * Set the number from a hex string * @param s the number in hexadecimal * @throws NumberFormatException if the number is invalid */ public void set(String s) throws NumberFormatException { hi8 = 0; lo8 = 0; final long lastDigit = 0xfl << 60; for (int i = 0; i < s.length(); ++i) { int digit = getHexDigit(s.charAt(i)); if ((lastDigit & hi8) != 0) { throw new NumberFormatException(s + " overflowed 16 bytes"); } hi8 <<= 4; hi8 |= (lo8 & lastDigit) >>> 60; lo8 <<= 4; lo8 |= digit; } } /** * Set the number to a given long. * @param l the new value, which is treated as an unsigned number */ public void set(long l) { lo8 = l; hi8 = 0; } /** * Map a hexadecimal character into a digit. * @param ch the character * @return the digit from 0 to 15 * @throws NumberFormatException */ private static int getHexDigit(char ch) throws NumberFormatException { if (ch >= '0' && ch <= '9') { return ch - '0'; } if (ch >= 'a' && ch <= 'f') { return ch - 'a' + 10; } if (ch >= 'A' && ch <= 'F') { return ch - 'A' + 10; } throw new NumberFormatException(ch + " is not a valid hex digit"); } private static final Unsigned16 TEN = new Unsigned16(10); public static Unsigned16 fromDecimal(String s) throws NumberFormatException { Unsigned16 result = new Unsigned16(); Unsigned16 tmp = new Unsigned16(); for(int i=0; i < s.length(); i++) { char ch = s.charAt(i); if (ch < '0' || ch > '9') { throw new NumberFormatException(ch + " not a valid decimal digit"); } int digit = ch - '0'; result.multiply(TEN); tmp.set(digit); result.add(tmp); } return result; } /** * Return the number as a hex string. */ public String toString() { if (hi8 == 0) { return Long.toHexString(lo8); } else { StringBuilder result = new StringBuilder(); result.append(Long.toHexString(hi8)); String loString = Long.toHexString(lo8); for(int i=loString.length(); i < 16; ++i) { result.append('0'); } result.append(loString); return result.toString(); } } /** * Get a given byte from the number. * @param b the byte to get with 0 meaning the most significant byte * @return the byte or 0 if b is outside of 0..15 */ public byte getByte(int b) { if (b >= 0 && b < 16) { if (b < 8) { return (byte) (hi8 >> (56 - 8*b)); } else { return (byte) (lo8 >> (120 - 8*b)); } } return 0; } /** * Get the hexadecimal digit at the given position. * @param p the digit position to get with 0 meaning the most significant * @return the character or '0' if p is outside of 0..31 */ public char getHexDigit(int p) { byte digit = getByte(p / 2); if (p % 2 == 0) { digit >>>= 4; } digit &= 0xf; if (digit < 10) { return (char) ('0' + digit); } else { return (char) ('A' + digit - 10); } } /** * Get the high 8 bytes as a long. */ public long getHigh8() { return hi8; } /** * Get the low 8 bytes as a long. */ public long getLow8() { return lo8; } /** * Multiple the current number by a 16 byte unsigned integer. Overflow is not * detected and the result is the low 16 bytes of the result. The numbers * are divided into 32 and 31 bit chunks so that the product of two chucks * fits in the unsigned 63 bits of a long. * @param b the other number */ void multiply(Unsigned16 b) { // divide the left into 4 32 bit chunks long[] left = new long[4]; left[0] = lo8 & 0xffffffffl; left[1] = lo8 >>> 32; left[2] = hi8 & 0xffffffffl; left[3] = hi8 >>> 32; // divide the right into 5 31 bit chunks long[] right = new long[5]; right[0] = b.lo8 & 0x7fffffffl; right[1] = (b.lo8 >>> 31) & 0x7fffffffl; right[2] = (b.lo8 >>> 62) + ((b.hi8 & 0x1fffffffl) << 2); right[3] = (b.hi8 >>> 29) & 0x7fffffffl; right[4] = (b.hi8 >>> 60); // clear the cur value set(0); Unsigned16 tmp = new Unsigned16(); for(int l=0; l < 4; ++l) { for (int r=0; r < 5; ++r) { long prod = left[l] * right[r]; if (prod != 0) { int off = l*32 + r*31; tmp.set(prod); tmp.shiftLeft(off); add(tmp); } } } } /** * Add the given number into the current number. * @param b the other number */ public void add(Unsigned16 b) { long sumHi; long sumLo; long reshibit, hibit0, hibit1; sumHi = hi8 + b.hi8; hibit0 = (lo8 & 0x8000000000000000L); hibit1 = (b.lo8 & 0x8000000000000000L); sumLo = lo8 + b.lo8; reshibit = (sumLo & 0x8000000000000000L); if ((hibit0 & hibit1) != 0 | ((hibit0 ^ hibit1) != 0 && reshibit == 0)) sumHi++; /* add carry bit */ hi8 = sumHi; lo8 = sumLo; } /** * Shift the number a given number of bit positions. The number is the low * order bits of the result. * @param bits the bit positions to shift by */ public void shiftLeft(int bits) { if (bits != 0) { if (bits < 64) { hi8 <<= bits; hi8 |= (lo8 >>> (64 - bits)); lo8 <<= bits; } else if (bits < 128) { hi8 = lo8 << (bits - 64); lo8 = 0; } else { hi8 = 0; lo8 = 0; } } } @Override public void readFields(DataInput in) throws IOException { hi8 = in.readLong(); lo8 = in.readLong(); } @Override public void write(DataOutput out) throws IOException { out.writeLong(hi8); out.writeLong(lo8); } }