58 Commits
Author SHA1 Message Date
Thomas Waldmann 3aab66de62 update .gitignore 2026-08-23 14:52:12 +02:00
Thomas WaldmannandClaude Fable 5 f16c440b45 *-aes chunkers: factor out the shared 80%, no performance change
rabin-aes, goldilocks-aes and toeplitz-aes differed only in their rolling
universal hash, but each carried its own copy of the AES layer, the
two-lane scan structure, the chunker class and the test suite - about 79%
of the goldilocks/toeplitz code was line-identical to rabin-aes.

Three shared pieces replace the copies:

- phte_core.h: AES-128 key expansion, endian-explicit LE helpers, hardware
  detection, single-block AES helpers and the context base (round keys, EVP
  context, path selection).
- phte_scan.h: the scan itself as a compile-time template. Each kernel
  defines its rolling functions and PH_PREFIX/PH_CTX/PH_ROLL/PH_ROLL2/
  PH_DIGEST64, then includes it, so the rolling code is still expanded
  inline into fully specialized scan loops - no function pointers, no
  cross-kernel calls, identical code generation.
- phte_chunker.pyx/.pxd: ChunkerPHTE, the shared chunker driver (buffering,
  min/max clamping, normalized chunking, scan loop, sparse handling). The
  subclasses implement _scan()/_digest64() as Cython cdef methods, i.e. one
  vtable call per scan span (up to a whole chunk) and per chunk start, never
  per byte.

Likewise for the tests: the behavior every one of these chunkers must have
(kernel equivalence, size distribution, shift resilience, param parsing,
fuzzing) now runs parametrized over all three in phte_chunkers_test.py; the
per-chunker modules keep only their frozen golden chunk points and the
tests of their own hash arithmetic and key derivation.

Net -2335 lines. Each kernel .c is now 84-125 lines (was 523-564) and each
chunker .pyx no longer contains the ~250-line class body.

Verified unchanged behavior and performance:
- all three frozen golden chunk-point tests unchanged, 508 tests pass,
  mypy/black/ruff clean, e2e create+extract with all three chunkers OK;
- C-level A/B of the production scan (same harness, HEAD kernels vs these,
  64 MiB, best of 8): rabin 683.6 -> 682.5, goldilocks 331.8 -> 331.8,
  toeplitz 687.4 -> 694.7 MB/s hw; EVP paths 376.6 -> 376.6, 251 -> 250,
  394.2 -> 394.0 - all within +-1%;
- disassembly of ra_scan keeps the identical AES and table-lookup structure
  (853 vs 850 instructions; the delta is prologue register shuffling caused
  by the changed struct field offsets).

