package au.gov.amsa.util; public final class SixBit { /** Precompiled list of int to six bit mappings. */ private static final int[] INT_TO_SIX_BIT = createIntToSixBit(); private static int[] createIntToSixBit() { int[] toSixbit = new int[256 * 256]; // we actually only use 256, but we // parse chars around instead of // bytes for (int chr = 0; chr < toSixbit.length; chr++) { if (chr < 48 || chr > 119 || chr > 87 && chr < 96) { toSixbit[chr] = -1; } else if (chr < 0x60) { toSixbit[chr] = chr - 48 & 0x3F; } else { toSixbit[chr] = chr - 56 & 0x3F; } } return toSixbit; } /** * Converts sixBit string characters to bits (boolean values in the array * bitSet) but just between the bit range {@code from} to {@code to} * exclusive. * * @param str * @param padBits * @param bitSet * @param calculated * @param from * @param to */ public static void convertSixBitToBits(String str, int padBits, boolean bitSet[], boolean[] calculated, int from, int to) { if (str.length() == 0) return; int index = from - from % 6; int strFrom = from / 6; int slen = str.length() - 1; int strTo = to / 6 + 1; for (int i = strFrom; i < Math.min(strTo, slen); i++) { if (!calculated[i]) { char chr = str.charAt(i); int binVal = INT_TO_SIX_BIT[chr]; if (binVal == -1) { throw new SixBitException("Illegal sixbit ascii char: " + chr); } bitSet[index] = (binVal & 32) > 0; bitSet[index + 1] = (binVal & 16) > 0; bitSet[index + 2] = (binVal & 8) > 0; bitSet[index + 3] = (binVal & 4) > 0; bitSet[index + 4] = (binVal & 2) > 0; bitSet[index + 5] = (binVal & 1) > 0; calculated[i] = true; } index += 6; } if (strTo > slen) { // Process the last char which might be padded char chr = str.charAt(slen); int binVal = INT_TO_SIX_BIT[chr]; if (binVal == -1) { throw new SixBitException("Illegal sixbit ascii char: " + chr); } int bits = 6 - padBits; switch (bits) { case 6: bitSet[index + 5] = (binVal & 1) > 0; case 5: bitSet[index + 4] = (binVal & 2) > 0; case 4: bitSet[index + 3] = (binVal & 4) > 0; case 3: bitSet[index + 2] = (binVal & 8) > 0; case 2: bitSet[index + 1] = (binVal & 16) > 0; case 1: bitSet[index] = (binVal & 32) > 0; } calculated[slen] = true; } } public static long getValue(int from, int to, boolean[] bitSet) { if (to > bitSet.length) { throw new SixBitException(bitSet.length + " is not enough bits. At least " + to + " expected."); } long val = 0; long powMask = 1; for (int i = to - 1; i >= from; i--) { if (bitSet[i]) { val += powMask; } powMask <<= 1; } return val; } public static long getSignedValue(int from, int to, boolean[] bitSet) { if (to > bitSet.length) { throw new SixBitException(bitSet.length + " is not enough bits. At least " + to + " expected."); } long val = 0; long powMask = 1; for (int i = to - 1; i >= from; i--) { if (bitSet[i]) { val += powMask; } powMask <<= 1; } if (bitSet[from]) { val = val - powMask; } return val; } public static String getString(int from, int to, boolean[] bitSet) { int len = (to - from) / 6; char[] resStr = new char[len]; int pos = from; int i; for (i = 0; i < len; i++) { // Note that SixBit.getValue() should never return a value > 63 // because it is only using 6 bits so intToAscii should never throw // a SixBitException. char ch = (char) intToAscii((char) SixBit.getValue(pos, pos + 6, bitSet)); // stops at the first instance of @ character if (ch == '@') { len = i; break; } resStr[i] = ch; pos += 6; } // remove trailing @ characters and spaces while (len > 0 && (resStr[len - 1] == ' ')) len -= 1; return new String(resStr, 0, len); } /** * Convert six bit int value to character * * @param val * @return */ private static int intToAscii(int val) { if (val > 63) { throw new SixBitException("Char value " + val + " not allowed"); } else if (val < 32) { return val + 64; } else { return val; } } }