cdbのJavaポーティング
sg-cdbっていうcdbのJavaポーティングがあったので、試してみたら、あまり早くなかった。
で、ソースを見たら普通に java.io.RandomAccessFile を使っていて、いかにも遅そう。なので、オリジナルの cdb を元に java.nio.MappedByteBuffer を使ったものに書き換えてみたら5倍くらい早くなった。
なんか、ファイルサイズが2GB超えるとやばい気もするけど、とりあえず作った読み込み部分をさらしておこう。へんなところがあったら誰かツッコミよろ。
package cdb; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; public class Cdb { private static final int HASHSTART = 5381; /** map for CDB file */ private ByteBuffer map; /** CDB File */ private RandomAccessFile file; /** number of hash slots searched under this key */ private int loop; /** initialized if loop is nonzero */ private int khash; private int kpos; private int hpos; private int hslots; public Cdb(String filename) throws IOException { file = new RandomAccessFile(filename, "r"); FileChannel channel = file.getChannel(); map = channel.map(MapMode.READ_ONLY, 0, file.length()); map.order(ByteOrder.LITTLE_ENDIAN); } public void close() { try { file.close(); file = null; map = null; } catch (IOException ex) { } } public void findstart() { loop = 0; } public byte[] findnext(byte[] key, int offset, int len) { int u; int dlen; if (loop == 0) { u = hash(key, offset, len); map.position((u << 3) & 2047); hpos = map.getInt(); hslots = map.getInt(); if (hslots == 0) { return null; } khash = u; u >>>= 8; u %= hslots; u <<= 3; kpos = hpos + u; } while (loop < hslots) { int pos; map.position(kpos); u = map.getInt(); pos = map.getInt(); if (pos == 0) { return null; } loop++; kpos += 8; if (kpos == hpos + (hslots << 3)) { kpos = hpos; } if (u == khash) { map.position(pos); u = map.getInt(); if (u == len) { dlen = map.getInt(); byte[] keyInDb = new byte[len]; map.get(keyInDb, 0, len); if (!match(key, offset, len, keyInDb)) { return null; } byte[] data = new byte[dlen]; map.get(data, 0, dlen); return data; } } } return null; } public byte[] findnext(byte[] key) { return findnext(key, 0, key.length); } public byte[] find(byte[] key, int offset, int len) { findstart(); return findnext(key, offset, len); } public byte[] find(byte[] key) { return find(key, 0, key.length); } private static int hash(byte[] buffer, int offset, int len) { long h = HASHSTART; final long mask = 0x00000000ffffffffL; for (int i = 0; i < len; i++) { h = (h + (h << 5) & mask) & mask; h = h ^ ((buffer[offset + i] + 0x100) & 0xff); } return (int) (h & mask); } private static boolean match(byte[] key, int offset, int len, byte[] keyInDb) { for (int i = 0; i < len; i++) { if (key[offset + i] != keyInDb[i]) { return false; } } return true; } }