Skip to main content

Fast, functional IP / CIDR toolkit backed by a native Zig core

Project description

zcidr

Parse millions of IP addresses per second and match them against huge CIDR rule sets — from Python.

zcidr is a fast, functional IP / CIDR toolkit backed by a native Zig core. On an ordinary laptop it parses 44M IPv4 addresses/s (~43× the stdlib) and answers "is this IP in any of these 2,000 networks?" at 19M queries/s — about 2,900× a stdlib scan and 170× netaddr (benchmarks).

If you filter request logs against blocklists, geo-map flows, enrich telemetry with network tags, or score millions of addresses against threat feeds, the address handling itself stops being your bottleneck.

Why it's fast

Pure-Python IP libraries pay twice: interpreted parsing, and one boxed address object per element. zcidr removes both costs:

  • A native core. Parsing, formatting, and longest-prefix-match run in optimized Zig behind a narrow C ABI.
  • Batch by design. The *_lines / *_many functions cross the Python↔native boundary once per workload, not once per address — buffers in, arrays out, no per-element objects. Errors don't interrupt a batch either: results carry a 1/0 validity mask instead of raising halfway.
  • Functional API. Simple values in, simple values out — ints, bytes, array — which is exactly what you can hand to NumPy, pandas, or a file without conversion.
  • True parallelism. Every native call releases the GIL, so batch calls scale across threads.

Install

pip install zcidr

Wheels bundle the prebuilt native library (loaded with cffi in ABI mode); no compiler is needed at install time. One wheel per platform serves every supported Python version (3.10+).

Sixty seconds of zcidr

import zcidr

# --- one-offs -------------------------------------------------------------
zcidr.parse_ipv4("192.168.0.1")        # -> 3232235521
zcidr.format_ipv4(3232235521)          # -> "192.168.0.1"
zcidr.parse_ipv6("::1")                # -> b'\x00...\x01' (16 bytes)
zcidr.normalize("2001:0db8:0000::1")   # -> "2001:db8::1"
zcidr.is_valid("::gg")                 # -> False

# --- the batch path (where the speed lives) --------------------------------
# One native call for a whole file: values + validity mask, no objects.
data = open("ips.txt", "rb").read()
values, valid = zcidr.parse_ipv4_lines(data)   # array('I'), bytes of 1/0

# The same function takes str blobs, open files, generators, lists:
values, valid = zcidr.parse_ipv4_lines(["1.2.3.4", "8.8.8.8"])
zcidr.format_ipv4_lines(values)                # -> b"1.2.3.4\n8.8.8.8"

# --- CIDR matching ----------------------------------------------------------
# Build a longest-prefix matcher from any mix of IPv4/IPv6 networks.
m = zcidr.build(["10.0.0.0/8", "10.1.2.0/24", "2001:db8::/32"])

zcidr.match(m, "10.1.2.9")             # -> 1   (index of the longest match)
zcidr.match(m, "8.8.8.8")              # -> None
zcidr.contains(m, "2001:db8::5")       # -> True

# The fused fast path: parse + match a whole workload in ONE native pass.
verdicts, found = zcidr.match_lines(m, data)   # array('Q'), bytes of 1/0

Values, not just membership

Each network can carry a uint64 payload — a rule ID, an ASN, a country code — returned on match. By default it's the network's index.

m = zcidr.build(["10.0.0.0/8", "172.16.0.0/12"], values=[3356, 64512])
zcidr.match(m, "172.20.1.1")           # -> 64512

build() takes rule sets the same way the parsers take addresses: a list, a generator, an open file, or a newline-delimited blob — inserted with one native batch call into an arena-allocated trie (~7M rules/s). Matchers free their native memory on garbage collection, eagerly via zcidr.free(m), or scoped:

with zcidr.build(open("blocklist.txt")) as m:
    _, found = zcidr.match_lines(m, open("access.log.ips"))

Big files, zero copies

Anything with the buffer protocol is read in place — bytes, bytearray, memoryview, NumPy arrays, mmap. A multi-gigabyte address file never gets copied into Python:

import mmap, zcidr

with open("addresses.txt", "rb") as f:
    with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
        values, valid = zcidr.parse_ipv4_lines(mm)   # native reads the mmap directly

