JavaでBase32のデコーダーを実装

スポンサーリンク

今書いてるプログラムでBase32エンコードされた文字列をデコードをする必要があったんですが、Java標準ライブラリにそういうクラスは無く、Apache Commonsで提供されているみたいですが、練習を兼ねて車輪の再発明をしました。

エンコードの仕組みについてはこちらのサイトを参考にさせて頂きました。
base32 についてメモ
ここで書かれていることの逆を実装していきます。

作ったクラスのコードはこちら
https://github.com/yoshi-self/Base32Decoder/blob/master/com/yoshi_self/Base32Decoder.java

以下解説

Base32エンコードは40bit(5byte)単位で8文字に変換される

なので8文字単位でデコードしていき最後につなげます。

// エンコード文字列を8で割ってを5を掛けたサイズのバッファを作る
int bufferSize = this.source.length / this.DECODE_UNIT * this.ENCODE_UNIT;
ByteBuffer resultBuffer = ByteBuffer.allocate(bufferSize);

// 8byteごとにデコードしてバッファに入れる
for(this.pos = 0; this.pos < this.source.length; this.pos += this.DECODE_UNIT) {
    byte[] unit = decodeUnit();
    resultBuffer.put(unit);
}

40bit(8byte)をデコード

40bitのBitSetを作成します

int bitSize =  this.DECODE_UNIT * this.ENCODE_UNIT;
BitSet bitSet = new BitSet(bitSize);

ABCDEFGHIJKLMNOPQRSTUVWXYZ234567の配列を作成しておき、1バイトごとに対応する数値を取ります。

byte b = this.source[i];
int n = TABLE.indexOf(b);

この数値の下位5ビットをBitSetにセットします。

for(int j = 0; j < this.ENCODE_UNIT; ++j) {
    if( ( (n >>> (this.ENCODE_UNIT - 1 - j)) & 1 ) != 0) {
        bitSet.set(bitPos + j);
    }
}

5byteのbyte[]を作り、BitSetと同じ並びにビットをセットします。(BitSet.toByteArray()を使うと順番がおかしくなります)

byte[] unitBytes = new byte[this.ENCODE_UNIT];

for(int i = 0; i < bitSize; ++i) {
    int bit = bitSet.get(i) ? 1 : 0;
    unitBytes[i/this.DECODE_UNIT] |= (bit << (this.DECODE_UNIT -1 - i % this.DECODE_UNIT));
}

return unitBytes;

結果を返す

最初charsetとか考えず5の倍数バイト全部返してて、それじゃダメだと気づいて0のバイト捨てるように直したんですが、更に考えてみると、オリジナルの文字コードが何バイトで意味をもつかわからないので結局charsetを指定させる実装にしました。

String result = new String(resultBuffer.array(), charset);

// find last index not null and return until it
int lastIdx = result.length() - 1;
for(int i = lastIdx; i >= 1; --i) {
    if(result.charAt(i) == '\000') {
        lastIdx = i - 1;
    }
    else {
        break;
    }
}
return result.substring(0, lastIdx + 1);

っていうかこのブログのコード表示ブロックの記号がエスケープされたままになってる…WordPressが自動エスケープしたあとpreとかで出てるからだろうか…そのうち直します。。

スポンサーリンク

シェアする

フォローする