Home / php
Name Modified Size InfoDownloads / Week
Parent folder
Chb.php 2026-07-26 17.2 kB
chb-tester.php 2026-07-10 9.4 kB
mulsze.php 2026-07-10 2.1 kB
Totals: 3 Items   28.6 kB 0

CHB (Colony Hash Bucket)

CHB is a lightweight on-disk hash table designed for fast key-value storage with a simple and deterministic binary format in 275 lines of pure PHP.

Installation

require_once('Chb.php');

Opening a Database

Open an existing CHB file or create a new one. MUL and SZE can be only specified at creation time.

$clu = 0;
$mul = 1;
$sze = 16;
$db = new Chb('storage.chb', $clu, $mul, $sze);

Parameters

Parameter Description
path Path to the CHB file
clu cluster optimization, set it to 0
mul Bucket multiplier (65536 × MUL buckets) from 1 to 255
sze An index of preset record sizes that range from few bytes to almost 65536

Storing Data

Insert or update a key/value pair.

$db->set(
    'customer:1001',
    json_encode([
        'name' => 'John',
        'email' => 'john@example.com'
    ])
);

Example

$db->set('username', 'admin');
$db->set('email', 'admin@example.com');

Retrieving Data

Retrieve a value associated with a key.

$value = $db->get('username');

if ($value !== null) {
    echo $value;
}

Example

$email = $db->get('email');

echo $email;

Deleting Data

Records are logically deleted and may be recycled later.

$db->delete('username');

Example

if ($db->delete('email')) {
    echo "Deleted";
}

Scanning Records

Iterate through all records stored in the database.

foreach ($db->scan() as $key => $value) {
    echo $key . PHP_EOL;
    echo $value . PHP_EOL;
}

Example

foreach ($db->scan() as $key => $value) {
    printf(
        "%s => %s\n",
        $key,
        $value
    );
}

Closing the Database

Always close the database when finished.

$db->close();

Complete Example

<?php

require_once('Chb.php');

$db = new Chb('example.chb');

$db->set('user:1', 'John Doe');
$db->set('user:2', 'Jane Doe');

echo $db->get('user:1') . PHP_EOL;
foreach ($db->scan() as $key => $value) {
    echo "$key => $value\n";
};

$db->delete('user:2');

echo "after deletion..." . PHP_EOL;
foreach ($db->scan() as $key => $value) {
    echo "$key => $value\n";
};

$db->close();

Notes

  • Keys may contain from 1 to 128 bytes.
  • Records have a fixed size defined at database creation time.
  • Deleted records are logically removed and can be reused.
  • Bucket count is calculated as 65536 × MUL.
  • Lookups are performed using hash-based bucket indexing.
  • CHB uses file locking to support concurrent access.
  • The file format is portable and easy to inspect or recover.

Typical Use Cases

  • Persistent caches
  • Metadata stores
  • Embedded applications
  • Index files
  • Key/value storage
  • Lightweight storage engines
  • Custom database projects

Details

  • default MUL is 1, that means CHB will prepare the db for 65536 buckets, useful for storing about 45000 records, you should adapt for a load factor of 0.7. With MUL set to 255 you can store 11000000 records.
  • default SZE is 16, that means 576 bytes for the key and the value (see below). Know your data, because the fixed record size can make your file very big with a lot of wasted space.
  • I have developed this class, because my hosting provider had the DBA extension disabled, and I wanted a simple way to store small JSON records.
  • The on-disk format is saved in little-endian.

Creating a Database

In case you need help to set the correct parameters, you can use the mulsze.php script. It accepts the expected records count and the expected content (key + value) size in bytes. In the following example I expect about 200000 record of 3200 bytes.

php mulsze.php 200000 3200

This is the output.

Expected records count: `200000`, expected record size: `3200`.

With 0.7 load factor you need the following MUL: 5 for a total of 327680 buckets.

The following is the value for SZE: 19 for a max key+value of 4086 bytes.
Note that for every record you will waste 886 bytes.


You can initialize your database in this way:

$db = new Chb('storage.chb', 0, 5, 19);
$db->close();

What about CLU

My use case requires records with content sized 576 bytes. When the content is less than 4096 bytes and the chains become very long, the algorithm can perform an optimization by preallocating records on the disk, marking them deleted (and losing space of course). In this way the locality improves and the performance is better. In this cases the right thing is to perform a rehash with new parameters.

In my case I was testing 5.000.000 records with 65536 buckets with chain longer than 70 hops. This is not something you want; using the CLU optimiziation the performance improved. Notice that this can be enabled after creation by passing $clu = 1, but the real solution is to rehash.


SZE values

SZE sets the record size, at left the index to be passed, at right the max content (key + value) size.

Index Max Content Size
1 4
2 6
3 12
4 26
5 30
6 36
7 54
8 56
9 82
10 96
11 108
12 186
13 264
14 306
15 446
16 576
17 810
18 1356
19 4086
20 8183
21 12279
22 16375
23 20471
24 24567
25 28663
26 32759
27 36855
28 40951
29 45047
30 49143
31 53239
32 57335
33 61431
34 65527

Changelog

2026-07-26(0.1.10) Serious bugfix that could render the old file unreadable, read the Chb source code, in the beginning there are more details.


Source: README.md, updated 2026-07-26