├── .gitignore ├── README.md ├── composer.json └── src ├── Bucket ├── Bucket.php └── ModBucket.php ├── Digest ├── BKDRDigest.php ├── DEKDigest.php ├── DJBDigest.php ├── Digest.php ├── ELFDigest.php ├── FNVDigest.php ├── JSDigest.php ├── PJWDigest.php └── SDBMDigest.php ├── Filter.php └── FilterInterface.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .php_cs.cache 3 | composer.lock 4 | vendor 5 | test.php -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Bloom Filter 2 | 3 | ## install 4 | ```shell script 5 | composer require xiaker/bloom-filter 6 | ``` 7 | 8 | ## usage 9 | 10 | ```php 11 | ... 12 | 13 | use Xiaker\Bloom\Bucket\ModBucket; 14 | use Xiaker\Bloom\Digest\BKDRDigest; 15 | use Xiaker\Bloom\Digest\DEKDigest; 16 | use Xiaker\Bloom\Digest\DJBDigest; 17 | use Xiaker\Bloom\Digest\ELFDigest; 18 | use Xiaker\Bloom\Digest\FNVDigest; 19 | use Xiaker\Bloom\Digest\JSDigest; 20 | use Xiaker\Bloom\Digest\PJWDigest; 21 | use Xiaker\Bloom\Digest\SDBMDigest; 22 | use Xiaker\Bloom\Filter; 23 | 24 | $redis = new Redis(); 25 | $redis->connect('127.0.0.1'); 26 | 27 | $digests = [ // you can select several or all of them 28 | new BKDRDigest(), 29 | new DEKDigest(), 30 | new DJBDigest(), 31 | new ELFDigest(), 32 | new FNVDigest(), 33 | new JSDigest(), 34 | new PJWDigest(), 35 | new SDBMDigest(), 36 | ]; 37 | 38 | $filter = new Filter(new ModBucket(24), $redis, ...$digests); 39 | 40 | $filter->add('fatrbaby'); 41 | ... 42 | 43 | if ($filter->exists('fatrbaby')) { 44 | ... 45 | } 46 | ``` -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xiaker/bloom-filter", 3 | "description": "A bloom filter wrote by PHP & storaged by Redis", 4 | "type": "library", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "fatrbaby", 9 | "email": "fatrbaby@qq.com" 10 | } 11 | ], 12 | "require": { 13 | "php": ">=7.2", 14 | "ext-redis": "*" 15 | }, 16 | "require-dev": { 17 | "symfony/finder": "^5.1" 18 | }, 19 | "autoload": { 20 | "psr-4": { 21 | "Xiaker\\Bloom\\": "src" 22 | } 23 | }, 24 | "autoload-dev": { 25 | "psr-4": { 26 | "Tests\\": "tests" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Bucket/Bucket.php: -------------------------------------------------------------------------------- 1 | total = $total; 17 | } 18 | 19 | protected function warp($value) 20 | { 21 | // PSR-16 22 | return '___xiaker.'.$value; 23 | } 24 | 25 | abstract public function dispatch(string $key); 26 | } 27 | -------------------------------------------------------------------------------- /src/Bucket/ModBucket.php: -------------------------------------------------------------------------------- 1 | warp(crc32($key) % $this->total); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Digest/BKDRDigest.php: -------------------------------------------------------------------------------- 1 | > 27)) ^ ord($string[$i]); 22 | } 23 | 24 | return ($hash % 0xFFFFFFFF) & 0xFFFFFFFF; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Digest/DJBDigest.php: -------------------------------------------------------------------------------- 1 | > 24); 26 | } 27 | 28 | $hash &= ~$x; 29 | } 30 | 31 | return ($hash % 0xFFFFFFFF) & 0xFFFFFFFF; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Digest/FNVDigest.php: -------------------------------------------------------------------------------- 1 | > 2)); 22 | } 23 | 24 | return ($hash % 0xFFFFFFFF) & 0xFFFFFFFF; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Digest/PJWDigest.php: -------------------------------------------------------------------------------- 1 | > (int) ($threeQuarters))) & (~$highBits)); 32 | } 33 | 34 | return ($hash % 0xFFFFFFFF) & 0xFFFFFFFF; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Digest/SDBMDigest.php: -------------------------------------------------------------------------------- 1 | bucket = $bucket; 31 | $this->redis = $redis; 32 | $this->digests = $digests; 33 | } 34 | 35 | public function add(string $string) 36 | { 37 | $bucket = $this->bucket->dispatch($string); 38 | 39 | $pipe = $this->redis->multi(); 40 | foreach ($this->digests as $digest) { 41 | $pipe->setBit($bucket, $digest->hash($string), true); 42 | } 43 | 44 | return $pipe->exec(); 45 | } 46 | 47 | public function exists(string $string) 48 | { 49 | $bucket = $this->bucket->dispatch($string); 50 | 51 | $pipe = $this->redis->multi(); 52 | $length = strlen($string); 53 | 54 | foreach ($this->digests as $digest) { 55 | $pipe->getBit($bucket, $digest->hash($string, $length)); 56 | } 57 | 58 | $payloads = $pipe->exec(); 59 | 60 | foreach ($payloads as $bit) { 61 | if (0 == $bit) { 62 | return false; 63 | } 64 | } 65 | 66 | return true; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/FilterInterface.php: -------------------------------------------------------------------------------- 1 |