Iterables of strings are consumed in bounded chunks, so streaming an unbounded generator works too — but when your data can be bytes, feed bytes: skipping per-line Python strings is itself a ~2× win.

Input conventions at a glance

Suffix Direction Input
*_lines text side newline-delimited str / bytes-like buffer, or any iterable of strings
*_many binary side fixed-width record buffer (uint32 for IPv4, 16-byte packed for IPv6)

Batch parses return (values, valid_mask); batch matches return (values, found_mask). Masks are bytes of 1/0 per record — invalid records never shift positions, and a string containing a stray newline raises instead of silently desyncing.

Performance

See benchmarks/ for the harness and full tables. Representative (Apple Silicon, ReleaseFast):

Workload zcidr vs stdlib ipaddress
IPv4 parse (batch) 44 M ops/s ~43×
IPv6 parse (batch) 7.2 M ops/s ~15×
Membership, 2k rules (fused) 19 M queries/s ~2,900× (170× netaddr)
Matcher build, 200k rules 3.7–7 M rules/s 7.5×¹

¹ vs merely parsing the rules with ip_network() — which builds no index.

Building from source

Requires Zig 0.16 (or pip install ziglang).

zig build -Doptimize=ReleaseFast   # native library -> zig-out/lib/
zig build test                     # Zig unit tests
pip install -e . && pytest         # Python wrapper + test suite

Architecture

  • Core (src/): Zig, exposed over a C ABI (include/zcidr.h). Bytes / ints / bools across the FFI line; an arena-backed longest-prefix-match trie for CIDR containment; batch parse/format/insert/match primitives that loop natively — including a fused parse+match kernel (zcidr_trie_match_lines).
  • Wrapper (zcidr/): purely functional cffi ABI/dlopen binding — no build step at install, one wheel per platform for all Python versions.
  • Packaging: cibuildwheel (manylinux / macOS / Windows) with the native toolchain supplied by the ziglang wheel.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