Also excludes *.pxd from the wheel (build-time only, borg's first .pxd).

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-04 15:56:36 +02:00
Thomas WaldmannandClaude Fable 5 eebdd3a69c toeplitz-aes: new chunker (tabulated LFSR/Toeplitz UHF + AES-128)
Third UHF-then-PRF chunker, and on the evidence the best construction of
the three: the universal hash is a tabulated LFSR-based Toeplitz hash
(Krawczyk, CRYPTO '94) - digest = sum_j x^(63-j) * T[b_j] over
GF(2)[x] mod P - with a secret random table T of 256 uint64 (2 KiB of
key material) and a FIXED PUBLIC irreducible P of degree 64
(x^64 + x^4 + x^3 + x + 1, the CLHASH polynomial, top bit implicit).

Collision bound: two distinct 64-byte windows collide with probability
EXACTLY 2^-64 over T - optimal for a 64-bit digest and unconditional
(rabin-aes: ~2^-55 probabilistic over the sampled P; goldilocks-aes:
~2^-58). Sketch: the difference is sum_v c_v * T[v] where some c_v is a
nonzero polynomial of degree < 64, hence an invertible element mod the
irreducible degree-64 P, making the sum uniform. This needs the fixed
64-byte window (distinct powers x^0..x^63); a plain-rotation variant
(keyed buzhash) fails - rotations satisfy R^64 = I, so e.g. two
all-same-byte windows collide with probability 1/2.

The rolling step d' = d*x ^ x^64*T[out] ^ T[in] reduces via a branchless
shift+masked-XOR and indexes tables only by plaintext bytes: no loads and
no secret-dependent memory access on the serial chain. Setup needs no
irreducibility sampling (P is public; the secrecy is the table).

Same C kernel architecture as rabin-aes/goldilocks-aes (two even/odd
lanes, grouped AES on arm64/AES-NI, batched OpenSSL EVP, bit-identical
paths), fully wired (constants, ChunkerParams, get_chunker, completion,
borg benchmark cpu, docs, tests with frozen golden chunk points),
verified end-to-end with repo-create/create/extract.

Measured (1 GiB random, 19,23,21, 5 runs x 2 reps): hw 666/713 (nc2)
MB/s - on par with rabin-aes 661/698; EVP 424/450 vs 405/434 (~+5%).
The tie on the hw path is itself a finding: at ~700 MB/s the grouped-AES
path is AES/transfer-bound, not roll-chain-bound, so the cheaper roller
only shows on the EVP path. Equal speed, strictly better bound.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-04 15:44:28 +02:00
Thomas WaldmannandClaude Fable 5 bb70e32187 goldilocks-aes: comparison chunker (prime-field UHF + AES-128)
Add a second UHF-then-PRF chunker implementing the reference
construction of "Breaking and Fixing Content-Defined Chunking"
(Truong et al., eprint 2025/558) with its original universal hash: a
rolling polynomial hash over the Goldilocks prime field GF(p),
p = 2^64 - 2^32 + 1, at a secret random evaluation point K, over the
same 64-byte window - followed by the same AES-128 PRF layer as
rabin-aes. The C kernel mirrors rabin_aes_impl.c exactly (two even/odd
rolling lanes, groups of 8 pipelined AES blocks on arm64/AES-NI,
batched OpenSSL EVP fallback, bit-identical paths); only the rolling
arithmetic differs. Negated removal tables keep the field multiply as
the only critical-path operation; all values stay canonical since the
state feeds AES verbatim.

Verified equivalent to the paper's Rust artifact on shared keys and
random data: rolling states, AES input blocks and ciphertexts are
bit-identical per position; only the cut rule differs (they mask the
last ciphertext bytes via a big-endian u128, we mask the first 8
little-endian), so cut positions do not coincide - by choice, both
rules see uniform PRF bits.

Purpose is an evidence baseline, not a new default: on an Apple
M-series (256 MiB random, 19,23,21), 354 MB/s hw / 267 MB/s EVP versus
rabin-aes 670 / 401 - the GF(p) multiply chain is ~2x the GF(2)
shift+xor chain, table loads hide off-path in both. Non-speed
trade-offs versus rabin-aes: slightly better collision bound (63/p ~=
2^-58 vs ~2^-55), trivial key sampling (no irreducibility search), no
secret-dependent table indices in the hot loop, and a cheaper wide-
digest upgrade path (two independent K vs GF(2^127) clmul); rabin-aes
keeps the 2x throughput. Wired into get_chunker ("goldilocks-aes",
own key domain) and chunker_bench.py only.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-04 15:44:27 +02:00
Thomas WaldmannandClaude Fable 5 b7dcf30bd4 rabin-aes: new chunker with provable fingerprinting resistance
Add a "UHF-then-PRF" content-defined chunker following the Chk-PHTE
construction of "Breaking and Fixing Content-Defined Chunking"
(Truong et al., eprint 2025/558): a rolling Rabin fingerprint over
GF(2)[x]/P(x) - with P a secret, random, irreducible polynomial of
degree 63 - hashes the last 64 bytes (a universal hash family), and
AES-128 with a secret key is applied to the digest; the cut decision
only looks at the AES output. Observed chunk boundaries are thus
pseudo-random and yield no usable equations about the chunking
secrets, unlike chunkers cutting directly on (keyed) rolling-hash
bits. Both secrets are derived from the id key (domain "rabin-aes").

The C scan kernel has three bit-identical paths: batched OpenSSL EVP
AES-128-ECB (portable), arm64 crypto-extension intrinsics and x86-64
AES-NI (runtime detected), interleaving the AES work with the serial
rolling-hash chain. Batching ahead of the cut decision is possible
because the digest stream does not depend on where cuts happen.

Throughput on an Apple M-series (1 GiB random, 19,23,21,2): 489 MB/s
(arm64 hw path), 334 MB/s (EVP path); chunk-size distribution, shift
resilience and dedup behavior are identical to fastcdc/buzhash64.
Normalized chunking, param shape rabin-aes,min,max,mask,nc_level.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-04 15:43:33 +02:00
Thomas WaldmannandClaude Opus 4.8 c5df93b705 add Claude Code config (CLAUDE.md, settings, .gitignore)
Port of the .claude config from the 1.4-maint line, adapted for master (borg 2.x):
CLAUDE.md now describes the borgstore-based repository, the archiver/ command package,
Python >=3.11 and the borg2 testsuite layout. Shared .claude/CLAUDE.md and settings.json
are tracked; machine-specific bits (local virtualenv path, GPG signing) live in a
gitignored .claude/CLAUDE.local.md. Switch .gitignore from ignoring all of .claude/ to
ignoring only the personal settings.local.json and CLAUDE.local.md.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dg49arm61F42gHR4aFD4ac
2026-07-23 00:33:02 +02:00
Mrityunjay Raj bc3f14d526 compact: reclaim superseded duplicate bytes, #9868
- compact_pack: walk gap object headers, drop copies whose chunk id is
  indexed elsewhere (superseded duplicates), keep unindexed ones for repair
- compact_pack: repoint kept objects in a single linear pass over drop ranges
- compact_packs: report total unindexed bytes when nonzero
- mark_soft_deleted_used: count and warn about objects missing from the index
- epilog: compact reclaims redundant indexed copies, repair handles the rest
- tests: superseded gap dropped, duplicate reclaimed on rewrite
2026-07-11 00:43:45 +05:30
Thomas WaldmannandClaude Opus 4.8 f2b96e3d15 fastcdc: add FastCDC chunker with a keyed Gear hash
Add a new "fastcdc" content-defined chunker selectable via --chunker-params.
It uses the FastCDC Gear rolling hash (fp = (fp << 1) + Gear[byte]), which is
window-less and cheaper per byte than buzhash's cyclic-polynomial update, so it
chunks noticeably faster (see "borg benchmark cpu" output), while producing
the same chunk-size distribution and deduplication.

The Gear table is keyed: it is derived from the repo id key via CSPRNG (own
"fastcdc" domain), exactly like the buzhash64 table, so chunk cut points stay
unpredictable without the key (anti-fingerprinting). It implements the same
FastCDC techniques as buzhash64 (sub-minimum skipping, normalized chunking with
a required nc_level, min/max clamping); the mask uses the high bits of the hash
(Gear accumulates entropy there).

chunker-params: "fastcdc,chunk_min,chunk_max,chunk_mask,nc_level" - there is no
window field, because Gear is window-less. e.g. fastcdc,19,23,21,2

Also: borg benchmark cpu now measures the fastcdc chunker; tests in
borg.testsuite.chunkers (golden vector, size distribution, keyed gear table,
param parsing, slow fuzz); docs and changelog.

Benchmarks (scripts/chunker_bench.py, buzhash64 vs fastcdc, both nc_level=2,
incompressible data unless noted):

  5 GiB, 2 MiB target (default params):
    buzhash64: CV 0.294, 1011 MB/s
    fastcdc:   CV 0.295, 1313 MB/s   (+30%)

  64 MiB, 64 KiB target:
    buzhash64: CV 0.374, shift-resilience 0.9928,  963 MB/s
    fastcdc:   CV 0.359, shift-resilience 0.9929, 1331 MB/s   (+38%)

  Re-backup of a 2.5 GiB file after scattered single-byte edits (dedup ratio,
  0.5 = v2 fully deduplicated, lower is better):
     64 edits:  buzhash64 0.5237, fastcdc 0.5236
    320 edits:  buzhash64 0.6133, fastcdc 0.6161

  borg benchmark cpu, 1 GB: fastcdc 3.80s, buzhash 4.36s, buzhash64 8.13s,
  fixed 0.56s.

Chunk-size distribution, deduplication and shift-resilience match buzhash64
within noise; fastcdc is consistently faster.

Also: fix bug when computing the mask, one needs to use 1ULL instead of
1, so the shifting computation is done in a uint64, not in a 32bit int.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-01 23:24:25 +02:00
Thomas Waldmann 2322d24e91 add .DS_Store to .gitignore 2026-03-15 14:37:51 +01:00
Thomas Waldmann 408b6e0513 coverage: misc. tweaks
moved --junitxml parameter to the tox configuration.

haiku: add coverage params to pytest invocation (tox not used there).

vm_tests: add test_results and coverage uploads.

hard-code coverage.xml as coverage filename
2026-03-05 13:42:16 +01:00
defnvary 075323b8e7 .gitignore: move entries to correct location in file 2026-02-23 10:54:14 +05:30
defnvary 398ac5ab84 ignore more artifacts generated during build 2026-02-23 00:16:54 +05:30
Thomas Waldmann 63ff136dfe buzhash64: integrate into build 2025-06-10 22:44:48 +02:00
Thomas Waldmann f44b1742f5 Extract ChunkerFixed into a dedicated module under chunkers.
Moved the `ChunkerFixed` implementation from `chunker` to a new `fixed` module for better modularity. Updated imports and type hints.

Removed now empty chunkers.chunker module.
2025-06-04 23:48:50 +02:00
Thomas Waldmann 31494f200d Extract buzhash logic into a dedicated module under chunkers
Moved `buzhash` implementation from `chunker` to a new `buzhash` module for better separation of concerns. Updated imports, adjusted `setup.py` and build configuration accordingly. Removed deprecated `Chunker` definitions from `chunker.pyi`.
2025-06-04 22:57:03 +02:00
Thomas Waldmann a78c310b72 Split reader functionality into a separate module under chunkers package
Extracted the `reader` logic from `chunker` into a dedicated `reader` module to improve modularity and maintainability. Updated imports, references, and build configurations accordingly.
2025-06-04 22:40:39 +02:00
Thomas Waldmann bc86bd4bb0 Move chunker module to chunkers package
Refactor by relocating the `chunker` module under a new `chunkers` package, adjusting imports and file references accordingly.
2025-06-04 22:12:45 +02:00
Thomas Waldmann b26f222ca4 .gitignore: update entries 2025-06-04 11:40:55 +02:00
Thomas Waldmann 1f90d4a77a fixup: fix some algorithms/* ignores 2022-03-17 23:34:26 +01:00
Thalian 62ace206d6 [DOCS] #4587 – Make Sphinx warnings break docs build
Add VS Code dir to ignores.
2020-03-16 19:06:00 +01:00
Will 2087eeb323 Ignore syncfilerange.c 2020-03-05 12:57:59 -05:00
Jürg Rast f0b74146da Created new scripts for preparing and building borg 2019-10-30 01:06:46 +01:00
Jürg Rast 6b426d08d7 Initial work to build and run borg under windows
- Created a batch file to build borg on windows
- Adjusted setup.py to be runnable on windows and build the windows
extension
- Extracted the free space check to a function in the platform module
- Created the minimal needed (dummy) functions for the windows platform
module
2019-08-24 10:17:18 +02:00
Thomas Waldmann 8cd451bd33 python 3.5+ does not generate .pyo any more 2017-07-29 21:13:57 +02:00
Marian Beermann 3f8a0221ee Revert "move chunker to borg.algorithms"
This reverts commit 956b50b29c.

# Conflicts:
#	setup.py
#	src/borg/archive.py
#	src/borg/helpers.py
2017-06-07 23:51:42 +02:00
Marian Beermann ee821a70f5 update .gitignore 2017-06-07 11:06:37 +02:00
Marian Beermann d32eadeb38 gitignore: complete paths for src/ excludes 2017-05-02 20:49:27 +02:00
Marian Beermann a976e11a63 create crypto package with key, keymanager, low_level 2017-05-02 20:49:27 +02:00
Marian Beermann fa381ffcbe create package borg.algorithms with borg.algorithms.crc32 module 2017-05-02 18:53:54 +02:00
Marian Beermann 9afebead84 two fast CRC implementations
CRC slice by 8 for generic CPUs outperforms zlib CRC32 on ppc
and x86 (ARM untested but expected to as well).

PCLMULQDQ derived from Intel's zlib patches outperforms every other
CRC implementation by a huge margin.
2017-01-02 15:54:14 +01:00
TW c0615c78e9 Merge pull request #1358 from PlasmaPower/git-ignore-platform-c
Ignore generated platform C files
2016-07-23 16:18:10 +02:00
Lee Bousfield 2eef0fb894 Ignore generated platform C files 2016-07-23 09:15:16 -04:00
Lee Bousfield 477b374a28 gitignore .coverage.* 2016-07-22 18:55:14 -04:00
Thomas Waldmann f363ddd7ca Merge branch '1.0-maint' 2016-07-04 20:11:21 +02:00
Lee Bousfield 58e42deaa0 Add .eggs to .gitignore 2016-06-30 18:00:39 -04:00
Thomas Waldmann 3ce35f6843 Merge branch 'master' into move-to-src 2016-05-21 19:06:01 +02:00
Thomas Waldmann 76839362e2 fix packaging/git/coverage for src/ path 2016-05-05 21:42:37 +02:00
Antoine Beaupré 8e36075fe9 commit usage files directly into git
the generation of those files was causing us way too much pain to
justify automatically generating them all the time.

those will have to be re-generated with `build_api` or `build_usage`
as appropriate, for example when function signatures or commandline
flags change.

see #384
2015-11-13 10:42:52 -05:00
TW 03b6b24730 Merge pull request #263 from anarcat/venv-docs
fix virtualenv path in .gitignore
2015-10-08 20:59:35 +02:00
Antoine Beaupré 712170c71a fix virtualenv path in .gitignore
the current instructions create a dirty tree that `git add .` would
commit into git. this is error prone and somewhat unclean.

i found it preferable to change the `.gitignore` than to change the instructions, since there are probably `borg-env` environments lying around everywhere already.
2015-10-07 22:42:33 -04:00
Antoine Beaupré 0b8e835eb8 api.rst is autogenerated, ignore it 2015-10-07 17:59:00 -04:00
Thomas Waldmann 56bf8b79cb add .vagrant to .gitignore 2015-09-15 18:27:16 +02:00
Ronny Pfannschmidt 8b6ca0d912 propperly handle borg._version using setuptools_scm 2015-08-22 15:54:40 +02:00
Thomas Waldmann 8af3aa3397 merged master 2015-08-09 23:51:46 +02:00
Thomas Waldmann 5864bd76eb fix test coverage / codecov.io, use xcode6.4 2015-08-08 18:31:41 +02:00
Thomas Waldmann 27de1b0a43 add a wrapper around liblz4 2015-08-01 15:07:54 +02:00
Thomas Waldmann fde952a6d9 .gitignore: add nuitka and cache stuff 2015-07-26 15:37:36 +02:00
Thomas Waldmann fbaa505191 PR #206 - Merge branch 'fixes' of https://github.com/thomaswaldmann/attic into merge 2015-03-06 23:18:08 +01:00
Thomas Waldmann 0e5ef376bf add .idea/ to gitignore, so PyCharm stuff does not get accidentally added/committed 2015-02-28 02:33:35 +01:00
Radek Podgorny 939e75467e add more .c files to .gitigore 2015-02-04 00:04:22 +01:00