Getting the base-36 representation of a byte[8]
Hello all.
I am in the process of writing a _J2ME_ client for a properitary protocol transmitting stock data.
Simply put my problem is that I need to get the base-36 (0-9AZ) representation of a 64 bit _unsigned_ number (stored in a byte[8]).
To be more elaborate, I am encrypting a string using the Blowfish algorithm (from www.bouncycastle.org), and the resulting blocks are to be sent to the server as a base36 string representing the block.
Since I am on J2ME I do not have access to Sun's BigInteger, but Bouncycastle provides a lightweight one, however I think it is a bit buggy. Nor do I have access to the J2SE formatter classes.
Ideally I would want to do something like this:
Long.toString( (long)bytearray, 36 );
This of course do not work since
1) Jave does not support casting byte[] to long, this could however be fixed by bitshifting.
2) Long is always signed and I am really at a loss on how to do this.
Any ideas?
--
Regards Johan Seland
Is your byte array big endian or little endian? I'll assume little endian.
package base36;
import java.util.*;
/**
*
Single Sentence Description.
*
Long Description.
*
Copyright: © 2003
*
* @author Paul Murray
* @version 1.0
*/
public class Main
{
public static void main(String[] args) {
Main main1 = new Main();
main1.go();
}
void go() {
for (int i = 0; i < 13; i++) {
System.out.print("36 ^ " + i + " = ");
for (int j = 0; j < 8; j++) {
System.out.print(Integer.toHexString((((int)powers[i][j])>>4)&15));
System.out.print(Integer.toHexString(((int)powers[i][j])&15));
System.out.print(' ');
}
System.out.println(' ');
}
byte[] amt;
amt = new byte[] {0, 0, 0, 0, 0, 0, 0, 0};
dump(amt); System.out.println(" = " + cvt(amt));
amt = new byte[] {1, 0, 0, 0, 0, 0, 0, 0};
dump(amt); System.out.println(" = " + cvt(amt));
amt = new byte[] {0, 1, 0, 0, 0, 0, 0, 0};
dump(amt); System.out.println(" = " + cvt(amt));
amt = new byte[] { -1, -1, 0, 0, 0, 0, 0, 0};
dump(amt); System.out.println(" = " + cvt(amt));
amt = new byte[] { 0, 0, 1, 0, 0, 0, 0, 0};
dump(amt); System.out.println(" = " + cvt(amt));
amt = new byte[] { -1, -1, -1, -1, -1, -1, -1, -1};
dump(amt); System.out.println(" = " + cvt(amt));
amt = new byte[] { -2, -1, -1, -1, -1, -1, -1, -1};
dump(amt); System.out.println(" = " + cvt(amt));
}
void dump(byte[] d) {
for(int i=0;i<8;i++) {
System.out.print(Integer.toHexString((((int)d[i])>>4)&15));
System.out.print(Integer.toHexString(((int)d[i])&15));
System.out.print(' ');
}
}
/////////////////////////////////////////////////////////////////////////////
/**
* powers of 36. we use an array of short to nix sign extension. I happen to know that
* there are 13 powers of 36: n < 2^64
**/
static short[][] powers = new short[13][];
static {
// initialise the array of powers of 36
short[] p = new short[] {
1, 0, 0, 0, 0, 0, 0, 0};
int index = 0;
for (; ; ) {
powers[index] = new short[8];
System.arraycopy(p, 0, powers[index], 0, 8);
index++;
int carry = 0;
for (int i = 0; i < 8; i++) {
int mul = p[i] * 36 + carry;
p[i] = (short) (mul & 0xFF);
carry = mul >> 8;
}
if (carry != 0)
break;
}
assert index == 13:"there are exactly 13 64-bit powers of 36";
}
// use a single instance - not thread safe
short working[] = new short[8];
StringBuffer amt = new StringBuffer();
String cvt(byte[] n) {
if (n.length != 8)
throw new IllegalArgumentException("length of 8, please");
amt.setLength(0);
for (int i = 0; i < 8; i++)
working[i] = (short) ( ( (int) n[i]) & 0xFF);
for (int currentPower = 12; currentPower >= 0; currentPower--) {
int digit = 0;
while (lessThanOrEqualToWorking(powers[currentPower])) {
digit++;
subtractFromWorking(powers[currentPower]);
}
if (digit < 10) {
amt.append((char)('0' + digit));
}
else {
amt.append((char)('A' + digit - 10));
}
}
return amt.toString();
}
boolean lessThanOrEqualToWorking(short[] p) {
assert p.length == 8;
for (int i = 7; i >= 0; i--) {
if (p[i] > working[i])
return false;
if (p[i] < working[i])
return true;
}
return true;
}
void subtractFromWorking(short[] p) {
assert p.length == 8;
boolean carry = false;
for (int i = 0; i < 8; i++) {
if (carry)
working[i]--;
working[i] -= p[i];
if (working[i] < 0) {
working[i] += 256;
carry = true;
}
else {
carry = false;
}
}
assert!carry:"param must be < working";
}
}
Hey Johan,
how about this one?
class Test {
public static void main(String[] args) {
char c;
byte[] bytes = new byte[] { 1, 3, 7, 15, 31, 63, 127, 85, 1, 3, 7, 15, 31, 63, 127, 85};
System.out.println(toBase36(bytes));
}
public static String toBase36(byte[] bytes) {
//can provide a (byte[], offset, length) method too
StringBuffer sb = new StringBuffer();
int bitsUsed = 0; //will point how many bits from the int are to be encoded
int temp = 0;
int tempBits = 0;
long swap;
int position = 0;
while ((position < bytes.length) || (bitsUsed != 0)) {
swap = 0;
if (tempBits > 0) {
//there are bits left over from previous iteration
swap = temp;
bitsUsed = tempBits;
tempBits = 0;
}
//fill some bytes
while ((position < bytes.length) && (bitsUsed < 36)) {
swap <<= 8;
swap |= bytes[position++];
bitsUsed += 8;
}
if (bitsUsed > 36) {
tempBits = bitsUsed - 36; //this is always 4
temp = (int)(swap & ((1 << tempBits) - 1)); //get low bits
swap >>= tempBits; //remove low bits
bitsUsed = 36;
}
sb.append(Long.toString(swap, 36));
bitsUsed = 0;
}
return sb.toString();
}
}
It translates 36 bits a time. They do fit into a long :-)
ounosa at 2007-7-14 20:21:43 >
