定制 toflar/fast-set 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

toflar/fast-set

Composer 安装命令:

composer require toflar/fast-set

包简介

Provides the means to create memory-efficient precompiled sets with very fast fingerprint lookups

README 文档

README

FastSet is a tiny, read-only, memory-efficient membership set for PHP.

It answers exactly one question:

Does this string exist in my set?

…and it does so very fast and with very little memory, even for large sets.

Why FastSet exists

PHP’s native arrays are extremely fast for lookups, but they are also very memory-hungry.
Storing large dictionaries (100k+ strings) quickly becomes impractical, especially when you need multiple sets at once.

FastSet is designed for cases where:

  • The dictionary is static (no inserts, deletes, or updates).
  • You only need has(string $entry): bool.
  • Memory usage matters.
  • Lookup speed must be predictable and fast.

Typical use cases:

  • Linguistic dictionaries (e.g. word decomposition)
  • Large allow/deny lists
  • Static vocabularies
  • Read-only validation sets

Design overview

FastSet uses a sorted fingerprint blob instead of storing the original strings.

Build time

  1. Read to original dictionary built using the SetBuilder
  2. Hash each entry using the configured hash
  3. Sort all fingerprints.
  4. Write two files:
    • hashes_<hash_algorithm>.bin – concatenated fingerprints (without the 2-byte prefixes for maximum compression)
    • index_<hash_algorithm>.bin – a 2-byte prefix index (65,537 offsets)

Lookup time

  1. Hash the input string with the same algorithm.
  2. Use the first 2 bytes of the hash to select a small bucket.
  3. Binary-search only inside that bucket.

Because hash prefixes are uniformly distributed, buckets are tiny. Real world tests on the fixture file that contains ~134,000 entries in this repository (tests/Fixtures/terms_de.txt) using the xxh3 hash algorithm:

  • Used buckets: 57,112 (≈ 87.2%)
  • Average non-empty bucket size: 2.36 entries
  • Worst case bucket size (biggest bucket): 11 entries
  • Worst case comparisons: log₂(11) = 4

Of course, the bigger your dictionary, the bigger the individual buckets.

All comparisons are fixed-width, not variable-length UTF-8 strings.

Properties

  • Very low memory usage
  • Predictable lookup cost
  • No PHP hash tables
  • No database
  • No dependencies
  • ⚠️ Read-only
  • ⚠️ Hash collisions are theoretically possible
    (acceptable for many linguistic and heuristic use cases)

Installation

composer require toflar/fast-set

Usage

1. Build the set (one-time)

Hashes are an excellent tool for evenly distributing entries, which is exactly what makes lookups in FastSet extremely fast. However, hashes are not well suited for distribution:

  • They are often larger than the original terms (especially for short words).
  • They are effectively random, which means gzip compression performs very poorly.

Shipping prebuilt hash files would therefore often mean shipping more data than the original dictionary.

The solution: The SetBuilder

This library ships with a SetBuilder that is designed specifically for distribution size efficiency. Instead of shipping hashes, you ship a compressed dictionary that:

  • exploits shared prefixes between terms
  • avoids repeating identical prefixes
  • compresses extremely well with gzip

The hash-based data structures are then generated locally at build time.

$myOriginalSet = __DIR__ . '/dictionary.txt'; // one entry per line

// Encode/Compress with the prefix algorithm:
SetBuilder::buildSet($myOriginalSet, './compressed.txt');

// Encode/Compress with the prefix algorithm and gzip on top (the .gz suffix determines that):
SetBuilder::buildSet($myOriginalSet, './compressed.gz');

// If your dictionary is not a line-feed separated file, but you have an array, you can also build it from an
// array directly:
SetBuilder::buildFromArray($myArray, './compressed.gz');

// You can use either xxh3 (default) or xxh128
// xxh128 will have a smaller probability for false-positives but require more memory
$hashAlgorithm = 'xxh3';

// You then ship either "compressed.txt" or "compressed.gz" with your application. Instantiating 
// is then done as follows:
$set = new FastSet(__DIR__ . '/dict', $hashAlgorithm);
$set->build(__DIR__ . '/compressed.(txt|gz)'); // Must be a file built using the SetBuilder

Calling build creates the following files on-the-fly:

dict/
├── hashes_<hash_algorithm>.bin
└── index_<hash_algorithm>.bin

Important: Do not ship hashes_<hash_algorithm>.bin or index_<hash_algorithm>.bin. Only ship the compressed dictionary created by the SetBuilder.

How to choose the right hashing algorithm

This library only supports the use of xxHash. Why? Because it's extremely fast. It does, however, not make sense to allow all versions of xxHash. Hence, only xxh3 (64bit) and xxh128 (128bit) are supported.

xxh3 will almost always be the right choice for you. That's why it's the default. But decide for yourself:

Collision probability by hash size for 100_000 terms:

Hash Bits Probability of ≥1 collision Interpretation
xxh32 32 ~0.31 (31%) ❌ Unacceptable
xxh3 64 ~2.7 × 10⁻¹⁰ ~1 in 3.7 billion
xxh128 128 ~1.5 × 10⁻²⁹ Effectively zero

Collision probability by hash size for 500_000 terms:

Hash Bits Probability of ≥1 collision Interpretation
xxh32 32 ~1.0 ❌ Guaranteed collisions
xxh3 64 ~6.8 × 10⁻⁹ ~1 in 147 million
xxh128 128 ~3.7 × 10⁻²⁸ Effectively zero

Which algorithm to choose depends on the risk of collisions you want to take but as long as you are < 1 million terms, 64-bit is astronomically safe. Maybe this is a good rule of thumb:

Use xxh3 by default. Switch to xxh128 if your set contains millions of terms, or you need essentially zero collision risk.

2. Lookup

Once you have initialized/built your FastSet calling build() so that the required files have been built, you can then use it as follows:

$set = new FastSet(__DIR__ . '/dict');

if ($set->has('look-me-up')) {
    // exists
}

The hashes_<hash_algorithm>.bin and index_<hash_algorithm>.bin files are loaded lazily on first lookup, but you can also call initialize() explicitly if you want to load them into memory at a specific point in time.

Caveats

  • Not suitable if you need exact, collision-free guarantees
  • Not suitable for mutable sets
  • Input normalization (case-folding, Unicode normalization) is intentionally left to the caller

License

MIT

toflar/fast-set 适用场景与选型建议

toflar/fast-set 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 1.24k 次下载、GitHub Stars 达 7, 最近一次更新时间为 2026 年 01 月 15 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「dictionary」 「hash」 「set」 「memory」 「binary」 「lookup」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 toflar/fast-set 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 toflar/fast-set 我们能提供哪些服务?
定制开发 / 二次开发

基于 toflar/fast-set 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 1.24k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 7
  • 点击次数: 42
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 7
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-01-15