/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package com.github.geophile.erdo.bloomfilter; import com.github.geophile.erdo.memorymonitor.MemoryTracker; import java.nio.ByteBuffer; import static java.lang.Math.*; /* * error rate bits/key hash functions * * 0.0001 19.2 13 * 0.0005 15.8 10 * 0.0010 14.4 10 * 0.0050 11.0 8 * 0.0100 9.6 6 * 0.0500 6.2 4 * */ public class BloomFilter implements MemoryTracker.Trackable<BloomFilter> { // MemoryTracker.Trackable interface public long sizeBytes() { return map.length; } // BloomFilter interface public boolean maybePresent(int hashCode) { for (int h = 0; h < nHashes; h++) { int position = position(hashCode, h); if ((map[position / 8] & (1 << (position % 8))) == 0) { return false; } } return true; } public void add(int hashCode) { for (int h = 0; h < nHashes; h++) { int position = position(hashCode, h); map[position / 8] |= (1 << (position % 8)); } } public void write(ByteBuffer buffer) { buffer.putInt(nBits); buffer.putInt(nHashes); buffer.putInt(map.length); buffer.put(map); } /** * @param nRecords Number of records to accomodate. * @param errorRate Allowable false positive rate. */ public BloomFilter(int nRecords, double errorRate) { // Formulas from http://en.wikipedia.org/wiki/Bloom_filter this.nBits = (int) round(-nRecords * log(errorRate) / pow(log(2), 2)); this.nHashes = (int) round((nBits / nRecords) * log(2)); this.map = new byte[(nBits + 7) / 8]; } public static BloomFilter read(ByteBuffer buffer) { int nBits = buffer.getInt(); int nHashes = buffer.getInt(); int size = buffer.getInt(); byte[] map = new byte[size]; buffer.get(map); return new BloomFilter(nBits, nHashes, map); } private int position(int hash, int h) { hash *= Primes.PRIMES[h]; if (hash < 0) { hash = -hash; } if (hash < 0) { // Integer.MIN_VALUE! hash = 0; } return hash % nBits; } private BloomFilter(int nBits, int nHashes, byte[] map) { this.nBits = nBits; this.nHashes = nHashes; this.map = map; } public static final boolean USE_BLOOM_FILTER = true; private final int nBits; private final int nHashes; private final byte[] map; }