zcidr-0.0.2.tar.gz (38.5 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

zcidr-0.0.2-py3-none-win_amd64.whl (118.1 kB view details)

Uploaded Python 3Windows x86-64

zcidr-0.0.2-py3-none-musllinux_1_2_x86_64.whl (52.0 kB view details)

Uploaded Python 3musllinux: musl 1.2+ x86-64

zcidr-0.0.2-py3-none-musllinux_1_2_aarch64.whl (48.7 kB view details)

Uploaded Python 3musllinux: musl 1.2+ ARM64

zcidr-0.0.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (51.4 kB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

zcidr-0.0.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (48.6 kB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

zcidr-0.0.2-py3-none-macosx_11_0_arm64.whl (23.7 kB view details)

Uploaded Python 3macOS 11.0+ ARM64

zcidr-0.0.2-py3-none-macosx_10_13_x86_64.whl (23.6 kB view details)

Uploaded Python 3macOS 10.13+ x86-64

File details

Details for the file zcidr-0.0.2.tar.gz.

File metadata

  • Download URL: zcidr-0.0.2.tar.gz
  • Upload date:
  • Size: 38.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for zcidr-0.0.2.tar.gz
Algorithm Hash digest
SHA256 6f6d1f547188f072183c325300439761cc246d00f4a3b44e4f7ed32c89cbeb59
MD5 a724d6db23f19db63b720eeecf5b8035
BLAKE2b-256 4898973b1bc3ad8ad29f7a5f36219a8fce5fbb36bd9c59fd69d1ca21d7e93741

See more details on using hashes here.

Provenance

The following attestation bundles were made for zcidr-0.0.2.tar.gz:

Publisher: wheels.yml on gustavoschmidt/zcidr

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file zcidr-0.0.2-py3-none-win_amd64.whl.

File metadata

  • Download URL: zcidr-0.0.2-py3-none-win_amd64.whl
  • Upload date:
  • Size: 118.1 kB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for zcidr-0.0.2-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 693dffb000b2426434ce0365dfdbd94df556d1c44308d15b4ff1b3906f9c9d7d
MD5 079dbf43fd9e57c97a0f3e15c9521ce6
BLAKE2b-256 a9426a6fb3807d9096e411d937498a503053be97a450aa129ee01e3890fbff60

See more details on using hashes here.

Provenance

The following attestation bundles were made for zcidr-0.0.2-py3-none-win_amd64.whl:

Publisher: wheels.yml on gustavoschmidt/zcidr

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file zcidr-0.0.2-py3-none-musllinux_1_2_x86_64.whl.

File metadata

  • Download URL: zcidr-0.0.2-py3-none-musllinux_1_2_x86_64.whl
  • Upload date:
  • Size: 52.0 kB
  • Tags: Python 3, musllinux: musl 1.2+ x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for zcidr-0.0.2-py3-none-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 41e62ece392b27f885e9b1f428450d6b5751aa1c0f23875bde8af93d657ffd2c
MD5 7c21e95d5231277cd799b4226e74c753
BLAKE2b-256 3308cfcbc78c96a19db74599eb08694dc456254eafbb1077972009feff63d230

See more details on using hashes here.

Provenance

The following attestation bundles were made for zcidr-0.0.2-py3-none-musllinux_1_2_x86_64.whl:

Publisher: wheels.yml on gustavoschmidt/zcidr

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file zcidr-0.0.2-py3-none-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for zcidr-0.0.2-py3-none-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 453dd57077c3e3d5ccd8dafc70d43e2f5fd34cca1dc08749b30d384fbee38c64
MD5 1d9db67486bc14cca350fb3ed551173d
BLAKE2b-256 684b890a9c9c8f0cb1edb85adf1c6258cbc36210e5de39d8c7177ace5f69182e

See more details on using hashes here.

Provenance

The following attestation bundles were made for zcidr-0.0.2-py3-none-musllinux_1_2_aarch64.whl:

Publisher: wheels.yml on gustavoschmidt/zcidr

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file zcidr-0.0.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for zcidr-0.0.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 46931f0af61344626a24a097fe15488e2e6a8cf397524b1f7144de8a864d0d39
MD5 4c09cd270f846b6a1c393cb7378e75b6
BLAKE2b-256 6028b2642cd2382f5f22e53cc5d61c47c2a582b3fa7b48f3155cc813f1e3d4ab

See more details on using hashes here.

Provenance

The following attestation bundles were made for zcidr-0.0.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: wheels.yml on gustavoschmidt/zcidr

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file zcidr-0.0.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for zcidr-0.0.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 b69e236822498ed2d3c9916e2804e498c599b49a664145fae24093ef6c892ab0
MD5 26fba7fb7b6c1afd262be62e5243ba28
BLAKE2b-256 4c3e2a6b55a02a4c3e2177300b4db6d6e703e431e7fc76c011c266c272915115

See more details on using hashes here.

Provenance

The following attestation bundles were made for zcidr-0.0.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: wheels.yml on gustavoschmidt/zcidr

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file zcidr-0.0.2-py3-none-macosx_11_0_arm64.whl.

File metadata

  • Download URL: zcidr-0.0.2-py3-none-macosx_11_0_arm64.whl
  • Upload date:
  • Size: 23.7 kB
  • Tags: Python 3, macOS 11.0+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for zcidr-0.0.2-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 3d4676cb10c0925c889d136bbda249897bfce5e0df5d52e5e3ae8dcf3a755435
MD5 6b45b468e655d02b350d0043337d04fb
BLAKE2b-256 3b1bcf7c891e0464ed0fe14f8beabca3fa2f7d6dabda6733a5ef0ebb8eb5161b

See more details on using hashes here.

Provenance

The following attestation bundles were made for zcidr-0.0.2-py3-none-macosx_11_0_arm64.whl:

Publisher: wheels.yml on gustavoschmidt/zcidr

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file zcidr-0.0.2-py3-none-macosx_10_13_x86_64.whl.

File metadata

File hashes

Hashes for zcidr-0.0.2-py3-none-macosx_10_13_x86_64.whl
Algorithm Hash digest
SHA256 5a431a03438c4207c151ac08c9cb7c33ffaf1fe8bae1de4388f8a11a0c89e9c6
MD5 570a89b3911bcfa14ce7a39b94b31b13
BLAKE2b-256 b42baab74fe076570399c334d77f30042e6b0a41f94b6ebec5dbe7372408fa95

See more details on using hashes here.

Provenance

The following attestation bundles were made for zcidr-0.0.2-py3-none-macosx_10_13_x86_64.whl:

Publisher: wheels.yml on gustavoschmidt/zcidr

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page