29ビット以上のint値にも対応できるようにSimple9を調整
Simple-9について解説 - tsubosakaの日記
http://ameblo.jp/th0083/entry-10483740192.html
Simple9は、整数列を高速に圧縮するアルゴリズムです。32bitのint値を上位4bitと下位28bitに分けて、下位28bitにデータをできるだけ多く詰め込み、上位4bitで詰め込みのパターンを表現します。パターンは全部で9種類あります。
上位4bit | パターン |
---|---|
0000 | 1bit x 28 |
0001 | 2bit x 14 |
0010 | 3bit x 9 |
0011 | 4bit x 7 |
0100 | 5bit x 5 |
0101 | 7bit x 4 |
0110 | 9bit x 3 |
0111 | 14bit x 2 |
1000 | 28bit x 1 |
ただしこのレイアウトでは、圧縮対象の整数列の中に29bit以上の数値が入っている場合に対応できません。そこで、詰め込み方を少し変更してみました。
上位4bit | パターン |
---|---|
0000 | 1bit x 28 |
0001 | 2bit x 14 |
0010 | 3bit x 9 |
0011 | 4bit x 7 |
0100 | 5bit x 5 |
0101 | 7bit x 4 |
0110 | 9bit x 3 |
0111 | 14bit x 2 |
10XX | 30bit x 1(XXのビットも使って30bitを表現する) |
1100 | ここでの残りビットは捨て、次の位置に32bit x 1を置く |
この表のように、上位4bitのうち2bitを使うことで30bitまでは詰め込めるようにしてみました。
以下、プログラムです。パフォーマンス重視のためTroveを使い、全部のパターンをハードコーディングしました。
import gnu.trove.TIntArrayList; public class SafeSimple9Codec { public int[] encode(int[] array) { // 再割り当てを避けるため初期サイズを大きめに取る TIntArrayList result = new TIntArrayList(array.length); int len = array.length; int i = 0, n = 0, code = 0, v = 0; while (i < len) { // 1bit x 28 for (n = 0, code = 0; n < 28 && i + n < len && ((v = array[i + n]) & ~0x1) == 0; n++) { code = (code << 1) | v; } if (n == 28) { result.add(0x00000000 | code); i += n; continue; } // 2bit x 14 for (n = 0, code = 0; n < 14 && i + n < len && ((v = array[i + n]) & ~0x3) == 0; n++) { code = (code << 2) | v; } if (n == 14) { result.add(0x10000000 | code); i += n; continue; } // 3bit x 9 for (n = 0, code = 0; n < 9 && i + n < len && ((v = array[i + n]) & ~0x7) == 0; n++) { code = (code << 3) | v; } if (n == 9) { result.add(0x20000000 | code); i += n; continue; } // 4bit x 7 for (n = 0, code = 0; n < 7 && i + n < len && ((v = array[i + n]) & ~0xf) == 0; n++) { code = (code << 4) | v; } if (n == 7) { result.add(0x30000000 | code); i += n; continue; } // 5bit x 5 for (n = 0, code = 0; n < 5 && i + n < len && ((v = array[i + n]) & ~0x1f) == 0; n++) { code = (code << 5) | v; } if (n == 5) { result.add(0x40000000 | code); i += n; continue; } // 7bit x 4 for (n = 0, code = 0; n < 4 && i + n < len && ((v = array[i + n]) & ~0x7f) == 0; n++) { code = (code << 7) | v; } if (n == 4) { result.add(0x50000000 | code); i += n; continue; } // 9bit x 3 for (n = 0, code = 0; n < 3 && i + n < len && ((v = array[i + n]) & ~0x1ff) == 0; n++) { code = (code << 9) | v; } if (n == 3) { result.add(0x60000000 | code); i += n; continue; } // 14bit x 2 for (n = 0, code = 0; n < 2 && i + n < len && ((v = array[i + n]) & ~0x3fff) == 0; n++) { code = (code << 14) | v; } if (n == 2) { result.add(0x70000000 | code); i += n; continue; } // 30bit x 1 if (((v = array[i]) & ~0x3fffffff) == 0) { result.add(0x80000000 | v); i++; continue; } // (over 30 bit) result.add(0xc0000000); result.add(v); i++; } return result.toNativeArray(); } public int[] decode(int[] array) { // 再割り当てを避けるため初期サイズを大きめに取る TIntArrayList result = new TIntArrayList(array.length); int len = array.length; for (int i = 0; i < len; i++) { int v = array[i]; int selector = (v >> 28) & 0xf; switch (selector) { case 0x0: // 1bit x 28 result.add((v >> 27) & 0x1); result.add((v >> 26) & 0x1); result.add((v >> 25) & 0x1); result.add((v >> 24) & 0x1); result.add((v >> 23) & 0x1); result.add((v >> 22) & 0x1); result.add((v >> 21) & 0x1); result.add((v >> 20) & 0x1); result.add((v >> 19) & 0x1); result.add((v >> 18) & 0x1); result.add((v >> 17) & 0x1); result.add((v >> 16) & 0x1); result.add((v >> 15) & 0x1); result.add((v >> 14) & 0x1); result.add((v >> 13) & 0x1); result.add((v >> 12) & 0x1); result.add((v >> 11) & 0x1); result.add((v >> 10) & 0x1); result.add((v >> 9) & 0x1); result.add((v >> 8) & 0x1); result.add((v >> 7) & 0x1); result.add((v >> 6) & 0x1); result.add((v >> 5) & 0x1); result.add((v >> 4) & 0x1); result.add((v >> 3) & 0x1); result.add((v >> 2) & 0x1); result.add((v >> 1) & 0x1); result.add(v & 0x1); break; case 0x1: // 2bit x 14 result.add((v >> 26) & 0x3); result.add((v >> 24) & 0x3); result.add((v >> 22) & 0x3); result.add((v >> 20) & 0x3); result.add((v >> 18) & 0x3); result.add((v >> 16) & 0x3); result.add((v >> 14) & 0x3); result.add((v >> 12) & 0x3); result.add((v >> 10) & 0x3); result.add((v >> 8) & 0x3); result.add((v >> 6) & 0x3); result.add((v >> 4) & 0x3); result.add((v >> 2) & 0x3); result.add(v & 0x3); break; case 0x2: // 3bit x 9 result.add((v >> 24) & 0x7); result.add((v >> 21) & 0x7); result.add((v >> 18) & 0x7); result.add((v >> 15) & 0x7); result.add((v >> 12) & 0x7); result.add((v >> 9) & 0x7); result.add((v >> 6) & 0x7); result.add((v >> 3) & 0x7); result.add(v & 0x7); break; case 0x3: // 4bit x 7 result.add((v >> 24) & 0xf); result.add((v >> 20) & 0xf); result.add((v >> 16) & 0xf); result.add((v >> 12) & 0xf); result.add((v >> 8) & 0xf); result.add((v >> 4) & 0xf); result.add(v & 0xf); break; case 0x4: // 5bit x 5 result.add((v >> 20) & 0x1f); result.add((v >> 15) & 0x1f); result.add((v >> 10) & 0x1f); result.add((v >> 5) & 0x1f); result.add(v & 0x1f); break; case 0x5: // 7bit x 4 result.add((v >> 21) & 0x7f); result.add((v >> 14) & 0x7f); result.add((v >> 7) & 0x7f); result.add(v & 0x7f); break; case 0x6: // 9bit x 3 result.add((v >> 18) & 0x1ff); result.add((v >> 9) & 0x1ff); result.add(v & 0x1ff); break; case 0x7: // 14bit x 2 result.add((v >> 14) & 0x3fff); result.add(v & 0x3fff); break; case 0xc: // (over 30bit) result.add(array[i + 1]); i++; break; default: // 30bit x 1 result.add(v & 0x3fffffff); break; } } return result.toNativeArray(); } }