pith-cdc 0.1.1b1__py3-none-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pith_cdc/__init__.py ADDED
@@ -0,0 +1,196 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 pith-hash
3
+ """pith-cdc SDK: FastCDC content-defined chunking through ctypes.
4
+
5
+ The single Rust core (the ``pith-cdc`` cdylib built by
6
+ ``cargo build --release``) is loaded at runtime; this package carries
7
+ no third-party dependency — ``ctypes`` is the standard library.
8
+
9
+ Discovery order (the suite's cdylib convention):
10
+
11
+ 1. ``PITH_CDYLIB`` — an explicit cdylib *file* path;
12
+ 2. ``PITH_CDYLIB_DIR`` — a *directory* scanned for the cdylib names
13
+ (the CD pipeline points this at ``target/release``);
14
+ 3. the package directory itself (the built wheel ships the cdylib as
15
+ package data);
16
+ 4. ``<repo root>/target/release`` — the repository working-tree layout,
17
+ so a source checkout runs against a local cargo build with no
18
+ configuration.
19
+
20
+ The FFI surface is one chunking operation plus one free:
21
+ ``pith_cdc_chunk`` runs the FastCDC scan over a byte slice and hands
22
+ back a flat ``(offset, length, hash)`` triple buffer (24 bytes per
23
+ chunk), and ``pith_cdc_free`` releases the handed-out buffer.
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import ctypes
29
+ import os
30
+ from dataclasses import dataclass
31
+ from pathlib import Path
32
+
33
+ __all__ = [
34
+ "Chunk",
35
+ "FfiError",
36
+ "LibraryNotFoundError",
37
+ "find_cdylib",
38
+ "chunk",
39
+ "LEVEL_0",
40
+ "LEVEL_1",
41
+ "LEVEL_2",
42
+ "LEVEL_3",
43
+ "STATUS_OK",
44
+ "STATUS_INVALID",
45
+ "STATUS_REJECTED",
46
+ ]
47
+
48
+ #: Status: success.
49
+ STATUS_OK = 0
50
+ #: Status: a caller argument is invalid (a null pointer, or a level
51
+ #: code the ABI does not define — only 0..=3 exist).
52
+ STATUS_INVALID = -1
53
+ #: Status: the core chunker refused the parameters (a size outside its
54
+ #: range, or a ``min <= avg <= max`` violation).
55
+ STATUS_REJECTED = -2
56
+
57
+ #: Normalization-level wire code: level 0, no normalization.
58
+ LEVEL_0 = 0
59
+ #: Normalization-level wire code: level 1.
60
+ LEVEL_1 = 1
61
+ #: Normalization-level wire code: level 2, the crate default.
62
+ LEVEL_2 = 2
63
+ #: Normalization-level wire code: level 3.
64
+ LEVEL_3 = 3
65
+
66
+ #: Every cdylib file name cargo may drop into the build directory, per
67
+ #: platform (windows / linux / macOS).
68
+ CDYLIB_NAMES = ("pith_cdc.dll", "libpith_cdc.so", "libpith_cdc.dylib")
69
+
70
+
71
+ @dataclass(frozen=True)
72
+ class Chunk:
73
+ """One content-defined chunk: a ``(offset, length)`` span plus the
74
+ Gear fingerprint value at the cut point (the ``hash`` — a rolling
75
+ fingerprint, not a digest)."""
76
+
77
+ #: Byte offset of the chunk start within the source.
78
+ offset: int
79
+ #: Chunk length in bytes.
80
+ length: int
81
+ #: Gear fingerprint as of the end of the chunk.
82
+ hash: int
83
+
84
+
85
+ class LibraryNotFoundError(OSError):
86
+ """No cdylib was found through the discovery chain."""
87
+
88
+
89
+ class FfiError(Exception):
90
+ """A non-zero status code came back from the cdylib."""
91
+
92
+ def __init__(self, op: str, status: int) -> None:
93
+ kind = {
94
+ STATUS_INVALID: "invalid argument",
95
+ STATUS_REJECTED: "parameters rejected",
96
+ }.get(status, "unknown failure")
97
+ super().__init__(f"{op} failed: {kind} (status {status})")
98
+ #: The raw status code the FFI returned.
99
+ self.status = status
100
+
101
+
102
+ def find_cdylib() -> Path:
103
+ """Locates the cdylib through the suite's discovery chain."""
104
+ explicit = os.environ.get("PITH_CDYLIB")
105
+ if explicit:
106
+ p = Path(explicit)
107
+ if p.is_file():
108
+ return p
109
+ env_dir = os.environ.get("PITH_CDYLIB_DIR")
110
+ candidates: list[Path] = []
111
+ if env_dir:
112
+ env_dir_path = Path(env_dir)
113
+ candidates.append(env_dir_path)
114
+ if not env_dir_path.is_absolute():
115
+ # CD and local runs invoke tools from the repository root or
116
+ # from sdk/<lang>; resolve the env value against both.
117
+ candidates.append(Path.cwd() / env_dir_path)
118
+ candidates.append(Path(__file__).resolve().parents[3] / env_dir_path)
119
+ candidates.append(Path(__file__).resolve().parent) # packaged wheel
120
+ candidates.append(Path(__file__).resolve().parents[3] / "target" / "release")
121
+ for directory in candidates:
122
+ for name in CDYLIB_NAMES:
123
+ p = directory / name
124
+ if p.is_file():
125
+ return p
126
+ raise LibraryNotFoundError(
127
+ "no pith-cdc cdylib found (searched PITH_CDYLIB, PITH_CDYLIB_DIR, "
128
+ "the package directory and <repo>/target/release); "
129
+ "run `cargo build --release` first"
130
+ )
131
+
132
+
133
+ _lib: ctypes.CDLL | None = None
134
+
135
+
136
+ def _load() -> ctypes.CDLL:
137
+ global _lib
138
+ if _lib is None:
139
+ lib = ctypes.CDLL(str(find_cdylib()))
140
+ lib.pith_cdc_chunk.argtypes = [
141
+ ctypes.c_void_p, # data
142
+ ctypes.c_size_t, # len
143
+ ctypes.c_size_t, # min_size
144
+ ctypes.c_size_t, # avg_size
145
+ ctypes.c_size_t, # max_size
146
+ ctypes.c_uint32, # level
147
+ ctypes.POINTER(ctypes.c_void_p), # out buffer
148
+ ctypes.POINTER(ctypes.c_size_t), # out length (bytes)
149
+ ]
150
+ lib.pith_cdc_chunk.restype = ctypes.c_int32
151
+ lib.pith_cdc_free.argtypes = [ctypes.c_void_p, ctypes.c_size_t]
152
+ lib.pith_cdc_free.restype = None
153
+ _lib = lib
154
+ return _lib
155
+
156
+
157
+ def chunk(
158
+ data: bytes,
159
+ min_size: int,
160
+ avg_size: int,
161
+ max_size: int,
162
+ level: int = LEVEL_2,
163
+ ) -> list[Chunk]:
164
+ """Chunks ``data`` with FastCDC and returns the chunk boundaries.
165
+
166
+ ``min_size``/``avg_size``/``max_size`` are validated by the Rust
167
+ core (the spec's defaults are 2048/8192/32768); ``level`` is one of
168
+ the ``LEVEL_*`` wire codes (the default is :data:`LEVEL_2`, the
169
+ paper's sweet spot).
170
+
171
+ Raises :class:`FfiError` with ``status == STATUS_REJECTED`` for
172
+ out-of-range or misordered sizes and ``status ==
173
+ STATUS_INVALID`` for an unknown level code — never a crash; an
174
+ empty input simply returns ``[]``.
175
+ """
176
+ # ctypes hands an empty bytes object as a valid buffer, but pass a
177
+ # real NULL for it so the (null, 0) contract is exercised exactly
178
+ # like the Go binding does.
179
+ buf = data if len(data) else None
180
+ out = ctypes.c_void_p()
181
+ out_len = ctypes.c_size_t()
182
+ status = _load().pith_cdc_chunk(
183
+ buf, len(data), min_size, avg_size, max_size, level,
184
+ ctypes.byref(out), ctypes.byref(out_len),
185
+ )
186
+ if status != STATUS_OK:
187
+ raise FfiError("pith_cdc_chunk", status)
188
+ try:
189
+ count = out_len.value // 24
190
+ words = (ctypes.c_uint64 * (count * 3)).from_address(out.value)
191
+ return [
192
+ Chunk(offset=words[i * 3], length=words[i * 3 + 1], hash=words[i * 3 + 2])
193
+ for i in range(count)
194
+ ]
195
+ finally:
196
+ _load().pith_cdc_free(out, out_len.value)
pith_cdc/pith_cdc.dll ADDED
Binary file
@@ -0,0 +1,12 @@
1
+ Metadata-Version: 2.4
2
+ Name: pith-cdc
3
+ Version: 0.1.1b1
4
+ Summary: FastCDC content-defined chunking SDK: normalized Gear-fingerprint chunk boundaries via the pith-cdc Rust cdylib (ctypes, zero-dep)
5
+ License-Expression: MIT
6
+ Project-URL: Repository, https://github.com/pith-hash/pith-cdc
7
+ Project-URL: Homepage, https://github.com/pith-hash/pith-cdc
8
+ Keywords: fastcdc,chunking,deduplication,content-defined,hashing
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Rust
11
+ Classifier: Topic :: System :: Archiving :: Backup
12
+ Requires-Python: >=3.9
@@ -0,0 +1,6 @@
1
+ pith_cdc/__init__.py,sha256=QqJcUO67UVvyTaybHqb33SEwy3JJDsuUtjbjHVfKEUs,6621
2
+ pith_cdc/pith_cdc.dll,sha256=0zzzVAZIVwI_awXlKAgWNABb0rxBvcL-mpSI6YwW0rA,119296
3
+ pith_cdc-0.1.1b1.dist-info/METADATA,sha256=9SH8YWigRuz2gotfmSB3-Af7sOPShhj4_P-oNFyJ8QM,582
4
+ pith_cdc-0.1.1b1.dist-info/WHEEL,sha256=Zx98gwb_dQKckJK3HEOVKnxxD52EfU_PXDG_usMJ2ng,98
5
+ pith_cdc-0.1.1b1.dist-info/top_level.txt,sha256=p17KSMz5mr8GxjQCAaGiG323RAViRSFwzXatPtYzhwo,9
6
+ pith_cdc-0.1.1b1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: false
4
+ Tag: py3-none-win_amd64
5
+
@@ -0,0 +1 @@
1
+ pith_cdc