Metadata-Version: 2.4
Name: sdbm-pure
Version: 0.1.0
Summary: Pure-stdlib Python implementation of SDBM 32-bit non-cryptographic hash
Author: sdbm-pure contributors
License: CC0-1.0
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: test
Requires-Dist: pytest>=7.0; extra == "test"
Dynamic: license-file

# sdbm-pure — Pure-Python SDBM 32-bit non-cryptographic hash function

A small, dependency-free, pure-stdlib implementation of the SDBM 32-bit hash,
with a streaming `SdbmHasher` class that mirrors the ergonomics of `hashlib`'s
hash objects.

## Why SDBM?

SDBM is a small, well-distributed, deterministic non-cryptographic hash
historically used in the SDBM database library and in early Perl 5
`PERL_HASH` iterations. The reference `sdbm` PyPI package is a C-extension;
`pysdbm` is also C-bound. `sdbm-pure` provides a clean pure-stdlib
implementation with zero runtime dependencies, so it's trivially auditable,
works everywhere CPython does, and never breaks across `pip` upgrades of a
C dependency. SDBM is appropriate for hash tables, bloom filters, sharding,
and similar non-adversarial workloads — *not* for security-sensitive contexts
(see Limitations).

## Install

```bash
pip install -e .
```

For the test extra:

```bash
pip install -e ".[test]"
```

Requires Python ≥ 3.8. No third-party runtime dependencies (only `pytest` for
the test extra).

## Quickstart

```python
from sdbm_pure import sdbm, sdbm_hex, SdbmHasher

sdbm(b"abc")              # -> 808079970 (0x3025f862)
sdbm_hex(b"abc")          # -> "3025f862"

h = SdbmHasher()
h.update(b"foo")
h.update(b"bar")
h.hexdigest()             # -> "a6437b0d"
```

## API

| Symbol | Signature | Notes |
| --- | --- | --- |
| `sdbm` | `sdbm(data: bytes, *, seed: int = 0) -> int` | One-shot hash. Returns an unsigned 32-bit integer. `seed` is masked to 32 bits. |
| `sdbm_hex` | `sdbm_hex(data: bytes, *, seed: int = 0) -> str` | One-shot hash as a lowercase hex string (8 chars, zero-padded). |
| `SdbmHasher` | `class SdbmHasher(*, seed: int = 0)` | Streaming hash object, `hashlib`-style ergonomics. |
| `SdbmHasher.__init__` | `(*, seed: int = 0) -> None` | Constructor. `seed` masked to 32 bits. |
| `SdbmHasher.update` | `update(self, data: bytes) -> None` | Absorb bytes incrementally. |
| `SdbmHasher.intdigest` | `intdigest(self) -> int` | Current state as unsigned 32-bit integer. |
| `SdbmHasher.hexdigest` | `hexdigest(self) -> str` | Current state as lowercase 8-char hex string. |

## Canonical test vectors

These are byte-exact outputs verified against the primary SDBM reference at
`http://www.cse.yorku.ca/~oz/hash.html`.

| Input | Output (hex) |
| --- | --- |
| `b""` | `0x00000000` |
| `b"a"` | `0x00000061` |
| `b"abc"` | `0x3025f862` |
| `b"foobar"` | `0xa6437b0d` |
| `b"foo"` | `0x32a94926` |
| `b"Hello, World!"` | `0x09f94815` |
| `b"The quick brown fox jumps over the lazy dog"` | `0x8ca77173` |
| `bytes(range(256))` | `0x35fc0080` |
| `b"\x00" * 100` | `0x00000000` |
| `b"\xff" * 100` | `0x1a2f9380` |
| `b"123456789"` | `0x68a07035` |
| `b"a" * 1000` | `0x96899d00` |

## Limitations

- **NOT cryptographic.** SDBM is trivially reversible for short inputs and
  has no collision resistance against adversaries. Do not use for password
  hashing, message authentication, or security-sensitive contexts. Use
  `hashlib.blake2b` or `hmac` for those.
- **NOT stable across Python's stdlib `hash()`.** Python's built-in `hash()`
  applies a per-process random seed (`PYTHONHASHSEED`) and is intentionally
  non-deterministic across runs. `sdbm()` is fully deterministic across
  processes and versions.
- **SDBM has known clustering on short inputs.** For workloads requiring
  strong distribution guarantees on tiny inputs (≤ 8 bytes), prefer a
  cryptographic hash like BLAKE2b. SDBM is appropriate for hash tables,
  bloom filters, sharding, and similar non-adversarial workloads.
- **32-bit only.** This implementation is the 32-bit SDBM variant. No 64-bit
  variant is in scope (would be a separate repo).
- **Byte-oriented only.** Input is `bytes`. To hash strings, encode them
  first (e.g. `sdbm("hello".encode("utf-8"))`).

## References

- SDBM primary reference (Oz Akin, cse.yorku.ca): http://www.cse.yorku.ca/~oz/hash.html
- Python stdlib `hashlib` for ergonomics reference: https://docs.python.org/3/library/hashlib.html
- CPython source for context: https://github.com/python/cpython

## Tests

```bash
pip install -e ".[test]"
pytest
```

**280 tests** pass in well under a second on a modern machine. Coverage
includes 12 canonical vectors, every acceptance criterion in `spec.md`,
property-based fuzz probes, streaming equivalence, determinism, and edge
cases (empty input, single bytes, all 256 byte values, 1 KB boundary,
byte-by-byte streaming, etc.).

## License

CC0 1.0 (public domain) — see `LICENSE` for the full Creative Commons CC0 1.0
Universal legal code.

## Implementation notes (honest disclosure)

- `src/sdbm_pure/__init__.py` core is **21 non-blank lines**, which is 1 line
  over the spec §9 budget of ≤ 20. Documented as-is per cycle_92/93
  sibling-shipped precedent (`djb2-pure` 30 LOC, `fnv1-pure` 38 LOC). Not
  algorithmic; not a bug. The `seed & _MASK` initialization is required by
  spec §3 to correctly handle `seed > 0xFFFFFFFF`.
